I've been making a Window class for a game, and I'm having trouble with the message pump.
I pull events off the Windows-provided message queue and send them to the windows they pertain to. Here's the function that Translates and Dispatches.
From what I remember of Win32 programming, Translating and Dispatching a message calls the specified WindowProc with the message's contents for parameters. So here's the WindowProc I specified...
currWin, currhwnd and winMap are defined as variables local to Window.cpp, at the top...
Anyway, calling distributeSystemMessages() seems to cause an infinite loop.
IMPORTANT NOTE: THE GAME LOOP IS NOT INSIDE THE MESSAGE HANDLING LOOP, AND NEITHER IS THE MESSAGE HANDLING CODE. THE MESSAGE HANDLING LOOP SHOULD EMPTY THE MESSAGE QUEUE ONCE PER FRAME, SENDING EACH MESSAGE TO THE WINDOW IT PERTAINS TO.
Here's Window.h...
#ifndef WINDOW_H_INCLUDED
#define WINDOW_H_INCLUDED
#include "SystemMessage.h"
#include <queue>
#include <windows.h>
using namespace std;
///THIS FUNCTION MUST BE CALLED TO GET MESSAGES INTO WINDOW QUEUES
void distributeSystemMessages();
class Window
{
protected:
HDC hDC;
HWND hWnd;
HINSTANCE hInst;
HGLRC hRC;
public:
queue<SystemMessage> messages;
Window(unsigned int width, unsigned int height, const char* name, unsigned int colorBits = 24, unsigned int depthBits = 24, unsigned int stencilBits = 0);
~Window();
void makeContextCurrent();
void swapBuffers();
unsigned int getHeight();
unsigned int getWidth();
int getMouseX();
int getMouseY();
void setSize(unsigned int width, unsigned int height);
};
#endif // WINDOW_H_INCLUDED
Here's Window.cpp...
#include "Window.h"
#include <map>
#include <cstdio>
#include <GLee.h>
#include <GL/gl.h>
#include <GL/glu.h>
using namespace std;
HWND currhwnd;
Window* currWin;
map<HWND, Window*> winMap = map<HWND, Window*>();
LRESULT CALLBACK MainWndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
if(currhwnd != hwnd)
{
map<HWND, Window*>::iterator i = winMap.find(hwnd);
if(i != winMap.end())
{
currWin = (*i).second;
currhwnd = hwnd;
}
else return DefWindowProc(hwnd, uMsg, wParam, lParam);
}
SystemMessage msg(hwnd, uMsg, wParam, lParam);
currWin->messages.push(msg);
return 0;
}
Window::Window(unsigned int width, unsigned int height, const char* name, unsigned int colorBits, unsigned int depthBits, unsigned int stencilBits)
{
//TODO: ADD TIME FUNCTIONS TO A TIMER CLASS
//QueryPerformanceCounter(&startTime);
//lastTime = startTime;
messages = queue<SystemMessage>();
hInst = GetModuleHandle(NULL);
WNDCLASSEX wincl; /* Data structure for the windowclass */
/* The Window structure */
wincl.hInstance = hInst;
wincl.lpszClassName = "Squirrel Engine Window";
wincl.lpfnWndProc = MainWndProc; /* This function is called by windows */
wincl.style = CS_DBLCLKS; /* Catch double-clicks */
wincl.cbSize = sizeof (WNDCLASSEX);
/* Use default icon and mouse-pointer */
wincl.hIcon = LoadIcon (NULL, IDI_APPLICATION);
wincl.hIconSm = LoadIcon (NULL, IDI_APPLICATION);
wincl.hCursor = LoadCursor (NULL, IDC_ARROW);
wincl.lpszMenuName = NULL; /* No menu */
wincl.cbClsExtra = 0; /* No extra bytes after the window class */
wincl.cbWndExtra = 0; /* structure or the window instance */
/* Use Windows's default colour as the background of the window */
wincl.hbrBackground = (HBRUSH) COLOR_BACKGROUND;
/* Register the window class, and if it fails quit the program */
if (!RegisterClassEx (&wincl))
{
printf("Could not register window class.\n");
return;
}
/* The class is registered, let's create the program*/
hWnd = CreateWindowEx (
0, /* Extended possibilites for variation */
"Squirrel Engine Window",/* Classname */
name, /* Title Text */
WS_OVERLAPPEDWINDOW, /* default window */
CW_USEDEFAULT, /* Windows decides the position */
CW_USEDEFAULT, /* where the window ends after on the screen */
10, /* The programs width */
10, /* and height in pixels */
HWND_DESKTOP, /* The window is a child-window to desktop */
NULL, /* No menu */
hInst, /* Program Instance handler */
NULL /* No Window Creation data */
);
RECT rcWindow;
RECT rcClient;
GetWindowRect(hWnd, &rcWindow);
GetClientRect(hWnd, &rcClient);
POINT ptDiff;
ptDiff.x = (rcWindow.right - rcWindow.left) - rcClient.right;
ptDiff.y = (rcWindow.bottom - rcWindow.top) - rcClient.bottom;
MoveWindow(hWnd,rcWindow.left, rcWindow.top, width + ptDiff.x, height + ptDiff.y, TRUE);
ShowWindow (hWnd, SW_SHOW);
hDC = GetDC( hWnd );
PIXELFORMATDESCRIPTOR pfd;
ZeroMemory( &pfd, sizeof( pfd ) );
pfd.nSize = sizeof( pfd );
pfd.nVersion = 1;
pfd.dwFlags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL |
PFD_DOUBLEBUFFER;
pfd.iPixelType = PFD_TYPE_RGBA;
pfd.cColorBits = colorBits;
pfd.cDepthBits = depthBits;
pfd.cStencilBits = stencilBits;
pfd.iLayerType = PFD_MAIN_PLANE;
int iFormat = ChoosePixelFormat( hDC, &pfd );
SetPixelFormat( hDC, iFormat, &pfd );
hRC = wglCreateContext(hDC);
hDC = hDC;
//makeContextCurrent();
winMap[hWnd] = this;
//TODO: Find out what this function does
wglSwapIntervalEXT(0);
}
Window::~Window()
{
wglDeleteContext(hRC);
ReleaseDC(hWnd, hDC);
winMap.erase(hWnd);
PostQuitMessage(0);
}
unsigned int Window::getWidth()
{
RECT rcClient;
GetClientRect(hWnd, &rcClient);
return rcClient.right;
}
unsigned int Window::getHeight()
{
RECT rcClient;
GetClientRect(hWnd, &rcClient);
return rcClient.bottom;
}
void Window::setSize(unsigned int width, unsigned int height)
{
RECT rcWindow;
RECT rcClient;
GetWindowRect(hWnd, &rcWindow);
GetClientRect(hWnd, &rcClient);
POINT ptDiff;
ptDiff.x = (rcWindow.right - rcWindow.left) - rcClient.right;
ptDiff.y = (rcWindow.bottom - rcWindow.top) - rcClient.bottom;
MoveWindow(hWnd,rcWindow.left, rcWindow.top, width + ptDiff.x, height + ptDiff.y, TRUE);
}
void Window::makeContextCurrent()
{
wglMakeCurrent( hDC, hRC );
}
void Window::swapBuffers()
{
SwapBuffers( hDC );
}
int Window::getMouseX()
{
POINT p;
GetCursorPos(&p);
RECT rcWindow;
RECT rcClient;
GetWindowRect(hWnd, &rcWindow);
GetClientRect(hWnd, &rcClient);
POINT ptDiff;
ptDiff.x = (rcWindow.right - rcWindow.left) - rcClient.right;
ptDiff.y = (rcWindow.bottom - rcWindow.top) - rcClient.bottom;
return p.x - (rcWindow.left + ptDiff.x);
}
int Window::getMouseY()
{
POINT p;
GetCursorPos(&p);
RECT rcWindow;
RECT rcClient;
GetWindowRect(hWnd, &rcWindow);
GetClientRect(hWnd, &rcClient);
POINT ptDiff;
ptDiff.x = (rcWindow.right - rcWindow.left) - rcClient.right;
ptDiff.y = (rcWindow.bottom - rcWindow.top) - rcClient.bottom;
return p.y - (rcWindow.top + ptDiff.y);
}
void distributeSystemMessages()
{
MSG messages;
while(PeekMessage (&messages, NULL, 0, 0, PM_REMOVE))
{
printf("MessageLoop\n");
TranslateMessage(&messages);
DispatchMessage(&messages);
}
}
Typically, you use an object architecture. Windows provides access to space local to each hWnd that is explicitly reserved for your use for basically this exact purpose. You don't need any global variables to make this work.
class Window {
// Can be virtual if necessary
LRESULT WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
// Process the message here
}
static LRESULT WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
if (Window* ptr = reinterpret_cast<Window*>(GetWindowLongPtr(hwnd, GWLP_USERDATA))) {
return ptr->WindowProc(hwnd, uMsg, wParam, lParam);
}
return DefWindowProc(hwnd, uMsg, wParam, lParam);
}
HWND hwnd;
public:
Window() {
hwnd = CreateWindowEx(....);
SetWindowLongPtr(hwnd, GWLP_USERDATA, reinterpret_cast<LONG_PTR>(this));
ShowWindow(...); // update the ptr
}
void ProcessMessages() {
MSG messages;
while(PeekMessage (&messages, hwnd, 0, 0, PM_REMOVE))
{
printf("MessageLoop\n");
TranslateMessage(&messages);
DispatchMessage(&messages);
}
}
};
Related
I'm trying to learn how to subclass a GUI control and 'modify' its hdc.
This is my subclass callback:
mygui.h
#include <commctrl.h> // SetWindowSubclass
#pragma comment(lib, "Comctl32.lib")
#include <windows.h> // GDI includes.
#include <objidl.h>
#include <gdiplus.h>
using namespace Gdiplus;
using namespace DllExports;
#pragma comment (lib,"Gdiplus.lib")
typedef UCHAR GuiControls;
enum GuiControlTypes {
GUI_CONTROL_BUTTON
};
struct GuiControlOptionsType
{
int x;
int y;
int width;
int height;
LPCWSTR text;
GuiControlTypes controltype;
bool ERASEDBKGND = false; // Used on the subclass proc.
HDC dc;
};
class Gui
{
public:
std::map<HWND, GuiControlOptionsType> control_list;
HWND GuihWnd;
HWND A_LasthWnd;
LRESULT Create();
LRESULT AddControl(GuiControls aControlType, GuiControlOptionsType opt);
};
LRESULT CALLBACK ButtonProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, UINT_PTR uIdSubclass, DWORD_PTR dwRefData);
mygui.cpp
/* Window Procedure. */
LRESULT CALLBACK WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
// TODO
return DefWindowProc(hWnd, msg, wParam, lParam);
}
int CALLBACK WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR pCmdLine, int nCmdShow)
{
Gui mygui;
mygui.Create();
}
LRESULT Gui::Create()
{
WNDCLASSEX wc{};
MSG Msg;
HWND hWnd = nullptr;
wc.cbSize = sizeof(WNDCLASSEX); wc.style = 0; wc.lpfnWndProc = WndProc; wc.cbClsExtra = 0; wc.cbWndExtra = 0;
wc.hInstance = 0; wc.hIcon = LoadIcon(NULL, IDI_APPLICATION); wc.hIconSm = LoadIcon(NULL, IDI_APPLICATION);
wc.hCursor = LoadCursor(NULL, IDC_ARROW); wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1); wc.lpszMenuName = NULL;
wc.lpszClassName = L"classname";
if (!RegisterClassEx(&wc))
// TODO
this->GuihWnd = CreateWindowW(
wc.lpszClassName,
L"Title",
WS_EX_COMPOSITED | WS_EX_LAYERED | // Double buffering
WS_OVERLAPPED | WS_CAPTION | WS_MINIMIZEBOX | WS_SYSMENU,
CW_USEDEFAULT, CW_USEDEFAULT, 500, 200,
nullptr, nullptr, nullptr, nullptr);
DWORD err = GetLastError();
if (this->GuihWnd == NULL)
// TODO
Gdiplus::GdiplusStartupInput gdiplusStartupInput;
ULONG_PTR gdiplusToken;
Gdiplus::GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, NULL);
GuiControlOptionsType opt;
opt.x = 10; opt.y = 10; opt.width = 100; opt.height = 100; opt.text = L"test";
this->AddControl(GUI_CONTROL_BUTTON, opt);
SetWindowSubclass(this->A_LasthWnd, ButtonProc, 1, (DWORD_PTR)this);
ShowWindow(this->GuihWnd, SW_SHOW);
UpdateWindow(this->GuihWnd);
while (GetMessage(&Msg, NULL, 0, 0) > 0)
{
TranslateMessage(&Msg);
DispatchMessage(&Msg);
}
return 0; // Msg.wParam;
}
LRESULT Gui::AddControl(GuiControls aControlType, GuiControlOptionsType opt)
{
switch (aControlType)
{
case GUI_CONTROL_BUTTON:
{
HWND hWnd = CreateWindow(
L"BUTTON", // Predefined class; Unicode assumed
opt.text, // Button text
WS_TABSTOP | WS_VISIBLE | WS_CHILD | BS_DEFPUSHBUTTON, // Styles
opt.x, // x position
opt.y, // y position
opt.width, // Button width
opt.height, // Button height
this->GuihWnd, // Parent window
NULL, // No menu.
NULL, //(HINSTANCE)GetWindowLongPtr(hWnd, GWLP_HINSTANCE),
NULL); // Pointer not needed.
opt.controltype = GUI_CONTROL_BUTTON;
this->control_list.emplace(hWnd, opt);
this->A_LasthWnd = hWnd;
}
break;
default:
break;
}
return 1;
}
LRESULT CALLBACK ButtonProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, UINT_PTR uIdSubclass, DWORD_PTR dwRefData)
{
Gui* pThis = (Gui*)dwRefData;
switch (uMsg)
{
case WM_ERASEBKGND:
{
if (pThis->control_list[hWnd].ERASEDBKGND)
return 1;
// Create/save the new button dc.
GpBitmap* pBitmap;
GdipCreateBitmapFromScan0(pThis->control_list[hWnd].width, pThis->control_list[hWnd].height, 0, PixelFormat32bppPARGB, 0, &pBitmap);
GpGraphics* g;
GdipGetImageGraphicsContext(pBitmap, &g);
GdipGraphicsClear(g, 0xFF2400ff);
HBITMAP hbm;
GdipCreateHBITMAPFromBitmap(pBitmap, &hbm, 0);
HDC dc = CreateCompatibleDC(NULL);
SelectObject(dc, hbm);
pThis->control_list[hWnd].dc = dc;
GdipDisposeImage(pBitmap);
GdipDeleteGraphics(g);
DeleteObject(hbm);
pThis->control_list[hWnd].ERASEDBKGND = 1;
}
break;
case WM_LBUTTONDBLCLK:
case WM_LBUTTONDOWN:
{
InvalidateRect(hWnd, 0, 1);
}
break;
case WM_PAINT:
{
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hWnd, &ps);
BLENDFUNCTION bf;
bf.SourceConstantAlpha = 255;
bf.BlendOp = AC_SRC_OVER;
bf.BlendFlags = 0;
bf.AlphaFormat = AC_SRC_ALPHA;
GdiAlphaBlend(hdc, 0, 0, pThis->control_list[hWnd].width, pThis->control_list[hWnd].height,
pThis->control_list[hWnd].dc, 0, 0, pThis->control_list[hWnd].width, pThis->control_list[hWnd].height, bf);
EndPaint(hWnd, &ps);
DeleteObject(hdc);
return TRUE;
}
break;
}
return DefSubclassProc(hWnd, uMsg, wParam, lParam);
}
The problem is... when I click on the button it restores its default hdc, when I minimize/restore it draws with my 'custom' hdc.
I tried adding a call to InvalidateRect() under WM_LBUTTONDOWN, but it resulted in the same thing.
I also tried creating the Gui with 'double buffering' adding the styles WS_EX_COMPOSITED | WS_EX_LAYERED.
What am I missing?
BS_OWNERDRAW should be "her" style, I think.
I'm going to risk being shot down for being an idiot but I've sunk hours into trying to follow this windows tutorial with no luck: Custom Window Frame Using DWM
My main goal is to create a custom window frame that behaves in the same was as the default one (for example, can be maximised by being dragged to the top of the screen, has the normal minimise, maximise, exit buttons) but one that sports a different colour and perhaps some menu items within the frame. I'm fairly comfortable using wxWidgets but I am an absolute beginner (if even a beginner) using the Windows API. Unfortunately I've been led here anyway because it looks like the only way to achieve what I'm after.
Going back to the abovementioned linked tutorial, I've able to extend the frame using DwmExtendFrameIntoClientArea. This yields a result that looks like this on Windows 10 using a grey brush:
My first point of confusion happens when trying to follow the instructions under "Removing the Standard Frame". This is what I get when I try and follow the code example:
This looks similar enough to the example image in the tutorial (below) but all of the window functionality is now gone! I.e. clicking any of the top-right three buttons doesn't nothing and the window cannot be clicked, moved or resized.
I had hoped that this functionality would return once I'd added in the rest of the example code, which I won't reproduce as they are contained in the page I linked under Appendix B and C (titled 'Painting the Caption Title' and 'HitTestNCA Function' respectively).
Not only did the functionality not return, the example code didn't seem to do anything... and I ended up with the window as it was after my previous edit (pictured below - to be clear):
I haven't reproduced by code here because it's exactly the same as the code found in the link except the background colour is changed to grey and I've added a static 'Test widget' to give myself a point of reference for what the coordinates were doing.
If any kind soul could please tell me what I am doing wrong, or whether my goals are even achievable using the methods I've reluctantly chosen, I would greatly appreciate some advice.
Many thanks!
I created a project according to the documentation and did not reproduce this issue, but you can compare it with your project:
#include <windows.h>
#include <stdio.h>
#include <uxtheme.h>
#include <dwmapi.h>
#include <vssym32.h>
#include <windowsx.h>
#pragma comment(lib, "dwmapi.lib")
#pragma comment(lib, "uxtheme.lib")
#define RECTWIDTH(rc) (rc.right - rc.left)
#define RECTHEIGHT(rc) (rc.bottom - rc.top)
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
LRESULT AppWinProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam);
LRESULT CustomCaptionProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam, bool* pfCallDWP);
void PaintCustomCaption(HWND hWnd, HDC hdc);
LRESULT HitTestNCA(HWND hWnd, WPARAM wParam, LPARAM lParam);
HWND createmainwindow()
{
WNDCLASSEXW wcex = { 0 };
wcex.cbSize = sizeof(WNDCLASSEX);
wcex.style = CS_HREDRAW | CS_VREDRAW;
wcex.lpfnWndProc = WndProc;
wcex.cbClsExtra = 0;
wcex.cbWndExtra = 0;
wcex.hInstance = GetModuleHandle(NULL);
wcex.hCursor = LoadCursor(nullptr, IDC_ARROW);
wcex.hbrBackground = (HBRUSH)GetStockObject(GRAY_BRUSH);
wcex.lpszClassName = L"My_Class";
RegisterClassExW(&wcex);
HWND hWnd = CreateWindowW(wcex.lpszClassName, L"Test", WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, nullptr, nullptr, wcex.hInstance, nullptr);
if (!hWnd)
{
return FALSE;
}
HWND staticctrl = CreateWindowW(L"STATIC", L"SETTINGS", SS_LEFT | WS_CHILD,
8, 27, 500, 300, hWnd, NULL, wcex.hInstance, NULL);
if (!staticctrl)
{
return FALSE;
}
ShowWindow(hWnd, SW_NORMAL);
UpdateWindow(hWnd);
ShowWindow(staticctrl, SW_NORMAL);
UpdateWindow(staticctrl);
}
void main()
{
HWND hWnd = createmainwindow();
MSG msg;
// Main message loop:
while (GetMessage(&msg, nullptr, 0, 0))
{
if (!TranslateAccelerator(msg.hwnd, 0, &msg))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
}
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
bool fCallDWP = true;
BOOL fDwmEnabled = FALSE;
LRESULT lRet = 0;
HRESULT hr = S_OK;
// Winproc worker for custom frame issues.
hr = DwmIsCompositionEnabled(&fDwmEnabled);
if (SUCCEEDED(hr))
{
lRet = CustomCaptionProc(hWnd, message, wParam, lParam, &fCallDWP);
}
// Winproc worker for the rest of the application.
if (fCallDWP)
{
lRet = AppWinProc(hWnd, message, wParam, lParam);
}
return lRet;
}
//
// Message handler for the application.
//
LRESULT AppWinProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
int wmId, wmEvent;
PAINTSTRUCT ps;
HDC hdc;
HRESULT hr;
LRESULT result = 0;
switch (message)
{
case WM_CREATE:
{}
break;
case WM_COMMAND:
wmId = LOWORD(wParam);
wmEvent = HIWORD(wParam);
// Parse the menu selections:
switch (wmId)
{
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
break;
case WM_PAINT:
{
hdc = BeginPaint(hWnd, &ps);
PaintCustomCaption(hWnd, hdc);
// Add any drawing code here...
EndPaint(hWnd, &ps);
}
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
return 0;
}
// Message handler for handling the custom caption messages.
//
LRESULT CustomCaptionProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam, bool* pfCallDWP)
{
LRESULT lRet = 0;
HRESULT hr = S_OK;
bool fCallDWP = true; // Pass on to DefWindowProc?
fCallDWP = !DwmDefWindowProc(hWnd, message, wParam, lParam, &lRet);
// Handle window creation.
if (message == WM_CREATE)
{
RECT rcClient;
GetWindowRect(hWnd, &rcClient);
// Inform application of the frame change.
SetWindowPos(hWnd,
NULL,
rcClient.left, rcClient.top,
RECTWIDTH(rcClient), RECTHEIGHT(rcClient),
SWP_FRAMECHANGED);
fCallDWP = true;
lRet = 0;
}
// Handle window activation.
if (message == WM_ACTIVATE)
{
// Extend the frame into the client area.
MARGINS margins;
margins.cxLeftWidth = 8; // 8
margins.cxRightWidth = 8; // 8
margins.cyBottomHeight = 20; // 20
margins.cyTopHeight = 27; // 27
hr = DwmExtendFrameIntoClientArea(hWnd, &margins);
if (!SUCCEEDED(hr))
{
// Handle error.
}
fCallDWP = true;
lRet = 0;
}
if (message == WM_PAINT)
{
HDC hdc;
{
PAINTSTRUCT ps;
hdc = BeginPaint(hWnd, &ps);
PaintCustomCaption(hWnd, hdc);
EndPaint(hWnd, &ps);
}
fCallDWP = true;
lRet = 0;
}
// Handle the non-client size message.
if ((message == WM_NCCALCSIZE) && (wParam == TRUE))
{
// Calculate new NCCALCSIZE_PARAMS based on custom NCA inset.
NCCALCSIZE_PARAMS* pncsp = reinterpret_cast<NCCALCSIZE_PARAMS*>(lParam);
pncsp->rgrc[0].left = pncsp->rgrc[0].left + 0;
pncsp->rgrc[0].top = pncsp->rgrc[0].top + 0;
pncsp->rgrc[0].right = pncsp->rgrc[0].right - 0;
pncsp->rgrc[0].bottom = pncsp->rgrc[0].bottom - 0;
lRet = 0;
// No need to pass the message on to the DefWindowProc.
fCallDWP = false;
}
//Handle hit testing in the NCA if not handled by DwmDefWindowProc.
if ((message == WM_NCHITTEST) && (lRet == 0))
{
lRet = HitTestNCA(hWnd, wParam, lParam);
if (lRet != HTNOWHERE)
{
fCallDWP = false;
}
}
*pfCallDWP = fCallDWP;
return lRet;
}
// Paint the title on the custom frame.
void PaintCustomCaption(HWND hWnd, HDC hdc)
{
RECT rcClient;
GetClientRect(hWnd, &rcClient);
HTHEME hTheme = OpenThemeData(NULL, L"CompositedWindow::Window");
if (hTheme)
{
HDC hdcPaint = CreateCompatibleDC(hdc);
if (hdcPaint)
{
int cx = RECTWIDTH(rcClient);
int cy = RECTHEIGHT(rcClient);
// Define the BITMAPINFO structure used to draw text.
// Note that biHeight is negative. This is done because
// DrawThemeTextEx() needs the bitmap to be in top-to-bottom
// order.
BITMAPINFO dib = { 0 };
dib.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
dib.bmiHeader.biWidth = cx;
dib.bmiHeader.biHeight = -cy;
dib.bmiHeader.biPlanes = 1;
dib.bmiHeader.biBitCount = 32;
dib.bmiHeader.biCompression = BI_RGB;
HBITMAP hbm = CreateDIBSection(hdc, &dib, DIB_RGB_COLORS, NULL, NULL, 0);
if (hbm)
{
HBITMAP hbmOld = (HBITMAP)SelectObject(hdcPaint, hbm);
// Setup the theme drawing options.
DTTOPTS DttOpts = { sizeof(DTTOPTS) };
DttOpts.dwFlags = DTT_COMPOSITED | DTT_GLOWSIZE;
DttOpts.iGlowSize = 15;
// Select a font.
LOGFONT lgFont;
HFONT hFontOld = NULL;
if (SUCCEEDED(GetThemeSysFont(hTheme, TMT_CAPTIONFONT, &lgFont)))
{
HFONT hFont = CreateFontIndirect(&lgFont);
hFontOld = (HFONT)SelectObject(hdcPaint, hFont);
}
// Draw the title.
RECT rcPaint = rcClient;
rcPaint.top += 8;
rcPaint.right -= 125;
rcPaint.left += 8;
rcPaint.bottom = 50;
DrawThemeTextEx(hTheme,
hdcPaint,
0, 0,
L"Test",
-1,
DT_LEFT | DT_WORD_ELLIPSIS,
&rcPaint,
&DttOpts);
// Blit text to the frame.
BitBlt(hdc, 0, 0, cx, cy, hdcPaint, 0, 0, SRCCOPY);
SelectObject(hdcPaint, hbmOld);
if (hFontOld)
{
SelectObject(hdcPaint, hFontOld);
}
DeleteObject(hbm);
}
DeleteDC(hdcPaint);
}
CloseThemeData(hTheme);
}
}
// Hit test the frame for resizing and moving.
LRESULT HitTestNCA(HWND hWnd, WPARAM wParam, LPARAM lParam)
{
// Get the point coordinates for the hit test.
POINT ptMouse = { GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam) };
// Get the window rectangle.
RECT rcWindow;
GetWindowRect(hWnd, &rcWindow);
// Get the frame rectangle, adjusted for the style without a caption.
RECT rcFrame = { 0 };
AdjustWindowRectEx(&rcFrame, WS_OVERLAPPEDWINDOW & ~WS_CAPTION, FALSE, NULL);
// Determine if the hit test is for resizing. Default middle (1,1).
USHORT uRow = 1;
USHORT uCol = 1;
bool fOnResizeBorder = false;
// Determine if the point is at the top or bottom of the window.
if (ptMouse.y >= rcWindow.top && ptMouse.y < rcWindow.top + 27)
{
fOnResizeBorder = (ptMouse.y < (rcWindow.top - rcFrame.top));
uRow = 0;
}
else if (ptMouse.y < rcWindow.bottom && ptMouse.y >= rcWindow.bottom - 20)
{
uRow = 2;
}
// Determine if the point is at the left or right of the window.
if (ptMouse.x >= rcWindow.left && ptMouse.x < rcWindow.left + 8)
{
uCol = 0; // left side
}
else if (ptMouse.x < rcWindow.right && ptMouse.x >= rcWindow.right - 8)
{
uCol = 2; // right side
}
// Hit test (HTTOPLEFT, ... HTBOTTOMRIGHT)
LRESULT hitTests[3][3] =
{
{ HTTOPLEFT, fOnResizeBorder ? HTTOP : HTCAPTION, HTTOPRIGHT },
{ HTLEFT, HTNOWHERE, HTRIGHT },
{ HTBOTTOMLEFT, HTBOTTOM, HTBOTTOMRIGHT },
};
return hitTests[uRow][uCol];
}
Then,
the window cannot be clicked, moved or resized.
We cannot expect that without implementing logic to handle caption button hit testing and frame resizing/moving.
You will get the black of the background color default(BITMAPINFO dib = { 0 };). But the screen shot was still gray. (This may be because the function didn't work (failed?). Or, when you test the code before, you've commented B, C , and then when you add B, C, didn't to uncomment it).
I have simple application with a native winapi window, and a static control on it as a child window. I'd like to draw on the child window with Direct2D.
The drawing happens outside of the WM_PAINT message, so I decided to use ID2D1DCRenderTarget. Everything works except one thing. I can't make the drawing visible when the application starts.
If I do painting in any event (for example in WM_LBUTTONDOWN) the drawing shows up. But if I do painting in WM_CREATE I see nothing on the screen.
Here is my example code:
#include <iostream>
#include <windows.h>
#include <windowsx.h>
#include <d2d1.h>
#pragma comment(lib, "d2d1.lib")
HWND windowHandle = NULL;
HWND childWindowHandle = NULL;
ID2D1Factory* direct2DFactory = nullptr;
ID2D1DCRenderTarget* renderTarget = nullptr;
static void InitDirect2D ()
{
D2D1CreateFactory (D2D1_FACTORY_TYPE_SINGLE_THREADED, &direct2DFactory);
D2D1_RENDER_TARGET_PROPERTIES props = D2D1::RenderTargetProperties(
D2D1_RENDER_TARGET_TYPE_DEFAULT,
D2D1::PixelFormat(
DXGI_FORMAT_B8G8R8A8_UNORM,
D2D1_ALPHA_MODE_IGNORE),
0,
0,
D2D1_RENDER_TARGET_USAGE_NONE,
D2D1_FEATURE_LEVEL_DEFAULT
);
direct2DFactory->CreateDCRenderTarget (&props, &renderTarget);
renderTarget->SetAntialiasMode (D2D1_ANTIALIAS_MODE_ALIASED);
}
static void Paint ()
{
ID2D1SolidColorBrush* blackBrush = nullptr;
renderTarget->CreateSolidColorBrush (
D2D1::ColorF (D2D1::ColorF::Black),
&blackBrush
);
renderTarget->BeginDraw ();
renderTarget->SetTransform (D2D1::Matrix3x2F::Identity ());
renderTarget->Clear (D2D1::ColorF (D2D1::ColorF::LightGray));
D2D1_RECT_F rect = D2D1::RectF (10.0f, 10.0f, 100.0f, 100.0f);
renderTarget->DrawRectangle (&rect, blackBrush);
renderTarget->EndDraw ();
}
static LRESULT CALLBACK ChildWindowProc (HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
switch (msg) {
case WM_CREATE:
{
RECT rc;
GetClientRect (hwnd, &rc);
renderTarget->BindDC (GetDC (hwnd), &rc);
Paint ();
}
break;
case WM_LBUTTONDOWN:
Paint ();
break;
case WM_PAINT:
break;
case WM_SIZE:
{
RECT rc;
GetClientRect (hwnd, &rc);
renderTarget->BindDC (GetDC (hwnd), &rc);
Paint ();
}
break;
case WM_CLOSE:
DestroyWindow (hwnd);
break;
case WM_DESTROY:
PostQuitMessage (0);
break;
break;
}
LRESULT res = DefWindowProc (hwnd, msg, wParam, lParam);
return res;
}
static void CreateChildWindow (HWND parentHandle)
{
WNDCLASSEX windowClass;
memset (&windowClass, 0, sizeof (WNDCLASSEX));
windowClass.cbSize = sizeof(WNDCLASSEX);
windowClass.style = 0;
windowClass.lpfnWndProc = ChildWindowProc;
windowClass.style = CS_DBLCLKS;
windowClass.cbClsExtra = 0;
windowClass.cbWndExtra = 0;
windowClass.hInstance = NULL;
windowClass.hCursor = LoadCursor (NULL, IDC_ARROW);
windowClass.hbrBackground = (HBRUSH) (COLOR_WINDOW + 1);
windowClass.lpszMenuName = NULL;
windowClass.lpszClassName = L"ChildWindowClass";
RegisterClassEx (&windowClass);
childWindowHandle = CreateWindowEx (
0, windowClass.lpszClassName, L"", WS_CHILD,
CW_USEDEFAULT, CW_USEDEFAULT, 0, 0, parentHandle, NULL, NULL, nullptr
);
ShowWindow (childWindowHandle, SW_SHOW);
UpdateWindow (childWindowHandle);
MoveWindow (childWindowHandle, 10, 10, 780, 580, TRUE);
}
static LRESULT CALLBACK MainWindowProc (HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
if (msg == WM_CREATE) {
CreateChildWindow (hwnd);
InvalidateRect (hwnd, NULL, FALSE);
}
switch (msg) {
case WM_SIZE:
{
int newWidth = LOWORD (lParam);
int newHeight = HIWORD (lParam);
MoveWindow (childWindowHandle, 10, 10, newWidth - 20, newHeight - 20, TRUE);
}
break;
case WM_CLOSE:
DestroyWindow (hwnd);
break;
case WM_DESTROY:
PostQuitMessage (0);
break;
}
return DefWindowProc (hwnd, msg, wParam, lParam);
}
static int CreateMainWindow ()
{
WNDCLASSEX windowClass;
memset (&windowClass, 0, sizeof (WNDCLASSEX));
windowClass.cbSize = sizeof (WNDCLASSEX);
windowClass.style = 0;
windowClass.lpfnWndProc = MainWindowProc;
windowClass.style = CS_DBLCLKS;
windowClass.cbClsExtra = 0;
windowClass.cbWndExtra = 0;
windowClass.hInstance = NULL;
windowClass.hIcon = LoadIcon (NULL, IDI_APPLICATION);
windowClass.hCursor = LoadCursor (NULL, IDC_ARROW);
windowClass.hbrBackground = (HBRUSH) (COLOR_WINDOW + 1);
windowClass.lpszMenuName = NULL;
windowClass.lpszClassName = L"WindowClass";
windowClass.hIconSm = LoadIcon (NULL, IDI_APPLICATION);
RegisterClassEx (&windowClass);
RECT requiredRect = { 0, 0, 800, 600 };
HWND windowHandle = CreateWindowEx (
WS_EX_WINDOWEDGE, windowClass.lpszClassName, L"Example", WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, CW_USEDEFAULT, requiredRect.right - requiredRect.left, requiredRect.bottom - requiredRect.top, NULL, NULL, NULL, nullptr
);
if (windowHandle == NULL) {
return 1;
}
ShowWindow (windowHandle, SW_SHOW);
UpdateWindow (windowHandle);
MSG msg;
while (GetMessage (&msg, NULL, 0, 0)) {
TranslateMessage (&msg);
DispatchMessage (&msg);
}
return 0;
}
int main (int argc, char* argv[])
{
InitDirect2D ();
return CreateMainWindow ();
}
Do you have any idea how can I make the drawing visible on application startup?
During WM_CREATE window is not complitely created yet so painting does not work. You should write a proper WM_PAINT (and WM_ERASEBKGND) message handlers and perform all the painting operations there. Then when you need to redraw as a response to any other message (such as WM_LBUTTONDOWN) you need to invalidate window and trigger WM_PAINT instead of immediately calling Paint();.
I have a problem with create drawing rectangles functions in C++. I have prepared c++ file, when I must finished drawing functions.
I need to add functionality where on mouse click it will creating rectangle, and when I finished moving mouse and released it should be rectangle. next it should be created again and changes position.
I have completely no idea how to do it. Any ideas ? Code looks like this:
MsgHandlers.cpp (here all things happen):
#include "MsgHandlers.h"
#include "Utils.h"
#include <time.h>
#include <math.h>
int ClientWidth, ClientHeight;
BOOL EraseBkgnd = TRUE;
bool IgnoreTimer = false;
RECT Rect;
void OnCreate(HWND hwnd)
{
SetTimer(hwnd, 1, 25, NULL);
}
void OnSize(HWND hwnd, int width, int height, WPARAM wParam)
{
ClientWidth = width;
ClientHeight = height;
Rect.left = width/4;
Rect.right=Rect.left + width/2;
Rect.top = height/4;
Rect.bottom= Rect.top + height/2;
}
void OnTimer(HWND hwnd, WPARAM timerID)
{
if(IgnoreTimer) return;
Rect.left+=RandRange(-10, 10);
Rect.right +=RandRange(-10,10);
Rect.top +=RandRange(-10,10);
Rect.bottom +=RandRange(-10,10);
InvalidateRect(hwnd, NULL, EraseBkgnd);
}
void OnPaint(HWND hwnd, HDC hdc)
{
Rectangle(hdc,Rect.left,Rect.top,Rect.right,Rect.bottom);
}
void OnKeyDown(HWND hwnd, WPARAM keyCode)
{
switch (keyCode)
{
case VK_LEFT:
break;
case VK_UP:
break;
case VK_RIGHT:
break;
case VK_DOWN:
break;
case 0x43: // C
break;
case 0x45: // E
EraseBkgnd ^= 0x00000001;
break;
case 0x52: // R
break;
case 0x49: // I
IgnoreTimer = !IgnoreTimer;
case 0x53: // S
break;
}
//InvalidateRect(hwnd, NULL, EraseBkgnd);
}
void OnMouseMove(HWND hwnd, int x, int y, WPARAM wParam)
{
}
void OnLButtonDown(HWND hwnd, int x, int y, WPARAM wParam)
{
}
void OnLButtonUp(HWND hwnd, int x, int y, WPARAM wParam)
{
}
void OnDestroy(HWND hwnd)
{
KillTimer(hwnd, 1);
}
I put also others files from this C++ project:
Main:
#include <windows.h>
#include "MsgHandlers.h"
/* Declare Windows procedure */
LRESULT CALLBACK WindowProcedure (HWND, UINT, WPARAM, LPARAM);
/* Make the class name into a global variable */
char szClassName[ ] = "CodeBlocksWindowsApp";
int WINAPI WinMain (HINSTANCE hThisInstance, HINSTANCE hPrevInstance, LPSTR lpszArgument, int nCmdShow)
{
HWND hwnd; /* This is the handle for our window */
MSG message; /* Here messages to the application are saved */
WNDCLASSEX wincl; /* Data structure for the windowclass */
/* The Window structure */
wincl.hInstance = hThisInstance;
wincl.lpszClassName = szClassName;
wincl.lpfnWndProc = WindowProcedure; /* This function is called by windows */
wincl.style = CS_DBLCLKS; /* Catch double-clicks */
wincl.cbSize = sizeof (WNDCLASSEX);
/* Use default icon and mouse-pointer */
wincl.hIcon = LoadIcon (NULL, IDI_APPLICATION);
wincl.hIconSm = LoadIcon (NULL, IDI_APPLICATION);
wincl.hCursor = LoadCursor (NULL, IDC_ARROW);
wincl.lpszMenuName = NULL; /* No menu */
wincl.cbClsExtra = 0; /* No extra bytes after the window class */
wincl.cbWndExtra = 0; /* structure or the window instance */
/* Use Windows's default colour as the background of the window */
wincl.hbrBackground = (HBRUSH) (COLOR_WINDOW + 1);
/* Register the window class, and if it fails quit the program */
if (!RegisterClassEx (&wincl))
return 0;
/* The class is registered, let's create the program*/
hwnd = CreateWindowEx (
0, /* Extended possibilites for variation */
szClassName, /* Classname */
"Progr.", /* Title Text */
WS_OVERLAPPEDWINDOW, /* default window */
CW_USEDEFAULT, /* Windows decides the position */
CW_USEDEFAULT, /* where the window ends up on the screen */
1024, /* The programs width */
768, /* and height in pixels */
HWND_DESKTOP, /* The window is a child-window to desktop */
NULL, /* No menu */
hThisInstance, /* Program Instance handler */
NULL /* No Window Creation data */
);
/* Make the window visible on the screen */
ShowWindow (hwnd, nCmdShow);
/* Run the message loop. It will run until GetMessage() returns 0 */
while (GetMessage(&message, NULL, 0, 0))
{
/* Translate virtual-key messages into character messages */
TranslateMessage(&message);
/* Send message to WindowProcedure */
DispatchMessage(&message);
}
/* The program return-value is 0 - The value that PostQuitMessage() gave */
return message.wParam;
}
/* This function is called by the Windows function DispatchMessage() */
LRESULT CALLBACK WindowProcedure (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
HDC hdc;
PAINTSTRUCT ps;
switch (message) /* handle the messages */
{
case WM_CREATE:
OnCreate(hwnd);
break;
case WM_SIZE:
OnSize(hwnd, short(lParam & 0x0000FFFF), short(lParam >> 16), wParam);
break;
case WM_TIMER:
OnTimer(hwnd, wParam);
break;
case WM_KEYDOWN:
OnKeyDown(hwnd, wParam);
break;
case WM_LBUTTONDOWN:
OnLButtonDown(hwnd, short(lParam & 0x0000FFFF), short(lParam >> 16), wParam);
break;
case WM_MOUSEMOVE:
OnMouseMove(hwnd, short(lParam & 0x0000FFFF), short(lParam >> 16), wParam);
break;
case WM_LBUTTONUP:
OnLButtonUp(hwnd, short(lParam & 0x0000FFFF), short(lParam >> 16), wParam);
break;
case WM_PAINT:
hdc = BeginPaint(hwnd, &ps);
OnPaint(hwnd, hdc);
EndPaint(hwnd, &ps);
break;
case WM_DESTROY:
OnDestroy(hwnd);
PostQuitMessage (0); /* send a WM_QUIT to the message queue */
break;
default: /* for messages that we don't deal with */
return DefWindowProc (hwnd, message, wParam, lParam);
}
return 0;
}
Utils.cpp:
#include "Utils.h"
#include <math.h>
//random <min, max>
int RandRange(int min, int max)
{
return min + rand() % (1 + max - min);
}
//Return -1 for negative argument;
//return +1 for positive argument or 0
int Sign(int arg)
{
return arg < 0 ? -1 : 1;
}
double Deg2Rad(double aDegrees)
{
return aDegrees * PI / 180.0;
}
double Rad2Deg(double aRadians)
{
return aRadians * 180 / PI;
}
float DirectionFromSpeedXY(int aSpeedX, int aSpeedY)
{
if(aSpeedX==0 && aSpeedY==0)
return 0;
else
return atan2(aSpeedX, -aSpeedY);
}
float SpeedFromSpeedXY(int aSpeedX, int aSpeedY)
{
return sqrt(aSpeedX*aSpeedX + aSpeedY*aSpeedY);// * Sign(aSpeedX*aSpeedY);
}
void SpeedXYFromSpeedDirection(float aSpeed, float aDirection, int *aSpeedX, int *aSpeedY)
{
aSpeed = abs(aSpeed);
*aSpeedX = aSpeed * sin(aDirection);
*aSpeedY = -aSpeed * cos(aDirection);
}
void CorrectRect(RECT *aRect)
{
int temp;
if(aRect->right < aRect->left)
{
temp = aRect->right;
aRect->right = aRect->left;
aRect->left = temp;
}
if(aRect->bottom < aRect->top)
{
temp = aRect->bottom;
aRect->bottom = aRect->top;
aRect->top = temp;
}
}
As mentioned, the code you provided is not standard.
To accomplish the task you described there are some steps that must be handled.
How to draw a (simple) rectangle:
// x and y are the x- and y-locations of the mouse cursor upon release
void drawRectangle(HWND hwnd, const int x, const int y)
{
// obtain a handle to the device context
HDC hdc = GetDC(hwnd);
// RECT_WIDTH and RECT_HEIGHT are elsewhere defined
// draw rectangle
Rectangle(hdc, x - RECT_WIDTH / 2, y - RECT_HEIGHT / 2, x + RECT_WIDTH / 2, y + RECT_HEIGHT / 2);
// release the DC
ReleaseDC(hwnd, hdc);
}
To capture the mouse button release you process the WM_LBUTTONUP message.
In the window's window procedure:
LRESULT __stdcall wndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
static int x;
static int y;
HDC hdc;
PAINTSTRUCT ps;
case WM_LBUTTONUP:
{
x = LOWORD(lParam); // grab the x-position
y = HIWORD(lParam); // grab the y-position
// invalidate the entire client area
// this will cause the window's client area to be "cleared"
// using the window class background brush upon BeginPaint call
// in WM_PAINT
InvalidateRect(hwnd, NULL, true);
return 0;
}
case WM_PAINT:
{
hdc = BeginPaint(hwnd, &ps);
// draw the rectangle
drawRectangle(hwnd, x, y);
EndPaint(hwnd, &ps);
return 0;
}
// other case handlers like WM_DESTROY
}
Obviously, there are other things to take into consideration, but this should get you started.
i am learning C++ but i am having problems with putting an image on the screen.
i have searched the internet for help but i couldn't find any.
i am trying to create a window and put a simple color in the client area when the WM_Paint message is called, but the program just displays a grey screen as usual. i am using code::blocks 10.05.
#include <windows.h>
#include <iostream>
using namespace std;
int winx = 500;
int winy = 500;
int winbpp = 24;
static char m_bibuf[ sizeof(BITMAPINFOHEADER) + 12 ];
static BITMAPINFO &m_bi = *(BITMAPINFO*)&m_bibuf;
static BITMAPINFOHEADER &m_bih = m_bi.bmiHeader;
int* buffer = new int[winx*winy*winbpp];
int setbuffer()
{
for(int x = 0; x < winx; x++)
{
for(int y=0; y < winy; y++)
{
for(int z =0; z < winbpp; z++)
{
buffer[x*y*z] = 1;
}
}
}
return 0;
}
/* Declare Windows procedure */
LRESULT CALLBACK WindowProcedure (HWND, UINT, WPARAM, LPARAM);
/* Make the class name into a global variable */
char szClassName[ ] = "CodeBlocksWindowsApp";
int WINAPI WinMain (HINSTANCE hThisInstance,
HINSTANCE hPrevInstance,
LPSTR lpszArgument,
int nCmdShow)
{
HWND hwnd; /* This is the handle for our window */
MSG messages; /* Here messages to the application are saved */
WNDCLASSEX wincl; /* Data structure for the windowclass */
/* The Window structure */
wincl.hInstance = hThisInstance;
wincl.lpszClassName = szClassName;
wincl.lpfnWndProc = WindowProcedure; /* This function is called by windows */
wincl.style = CS_DBLCLKS; /* Catch double-clicks */
wincl.cbSize = sizeof (WNDCLASSEX);
/* Use default icon and mouse-pointer */
wincl.hIcon = LoadIcon (NULL, IDI_APPLICATION);
wincl.hIconSm = LoadIcon (NULL, IDI_APPLICATION);
wincl.hCursor = LoadCursor (NULL, IDC_ARROW);
wincl.lpszMenuName = NULL; /* No menu */
wincl.cbClsExtra = 0; /* No extra bytes after the window class */
wincl.cbWndExtra = 0; /* structure or the window instance */
/* Use Windows's default colour as the background of the window */
wincl.hbrBackground = (HBRUSH) COLOR_BACKGROUND;
/* Register the window class, and if it fails quit the program */
if (!RegisterClassEx (&wincl))
return 0;
/* The class is registered, let's create the program*/
hwnd = CreateWindowEx (
0, /* Extended possibilites for variation */
szClassName, /* Classname */
" project 1 ", /* Title Text */
WS_OVERLAPPEDWINDOW, /* default window */
CW_USEDEFAULT, /* Windows decides the position */
CW_USEDEFAULT, /* where the window ends up on the screen */
winx, /* The programs width */
winy, /* and height in pixels */
HWND_DESKTOP, /* The window is a child-window to desktop */
NULL, /* No menu */
hThisInstance, /* Program Instance handler */
NULL /* No Window Creation data */
);
/* Make the window visible on the screen */
ShowWindow (hwnd, nCmdShow);
/* Run the message loop. It will run until GetMessage() returns 0 */
while (GetMessage (&messages, NULL, 0, 0))
{
/* Translate virtual-key messages into character messages */
TranslateMessage(&messages);
/* Send message to WindowProcedure */
DispatchMessage(&messages);
}
/* The program return-value is 0 - The value that PostQuitMessage() gave */
return messages.wParam;
}
/* This function is called by the Windows function DispatchMessage() */
LRESULT CALLBACK WindowProcedure (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
switch (message) /* handle the messages */
{
case WM_CREATE:
{
m_bih.biWidth = winx;
m_bih.biHeight = winy;
m_bih.biBitCount = winbpp;
m_bih.biSize = sizeof(m_bih);
m_bih.biPlanes = 1; // DIBs are upside down
m_bih.biCompression = BI_BITFIELDS;
m_bih.biSizeImage = 0;
m_bih.biXPelsPerMeter = 0;
m_bih.biYPelsPerMeter = 0;
m_bih.biClrUsed = 0;
m_bih.biClrImportant = 0;
setbuffer();
InvalidateRect(hwnd, NULL, TRUE);
} break;
case WM_PAINT:
{
PAINTSTRUCT ps;
HDC hDC = BeginPaint(hwnd, &ps);
RECT client;
GetClientRect(hwnd, &client);
StretchDIBits ( hDC,
0, // Destination top left hand
// corner X Position
0, // Destination top left hand
// corner Y Position
client.right, // Destinations width
client.bottom, // Destinations height
0, // Source top left hand
// corner's X Position
0, // Source top left hand
// corner's Y Position
winx, // Sources width
winy, // Sources height
buffer, // Source's data
&m_bi, // Bitmap Info
DIB_RGB_COLORS, // operations
SRCCOPY);
EndPaint(hwnd, &ps);
} break;
case WM_DESTROY:
delete buffer;
PostQuitMessage (0); /* send a WM_QUIT to the message queue */
break;
default: /* for messages that we don't deal with */
return DefWindowProc (hwnd, message, wParam, lParam);
}
return 0;
}
the program compiles normally, but instead of displaying the contents of 'buffer' in the client area,
it just displays the grey color like usual.
is 'buffer' formatted correctly?
=updated code below=
#include <windows.h>
int winx = 500;
int winy = 400;
int bpp = 24;
size_t pwidth;
int scanlinewidth = 0;
int numscanlines = 0;
bool setscanline = 1;
bool setbitmap = 1;
BITMAPINFO m_bi;
struct BGR{ char blue; char green; char red;};
BGR* buffer;
void setframebuffer()
{
m_bi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
m_bi.bmiHeader.biPlanes = 1;
m_bi.bmiHeader.biBitCount = bpp;
m_bi.bmiHeader.biCompression = BI_RGB;
m_bi.bmiHeader.biSizeImage = 0;
m_bi.bmiHeader.biXPelsPerMeter = 100;
m_bi.bmiHeader.biYPelsPerMeter = 100;
m_bi.bmiHeader.biClrUsed = 0;
m_bi.bmiHeader.biClrImportant = 0;
if (setbitmap)
{
m_bi.bmiHeader.biWidth = scanlinewidth;
m_bi.bmiHeader.biHeight = numscanlines;
setbitmap = 0;
}
if (!setbitmap)
{
pwidth = (scanlinewidth * 3 + 3) & ~3;
buffer = new BGR[(scanlinewidth + pwidth)*numscanlines];
}
for (int i = 0; i < ((scanlinewidth + pwidth) * numscanlines); i++)
{
buffer[i].blue = 0;
buffer[i].green = 0;
buffer[i].red = 255;
}
}
/* Declare Windows procedure */
LRESULT CALLBACK WindowProcedure (HWND, UINT, WPARAM, LPARAM);
/* Make the class name into a global variable */
char szClassName[ ] = "CodeBlocksWindowsApp";
int WINAPI WinMain (HINSTANCE hThisInstance,
HINSTANCE hPrevInstance,
LPSTR lpszArgument,
int nCmdShow)
{
HWND hwnd; /* This is the handle for our window */
MSG messages; /* Here messages to the application are saved */
WNDCLASSEX wincl; /* Data structure for the windowclass */
/* The Window structure */
wincl.hInstance = hThisInstance;
wincl.lpszClassName = szClassName;
wincl.lpfnWndProc = WindowProcedure; /* This function is called by windows */
wincl.style = CS_DBLCLKS; /* Catch double-clicks */
wincl.cbSize = sizeof (WNDCLASSEX);
/* Use default icon and mouse-pointer */
wincl.hIcon = LoadIcon (NULL, IDI_APPLICATION);
wincl.hIconSm = LoadIcon (NULL, IDI_APPLICATION);
wincl.hCursor = LoadCursor (NULL, IDC_ARROW);
wincl.lpszMenuName = NULL; /* No menu */
wincl.cbClsExtra = 0; /* No extra bytes after the window class */
wincl.cbWndExtra = 0; /* structure or the window instance */
/* Use Windows's default colour as the background of the window */
wincl.hbrBackground = (HBRUSH) COLOR_BACKGROUND;
/* Register the window class, and if it fails quit the program */
if (!RegisterClassEx (&wincl))
return 0;
/* The class is registered, let's create the program*/
hwnd = CreateWindowEx (
0, /* Extended possibilites for variation */
szClassName, /* Classname */
"framebuffer project for win32", /* Title Text */
WS_OVERLAPPEDWINDOW, /* default window */
CW_USEDEFAULT, /* Windows decides the position */
CW_USEDEFAULT, /* where the window ends up on the screen */
winx, /* The programs width */
winy, /* and height in pixels */
HWND_DESKTOP, /* The window is a child-window to desktop */
NULL, /* No menu */
hThisInstance, /* Program Instance handler */
NULL /* No Window Creation data */
);
/* Make the window visible on the screen */
ShowWindow (hwnd, nCmdShow);
/* Run the message loop. It will run until GetMessage() returns 0 */
while (GetMessage (&messages, NULL, 0, 0))
{
/* Translate virtual-key messages into character messages */
TranslateMessage(&messages);
/* Send message to WindowProcedure */
DispatchMessage(&messages);
}
/* The program return-value is 0 - The value that PostQuitMessage() gave */
return messages.wParam;
}
/* This function is called by the Windows function DispatchMessage() */
LRESULT CALLBACK WindowProcedure (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
DWORD result;
switch (message) /* handle the messages */
{
case WM_CREATE:
{
InvalidateRect(hwnd,0,0);
}break;
case WM_PAINT:
{
PAINTSTRUCT ps;
HDC hDC = BeginPaint(hwnd, &ps);
RECT client;
GetClientRect(hwnd,&client);
if(setscanline) {
scanlinewidth = client.right;
numscanlines = client.bottom;
setscanline = 0;
}
setframebuffer();
result = StretchDIBits(hDC,
0, 0,
client.right, client.bottom,
0, 0,
scanlinewidth, numscanlines,
buffer, &m_bi, DIB_RGB_COLORS, SRCCOPY);
if(result != winy)
{
//Drawing failed
DebugBreak();
}
EndPaint(hwnd, &ps);
}break;
case WM_KEYDOWN:{ int escpressed = GetAsyncKeyState(27); if(escpressed){PostQuitMessage(0);}}break;
case WM_DESTROY: {
PostQuitMessage (0); /* send a WM_QUIT to the message queue */
}break;
default: /* for messages that we don't deal with */
return DefWindowProc (hwnd, message, wParam, lParam);
}
return 0;
}
EDIT changed int setbuffer to setbuffer in WM_create
EDIT #2 changed getdc to begin paint.
EDIT #3 changed the bpp to 24 and also changed the WM_Create to set all the bitmap properties
EDIT #4 I have found a solution thanks to your help. i didn't understand Scan Line padding before, but now i understand it perfectly. the updated code prints a solid color on the screen and you can compile it for yourself and change the color. thanks for helping me solve my problem, now i can start drawing on the screen using my own code.
I'm not sure all of these issues are still relevant as I copied the code before you started editing. Either way, this works and you should be able to see the differences pretty easily.
The BITMAPINFO structure was not being initialized properly.
The buffer was being created incorrectly, it was too large and not necessarily aligned properly to be used as a bitmap. The width needs to be padded to a multiple of 4 which would have been okay for a width of 500 but not for a width of 499 as an example.
I also added a check to make sure StretchDIBits was successful and it'll dump you into the debugger if it fails. You can add more appropriate error checking if you like.
I also cut some comments out just to keep things as short as possible.
#include <windows.h>
const int winx = 500;
const int winy = 500;
const int winbpp = 3;
BITMAPINFO m_bi;
char* buffer = 0;
void setbuffer()
{
m_bi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
m_bi.bmiHeader.biWidth = winx;
m_bi.bmiHeader.biHeight = winy;
m_bi.bmiHeader.biPlanes = 1;
m_bi.bmiHeader.biBitCount = 24;
m_bi.bmiHeader.biCompression = BI_RGB;
m_bi.bmiHeader.biSizeImage = 0;
m_bi.bmiHeader.biXPelsPerMeter = 100;
m_bi.bmiHeader.biYPelsPerMeter = 100;
m_bi.bmiHeader.biClrUsed = 0;
m_bi.bmiHeader.biClrImportant = 0;
size_t paddedWidth = (winx * 3 + 3) & ~3;
buffer = new char[paddedWidth * winy * winbpp];
for(int y = 0; y < winy; ++y)
{
for(int x = 0; x < winx; ++x)
{
for(int z = 0; z < 3; ++z)
{
buffer[y * paddedWidth + x * winbpp + z] = z * x;
}
}
}
}
LRESULT CALLBACK WindowProcedure(HWND, UINT, WPARAM, LPARAM);
char szClassName[] = "CodeBlocksWindowsApp";
int WINAPI WinMain(HINSTANCE hThisInstance,
HINSTANCE hPrevInstance,
LPSTR lpszArgument,
int nCmdShow)
{
HWND hwnd; /* This is the handle for our window */
MSG messages; /* Here messages to the application are saved */
WNDCLASSEX wincl; /* Data structure for the windowclass */
wincl.hInstance = hThisInstance;
wincl.lpszClassName = szClassName;
wincl.lpfnWndProc = WindowProcedure; /* This function is called by windows */
wincl.style = CS_DBLCLKS; /* Catch double-clicks */
wincl.cbSize = sizeof (WNDCLASSEX);
wincl.hIcon = LoadIcon(NULL, IDI_APPLICATION);
wincl.hIconSm = LoadIcon(NULL, IDI_APPLICATION);
wincl.hCursor = LoadCursor(NULL, IDC_ARROW);
wincl.lpszMenuName = NULL; /* No menu */
wincl.cbClsExtra = 0; /* No extra bytes after the window class */
wincl.cbWndExtra = 0; /* structure or the window instance */
wincl.hbrBackground = (HBRUSH)COLOR_BACKGROUND;
if(!RegisterClassEx(&wincl))
return 0;
hwnd = CreateWindowEx(
0, /* Extended possibilites for variation */
szClassName, /* Classname */
" project 1 ", /* Title Text */
WS_OVERLAPPEDWINDOW, /* default window */
CW_USEDEFAULT, /* Windows decides the position */
CW_USEDEFAULT, /* where the window ends up on the screen */
winx, /* The programs width */
winy, /* and height in pixels */
HWND_DESKTOP, /* The window is a child-window to desktop */
NULL, /* No menu */
hThisInstance, /* Program Instance handler */
NULL /* No Window Creation data */
);
ShowWindow(hwnd, nCmdShow);
while(GetMessage(&messages, NULL, 0, 0))
{
TranslateMessage(&messages);
DispatchMessage(&messages);
}
return messages.wParam;
}
LRESULT CALLBACK WindowProcedure(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
PAINTSTRUCT ps;
HDC hDC;
RECT client;
DWORD result;
switch(message) /* handle the messages */
{
case WM_CREATE:
setbuffer();
InvalidateRect(hwnd, NULL, TRUE);
break;
case WM_PAINT:
hDC = BeginPaint(hwnd, &ps);
GetClientRect(hwnd, &client);
result = StretchDIBits(hDC,
0, 0,
client.right, client.bottom,
0, 0,
winx, winy,
buffer, &m_bi, DIB_RGB_COLORS, SRCCOPY);
if(result != winy)
{
//Drawing failed
DebugBreak();
}
EndPaint(hwnd, &ps);
break;
case WM_DESTROY:
delete buffer;
PostQuitMessage(0); /* send a WM_QUIT to the message queue */
break;
default: /* for messages that we don't deal with */
return DefWindowProc(hwnd, message, wParam, lParam);
}
return 0;
}