Hinweis
Für den Zugriff auf diese Seite ist eine Autorisierung erforderlich. Sie können versuchen, sich anzumelden oder das Verzeichnis zu wechseln.
Für den Zugriff auf diese Seite ist eine Autorisierung erforderlich. Sie können versuchen, das Verzeichnis zu wechseln.
In diesem Abschnitt werden die folgenden Themen erläutert.
- Erstellen eines Cursors
- Verwenden von Cursorfunktionen zum Erstellen eines Maustrap
- Erstellen eines Alphablending-Cursors
- Ermitteln einer Cursorgröße
- Anzeigen eines Cursors
- Einschränken eines Cursors
- Verwenden der Tastatur zum Verschieben des Cursors
Erstellen eines Cursors
Im folgenden Beispiel werden zwei Cursorhandles erstellt: eine für den Standard-Sanduhrcursor und einen für einen benutzerdefinierten Cursor, der als Ressource in der Ressourcendefinitionsdatei der Anwendung enthalten ist.
HCURSOR hCurs1 = LoadCursor(NULL, IDC_WAIT);
HCURSOR hCurs2 = LoadCursor(hInstance, MAKEINTRESOURCE(IDC_MYICON));
Anwendungen sollten benutzerdefinierte Cursor als Ressourcen implementieren und LoadCursor, LoadCursorFromFile oder LoadImage verwenden, anstatt den Cursor zur Laufzeit zu erstellen. Die Verwendung von Cursorressourcen vermeidet Geräteabhängigkeit, vereinfacht die Lokalisierung und ermöglicht Es Anwendungen, Cursordesigns gemeinsam zu nutzen.
Im folgenden Beispiel wird die CreateCursor-Funktion verwendet, um zur Laufzeit einen benutzerdefinierten Monochromcursor zu erstellen. Das Beispiel wird hier bereitgestellt, um zu veranschaulichen, wie das System Cursormasken interpretiert.
Jedes Pixel in der Cursorzuordnung wird durch ein einzelnes Zeichen dargestellt:
| Symbol | AND-Bit | XOR-Bit | Bildschirm |
|---|---|---|---|
|
1 | 0 | Transparent |
X |
1 | 1 | Bildschirm umkehren |
o |
0 | 1 | Weiß |
+ |
0 | 0 | Schwarz |
#define CURSOR_SIZE 32
// Symbol encoding: ' '=transparent 'o'=white '+'=black
static const char *const yin_cursor[CURSOR_SIZE] = {
" ++++ ",
" ++++oooo+ ",
" ++oooooo++ ",
" +ooooooo+ ",
" +oooooooo+ ",
" +oooooooo+ ",
" +oooooooo+ ",
" +oooooooo+ ",
" +ooooooooo+ ",
" +oooooooo+ ",
" +ooooooooo+ ",
" +ooooooooo+ ",
" +oooooooooo+ ",
"+ooooooooooo+ ",
"+oooooooooooo+ ",
"+ooooooooooooo++ ",
"+ooooooooooooooo+ ",
"+oooooooooooooooo++ ",
"+oooooooooooooooooo+ ",
" +ooooooooooooooooo+ ",
" +oooooooo+++ooooooo+ ",
" +ooooooo+++++oooooo+ ",
" +oooooo+++++oooooo+ ",
" +oooooo+++++ooooo+ ",
" +oooooo+++oooooo+ ",
" +ooooooooooooo+ ",
" +ooooooooooooo+ ",
" ++oooooooooo+ ",
" +oooooooo+ ",
" ++oooo++ ",
" ++++ ",
" ",
};
// Pack an XPM-style cursor map into separate 1bpp AND and XOR bit planes.
// Output buffers must be sized: ((w + 15) / 16) * 2 * h bytes.
static void PackCursorMasks(const char *const rows[], int w, int h,
BYTE *pbAnd, BYTE *pbXor)
{
int stride = ((w + 15) / 16) * 2; // WORD-aligned row stride, per CreateCursor contract
ZeroMemory(pbAnd, stride * h);
ZeroMemory(pbXor, stride * h);
for (int y = 0; y < h; y++) {
for (int x = 0; x < w; x++) {
BYTE bit = (BYTE)(0x80 >> (x % 8));
int idx = y * stride + x / 8;
char sym = rows[y][x];
if (sym == ' ' || sym == 'X') pbAnd[idx] |= bit;
if (sym == 'o' || sym == 'X') pbXor[idx] |= bit;
}
}
}
// Row stride for a 32-pixel-wide 1bpp mask: ((32 + 15) / 16) * 2 = 4 bytes.
BYTE abAnd[CURSOR_SIZE * 4];
BYTE abXor[CURSOR_SIZE * 4];
PackCursorMasks(yin_cursor, CURSOR_SIZE, CURSOR_SIZE, abAnd, abXor);
// hInstance is the application's HINSTANCE from WinMain.
// Call DestroyCursor when the cursor is no longer needed.
HCURSOR hCurs3 = CreateCursor(
hInstance, // application instance
19, // hot spot x
2, // hot spot y
CURSOR_SIZE,
CURSOR_SIZE,
abAnd,
abXor);
Weitere Informationen finden Sie unter Bitmaps.
Verwenden von Cursorfunktionen zum Erstellen eines Maustrap
Im folgenden Beispiel werden die SetCursorPos-, GetCursorPos-, CreateCursor-, CreateIcon-, SetCursor- und DrawIconEx-Funktionen verwendet, um eine einfache Mausefalle zu erstellen. Ein yangförmiges Symbol wird in der Mitte des Fensters gezeichnet. Wenn sich der Cursor 3 Sekunden lang nicht bewegt hat, springt er zum Yang-Symbol und nimmt die Yin-Form an. Das Bewegen der Maus setzt die Falle zurück.
// PackCursorMasks, yin_cursor, and CURSOR_SIZE are defined in the preceding example.
#define ICON_SIZE 32
#define YIN_HOT_X 19
#define YIN_HOT_Y 2
#define IDT_CURSOR 1
#define TRAP_DELAY 3000 // ms of inactivity before the cursor snaps to the icon
// Symbol encoding: ' '=transparent 'o'=white '+'=black
static const char *const yang_icon[ICON_SIZE] = {
" ",
" ++++ ",
" ++++++++ ",
" ++++++++++ ",
" +++++++++++++ ",
" +++++++++++++++ ",
" +++++++++++++++ ",
" ++++++ooo++++++++ ",
" +++++ooooo++++++++ ",
" ++++++ooooo++++++++ ",
" ++++++ooooo+++++++++ ",
" +++++++ooo++++++++++ ",
" +++++++++++++++++++ ",
" ++++++++++++++++++++",
" +++++++++++++++++++",
" ++++++++++++++++++",
" ++++++++++++++++",
" +++++++++++++++",
" +++++++++++++",
" ++++++++++++ ",
" +++++++++++ ",
" +++++++++++ ",
" ++++++++++ ",
" +++++++++++ ",
" ++++++++++ ",
" ++++++++++ ",
" ++++++++++ ",
" ++++++++++ ",
" +++++++++ ",
" ++++++++++ ",
" +++++++++ ",
" ++++ ",
};
static HICON hYang;
static HCURSOR hYin;
static POINT ptLast;
static BOOL bTrapped;
LRESULT CALLBACK MainWndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
switch (uMsg)
{
case WM_CREATE:
{
HINSTANCE hInstance = ((LPCREATESTRUCT)lParam)->hInstance;
BYTE abAndIcon[ICON_SIZE * 4];
BYTE abXorIcon[ICON_SIZE * 4];
PackCursorMasks(yang_icon, ICON_SIZE, ICON_SIZE, abAndIcon, abXorIcon);
hYang = CreateIcon(hInstance, ICON_SIZE, ICON_SIZE, 1, 1,
abAndIcon, abXorIcon);
BYTE abAndCursor[CURSOR_SIZE * 4];
BYTE abXorCursor[CURSOR_SIZE * 4];
PackCursorMasks(yin_cursor, CURSOR_SIZE, CURSOR_SIZE, abAndCursor, abXorCursor);
hYin = CreateCursor(hInstance, YIN_HOT_X, YIN_HOT_Y, CURSOR_SIZE, CURSOR_SIZE,
abAndCursor, abXorCursor);
GetCursorPos(&ptLast);
SetTimer(hWnd, IDT_CURSOR, TRAP_DELAY, (TIMERPROC)NULL);
return 0;
}
case WM_PAINT:
{
PAINTSTRUCT ps;
HDC hDC = BeginPaint(hWnd, &ps);
RECT rc;
GetClientRect(hWnd, &rc);
DrawIconEx(hDC,
(rc.right - ICON_SIZE) / 2,
(rc.bottom - ICON_SIZE) / 2,
hYang, ICON_SIZE, ICON_SIZE, 0, NULL, DI_NORMAL);
EndPaint(hWnd, &ps);
return 0;
}
case WM_MOUSEMOVE:
{
POINT ptNow;
GetCursorPos(&ptNow);
if (ptNow.x != ptLast.x || ptNow.y != ptLast.y)
{
ptLast = ptNow;
bTrapped = FALSE;
SetTimer(hWnd, IDT_CURSOR, TRAP_DELAY, (TIMERPROC)NULL);
}
break;
}
case WM_TIMER:
{
POINT ptNow;
GetCursorPos(&ptNow);
if (ptNow.x == ptLast.x && ptNow.y == ptLast.y)
{
RECT rc;
GetClientRect(hWnd, &rc);
POINT ptTarget = {
(rc.right - ICON_SIZE) / 2 + YIN_HOT_X,
(rc.bottom - ICON_SIZE) / 2 + YIN_HOT_Y
};
ClientToScreen(hWnd, &ptTarget);
SetCursorPos(ptTarget.x, ptTarget.y);
// Ignore the mouse-move notification generated by this repositioning.
GetCursorPos(&ptLast);
SetCursor(hYin);
bTrapped = TRUE;
KillTimer(hWnd, IDT_CURSOR);
}
else
{
// Movement outside this window does not generate WM_MOUSEMOVE here.
ptLast = ptNow;
}
return 0;
}
case WM_SETCURSOR:
if (bTrapped)
{
SetCursor(hYin);
return TRUE;
}
break;
case WM_DESTROY:
KillTimer(hWnd, IDT_CURSOR);
DestroyCursor(hYin);
DestroyIcon(hYang);
PostQuitMessage(0);
return 0;
}
return DefWindowProc(hWnd, uMsg, wParam, lParam);
}
Erstellen eines Alphablending-Cursors
Führen Sie die folgenden Schritte aus, um zur Laufzeit einen alpha-gemischten Cursor oder ein Symbol zu erstellen:
- Füllen Sie einen BITMAPINFOHEADER für eine Top-down-DIB (
biBitCount=32, , negativ) aus und rufen SiebiHeightauf, um die Farbbitmap abzurufen. Verwenden Sie denselben Header mitbiBitCount=1, um die 1-bpp-AND-Maske zu erstellen. - Zeichnen Sie das Cursorbild in den Farb-DIB-Abschnitt, und legen Sie das Alphabyte jedes Pixels fest.
- Füllen Sie die AND-Maske: Setzen Sie jedes Bit dort auf 0 (undurchsichtig), wo
alpha > 0, und dort auf 1 (transparent), woalpha == 0. - Füllen Sie eine ICONINFO-Struktur mit der DIB-Section als
hbmColorund der AND-Maske alshbmMask. - Rufen Sie CreateIconIndirect auf, um den Cursor oder das Symbol zu erstellen.
Der folgende Code veranschaulicht, wie sie einen alpha-gemischten Cursor erstellen.
DrawRGBCircles schreibt drei überlappende R/G/B semitransparente Kreise direkt in einen Pixelpuffer: Sie zusammengesetzt die Kreise mithilfe der AC_SRC_OVER Formel analytisch und speichert prämultiplizierte RGB-Kanäle mit Alpha in rgbReserved. Die Cursorgröße wird aus GetSystemMetrics gelesen, um der Einstellung der Systemcursorgröße zu entsprechen. Ändern Sie fIcon in TRUE, um stattdessen ein Symbol zu erstellen.
#define WIDTHBYTES(bits) ((DWORD)(((bits) + 31) & ~31) / 8)
// Fill pixels (top-down, cx-cy) with three overlapping R/G/B circles, each alpha=128.
// Composites with AC_SRC_OVER and preserves premultiplied RGB channels.
static void DrawRGBCircles(RGBQUAD *pixels, int cx, int cy)
{
// r = cx/3: each center is r px from its nearest edge, so the circle touches exactly.
const int r = cx / 3;
struct { int x, y; RGBQUAD color; } circles[3] = {
// B G R A
{ cx / 2, r, { 0, 0, 255, 128 } }, // red, top-center
{ r, cy - r, { 0, 255, 0, 128 } }, // green, bottom-left
{ cx - r, cy - r, {255, 0, 0, 128 } }, // blue, bottom-right
};
for (int y = 0; y < cy; y++) {
for (int x = 0; x < cx; x++) {
DWORD R = 0, G = 0, B = 0, A = 0;
for (int i = 0; i < 3; i++) {
int dx = x - circles[i].x, dy = y - circles[i].y;
if (dx*dx + dy*dy > r*r) continue;
DWORD a = circles[i].color.rgbReserved, inv = 255 - a;
// AC_SRC_OVER (premultiplied): dst = src + dst * (1 - src_a/255)
R = circles[i].color.rgbRed * a / 255 + R * inv / 255;
G = circles[i].color.rgbGreen * a / 255 + G * inv / 255;
B = circles[i].color.rgbBlue * a / 255 + B * inv / 255;
A = a + A * inv / 255;
}
RGBQUAD *p = &pixels[y * cx + x];
p->rgbReserved = (BYTE)A;
p->rgbRed = (BYTE)R;
p->rgbGreen = (BYTE)G;
p->rgbBlue = (BYTE)B;
}
}
}
HCURSOR CreateAlphaCursor(void)
{
int cx = GetSystemMetrics(SM_CXCURSOR);
int cy = GetSystemMetrics(SM_CYCURSOR);
// Extra RGBQUAD at the end for the 1bpp colour table (index 1 = white).
BYTE bmiBuffer[sizeof(BITMAPINFO) + sizeof(RGBQUAD)] = {0};
BITMAPINFO *pBmi = (BITMAPINFO *)bmiBuffer;
pBmi->bmiHeader.biSize = sizeof(pBmi->bmiHeader);
pBmi->bmiHeader.biWidth = cx;
pBmi->bmiHeader.biHeight = -cy; // negative = top-down
pBmi->bmiHeader.biPlanes = 1;
pBmi->bmiHeader.biCompression = BI_RGB;
// Create a top-down 32bpp DIB section for the color (XOR) image.
pBmi->bmiHeader.biBitCount = 32;
RGBQUAD *lpBits;
HBITMAP hBitmap = CreateDIBSection(NULL, pBmi, DIB_RGB_COLORS, (void **)&lpBits, NULL, 0);
DrawRGBCircles(lpBits, cx, cy);
// Reuse the same header for the 1bpp AND mask; switch biBitCount and set the colour table.
pBmi->bmiHeader.biBitCount = 1;
pBmi->bmiColors[0].rgbRed = pBmi->bmiColors[0].rgbGreen = pBmi->bmiColors[0].rgbBlue = 0;
pBmi->bmiColors[1].rgbRed = pBmi->bmiColors[1].rgbGreen = pBmi->bmiColors[1].rgbBlue = 255;
BYTE *andBits;
HBITMAP hMaskBitmap = CreateDIBSection(NULL, pBmi, DIB_RGB_COLORS, (void **)&andBits, NULL, 0);
// Set AND mask: bit=1 (transparent) wherever no circle was drawn.
int maskStride = WIDTHBYTES(cx * 1);
for (int y = 0; y < cy; y++) {
for (int x = 0; x < cx; x++) {
if (lpBits[y * cx + x].rgbReserved == 0)
andBits[y * maskStride + x / 8] |= (BYTE)(0x80 >> (x % 8));
}
}
ICONINFO ii = {0};
ii.fIcon = FALSE;
ii.xHotspot = cx / 2;
ii.yHotspot = cy / 2;
ii.hbmMask = hMaskBitmap;
ii.hbmColor = hBitmap;
HCURSOR hCursor = (HCURSOR)CreateIconIndirect(&ii);
DeleteObject(hBitmap);
DeleteObject(hMaskBitmap);
return hCursor;
}
Vor dem Schließen müssen Sie die DestroyCursor-Funktion verwenden, um alle Cursor zu zerstören, die Sie mit CreateCursor oder CreateIconIndirect erstellt haben. Es ist nicht erforderlich, Cursor zu zerstören, die von anderen Funktionen erstellt wurden.
Ermitteln der Cursorgröße
Im folgenden Beispiel werden die Abmessungen eines Cursors oder Symbols aus dem Handle abgerufen:
BOOL GetCursorDimensions(_In_ HCURSOR hcur, _Out_ SIZE *psiz)
{
ICONINFO ii;
BOOL fResult = GetIconInfo(hcur, &ii);
if (fResult) {
BITMAP bm;
fResult = GetObject(ii.hbmMask, sizeof(bm), &bm) == sizeof(bm);
if (fResult) {
psiz->cx = bm.bmWidth;
psiz->cy = ii.hbmColor ? bm.bmHeight : bm.bmHeight / 2;
}
DeleteObject(ii.hbmMask);
if (ii.hbmColor) DeleteObject(ii.hbmColor);
}
return fResult;
}
Anzeigen eines Cursors
Das System zeigt den Klassencursor automatisch an, also den Cursor, der dem Fenster zugeordnet ist, auf das er zeigt. Sie können beim Registrieren einer Fensterklasse einen Klassencursor zuweisen. Das folgende Beispiel veranschaulicht dies durch Zuweisen eines Cursorhandles zum hCursor-Element der WNDCLASS-Struktur , die durch den WC-Parameter identifiziert wird.
WNDCLASS wc = {0};
wc.lpfnWndProc = MainWndProc;
wc.hInstance = hInstance;
wc.hIcon = LoadIcon(NULL, IDI_APPLICATION);
wc.hCursor = LoadCursor(hInstance, MAKEINTRESOURCE(IDC_MYICON));
wc.hbrBackground = GetStockObject(WHITE_BRUSH);
wc.lpszMenuName = TEXT("GenericMenu");
wc.lpszClassName = TEXT("GenericWClass");
return RegisterClass(&wc);
Wenn die Fensterklasse registriert ist, ist der in der Ressourcendefinitionsdatei der Anwendung identifizierte IDC_MYICON Cursor der Standardcursor für alle Fenster, die auf der Klasse basieren.
Ihre Anwendung kann den Entwurf des Cursors mithilfe der SetCursor-Funktion ändern und einen anderen Cursorziehpunkt angeben. Wenn der Cursor jedoch bewegt wird, wird der Klassencursor an der neuen Position neu gezeichnet. Um zu verhindern, dass der Klassencursor neu gezeichnet wird, müssen Sie die WM_SETCURSOR Nachricht verarbeiten. Jedes Mal, wenn der Cursor bewegt wird und die Mauseingabe nicht erfasst wird, sendet das System diese Meldung an das Fenster, in dem sich der Cursor bewegt.
Sie können unterschiedliche Cursor für unterschiedliche Bedingungen angeben, während WM_SETCURSOR verarbeitet wird. Prüfen Sie LOWORD(lParam), um den Clientbereich von Nichtclientbereichen wie z. B. Größenänderungsgriffen zu unterscheiden; übergeben Sie Treffer im Nichtclientbereich an DefWindowProc, damit das System dort den entsprechenden Systemcursor setzt.
case WM_SETCURSOR:
if (LOWORD(lParam) == HTCLIENT)
{
SetCursor(hCurs3);
return TRUE;
}
return DefWindowProc(hWnd, uMsg, wParam, lParam);
Die Rückgabe von TRUE verhindert, dass DefWindowProc den Cursor auf den Klassencursor zurücksetzt. Die Übergabe von Nichtclient-Treffern an DefWindowProc bewahrt die systemeigenen Cursor für Größenänderung und Verschieben.
Sie können einen Klassencursor mithilfe der SetClassLongPtr-Funktion ersetzen. Diese Funktion ändert die Standardfenstereinstellungen für alle Fenster einer angegebenen Klasse. Im folgenden Beispiel wird der vorhandene Klassencursor durch den hCurs2 Cursor ersetzt.
SetClassLongPtr(hWnd, GCLP_HCURSOR, (LONG_PTR)hCurs2);
Weitere Informationen finden Sie unter "Window Classes " und "Mouse Input".
Einschränken eines Cursors
Im folgenden Beispiel wird der Cursor auf das Fenster der Anwendung beschränkt und daraufhin auf das vorherige Fenster zurückgesetzt. Im Beispiel wird die GetClipCursor-Funktion verwendet, um den Bereich aufzuzeichnen, in dem der Cursor verschoben werden kann, und die ClipCursor-Funktion , um den Cursor einzugrenzen und wiederherzustellen.
RECT rcOldClip;
GetClipCursor(&rcOldClip);
RECT rcClip;
GetWindowRect(hWnd, &rcClip);
ClipCursor(&rcClip);
// ... process input from the confined cursor ...
ClipCursor(&rcOldClip);
Da im System jeweils nur ein Cursor verfügbar ist, muss eine Anwendung, die den Cursor beschränkt, den Cursor wiederherstellen, bevor das Steuerelement an ein anderes Fenster übergeben wird.
Verwenden der Tastatur zum Verschieben des Cursors
Da das System keine Maus erfordert, sollte eine Anwendung Mausaktionen mit der Tastatur simulieren können. Das folgende Beispiel zeigt, wie Sie dies mithilfe der Funktionen "GetCursorPos " und "SetCursorPos " erreichen und Eingaben aus den Pfeiltasten verarbeiten.
static int nRepeat = 1;
switch (uMsg)
{
case WM_KEYDOWN:
{
int dx = 0, dy = 0;
switch (wParam)
{
case VK_LEFT: dx = -nRepeat; break;
case VK_RIGHT: dx = +nRepeat; break;
case VK_UP: dy = -nRepeat; break;
case VK_DOWN: dy = +nRepeat; break;
default: return DefWindowProc(hWnd, uMsg, wParam, lParam);
}
POINT pt;
GetCursorPos(&pt);
ScreenToClient(hWnd, &pt);
pt.x += dx;
pt.y += dy;
RECT rc;
GetClientRect(hWnd, &rc);
if (pt.x < rc.left) pt.x = rc.left;
else if (pt.x >= rc.right) pt.x = rc.right - 1;
if (pt.y < rc.top) pt.y = rc.top;
else if (pt.y >= rc.bottom) pt.y = rc.bottom - 1;
ClientToScreen(hWnd, &pt);
SetCursorPos(pt.x, pt.y);
if (nRepeat < 32) nRepeat++;
return 0;
}
case WM_KEYUP:
nRepeat = 1;
return 0;
}