I am a bit new to c++ and I am trying to create a gui application to tell me whether my caps lock is active or not. I have already set up the basic UI and it starts up according to plan (by showing me my lock state via colors) but I cannot manage to change the window color at runtime.
here's my code :
LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
bool state = false;
switch (uMsg) {
case WM_PAINT:
{
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hwnd, &ps);
// All painting occurs here, between BeginPaint and EndPaint.
if ((GetKeyState(VK_CAPITAL) & 0x0001) != 0) {
FillRect(hdc, &ps.rcPaint, CreateSolidBrush(RGB(0, 255, 0)));
}
else {
FillRect(hdc, &ps.rcPaint, CreateSolidBrush(RGB(255, 0, 0)));
}
EndPaint(hwnd, &ps);
}
case WM_KEYUP:
{
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hwnd, &ps);
// All painting occurs here, between BeginPaint and EndPaint.
if ((GetKeyState(VK_CAPITAL) & 0x0001) != 0) {
FillRect(hdc, &ps.rcPaint, CreateSolidBrush(RGB(0, 255, 0)));
}
else {
FillRect(hdc, &ps.rcPaint, CreateSolidBrush(RGB(255, 0, 0)));
}
EndPaint(hwnd, &ps);
}
return 0;
}
return DefWindowProc(hwnd, uMsg, wParam, lParam);
}
and thanks in advance.
Don't call (Begin/End)Paint() in your WM_KEYUP handler, and don't call GetKeyState() in your WM_PAINT handler. Have WM_KEY(DOWN|UP) save the desired color to a variable and then call InvalidateRect() whenver that variable changes value to trigger a repaint of your window. Let WM_PAINT draw the window using the current value of that variable as needed.
Also, your case blocks are missing break statements. And you are leaking the HBRUSH returned by CreateSolidBrush().
Try something more like this:
COLORREF color;
void UpdateColorForCapsLock()
{
if (GetKeyState(VK_CAPITAL) & 0x0001) {
color = RGB(0, 255, 0);
}
else {
color = RGB(255, 0, 0);
}
}
RESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
switch (uMsg) {
case WM_CREATE:
{
UpdateColorForCapsLock();
break;
}
case WM_PAINT:
{
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hwnd, &ps);
HBRUSH hBrush = CreateSolidBrush(color);
FillRect(hdc, &ps.rcPaint, hBrush);
DeleteObject(hBrush);
EndPaint(hwnd, &ps);
return 0;
}
case WM_KEYDOWN:
case WM_KEYUP:
{
if (wParam == VK_CAPITAL)
{
UpdateColorForCapsLock();
InvalidateRect(hwnd, NULL, TRUE);
}
break;
}
}
return DefWindowProc(hwnd, uMsg, wParam, lParam);
}
Capture the WM_KEYDOWN message. If i's a Caps Lock key press (check the parameters), make your window repaint itself by calling InvalidateRect.
Related
I am writing the WinAPI Application, and I tried to write a function, that would automate for me creating Rectangles for text input (with Font, BkMode, TextColor), that then is called by DrawContent function called by WM_PAINT. The text does not appear, after I call ELW::AddTextControl.
LRESULT CALLBACK ELW::WindowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
switch (uMsg)
{
case WM_DESTROY:
PostQuitMessage(0);
break;
case WM_PAINT:
{
ELW::DrawContent(hWnd, hInst, hdc);
}
break;
case WM_CLOSE:
{
DestroyWindow(hWnd);
UnregisterClass(CLASS_NAME, hInst);
}
break;
case WM_SYSCOMMAND:
{
if (wParam == SC_CLOSE);
break;
}
}
return DefWindowProc(hWnd, uMsg, wParam, lParam);
}
// Content drawing
void ELW::DrawContent(HWND hWnd, HINSTANCE hInst, HDC hdc)
{
PAINTSTRUCT ps;
hdc = BeginPaint(hWnd, &ps);
RECT testowyRectangle = { 0, 1 , 20 , 30 };
FillRect(hdc, &ps.rcPaint, (HBRUSH)(COLOR_GRAYTEXT + 1));
ELW::AddTextControl("Testowy test", 80, 80, 100, 100, CUSTOM_COLOR_YELLOW_COLORREF, 10, "Euroscope", hdc);
EndPaint(hWnd, &ps);
};
void ELW::AddTextControl(const char* text, int xPos, int yPos, int xSize, int ySize, COLORREF rgb, int fontSize, const char* fontName, HDC hdc)
{
RECT TextRectangle = { xPos, yPos, ( xPos + xSize ) , ( yPos + ySize ) }; // Left, Top, Right, Bottom
if (rgb == NULL)
rgb = RGB(0, 0, 0);
if (fontSize == NULL)
fontSize = 10;
if (fontName == NULL)
fontName = "Euroscope";
HFONT hFont = CreateFont(fontSize, 8, 0, 0, 600, FALSE, FALSE, FALSE, DEFAULT_CHARSET, OUT_OUTLINE_PRECIS, CLIP_DEFAULT_PRECIS, CLEARTYPE_QUALITY, VARIABLE_PITCH, TEXT(fontName));
SelectObject(hdc, hFont);
SetBkMode(hdc, TRANSPARENT);
SetTextColor(hdc, rgb);
DrawText(hdc, TEXT(text), strlen(text), &TextRectangle, DT_CENTER | DT_NOCLIP);
}
Found an error:
strlen(text)
should be
strlen(TEXT(text))
I'm working on a windows desktop application and I noticed to my dismay that my text was flickering so upon searching I found two methods - return true for erasebkgnd and double buffering. The first method didn't do anything and the second made my background all black for some reason and it still flickers.
Below is where I call paint and the buffering:
case WM_PAINT: {
PAINTSTRUCT ps;
HDC screen = BeginPaint(hWnd, &ps);
putImage(screen, hWnd);
RECT rc;
GetClientRect(hWnd, &rc);
HDC memdc;
auto hbuff = BeginBufferedPaint(screen, &rc, BPBF_COMPATIBLEBITMAP, NULL, &memdc);
EndBufferedPaint(hbuff, TRUE);
EndPaint(hWnd, &ps); } break;
and this next part is the putImage function
void putImage(HDC hdc, HWND hWnd)
{
Graphics graphic(hdc);
graphic.DrawImage(Image::FromFile(filePath), 10, 10);
RedrawWindow(hWnd, NULL, NULL, RDW_INVALIDATE | RDW_UPDATENOW);
}
I was wondering what the problem was and if there is a fix for this.
my background all black
This issue is due to not filling the your background. You can fill your background before draw the image like this:
RECT rc;
GetClientRect(hWnd, &rc);
FillRect(screen, &rc, GetSysColorBrush(COLOR_WINDOW));
flickering
This issue is due to not using BeginBufferedPaint in correct manner. Call BeginBufferedPaint before drawing the image like this:
case WM_PAINT: {
PAINTSTRUCT ps;
HDC screen = BeginPaint(hWnd, &ps);
HPAINTBUFFER hbuff = BeginBufferedPaint(ps.hdc, &ps.rcPaint, BPBF_COMPATIBLEBITMAP, NULL, &screen);
if (hbuff)
{
RECT rc;
GetClientRect(hWnd, &rc);
FillRect(screen, &rc, GetSysColorBrush(COLOR_WINDOW));
putImage(screen, hWnd);
hr = EndBufferedPaint(hbuff, TRUE);
}
EndPaint(hWnd, &ps); } break;
And don't forget to call BufferedPaintInit and BufferedPaintUnInit. For example like this:
case WM_CREATE:
{
hr = BufferedPaintInit();
}
break;
//...
case WM_DESTROY:
{
BufferedPaintUnInit();
PostQuitMessage(0);
}
break;
Update: The complete code.
void putImage(HDC hdc, HWND hWnd)
{
Graphics graphic(hdc);
Image* image = Image::FromFile(L"path-to\\test.png");
Status status = graphic.DrawImage(image, 10, 10);
RedrawWindow(hWnd, NULL, NULL, RDW_INVALIDATE | RDW_UPDATENOW);
}
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
DWORD err;
HRESULT hr;
static HDC memdc;
switch (message)
{
case WM_CREATE:
{
hr = BufferedPaintInit();
}
break;
case WM_COMMAND:
{
int wmId = LOWORD(wParam);
// Parse the menu selections:
switch (wmId)
{
case IDM_ABOUT:
DialogBox(hInst, MAKEINTRESOURCE(IDD_ABOUTBOX), hWnd, About);
break;
case IDM_EXIT:
DestroyWindow(hWnd);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
}
break;
case WM_PAINT: {
PAINTSTRUCT ps;
HDC screen = BeginPaint(hWnd, &ps);
HPAINTBUFFER hbuff = BeginBufferedPaint(ps.hdc, &ps.rcPaint, BPBF_COMPATIBLEBITMAP, NULL, &screen);
if (hbuff)
{
RECT rc;
GetClientRect(hWnd, &rc);
FillRect(screen, &rc, GetSysColorBrush(COLOR_WINDOW));
putImage(screen, hWnd);
hr = EndBufferedPaint(hbuff, TRUE);
}
EndPaint(hWnd, &ps); } break;
case WM_DESTROY:
{
BufferedPaintUnInit();
PostQuitMessage(0);
}
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
return 0;
}
I want to draw a line by clicking and moving cursor from point to point, I've just copied following code any my WindowProcedure looks like that:
LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
//PAINTSTRUCT ps;
HDC hdc;
bool draw = false;
int x = 0, y=0;
//InvalidateRect(hwnd, NULL, true);
switch (uMsg)
{
case WM_LBUTTONDOWN:
draw = true;
x = LOWORD(lParam);
y = HIWORD(lParam);
return 0;
case WM_LBUTTONUP:
if (draw)
{
hdc = GetDC(hwnd);
MoveToEx(hdc, x, y, NULL);
LineTo(hdc, LOWORD(lParam), HIWORD(lParam));
ReleaseDC(hwnd, hdc);
}
draw = FALSE;
return 0;
case WM_MOUSEMOVE:
if (draw)
{
hdc = GetDC(hwnd);
MoveToEx(hdc, x, y, NULL);
LineTo(hdc, x = LOWORD(lParam), y = HIWORD(lParam));
ReleaseDC(hwnd, hdc);
}
return 0;
case WM_DESTROY:
PostQuitMessage(0);
return 0;
}
return DefWindowProc(hwnd, uMsg, wParam, lParam);
}
But when i click nothing happens, it is just like only one case at time can be handled, is it correct? When I comment first two cases it draws lines,so it enters to switch but it is not what i wanted to do. Any advices?
LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
//PAINTSTRUCT ps;
HDC hdc;
static bool draw = false; // ←static
static int x = 0, y=0; // ←
How about this?
So I'm working on an emulator, and got stuck on trying to paint a GDI window. Basically I have this code:
switch(uMsg)
{
case WM_SIZE:
break;
case WM_PAINT:
{
PAINTSTRUCT Ps;
HDC const PaintDC = BeginPaint(hwnd, &Ps);
RECT ClientRect;
GetClientRect(hwnd, &ClientRect);
int const WindowWidth = ClientRect.right - ClientRect.left;
int const WindowHeight = ClientRect.bottom - ClientRect.top;
StretchDIBits(PaintDC,
0, 0,
WindowWidth,
WindowHeight,
0, 0,
DecodedScreenMemory.Info.bmiHeader.biWidth,
DecodedScreenMemory.Info.bmiHeader.biHeight,
DecodedScreenMemory.Memory,
&DecodedScreenMemory.Info,
DIB_RGB_COLORS,
SRCCOPY);
EndPaint(hwnd, &Ps);
break;
}
case WM_CLOSE:
{
Running = false;
Result = 0;
break;
}
default:
{
Result = DefWindowProc(hwnd,
uMsg,
wParam,
lParam);
}
}
In another place of the program I have a piece of code that, for now, fills an 8 pixels wide border with white leaving the rest black. So, from what I understand, I have the window buffer, then I call StretchDIBits with appropriate parameters, then at every frame I call UpdateWindow and it should render the buffer on screen. Why does it turn black instead?
I'm trying to make a window with a glass background, but it's not working. See my code:
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) {
switch (message) {
case WM_ERASEBKGND: {
RECT rect;
GetClientRect(hWnd, &rect);
FillRect(GetDC(hWnd), &rect, (HBRUSH)GetStockObject(BLACK_BRUSH));
return(0);
} case WM_PAINT: {
RECT rect;
GetClientRect(hWnd, &rect);
rect.bottom = 262;
FillRect(GetDC(hWnd), &rect, (HBRUSH)COLOR_WINDOW);
return(0);
} case WM_CREATE: {
if (IsWindowsVistaOrGreater()) {
BOOL IsCompositionEnabled = FALSE;
DwmIsCompositionEnabled(&IsCompositionEnabled);
if (IsCompositionEnabled) {
MARGINS margins = {0, 0, 0, 0};
margins.cyBottomHeight = 100;
HRESULT hr = DwmExtendFrameIntoClientArea(hWnd, &margins);
if (SUCCEEDED(hr)) {
}
}
}
return(0);
} case WM_CLOSE: {
DestroyWindow(hWnd);
return(0);
} case WM_DESTROY: {
PostQuitMessage(0);
return(0);
} default: {
return(DefWindowProc(hWnd, message, wParam, lParam));
}
}
}
This code makes the following window: See the image
This window is what I'm trying to do but there's a problem. When I move the window it blinks. Why does it blink?
There are a few things you should change:
When you handle WM_PAINT, you should call BeginPaint to get the DC and other info for your painting, then call EndPaint when you are done. This gives you a DC that restricts your painting to the clipping region needed, and prevents the flicker.
case WM_PAINT: {
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hWnd, &ps);
RECT rect;
GetClientRect(hWnd, &rect);
rect.bottom = 262;
FillRect(hdc, &rect, (HBRUSH)COLOR_WINDOW);
EndPaint(hWnd, &ps);
return(0);
}
You should also return 1 from WM_ERASEBKGND as you have done the erasing.
The HDC to use when erasing is passed in wParam so use that rather than the window DC.
case WM_ERASEBKGND: {
RECT rect;
GetClientRect(hWnd, &rect);
FillRect((HDC)(wParam), &rect, (HBRUSH)GetStockObject(BLACK_BRUSH));
return(1);
}