Observação
O acesso a essa página exige autorização. Você pode tentar entrar ou alterar diretórios.
O acesso a essa página exige autorização. Você pode tentar alterar os diretórios.
Esta seção discute os tópicos a seguir.
- Criando um cursor
- Usando funções de cursor para criar uma mousetrap
- Criando um cursor combinado alfa
- Obtenção do tamanho do cursor
- Exibindo um cursor
- Confinar um cursor
- Usando o teclado para mover o cursor
Criando um cursor
O exemplo a seguir cria dois identificadores de cursor: um para o cursor de ampulheta padrão e outro para um cursor personalizado incluído como um recurso no arquivo de definição de recurso do aplicativo.
HCURSOR hCurs1 = LoadCursor(NULL, IDC_WAIT);
HCURSOR hCurs2 = LoadCursor(hInstance, MAKEINTRESOURCE(IDC_MYICON));
Os aplicativos devem implementar cursores personalizados como recursos e usar LoadCursor, LoadCursorFromFile ou LoadImage em vez de criar o cursor em tempo de execução. O uso de recursos de cursor evita a dependência do dispositivo, simplifica a localização e permite que os aplicativos compartilhem designs de cursor.
O exemplo a seguir usa a função CreateCursor para criar um cursor monocromático personalizado em tempo de execução. O exemplo está incluído aqui para ilustrar como o sistema interpreta máscaras de cursor.
Cada pixel no mapa do cursor é representado por um único caractere:
| Símbolo | AND de bit | bit de XOR | Exibição |
|---|---|---|---|
|
1 | 0 | Transparent |
X |
1 | 1 | Inverter tela |
o |
0 | 1 | Branco |
+ |
0 | 0 | Preto |
#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);
Para obter mais informações, consulte Bitmaps.
Usando funções de cursor para criar uma mousetrap
O exemplo a seguir usa as funções SetCursorPos, GetCursorPos, CreateCursor, CreateIcon, SetCursor e DrawIconEx para criar um mousetrap simples. Um ícone em forma de yang é desenhado no centro da janela. Se o cursor não tiver sido movido por 3 segundos, ele se encaixará no ícone yang e mudará para a forma de yin. Mover o mouse redefine a interceptação.
// 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);
}
Criar um cursor combinado alfa
Siga estas etapas para criar um cursor ou ícone de mesclagem alfa em tempo de execução:
- Preencha um BITMAPINFOHEADER para um DIB de cima para baixo (
biBitCount=32,biCompression=BI_RGB,biHeightnegativos) e chame CreateDIBSection para obter o bitmap colorido. Reutilize o mesmo cabeçalho combiBitCount=1para criar a máscara AND de 1bpp. - Desenhe a imagem do cursor na seção DIB colorida e defina o byte alfa de cada pixel.
- Preencha a máscara AND: defina cada bit como 0 (opaco) onde
alpha > 0e como 1 (transparente) ondealpha == 0. - Preencha uma estrutura ICONINFO tendo
hbmColorcomo a seção DIB ehbmMaskcomo a máscara AND. - Chame CreateIconIndirect para criar o cursor ou ícone.
O código a seguir demonstra como criar um cursor combinado alfa.
DrawRGBCircles grava três círculos R/G/B semitransparentes sobrepostos diretamente em um buffer de pixels: ele compõe os círculos analiticamente usando a fórmula AC_SRC_OVER e armazena os canais RGB pré-multiplicados por alfa em rgbReserved. O tamanho do cursor é lido de GetSystemMetrics para corresponder à configuração de tamanho do cursor do sistema. Altere fIcon para TRUE para criar um ícone em vez disso.
#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;
}
Antes de fechar, você deve usar a função DestroyCursor para destruir todos os cursores criados com CreateCursor ou CreateIconIndirect. Não é necessário destruir cursores criados por outras funções.
Obtenção do tamanho do cursor
Este exemplo recupera as dimensões de um cursor ou ícone a partir do identificador:
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;
}
Exibindo um cursor
O sistema exibe automaticamente o cursor de classe (o cursor associado à janela à qual o cursor está apontando). Você pode atribuir um cursor de classe ao registrar uma classe de janela. O exemplo a seguir ilustra isso atribuindo um identificador de cursor ao membro hCursor da estrutura WNDCLASS identificada pelo parâmetro wc .
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);
Quando a classe de janela é registrada, o cursor identificado no IDC_MYICON arquivo de definição de recurso do aplicativo é o cursor padrão para todas as janelas com base na classe.
Seu aplicativo pode alterar o design do cursor usando a função SetCursor e especificando um identificador de cursor diferente. No entanto, quando o cursor é movido, o sistema redesenha o cursor da classe no novo local. Para impedir que o cursor de classe seja redesenhado, você deve processar a mensagem de WM_SETCURSOR . Sempre que o cursor se move e a entrada do mouse não é capturada, o sistema envia essa mensagem para a janela na qual o cursor está se movendo.
Você pode especificar cursores diferentes para diferentes condições durante o processamento WM_SETCURSOR. Verifique LOWORD(lParam) para diferenciar a área do cliente das áreas não cliente, como os identificadores de redimensionamento; passe acessos não cliente para DefWindowProc para que o sistema defina ali o cursor de sistema apropriado.
case WM_SETCURSOR:
if (LOWORD(lParam) == HTCLIENT)
{
SetCursor(hCurs3);
return TRUE;
}
return DefWindowProc(hWnd, uMsg, wParam, lParam);
Retornar TRUE impede que DefWindowProc redefina o cursor para o cursor da classe. Encaminhar eventos da área não cliente para DefWindowProc preserva os cursores do sistema de redimensionamento e movimentação.
Você pode substituir um cursor de classe usando a função SetClassLongPtr . Essa função altera as configurações de janela padrão para todas as janelas de uma classe especificada. O exemplo a seguir substitui o cursor de classe existente pelo hCurs2 cursor.
SetClassLongPtr(hWnd, GCLP_HCURSOR, (LONG_PTR)hCurs2);
Para obter mais informações, consulte Classes de Janela e Entrada do Mouse.
Confinar um cursor
O exemplo a seguir limita o cursor à janela do aplicativo e restaura o cursor para sua janela anterior. O exemplo usa a função GetClipCursor para registrar a área em que o cursor pode se mover e a função ClipCursor para limitar e restaurar o cursor.
RECT rcOldClip;
GetClipCursor(&rcOldClip);
RECT rcClip;
GetWindowRect(hWnd, &rcClip);
ClipCursor(&rcClip);
// ... process input from the confined cursor ...
ClipCursor(&rcOldClip);
Como há apenas um cursor por vez disponível no sistema, um aplicativo que limita o cursor deve restaurar o cursor antes de abrir mão do controle para outra janela.
Usando o teclado para mover o cursor
Como o sistema não requer um mouse, um aplicativo deve ser capaz de simular ações do mouse com o teclado. O exemplo a seguir mostra como fazer isso usando as funções GetCursorPos e SetCursorPos e processando a entrada das teclas de direção.
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;
}