sessão 10 – Programação gráfica orientada a eventos (2ª versão)
Referências bibliográficas:
Capítulos 2-5 e 7-9 do Livro Windows NT 4 Programming
MSDN:
Introduction to Windows Programming in C++
https://msdn.microsoft.com/en-us/library/ff381398(v=vs.85).aspx
Mouse Input
https://msdn.microsoft.com/en-us/library/gg153549(v=vs.85).aspx
Keyboard Input
https://msdn.microsoft.com/en-us/library/gg153546(v=vs.85).aspx
Mouse Movement
https://msdn.microsoft.com/en-us/library/gg153550(v=vs.85).aspx
Dialog Boxes
https://msdn.microsoft.com/en-us/library/windows/desktop/ms632588(v=vs.85).aspx
Menus
https://msdn.microsoft.com/en-us/library/windows/desktop/ms646977(v=vs.85).aspx
Windows Graphics Device Interface (GDI)
https://msdn.microsoft.com/en-us/library/dd145203(v=vs.85).aspx
Tutorial online: Win32 Fundamentals
http://www.functionx.com/win32/Lesson01.htm
Events
http://www.functionx.com/win32/Lesson05.htm
Object-Oriented Win32
http://www.functionx.com/win32/Lesson06.htm
Esta é a segunda revisão destes conteúdos (desenhar completo):
#include <windows.h> #include <tchar.h> #include <windowsx.h> //função que vai ser chamada pelo windows sempre que acontece alguma coisa: eventos.. LRESULT CALLBACK TrataEventos(HWND, UINT, WPARAM, LPARAM); TCHAR szProgName[] = TEXT("Base"); //nome da janela //winMain: main na parte gráfica int WINAPI WinMain(HINSTANCE hInst, HINSTANCE hPrevInst, LPSTR lpCmdLine, int nCmdShow) { HWND hWnd; // hWnd é o handler da janela, gerado mais abaixo por CreateWindow() MSG lpMsg; // MSG é uma estrutura definida no Windows para as mensagens WNDCLASSEX wcApp; // WNDCLASSEX definir as características da classe da janela //1. caracteristicas da janela wcApp.cbSize = sizeof(WNDCLASSEX); // Tamanho da estrutura WNDCLASSEX wcApp.hInstance = hInst;// Instância da janela actualmente exibida wcApp.lpszClassName = szProgName; // Nome da janela (neste caso = nome do programa) wcApp.lpfnWndProc = TrataEventos; //indicar a a função que trata eventos wcApp.style = CS_HREDRAW | CS_VREDRAW; // Estilo da janela: Fazer o redraw se for // modificada horizontal ou verticalmente wcApp.hIcon = LoadIcon(NULL, IDI_INFORMATION); // "NULL" = Icon definido no Windows wcApp.hIconSm = LoadIcon(NULL, IDI_INFORMATION); // "NULL" = Icon definido no Windows wcApp.hCursor = LoadCursor(NULL, IDC_ARROW); // "hCursor" = handler do cursor (rato) // "NULL" = Forma definida no Windows // "IDC_ARROW" Aspecto "seta" wcApp.lpszMenuName = NULL; // (NULL = não tem menu) wcApp.cbClsExtra = 0; // Livre, para uso particular wcApp.cbWndExtra = 0; // Livre, para uso particular wcApp.hbrBackground = CreateSolidBrush(RGB(0,255,0)); //cor de fundo // 2. Registar a classe "wcApp" no Windows if (!RegisterClassEx(&wcApp)) return(0); // 3. Criar a janela hWnd = CreateWindow( szProgName, // Nome da janela (programa) definido acima TEXT("Estudo para o exame"),// Texto que figura na barra do título WS_OVERLAPPEDWINDOW, // Estilo da janela (WS_OVERLAPPED= normal) CW_USEDEFAULT, // Posição x pixels (CW_USEDEFAULT=à direita da última) CW_USEDEFAULT, // Posição y pixels (CW_USEDEFAULT=abaixo da última) 800, // Largura da janela (em pixels) 600, // Altura da janela (em pixels) (HWND)HWND_DESKTOP, // handle da janela pai ou HWND_DESKTOP se a janela for a primeira, criada a partir do "desktop" (HMENU)NULL, // handle do menu da janela (se tiver menu) (HINSTANCE)hInst, 0); // 4. Mostrar a janela ShowWindow(hWnd, nCmdShow); // "hWnd"= handler da janela, devolvido por // "CreateWindow"; "nCmdShow"= modo de exibição (p.e. // normal/modal); é passado como parâmetro de WinMain() UpdateWindow(hWnd); // Refrescar a janela (Windows envia à janela uma mensagem para pintar, mostrar dados, (refrescar)… // 5. Loop de Mensagens while (GetMessage(&lpMsg, NULL, 0, 0)) { TranslateMessage(&lpMsg); // pre processar os eventos DispatchMessage(&lpMsg); // } //GetMessage se recebe zero, fechar a janela, sai do ciclo // 6. Fim do programa return((int)lpMsg.wParam); // Retorna sempre o parâmetro wParam da estrutura lpMsg } int contaCliques =0; typedef struct { int xPos, yPos; TCHAR c; } PosicaoCaracter; LRESULT CALLBACK TrataEventos(HWND hWnd, UINT messg, WPARAM wParam, LPARAM lParam) { // UINT messg: código do evento de carregar numa tecla // valor da tecla que foi carregada é do WPARAM wParam // lParam: posixaoX, posicaoY do rato na janela static TCHAR c = '?'; //ou global //static int posX = 0; //ou static ou global, com o BeginPaint //static int posY = 0; //ou static ou global, com o BeginPaint HDC hdc; //para a escrita em janela!, handle device content RECT rect; //espaço de desenho static TCHAR charValue = '?'; PAINTSTRUCT ps; //array dos histórico das posicções static PosicaoCaracter posicoes[500]; static int totoalPosicoes = 0; int i; switch (messg) { case WM_PAINT: //evento para refresh da janela //operação de escrita REAL na janela é aqui hdc= BeginPaint(hWnd, &ps); //substitui GetDC(hWnd); // a posição da escrita GetClientRect(hWnd, &rect); FillRect(hWnd, &rect, CreateSolidBrush(RGB(0, 255, 0))); //pintar funto controlado SetTextColor(hdc, RGB(0, 123, 0)); //fundo transparente SetBkColor(hdc, TRANSPARENT); for(i=0; i < totoalPosicoes; i++){ rect.left = posicoes[i].xPos; rect.top = posicoes[i].yPos; //escrita do texto DrawText(hdc, &posicoes[i].c, 1, &rect, DT_SINGLELINE | DT_NOCLIP); } EndPaint(hWnd, &ps); //substitui ReleaseDC(hWnd, hdc); break; //case WM_ERASEBKGND: //somos nós a tratar o BKRND // return TRUE; case WM_CHAR: c = (TCHAR)wParam; break; case WM_LBUTTONDOWN: contaCliques++; charValue = contaCliques + '0'; break; case WM_RBUTTONDOWN: if(totoalPosicoes < 500){ posicoes[totoalPosicoes].xPos = GET_X_LPARAM(lParam); //macro GET_X_LPARAM posicoes[totoalPosicoes].yPos = GET_Y_LPARAM(lParam);//macro GET_Y_LPARAM posicoes[totoalPosicoes].c = charValue; totoalPosicoes++; InvalidateRect(hWnd, NULL, FALSE); //janela invalida, pinta de novo } //limpar a janela com o beginpaint //inicio processo desenhar/escrever //hdc = GetDC(hWnd); //fim processo desenhar/escrever //ReleaseDC(hWnd, hdc); break; case WM_CLOSE: //fechar a janela if( MessageBox( hWnd, TEXT("tem a certeza que quer sair?"), TEXT("Confirmação"), MB_YESNO | MB_ICONQUESTION) //combinar flags | == IDYES) { DestroyWindow(hWnd); //fechar a janela } break; case WM_DESTROY: // Destruir a janela e terminar o programa PostQuitMessage(0); // "PostQuitMessage(Exit Status)" break; default: //mt importante, para lidar com o que não foi feito tratamento return(DefWindowProc(hWnd, messg, wParam, lParam)); break; // break tecnicamente desnecessário por causa do return } return(0); }
Esta é a segunda revisão destes conteúdos (desenhar completo e limpo):
#include <windows.h> #include <tchar.h> #include <windowsx.h> LRESULT CALLBACK TrataEventos(HWND, UINT, WPARAM, LPARAM); TCHAR szProgName[] = TEXT("Base"); int WINAPI WinMain(HINSTANCE hInst, HINSTANCE hPrevInst, LPSTR lpCmdLine, int nCmdShow) { HWND hWnd; MSG lpMsg; WNDCLASSEX wcApp; wcApp.cbSize = sizeof(WNDCLASSEX); wcApp.hInstance = hInst; wcApp.lpszClassName = szProgName; wcApp.lpfnWndProc = TrataEventos; wcApp.style = CS_HREDRAW | CS_VREDRAW; wcApp.hIcon = LoadIcon(NULL, IDI_INFORMATION); wcApp.hIconSm = LoadIcon(NULL, IDI_INFORMATION); wcApp.hCursor = LoadCursor(NULL, IDC_ARROW); wcApp.lpszMenuName = NULL; wcApp.cbClsExtra = 0; wcApp.cbWndExtra = 0; wcApp.hbrBackground = CreateSolidBrush(RGB(0,255,0)); if (!RegisterClassEx(&wcApp)) return(0); hWnd = CreateWindow( szProgName, TEXT("Estudo para o exame"), WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, 800, 600, (HWND)HWND_DESKTOP, (HMENU)NULL, (HINSTANCE)hInst, 0); ShowWindow(hWnd, nCmdShow); UpdateWindow(hWnd); while (GetMessage(&lpMsg, NULL, 0, 0)) { TranslateMessage(&lpMsg); DispatchMessage(&lpMsg); } return((int)lpMsg.wParam); } int contaCliques =0; typedef struct { int xPos, yPos; TCHAR c; } PosicaoCaracter; LRESULT CALLBACK TrataEventos(HWND hWnd, UINT messg, WPARAM wParam, LPARAM lParam) { static TCHAR c = '?'; HDC hdc; RECT rect; static TCHAR charValue = '?'; PAINTSTRUCT ps; static PosicaoCaracter posicoes[500]; static int totoalPosicoes = 0; int i; switch (messg) { case WM_PAINT: hdc= BeginPaint(hWnd, &ps); GetClientRect(hWnd, &rect); FillRect(hWnd, &rect, CreateSolidBrush(RGB(0, 255, 0))); SetTextColor(hdc, RGB(0, 123, 0)); SetBkColor(hdc, TRANSPARENT); for(i=0; i < totoalPosicoes; i++){ rect.left = posicoes[i].xPos; rect.top = posicoes[i].yPos; DrawText(hdc, &posicoes[i].c, 1, &rect, DT_SINGLELINE | DT_NOCLIP); } EndPaint(hWnd, &ps); break; case WM_CHAR: c = (TCHAR)wParam; break; case WM_LBUTTONDOWN: contaCliques++; charValue = contaCliques + '0'; break; case WM_RBUTTONDOWN: if(totoalPosicoes < 500){ posicoes[totoalPosicoes].xPos = GET_X_LPARAM(lParam); posicoes[totoalPosicoes].yPos = GET_Y_LPARAM(lParam); posicoes[totoalPosicoes].c = charValue; totoalPosicoes++; InvalidateRect(hWnd, NULL, FALSE); } break; case WM_CLOSE: if( MessageBox( hWnd, TEXT("tem a certeza que quer sair?"), TEXT("Confirmação"), MB_YESNO | MB_ICONQUESTION) == IDYES) { DestroyWindow(hWnd); } break; case WM_DESTROY: PostQuitMessage(0); break; default: return(DefWindowProc(hWnd, messg, wParam, lParam)); break; } return(0); }
Usar uma BMP na janela (segunda versão):
#include <windows.h> #include <tchar.h> #include <windowsx.h> //exercicio 6) LRESULT CALLBACK TrataEventos(HWND, UINT, WPARAM, LPARAM); TCHAR szProgName[] = TEXT("Base"); //lidar com o bitmap, globais: têm que ser acedidas por várias funções, ou uma struct 1 uma variavel global HBITMAP hBMP; //acesso ao bitmap HDC bmpDC; BITMAP bmp; //estrutura do bitmap int xBitmap; int yBitmap; int WINAPI WinMain(HINSTANCE hInst, HINSTANCE hPrevInst, LPSTR lpCmdLine, int nCmdShow) { HWND hWnd; MSG lpMsg; WNDCLASSEX wcApp; wcApp.cbSize = sizeof(WNDCLASSEX); wcApp.hInstance = hInst; wcApp.lpszClassName = szProgName; wcApp.lpfnWndProc = TrataEventos; wcApp.style = CS_HREDRAW | CS_VREDRAW; wcApp.hIcon = LoadIcon(NULL, IDI_INFORMATION); wcApp.hIconSm = LoadIcon(NULL, IDI_INFORMATION); wcApp.hCursor = LoadCursor(NULL, IDC_ARROW); wcApp.lpszMenuName = NULL; wcApp.cbClsExtra = 0; wcApp.cbWndExtra = 0; wcApp.hbrBackground = CreateSolidBrush(RGB(0, 255, 0)); if (!RegisterClassEx(&wcApp)) return(0); hWnd = CreateWindow( szProgName, TEXT("Estudo para o exame"), WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, 800, 600, (HWND)HWND_DESKTOP, (HMENU)NULL, (HINSTANCE)hInst, 0); //lidar com o bitmap hBMP = (HBITMAP)LoadImage(NULL, TEXT("imagem.bmp"), IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE); //preciso da informação da imagem (metadados) GetObject(hBMP, sizeof(bmp), &bmp); //passar a informação para o device content HDC hdc; hdc = GetDC(hWnd); //criar a copia bmpDC = CreateCompatibleDC(hdc); //aplicar o bitmap ao DC SelectObject(bmpDC, hBMP); ReleaseDC(hWnd, hdc); //definir as posições iniciais da imagem: ao centro RECT rect; //largura da janela actual do cliente GetClientRect(hWnd, &rect); //xBitmap = (rect.right / 2) - (bmp.bmWidth / 2); //yBitmap = (rect.bottom / 2) - (bmp.bmHeight / 2); ShowWindow(hWnd, nCmdShow); UpdateWindow(hWnd); while (GetMessage(&lpMsg, NULL, 0, 0)) { TranslateMessage(&lpMsg); DispatchMessage(&lpMsg); } return((int)lpMsg.wParam); } LRESULT CALLBACK TrataEventos(HWND hWnd, UINT messg, WPARAM wParam, LPARAM lParam) { HDC hdc; RECT rect; PAINTSTRUCT ps; switch (messg) { case WM_PAINT: hdc = BeginPaint(hWnd, &ps); GetClientRect(hWnd, &rect); FillRect(hdc, &rect, CreateSolidBrush(RGB(0, 255, 0))); //SetTextColor(hdc, RGB(0, 123, 0)); //SetBkColor(hdc, TRANSPARENT); //desenhar BitBlt(hdc, xBitmap, yBitmap, bmp.bmWidth, bmp.bmHeight, bmpDC, 0, 0, SRCCOPY); EndPaint(hWnd, &ps); break; case WM_SIZE: //lidar com o resize da janela //LOWORD(lParam) -> parte menos significativa, largura xBitmap = (LOWORD(lParam) / 2) - (bmp.bmWidth / 2); //LOWORD(lParam) -> parte mais significativa, altura yBitmap = (HIWORD(lParam) / 2) - (bmp.bmHeight / 2); break; case WM_CLOSE: if ( MessageBox( hWnd, TEXT("tem a certeza que quer sair?"), TEXT("Confirmação"), MB_YESNO | MB_ICONQUESTION) == IDYES) { DestroyWindow(hWnd); } break; case WM_DESTROY: PostQuitMessage(0); break; default: return(DefWindowProc(hWnd, messg, wParam, lParam)); break; } return(0); }
Usar um BMP animado na janela (segunda versão):
#include <windows.h> #include <tchar.h> #include <windowsx.h> //exercicio 6) LRESULT CALLBACK TrataEventos(HWND, UINT, WPARAM, LPARAM); TCHAR szProgName[] = TEXT("Base"); //lidar com o bitmap, globais: têm que ser acedidas por várias funções, ou uma struct 1 uma variavel global HBITMAP hBMP; //acesso ao bitmap HDC bmpDC; BITMAP bmp; //estrutura do bitmap int xBitmap; int yBitmap; //lidar com a animção int limDireito; HWND hWndGlobalJanela; DWORD WINAPI MovimentaImagem(LPVOID lParam) { int direcaoMovimento = 1; // 1: direita, -1: esquerda int saltoPixeis = 2; while (1) { xBitmap = xBitmap + (direcaoMovimento)*saltoPixeis; if (xBitmap <= 0) { xBitmap = 0; direcaoMovimento = 1; } else if (xBitmap >= limDireito) { xBitmap = limDireito; direcaoMovimento = -1; } //nova pintua da janela: avisar o SO InvalidateRect(hWndGlobalJanela, NULL, FALSE); Sleep(10); } } int WINAPI WinMain(HINSTANCE hInst, HINSTANCE hPrevInst, LPSTR lpCmdLine, int nCmdShow) { HWND hWnd; MSG lpMsg; WNDCLASSEX wcApp; wcApp.cbSize = sizeof(WNDCLASSEX); wcApp.hInstance = hInst; wcApp.lpszClassName = szProgName; wcApp.lpfnWndProc = TrataEventos; wcApp.style = CS_HREDRAW | CS_VREDRAW; wcApp.hIcon = LoadIcon(NULL, IDI_INFORMATION); wcApp.hIconSm = LoadIcon(NULL, IDI_INFORMATION); wcApp.hCursor = LoadCursor(NULL, IDC_ARROW); wcApp.lpszMenuName = NULL; wcApp.cbClsExtra = 0; wcApp.cbWndExtra = 0; wcApp.hbrBackground = CreateSolidBrush(RGB(0, 255, 0)); if (!RegisterClassEx(&wcApp)) return(0); hWnd = CreateWindow( szProgName, TEXT("Estudo para o exame"), WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, 800, 600, (HWND)HWND_DESKTOP, (HMENU)NULL, (HINSTANCE)hInst, 0); //lidar com o bitmap hBMP = (HBITMAP)LoadImage(NULL, TEXT("imagem.bmp"), IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE); //preciso da informação da imagem (metadados) GetObject(hBMP, sizeof(bmp), &bmp); //passar a informação para o device content HDC hdc; hdc = GetDC(hWnd); //criar a copia bmpDC = CreateCompatibleDC(hdc); //aplicar o bitmap ao DC SelectObject(bmpDC, hBMP); ReleaseDC(hWnd, hdc); //definir as posições iniciais da imagem: ao centro RECT rect; //largura da janela actual do cliente GetClientRect(hWnd, &rect); //xBitmap = (rect.right / 2) - (bmp.bmWidth / 2); //yBitmap = (rect.bottom / 2) - (bmp.bmHeight / 2); //para o MovimentaImagem limDireito = rect.right - bmp.bmWidth; hWndGlobalJanela = hWnd; CreateThread(NULL, 0, MovimentaImagem, NULL, 0, NULL); ShowWindow(hWnd, nCmdShow); UpdateWindow(hWnd); while (GetMessage(&lpMsg, NULL, 0, 0)) { TranslateMessage(&lpMsg); DispatchMessage(&lpMsg); } return((int)lpMsg.wParam); } LRESULT CALLBACK TrataEventos(HWND hWnd, UINT messg, WPARAM wParam, LPARAM lParam) { HDC hdc; RECT rect; PAINTSTRUCT ps; switch (messg) { case WM_PAINT: hdc = BeginPaint(hWnd, &ps); GetClientRect(hWnd, &rect); FillRect(hdc, &rect, CreateSolidBrush(RGB(0, 255, 0))); //SetTextColor(hdc, RGB(0, 123, 0)); //SetBkColor(hdc, TRANSPARENT); //desenhar BitBlt(hdc, xBitmap, yBitmap, bmp.bmWidth, bmp.bmHeight, bmpDC, 0, 0, SRCCOPY); EndPaint(hWnd, &ps); break; case WM_SIZE: //lidar com o resize da janela //LOWORD(lParam) -> parte menos significativa, largura xBitmap = (LOWORD(lParam) / 2) - (bmp.bmWidth / 2); //LOWORD(lParam) -> parte mais significativa, altura yBitmap = (HIWORD(lParam) / 2) - (bmp.bmHeight / 2); //a ver com a animação, update limDireito = LOWORD(lParam) - bmp.bmWidth; break; case WM_CLOSE: if ( MessageBox( hWnd, TEXT("tem a certeza que quer sair?"), TEXT("Confirmação"), MB_YESNO | MB_ICONQUESTION) == IDYES) { DestroyWindow(hWnd); } break; case WM_DESTROY: PostQuitMessage(0); break; default: return(DefWindowProc(hWnd, messg, wParam, lParam)); break; } return(0); }
Esta é a segunda revisão destes conteúdos (desenhar animado e lidar com a concorrência):
#include <windows.h> #include <tchar.h> #include <windowsx.h> //exercicio 6) LRESULT CALLBACK TrataEventos(HWND, UINT, WPARAM, LPARAM); TCHAR szProgName[] = TEXT("Base"); //lidar com o bitmap, globais: têm que ser acedidas por várias funções, ou uma struct 1 uma variavel global HBITMAP hBMP; //acesso ao bitmap HDC bmpDC; BITMAP bmp; //estrutura do bitmap int xBitmap; int yBitmap; //lidar com a animação int limDireito; HWND hWndGlobalJanela; //concorrencia, problema de sincronização HANDLE hMutex; DWORD WINAPI MovimentaImagem(LPVOID lParam) { int direcaoMovimento = 1; // 1: direita, -1: esquerda int saltoPixeis = 2; while (1) { WaitForSingleObject(hMutex, INFINITE); //problema de sincronização xBitmap = xBitmap + (direcaoMovimento)*saltoPixeis; if (xBitmap <= 0) { xBitmap = 0; direcaoMovimento = 1; } else if (xBitmap >= limDireito) { xBitmap = limDireito; direcaoMovimento = -1; } ReleaseMutex(hMutex); //nova pintua da janela: avisar o SO InvalidateRect(hWndGlobalJanela, NULL, FALSE); Sleep(10); } } int WINAPI WinMain(HINSTANCE hInst, HINSTANCE hPrevInst, LPSTR lpCmdLine, int nCmdShow) { HWND hWnd; MSG lpMsg; WNDCLASSEX wcApp; wcApp.cbSize = sizeof(WNDCLASSEX); wcApp.hInstance = hInst; wcApp.lpszClassName = szProgName; wcApp.lpfnWndProc = TrataEventos; wcApp.style = CS_HREDRAW | CS_VREDRAW; wcApp.hIcon = LoadIcon(NULL, IDI_INFORMATION); wcApp.hIconSm = LoadIcon(NULL, IDI_INFORMATION); wcApp.hCursor = LoadCursor(NULL, IDC_ARROW); wcApp.lpszMenuName = NULL; wcApp.cbClsExtra = 0; wcApp.cbWndExtra = 0; wcApp.hbrBackground = CreateSolidBrush(RGB(0, 255, 0)); if (!RegisterClassEx(&wcApp)) return(0); hWnd = CreateWindow( szProgName, TEXT("Estudo para o exame"), WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, 800, 600, (HWND)HWND_DESKTOP, (HMENU)NULL, (HINSTANCE)hInst, 0); //lidar com o bitmap hBMP = (HBITMAP)LoadImage(NULL, TEXT("imagem.bmp"), IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE); //preciso da informação da imagem (metadados) GetObject(hBMP, sizeof(bmp), &bmp); //passar a informação para o device content HDC hdc; hdc = GetDC(hWnd); //criar a copia bmpDC = CreateCompatibleDC(hdc); //aplicar o bitmap ao DC SelectObject(bmpDC, hBMP); ReleaseDC(hWnd, hdc); //definir as posições iniciais da imagem: ao centro RECT rect; //largura da janela actual do cliente GetClientRect(hWnd, &rect); //xBitmap = (rect.right / 2) - (bmp.bmWidth / 2); //yBitmap = (rect.bottom / 2) - (bmp.bmHeight / 2); //para o MovimentaImagem limDireito = rect.right - bmp.bmWidth; hWndGlobalJanela = hWnd; hMutex = CreateMutex(NULL, FALSE, NULL); CreateThread(NULL, 0, MovimentaImagem, NULL, 0, NULL); ShowWindow(hWnd, nCmdShow); UpdateWindow(hWnd); while (GetMessage(&lpMsg, NULL, 0, 0)) { TranslateMessage(&lpMsg); DispatchMessage(&lpMsg); } return((int)lpMsg.wParam); } LRESULT CALLBACK TrataEventos(HWND hWnd, UINT messg, WPARAM wParam, LPARAM lParam) { HDC hdc; RECT rect; PAINTSTRUCT ps; switch (messg) { case WM_PAINT: hdc = BeginPaint(hWnd, &ps); GetClientRect(hWnd, &rect); FillRect(hdc, &rect, CreateSolidBrush(RGB(0, 255, 0))); //SetTextColor(hdc, RGB(0, 123, 0)); //SetBkColor(hdc, TRANSPARENT); //desenhar WaitForSingleObject(hMutex, INFINITE); //problema de sincronização BitBlt(hdc, xBitmap, yBitmap, bmp.bmWidth, bmp.bmHeight, bmpDC, 0, 0, SRCCOPY); ReleaseMutex(hMutex); EndPaint(hWnd, &ps); break; case WM_SIZE: //lidar com o resize da janela //LOWORD(lParam) -> parte menos significativa, largura WaitForSingleObject(hMutex, INFINITE); //problema de sincronização xBitmap = (LOWORD(lParam) / 2) - (bmp.bmWidth / 2); ReleaseMutex(hMutex); //LOWORD(lParam) -> parte mais significativa, altura yBitmap = (HIWORD(lParam) / 2) - (bmp.bmHeight / 2); //a ver com a animação, update limDireito = LOWORD(lParam) - bmp.bmWidth; break; case WM_CLOSE: if ( MessageBox( hWnd, TEXT("tem a certeza que quer sair?"), TEXT("Confirmação"), MB_YESNO | MB_ICONQUESTION) == IDYES) { DestroyWindow(hWnd); } break; case WM_DESTROY: PostQuitMessage(0); break; default: return(DefWindowProc(hWnd, messg, wParam, lParam)); break; } return(0); }
Esta é a segunda revisão destes conteúdos (desenhar animado e lidar com a concorrência e lidar com a flickering cintilação) com uso do doubleBuffering:
#include <windows.h> #include <tchar.h> #include <windowsx.h> //exercicio 6) LRESULT CALLBACK TrataEventos(HWND, UINT, WPARAM, LPARAM); TCHAR szProgName[] = TEXT("Base"); HBITMAP hBMP; HDC bmpDC; BITMAP bmp; int xBitmap; int yBitmap; int limDireito; HWND hWndGlobalJanela; HANDLE hMutex; //1) lidar com o flickering HDC memDC = NULL; HBITMAP hCopiaBMP; //copiar as caracteristicas DWORD WINAPI MovimentaImagem(LPVOID lParam) { int direcaoMovimento = 1; int saltoPixeis = 2; while (1) { WaitForSingleObject(hMutex, INFINITE); xBitmap = xBitmap + (direcaoMovimento)*saltoPixeis; if (xBitmap <= 0) { xBitmap = 0; direcaoMovimento = 1; } else if (xBitmap >= limDireito) { xBitmap = limDireito; direcaoMovimento = -1; } ReleaseMutex(hMutex); InvalidateRect(hWndGlobalJanela, NULL, FALSE); Sleep(10); } } int WINAPI WinMain(HINSTANCE hInst, HINSTANCE hPrevInst, LPSTR lpCmdLine, int nCmdShow) { HWND hWnd; MSG lpMsg; WNDCLASSEX wcApp; wcApp.cbSize = sizeof(WNDCLASSEX); wcApp.hInstance = hInst; wcApp.lpszClassName = szProgName; wcApp.lpfnWndProc = TrataEventos; wcApp.style = CS_HREDRAW | CS_VREDRAW; wcApp.hIcon = LoadIcon(NULL, IDI_INFORMATION); wcApp.hIconSm = LoadIcon(NULL, IDI_INFORMATION); wcApp.hCursor = LoadCursor(NULL, IDC_ARROW); wcApp.lpszMenuName = NULL; wcApp.cbClsExtra = 0; wcApp.cbWndExtra = 0; wcApp.hbrBackground = CreateSolidBrush(RGB(0, 255, 0)); if (!RegisterClassEx(&wcApp)) return(0); hWnd = CreateWindow( szProgName, TEXT("Estudo para o exame"), WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, 800, 600, (HWND)HWND_DESKTOP, (HMENU)NULL, (HINSTANCE)hInst, 0); hBMP = (HBITMAP)LoadImage(NULL, TEXT("imagem.bmp"), IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE); GetObject(hBMP, sizeof(bmp), &bmp); HDC hdc; hdc = GetDC(hWnd); bmpDC = CreateCompatibleDC(hdc); SelectObject(bmpDC, hBMP); ReleaseDC(hWnd, hdc); RECT rect; GetClientRect(hWnd, &rect); limDireito = rect.right - bmp.bmWidth; hWndGlobalJanela = hWnd; hMutex = CreateMutex(NULL, FALSE, NULL); CreateThread(NULL, 0, MovimentaImagem, NULL, 0, NULL); ShowWindow(hWnd, nCmdShow); UpdateWindow(hWnd); while (GetMessage(&lpMsg, NULL, 0, 0)) { TranslateMessage(&lpMsg); DispatchMessage(&lpMsg); } return((int)lpMsg.wParam); } LRESULT CALLBACK TrataEventos(HWND hWnd, UINT messg, WPARAM wParam, LPARAM lParam) { HDC hdc; RECT rect; PAINTSTRUCT ps; switch (messg) { case WM_PAINT: hdc = BeginPaint(hWnd, &ps); GetClientRect(hWnd, &rect); //2) lidar com o flickering if(memDC == NULL) { //primeiro vez que estou a passar pela memoria //criar a cópia memDC = CreateCompatibleDC(hdc); hCopiaBMP = CreateCompatibleBitmap(hdc, rect.right, rect.bottom); SelectObject(memDC, hCopiaBMP); DeleteObject(hCopiaBMP); } //3) usar a copia em memoria e nao na principal //FillRect(hdc, &rect, CreateSolidBrush(RGB(0, 255, 0))); FillRect(memDC, &rect, CreateSolidBrush(RGB(0, 255, 0))); WaitForSingleObject(hMutex, INFINITE); //BitBlt(hdc, xBitmap, yBitmap, bmp.bmWidth, bmp.bmHeight, bmpDC, 0, 0, SRCCOPY); BitBlt(memDC, xBitmap, yBitmap, bmp.bmWidth, bmp.bmHeight, bmpDC, 0, 0, SRCCOPY); ReleaseMutex(hMutex); //4) escrever a copia em memoria e aplicar na totalidade da janela BitBlt(hdc, 0, 0, rect.right, rect.bottom, memDC, 0, 0, SRCCOPY); EndPaint(hWnd, &ps); break; case WM_SIZE: WaitForSingleObject(hMutex, INFINITE); xBitmap = (LOWORD(lParam) / 2) - (bmp.bmWidth / 2); yBitmap = (HIWORD(lParam) / 2) - (bmp.bmHeight / 2); limDireito = LOWORD(lParam) - bmp.bmWidth; //5) para obrigar a entrar no if no WM_PAINT ReleaseDC(hWnd, memDC); memDC = NULL; ReleaseMutex(hMutex); break; case WM_CLOSE: if ( MessageBox( hWnd, TEXT("tem a certeza que quer sair?"), TEXT("Confirmação"), MB_YESNO | MB_ICONQUESTION) == IDYES) { DestroyWindow(hWnd); } break; case WM_DESTROY: PostQuitMessage(0); break; default: return(DefWindowProc(hWnd, messg, wParam, lParam)); break; } return(0); }
0 thoughts on “sessão 10 – Programação gráfica orientada a eventos (2ª versão)”