How would I animate a window with the Win32 Api? - winapi

How would you go about painting something onto a window in regular intervals.
I have come up with the this (stripped quite a bit for clarity)
#include <windows.h>
void DrawOntoDC (HDC dc) {
pen = CreatePen(...)
penOld = SelectObject(dc, pen)
..... Here is the actual drawing, that
..... should be regurarly called, since
..... the drawn picture changes as time
..... progresses
SelectObject(dc, pen_old);
DeleteObject(pen);
}
LRESULT CALLBACK WindowProc(....) {
switch(Msg) {
case WM_PAINT: {
PAINTSTRUCT ps;
dc = BeginPaint(hWnd, &ps);
..... A Memory DC is created
..... In order to prevent flickering.
HBITMAP PersistenceBitmap;
PersistenceBitmap = CreateCompatibleBitmap(dc, windowHeight, windowHeight);
HDC dcMemory = CreateCompatibleDC(dc);
HBITMAP oldBmp = (HBITMAP) SelectObject(dcMemory, PersistenceBitmap);
DrawOntoDC(dcMemory);
..... "copying" the memory dc in one go unto dhe window dc:
BitBlt ( dc,
0, 0, windowWidth, windowHeight,
dcMemory,
0, 0,
SRCCOPY
);
..... destroy the allocated bitmap and memory DC
..... I have the feeling that this could be implemented
..... better, i.e. without allocating and destroying the memroy dc
..... and bitmap with each WM_PAINT.
SelectObject(dcMemory, oldBmp);
DeleteDC(dcMemory);
DeleteObject(PersistenceBitmap);
EndPaint (hWnd, &ps);
return 0;
}
default:
return DefWindowProc(hWnd, Msg, wParam, lParam);
}
}
DWORD WINAPI Timer(LPVOID p) {
..... The 'thread' that makes sure that the window
..... is regularly painted.
HWND hWnd = (HWND) *((HWND*) p);
while (1) {
Sleep(1000/framesPerSecond);
InvalidateRect(hWnd, 0, TRUE);
}
}
int APIENTRY WinMain(...) {
WNDCLASSEX windowClass;
windowClass.lpfnWndProc = WindowProc;
windowClass.lpszClassName = className;
....
RegisterClassEx(&windowClass);
HWND hwnd = CreateWindowEx(
....
className,
....);
ShowWindow(hwnd, SW_SHOW);
UpdateWindow(hwnd);
DWORD threadId;
HANDLE hTimer = CreateThread(
0, 0,
Timer,
(LPVOID) &hwnd,
0, &threadId );
while( GetMessage(&Msg, NULL, 0, 0) ) {
....
}
return Msg.wParam;
}
I guess there's a lot that could be improved and I'd appreciate any pointer to things I have overlooked.

Doing this kind of thing with a worker thread is not optimal.
Given that the optimal code path for painting is always via a WM_PAINT that leaves two ways to do this:
Simply create a timer on the GUI thread, post WM_TIMER messages to a timerproc, or the window directly, and invoke the OnTick() part of your engine. IF any sprites move, they invalidate their area using InvalidateRect() and windows follows up by automatically posting a WM_PAINT. This has the advantage of having a very low CPU usage if the game is relatively idle.
Most games want stricter timing that can be achieved using a low priority WM_TIMER based timer. In that case, you implement a game loop something like this:
Message Loop:
while(stillRunning)
{
DWORD ret = MsgWaitForMultipleObjects(0,NULL,FALSE,frameIntervalMs,QS_ALLEVENTS);
if(ret == WAIT_OBJECT_0){
while(PeekMessage(&msg,0,0,0,PM_REMOVE)){
TranslateMessage(&msg);
DispatchMessage(&msg);
}
if(TickGame()) // if TickGame indicates that enough time passed for stuff to change
RedrawWindow(hwndGame,...); // Dispatch a WM_PAINT immediately.
}
The danger with this kind of message loop is, if the ... application goes into any kind of modal state :- the user starts to drag the window / a modal dialog box pops up, then messages are being pumped by the modal loop, so the animation stops. As a result you need to have a fallback timer if you need to mix a high performance message loop with modal operations.
WRT your WM_PAINT implementation - its usually better to (re)create your backbuffer in response to WM_SIZE messages. That way its always the right size, and you don't incurr the rather large cost of recreating a large memory buffer many times per second.

Borrowing bits and pieces from various places, I came up with the following approach for my similar problem. The following is a test application to test the concept.
In this test application I am using an MFC static window which I am updating with a text string by periodically calling the ::SetWindowText() function with the MFC static window's handle. This works fine to display a marching right angle bracket to demonstrate that the animation works.
In the future I intend on using a memory resident bitmap image which is modified in the animation loop which is then posted into a bitmap attached to the static text window. This technique allows for an animated bitmap to be presented with a more elegant indication of something in progress.
Testing this concept was done with an MFC dialog application that contains two static windows for the progress indicators and two additional buttons, Start and Stop, to start and stop the progress indicators. The goal is that when the Start button is pressed a series of greater than signs are written across the static window and then cleared and then once more started. So the animation looks like an LED sign that has arrows marching across from left to right in a horizontal marque.
These two buttons do nothing more than set an indicator in the animation object to be either one (on) or zero (off). The animation object thread which does the actual animation just reads from the m_state variable and does not modify it.
The timer delay quantity is hardcoded for the purposes of this test. It could easily be a parameter.
The dialog is still responsive and even as it is updating I can display the default About Box for the dialog application and move the About Box around. I can also drag the dialog application itself around screen (without the About Box displayed as that is a modal dialog) with the static window still updating.
The Animation class source
The source code for the animation logic is a simple class that starts a thread which then updates the specified dialog control. While the animation loop is a static method of the class, the data used by the loop is specified in the object itself so multiple animations can be done with different objects using the same static loop.
This approach is fairly straightforward and simple. Rather than using a more sophisticated approach with a thread pool and a timer pool, it dedicates a thread and a timer object to a single animation. It appears obvious that this approach will not scale well however for an application with a couple of animations, it works well enough.
class AnimatedImage
{
UINT m_state; // current on/off state of the animation. if off (0) then the window is not updated
UINT m_itemId; // control identifier of the window that we are going to be updating.
HWND m_targetHwnd; // window handle of the parent dialog of the window we are going to be updating
UINT m_i; // position for the next right angle bracket
wchar_t m_buffer[32]; // text buffer containing zero or more angle brackets which we use to update the window
DWORD m_lastError; // result of GetLastError() in case of an error.
HANDLE m_hTimer; // handle for the timer
HANDLE m_hThread; // handle for the thread created.
LARGE_INTEGER m_liDueTime; // time delay between updates
public:
AnimatedImage(UINT itemId = 0, HWND hWnd = NULL) : m_state(0), m_itemId(itemId), m_targetHwnd(hWnd), m_i(0), m_lastError(0), m_hTimer(NULL), m_hThread(NULL) { memset(m_buffer, 0, sizeof(m_buffer)) ; }
~AnimatedImage() { Kill(); CloseHandle(m_hTimer); CloseHandle(m_hThread); } // clean up the timer and thread handle.
static unsigned __stdcall loop(AnimatedImage *p); // animation processing loop
void Run(); // starts the animation thread
void Start(); // starts the animation
void Stop(); // stops the animation
void Kill(); // indicates the thread is to exit.
// Functions used to get the target animation window handle
// and to set the parent window handle and the dialog control identifier.
// This could be simpler by just requiring the target animation window handle
// and letting the user do the GetDlgItem() function themselves.
// That approach would make this class more flexible.
HWND GetImageWindow() { return ::GetDlgItem(m_targetHwnd, m_itemId); }
void SetImageWindow(UINT itemId, HWND hWnd) { m_itemId = itemId; m_targetHwnd = hWnd; }
};
unsigned __stdcall AnimatedImage::loop(AnimatedImage *p)
{
p->m_liDueTime.QuadPart = -10000000LL;
// Create an unnamed waitable timer. We use this approach because
// it makes for a more dependable timing source than if we used WM_TIMER
// or other messages. The timer resolution is much better where as with
// WM_TIMER is may be no better than 50 milliseconds and the reliability
// of getting the messages regularly can vary since WM_TIMER are lower
// in priority than other messages such as mouse messages.
p->m_hTimer = CreateWaitableTimer(NULL, TRUE, NULL);
if (NULL == p->m_hTimer)
{
return 1;
}
for (; ; )
{
// Set a timer to wait for the specified time period.
if (!SetWaitableTimer(p->m_hTimer, &p->m_liDueTime, 0, NULL, NULL, 0))
{
p->m_lastError = GetLastError();
return 2;
}
// Wait for the timer.
if (WaitForSingleObject(p->m_hTimer, INFINITE) != WAIT_OBJECT_0) {
p->m_lastError = GetLastError();
return 3;
}
else {
if (p->m_state < 1) {
p->m_i = 0;
memset(p->m_buffer, 0, sizeof(m_buffer));
::SetWindowText(p->GetImageWindow(), p->m_buffer);
}
else if (p->m_state < 2) {
// if we are updating the window then lets add another angle bracket
// to our text buffer and use SetWindowText() to put it into the
// window area.
p->m_buffer[p->m_i++] = L'>';
::SetWindowText(p->GetImageWindow(), p->m_buffer);
p->m_i %= 6; // for this demo just do a max of 6 brackets before we reset.
if (p->m_i == 0) {
// lets reset our buffer so that the next time through we will start
// over in position zero (first position) with our angle bracket.
memset(p->m_buffer, 0, sizeof(m_buffer));
}
}
else {
// we need to exit our thread so break from the loop and return.
break;
}
}
}
return 0;
}
void AnimatedImage::Run()
{
m_hThread = (HANDLE)_beginthreadex(NULL, 0, (_beginthreadex_proc_type)&AnimatedImage::loop, this, 0, NULL);
}
void AnimatedImage::Start()
{
m_state = 1;
}
void AnimatedImage::Stop()
{
m_state = 0;
}
void AnimatedImage::Kill()
{
m_state = 3;
}
How the class is used
For this simple test dialog application, we just create a couple of global objects for our two animations.
AnimatedImage xxx;
AnimatedImage xx2;
In the OnInitDialog() method of the dialog application the animations are initialized before returning.
// TODO: Add extra initialization here
xxx.SetImageWindow(IDC_IMAGE1, this->m_hWnd);
xxx.Run();
xx2.SetImageWindow(IDC_IMAGE2, this->m_hWnd);
xx2.Run();
return TRUE; // return TRUE unless you set the focus to a control
There are two button click handlers which handle a click on either the Start button or the Stop button.
void CMFCApplication2Dlg::OnBnClickedButton1()
{
// TODO: Add your control notification handler code here
xxx.Start();
xx2.Start();
}
void CMFCApplication2Dlg::OnBnClickedButton2()
{
// TODO: Add your control notification handler code here
xxx.Stop();
xx2.Stop();
}
The dialog application main dialog resource is defined as follows in the resource file.
IDD_MFCAPPLICATION2_DIALOG DIALOGEX 0, 0, 320, 200
STYLE DS_SETFONT | DS_FIXEDSYS | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU | WS_THICKFRAME
EXSTYLE WS_EX_APPWINDOW
FONT 8, "MS Shell Dlg", 0, 0, 0x1
BEGIN
DEFPUSHBUTTON "OK",IDOK,209,179,50,14
PUSHBUTTON "Cancel",IDCANCEL,263,179,50,14
CTEXT "TODO: Place dialog controls here.",IDC_STATIC,10,96,300,8
LTEXT "Static",IDC_IMAGE1,7,7,110,21
LTEXT "Static",IDC_IMAGE2,64,43,112,27
PUSHBUTTON "Start",IDC_BUTTON1,252,16,50,19
PUSHBUTTON "Stop",IDC_BUTTON2,248,50,57,21
END

Related

How to paint title bar window only?

As mention from microsft doc. WM_NCPAINT is use to paint non-client area. it means like title bar. https://learn.microsoft.com/en-us/windows/win32/gdi/wm-ncpaint. But i get unexpected result. it paint client area too. and the weird one. title bar is gone. when launch. after ALT+TAB. the title bar appears with windows 7 style in windows 10.
class CMainFrame::CFrameWnd
{
public:
CMainFrame()
{
Create(
NULL,
"Hello World!",
WS_OVERLAPPED | WS_MINIMIZEBOX | WS_SYSMENU,
CRect(CPoint(100, 100), CSize(640, 360));
}
protected:
afx_msg void OnNcPaint();
DECLARE_MESSAGE_MAP()
}
BEGIN_MESSAGE_MAP(CMainFrame, CFrameWnd)
ON_WM_NCPAINT()
END_MESSAGE_MAP()
void CMainFrame::OnNcPaint()
{
PAINTSTRUCT ps;
CBrush brush;
brush.CreateSolidBrush(RGB(0, 0, 255));
CDC *pDC = BeginPaint(&ps);
pDC->FillRect(&ps.rcPaint, &brush);
EndPaint(&ps);
}
class CApplication : public CWinApp {
BOOL InitInstance() {
CMainFrame* mainWnd = new CMainFrame();
m_pMainWnd = mainWnd;
mainWnd->ShowWindow(SW_NORMAL);
mainWnd->UpdateWindow();
return TRUE;
}
};
In InitInstance, you must call the base class CWinApp::InitInstance() in the first line.
Create your main frame window using
CreateEx(0, AfxRegisterWndClass(0), "Hello World!",
WS_VISIBLE | WS_OVERLAPPEDWINDOW, 100, 100, 640, 360, NULL, 0);
Add PreCreateWindow to control the edges and other properties
int CMainFrame::PreCreateWindow(CREATESTRUCT& cs)
{
auto res = CFrameWnd::PreCreateWindow(cs);
cs.dwExStyle &= ~WS_EX_CLIENTEDGE;
return res;
}
Use the appropriate paint classes, CPaintDC dc(this), CWindowDC, for overriding OnPaint or OnNcPaint (avoid overriding OnNcPaint), use CClientDC dc(this) for other client area paints. Avoid calling WinAPI functions directly, for example avoid calling BeginPaint. You can also use Visual Studio to create a sample dialog based application, it will be easier to get started with that.

Windows clipboard ::GetClipboardData() for CF_DIBV5 causes the image on the clipboard to be modified and corrupted?

I found on (at least) Win10 that calling ::GetClipboardData() on a CF_DIBV5 created via Alt-PrtScrn (may be a synthesized format) causes the image to be modified (and basically corrupted).
For example, on the handler for ON_WM_CLIPBOARDUPDATE() the simple loop below will cause the corruption (note that you need to use debug mode so the ::GetClipboardData() is not optimized out).
To test, first don't run your app that processes the clipboard, use Alt-PrntScrn to capture data, then paste it to Paint. Now run the app that process the clipboard (with below sample below). Repeat Alt-PrntScrn process and you'll see it's different where the right side of the captured window ends up on the left and not centered in the area.
void CMainFrame::OnClipboardUpdate()
if (::OpenClipboard(AfxGetMainWnd()->m_hWnd)) {
UINT uformat=0;
while ((uformat=::EnumClipboardFormats(uformat))!=0) {
if (uformat==CF_DIBV5) {
// get the data - run in debug mode so not optimized out
HGLOBAL hglobal=::GetClipboardData(uformat);
}
}
// clean up
::CloseClipboard();
}
}
To enable the handler you need to call AddClipboardFormatListener(GetSafeHwnd()); in something like int CMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct) then RemoveClipboardFormatListener(GetSafeHwnd()); on void CMainFrame::OnDestroy()
So is this a bug in Win10 (and other Windows Versions) or should I be doing something else that the sample isn't doing? (I know other formats exist, but the CF_DBIV5 is what I wanted).
I'm on Version 1903 (OS Build 18362.838)
Note the sample pic has right side items on left and some garbage pixels in lower left. I alt-prtscrn while app running, pasted in paint.
My resolution is 2560x1600.
Here's a link to a project that will cause the problem:
Sample Project
You can find the following description in the documentation :
The red, green, and blue bitfield masks for BI_BITFIELD bitmaps
immediately follow the BITMAPINFOHEADER, BITMAPV4HEADER, and
BITMAPV5HEADER structures. The BITMAPV4HEADER and BITMAPV5HEADER
structures contain additional members for red, green, and blue masks
as follows.
When the biCompression member of BITMAPINFOHEADER is set to BI_BITFIELDS and the function receives an argument of type LPBITMAPINFO, the color masks will immediately follow the header. The color table, if present, will follow the color masks. BITMAPCOREHEADER bitmaps do not support color masks.
When you handle CF_DIBV5 correctly you will draw the image successfully. The following is an example of Win32 C++ you can refer to:
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
static UINT uFormat = (UINT) -1;
HDC hdcMem = NULL;
RECT rc = {0};
BYTE * pData = NULL;
BITMAPV5HEADER *pDibv5Info = NULL;
switch (message)
{
case WM_CLIPBOARDUPDATE:
{
if (IsClipboardFormatAvailable(CF_DIBV5))
{
uFormat = CF_DIBV5;
::CloseClipboard();
GetClientRect(hWnd, &rc);
InvalidateRect(hWnd, &rc, TRUE);
}
}
break;
case WM_PAINT:
{
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hWnd, &ps);
switch (uFormat)
{
case CF_DIBV5:
hdcMem = CreateCompatibleDC(hdc);
if (hdcMem != NULL)
{
if (::OpenClipboard(hWnd)) {
HANDLE hglobal = ::GetClipboardData(uFormat);
pData = (BYTE*)GlobalLock(hglobal);
if (pData)
{
pDibv5Info = (BITMAPV5HEADER *)pData;
int offset = pDibv5Info->bV5Size + pDibv5Info->bV5ClrUsed * sizeof(RGBQUAD);
if (pDibv5Info->bV5Compression == BI_BITFIELDS)
offset += 3 * sizeof(DWORD); //three DWORD color masks that specify the red, green, and blue components
pData += offset;
SetDIBitsToDevice(hdc, 20, 20, pDibv5Info->bV5Width, pDibv5Info->bV5Height, 0, 0, 0, pDibv5Info->bV5Height, pData, (BITMAPINFO *)pDibv5Info, 0);
}
GlobalUnlock(hglobal);
::CloseClipboard();
}
}
break;
}
EndPaint(hWnd, &ps);
}
break;
}
The correct image drawn in my application window:
I can reproduce the same issue without code:
if (pDibv5Info->bV5Compression == BI_BITFIELDS)
offset += 3 * sizeof(DWORD);
The corrupted image:

Approaches for reducing rate of resource file growth as dialogs are enhanced/extended

As part of extending the functionality of a dialog in an old Windows WinAPI GUI application written in C, I was faced with once more adding multiple check boxes for each line of a six line data entry dialog. I just could not bear the hassle of repetitive resource file and source code file changes and decided to borrow a UI design approach from Java UIs of building up a UI from panes.
The Visual Studio tools, at least Visual Studio 2005, seem to discourage this approach and in this case I hand edited the resource file. Perhaps the resource editing tools of Visual Studio 2017 are more flexible.
My question is what is an alternative to this approach that would seem to be as easy to do and would better fit with the Visual Studio philosophy.
I am also wondering about the downside of this approach.
This approach seems unusual for a Visual Studio C WinAPI GUI application which troubles me. I can't claim to be especially innovative so I wonder what I am missing as the approach seems to work well at least when doing hand edits of the resource file.
I am considering doing another iteration in which I move the list of controls for each line that is repeated into the modeless dialog box template as well and just having the original dialog be a stack of 6 static windows, one for each line.
Benefits of this approach was fewer defines and being able to reuse defines. It was also easier to insert the new functionality into the existing dialog behavior source code though that was mostly because these were just simple auto check boxes.
The one issue I see is using the Visual Studio tools after doing this change. However this particular application's resource file doesn't work well with the Visual Studio resource editing tools anyway.
This approach has already had a payback when I needed to add some additional checkboxes to the modeless dialog template. The resource file changes I had to do to was to add the additional checkboxes to the new dialog template and adjust the original dialog size, the modeless dialog size, and the sizes of the static windows to make everything visible.
The Implementation
The alternative I have implemented is to:
create a dialog template with the set of checkboxes
modify the dialog template style of the modeless dialog to WS_CHILD
create a static window on each of the six lines of the original dialog for the new dialog template
place an instance of the modeless dialog box into the static window on each line
The new version of the dialog looks like
When the original dialog is displayed, the handler for the init dialog message creates a set of six modeless dialogs, one for each of the newly added static windows with the parent window for the dialogs being the static window. This places the modeless dialog into the static window and when the static window moves so does the modeless dialog.
All six of the modeless dialogs use the same dialog message handler. The message handler doesn't handle any messages itself.
The modeless dialog template is:
IDD_A170_DAYS DIALOG DISCARDABLE 0, 0, 240, 20
STYLE WS_CHILD | WS_VISIBLE
FONT 8, "MS Sans Serif"
BEGIN
CONTROL "Ovr",IDD_A170_STR1,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,5,1,25,10
CONTROL "AND",IDD_A170_STR2,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,35,1,40,10
CONTROL "S",IDD_A170_CAPTION1,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,75,1,20,10
CONTROL "M",IDD_A170_CAPTION2,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,100,1,20,10
CONTROL "T",IDD_A170_CAPTION3,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,125,1,20,10
CONTROL "W",IDD_A170_CAPTION4,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,150,1,20,10
CONTROL "T",IDD_A170_CAPTION5,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,175,1,20,10
CONTROL "F",IDD_A170_CAPTION6,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,195,1,20,10
CONTROL "S",IDD_A170_CAPTION7,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,220,1,20,10
END
and the main dialog with the static windows is:
IDD_A170 DIALOG DISCARDABLE 2, 17, 530, 190
STYLE DS_MODALFRAME | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU
CAPTION "Set Sales Code Restriction Table of PLU (AC 170)"
FONT 8, "MS Sans Serif"
BEGIN
LTEXT "Address (PLU Sales Code)",IDD_A170_CAPTION1,14,10,64,20
LTEXT "Date",IDD_A170_CAPTION2,86,14,28,12
LTEXT "Day of week",IDD_A170_CAPTION3,115,10,33,21
LTEXT "Start hour",IDD_A170_CAPTION4,153,10,20,18
LTEXT "Minute",IDD_A170_CAPTION5,182,14,26,12
LTEXT "End hour",IDD_A170_CAPTION6,217,10,20,18
LTEXT "Minute",IDD_A170_CAPTION7,245,14,26,12
LTEXT "Override/Type",IDC_STATIC,290,14,50,12
LTEXT "Days To Restrict",IDC_STATIC,390,14,100,12
LTEXT "",IDD_A170_STR1,8,34,74,12 // first control on line 1
EDITTEXT IDD_A170_DATE1,87,33,18,12,ES_AUTOHSCROLL
SCROLLBAR IDD_A170_DATESPIN1,104,33,8,12,SBS_VERT
EDITTEXT IDD_A170_WEEK1,119,33,18,12,ES_AUTOHSCROLL
SCROLLBAR IDD_A170_WEEKSPIN1,136,33,8,12,SBS_VERT
EDITTEXT IDD_A170_SHOUR1,151,33,18,12,ES_AUTOHSCROLL
SCROLLBAR IDD_A170_SHOURSPIN1,168,33,8,12,SBS_VERT
EDITTEXT IDD_A170_SMINUTE1,183,33,18,12,ES_AUTOHSCROLL
SCROLLBAR IDD_A170_SMINUTESPIN1,200,33,8,12,SBS_VERT
EDITTEXT IDD_A170_EHOUR1,214,33,18,12,ES_AUTOHSCROLL
SCROLLBAR IDD_A170_EHOURSPIN1,231,33,8,12,SBS_VERT
EDITTEXT IDD_A170_EMINUTE1,246,33,18,12,ES_AUTOHSCROLL
SCROLLBAR IDD_A170_EMINUTESPIN1,263,33,8,12,SBS_VERT
LTEXT "D1",IDD_A170_DAYS_1,281,33,240,20 // static window to contain the modeless dialog box from the template IDD_A170_DAYS above
// .. repeated sequence for 5 more lines
CONTROL "MDC 298 - Sales Restriction Type is AND",IDD_A170_MDC_PLU5_ADR,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,9,140,170,9
LTEXT "[Address : 1 - 6, Date : 0 - 31",IDD_A170_CAPTION8,9,154,99,9
LTEXT "Day of week : 0 - 7 (1 - Sunday, 7 - Saturday)]",IDD_A170_CAPTION9,110,154,167,9
LTEXT "[Hour : 0 - 24, Minute : 0 - 59 (For 0:00, enter 24:00)]",IDD_A170_CAPTION10,9,168,167,9
PUSHBUTTON "&Ok",IDOK,285,154,48,20
PUSHBUTTON "&Cancel",IDCANCEL,345,154,48,20
END
You may notice that I just reused some of the defines in the new modeless dialog box that were already being used in the original dialog box. I was able to do so because control identifiers are specific to the dialog box itself. So using the same define in different dialog boxes does not cause a problem since the use of GetDlgItem() to obtain the window handle of a control within a dialog box requires the window handle of a specific dialog instance.
I then created a set of helper functions that handle an instance of the modeless dialog.
static struct {
int iId;
HWND hWnd;
} A170DlgTabs[10] = { {0, 0} };
// modeless dialog box message handler which has nothing to do but the
// WinAPI requires it.
BOOL WINAPI A170DlgChildProc(HWND hDlg, UINT wMsg, WPARAM wParam, LPARAM lParam)
{
return FALSE;
}
void A170ModeLessChildDialogClear ()
{
memset (A170DlgTabs, 0, sizeof(A170DlgTabs));
}
HWND A170ModeLessChildDialog (HWND hParentWnd, int nCmdShow, int iId)
{
int i;
HWND hWnd = DialogCreation(hResourceDll/*hActInst*/, //RPH 4-23-03 Multilingual
MAKEINTRESOURCEW(IDD_A170_DAYS),
hParentWnd,
A170DlgChildProc);
hWnd && ShowWindow (hWnd, nCmdShow);
for (i = 0; i < sizeof(A170DlgTabs)/sizeof(A170DlgTabs[0]); i++) {
if (A170DlgTabs[i].hWnd == 0) {
A170DlgTabs[i].iId = iId;
A170DlgTabs[i].hWnd = hWnd;
break;
}
}
return hWnd;
}
HWND A170ModeLessChildDialogFind (int iId)
{
int i;
HWND hWnd = NULL;
for (i = 0; i < sizeof(A170DlgTabs)/sizeof(A170DlgTabs[0]); i++) {
if (A170DlgTabs[i].iId == iId) {
hWnd = A170DlgTabs[i].hWnd;
break;
}
}
return hWnd;
}
USHORT A170ModeLessChildDialogSettings (int iId)
{
int i;
USHORT iBits = 0, kBits = 1;
HWND hWnd = A170ModeLessChildDialogFind (iId);
// least significant byte contains the bit mask for the days of the week.
// the next higher byte contains the indicators for the override type or
// whether MDC 298 is to be overriden or not.
for (i = IDD_A170_CAPTION1; i <= IDD_A170_CAPTION7; i++, (kBits <<= 1)) {
iBits |= IsDlgButtonChecked (hWnd, i) ? kBits : 0;
}
iBits |= iBits ? RESTRICT_WEEK_DAYS_ON : 0;
iBits |= IsDlgButtonChecked(hWnd, IDD_A170_STR1) ? KBITS_RESTRICT_OVERRIDE_ANDOR : 0;
iBits |= IsDlgButtonChecked(hWnd, IDD_A170_STR2) ? KBITS_RESTRICT_OVERRIDE_AND : 0;
return iBits;
}
USHORT A170ModeLessChildDialogSettingsSetMask (int iId, USHORT usMask)
{
int i;
USHORT k = 1;
HWND hWnd = A170ModeLessChildDialogFind (iId);
CheckDlgButton(hWnd, IDD_A170_STR1, (usMask & KBITS_RESTRICT_OVERRIDE_ANDOR) ? TRUE : FALSE);
CheckDlgButton(hWnd, IDD_A170_STR2, (usMask & KBITS_RESTRICT_OVERRIDE_AND) ? TRUE : FALSE);
for (i = IDD_A170_CAPTION1; i <= IDD_A170_CAPTION7; i++, (k <<= 1)) {
CheckDlgButton(hWnd, i, (usMask & k) ? TRUE : FALSE);
}
return usMask;
}
Using Visual Studio 2017 Community Edition made taking this approach to creating components from dialog templates which are then used to build a GUI easier.
I did this rough, proof of concept exercise using the About dialog that is automatically generated by the creation of a new Windows Desktop Application project since it was handy.
This was a simple component being expressed in a dialog template and that simplicity may make this proof of concept misleading.
I started with an initial Windows Desktop Application skeleton created as a new project. I then made the following modifications to the About dialog that is automatically generated with a New project. I was able to use the IDE and Resource Editor without doing any hand editing of the resource file.
The steps were as follows:
use the Resource Editor to modify the existing About dialog and create a new modeless dialog
add a new class to manage the new modeless dialog
modify the About dialog message handler to use the new class
Modifications to the dialog resources using the Resource Editor was fairly straightforward:
modify the automatically generated About dialog by making it larger and adding two static text windows in a column
specify actual control identifiers for each static text box added to the About dialog so that they could be referenced with GetDlgItem(), an important step as the IDE does not assign usable control identifiers by default to static windows
created a new dialog template using the Resource Editor after switching to Resource View
modified a couple of Appearance attributes in the Properties list of the dialog box to change the dialog to a modeless dialog with no border
added the checkboxes to the new dialog using the Resource Editor and removed the default buttons
The source code changes were also fairly straightforward as this is a simple control.
The new About dialog box looks like this. I added two static windows to the About dialog after making it larger. Each of the static windows has its own instance of the new dialog template based control. Notice that the size of the dialog template is larger than the size of the static windows which results in clipping of the dialog template to the display area of the static window.
The Details of What Was Done
Create and Modify the Dialog Styles
Using Resource View I added a new dialog. I clicked on the new dialog to bring it up in the Resource Editor. Then I modified the initial modal dialog template by changing the border attribute and the style attribute along with removing the default buttons the IDE added when it first created the dialog template, I turned the dialog template into a modeless dialog suitable for putting into a static window container.
Create the Code for the Behavior
I then created a class, CDialogChild to manage this new modeless dialog with the following source:
#pragma once
#include "resource.h"
class CDialogChild
{
private:
// static shared by all instances of this class
static LRESULT CALLBACK WndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);
enum {DIALOG_ID = IDD_DIALOG1};
static const UINT idCheckBox[3]; // identifiers for the controls of the modeless dialog.
// management data for each modeless dialog instance created.
HINSTANCE m_hinst; // handle to the instance resources
HWND m_hWnd; // handle to the modeless dialog window
HWND m_hParent; // handle to the parent of the modeless dialog, usually static window
// operational data displayed and captured by this modeless dialog
// this is the data for the various controls we have in the dialog template.
bool bCheckBox[3] = { false, false, false };
public:
CDialogChild();
~CDialogChild();
bool GetCheck(int iIndex); // for reading checkbox value
void GetCheckFinal(void); // for capturing final checkbox states
bool SetCheck(int iIndex, bool bValue); // for writing checkbox value
void SetCheckInitial(void); // for setting the initial checkbox states.
HWND Create(HWND hParent, HINSTANCE hinst);
};
with an implementation of:
#include "stdafx.h"
#include "CDialogChild.h"
const UINT CDialogChild::idCheckBox[3] = { IDC_CHECK1, IDC_CHECK2, IDC_CHECK3 };
CDialogChild::CDialogChild()
{
}
CDialogChild::~CDialogChild()
{
}
HWND CDialogChild::Create(HWND hParent, HINSTANCE hinst)
{
// called to create the modeless dialog using the dialog resource in the
// specified resource file instance. the hParent is the container we are
// going to put this modeless dialogbox into.
m_hinst = hinst;
m_hParent = hParent;
m_hWnd = CreateDialog(hinst, MAKEINTRESOURCE(DIALOG_ID), hParent, (DLGPROC)CDialogChild::WndProc);
ShowWindow(m_hWnd, SW_SHOW);
return m_hWnd;
}
bool CDialogChild::GetCheck(int iIndex)
{
if (iIndex > 0 && iIndex < sizeof(bCheckBox) / sizeof(bCheckBox[0])) {
iIndex = 0;
}
bCheckBox [iIndex] = IsDlgButtonChecked(m_hWnd, idCheckBox[iIndex]);
return bCheckBox [iIndex];
}
bool CDialogChild::SetCheck(int iIndex, bool bValue)
{
if (iIndex > 0 && iIndex < sizeof(bCheckBox) / sizeof(bCheckBox[0])) {
iIndex = 0;
}
CheckDlgButton (m_hWnd, idCheckBox[iIndex], bValue);
bCheckBox[iIndex] = bValue;
return bCheckBox [iIndex];
}
void CDialogChild::GetCheckFinal(void)
{
for (int iIndex = 0; iIndex < sizeof(bCheckBox) / sizeof(bCheckBox[0]); iIndex++) {
bCheckBox[iIndex] = IsDlgButtonChecked(m_hWnd, idCheckBox[iIndex]);
}
}
void CDialogChild::SetCheckInitial(void)
{
for (int iIndex = 0; iIndex < sizeof(bCheckBox) / sizeof(bCheckBox[0]); iIndex++) {
CheckDlgButton(m_hWnd, idCheckBox[iIndex], bCheckBox[iIndex]);
}
}
// CDialogChild class Windows message procedure to handle any messages sent
// to a modeless dialog window. This simple example there is not much to do.
LRESULT CALLBACK CDialogChild::WndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
{
return (INT_PTR)FALSE;
}
Use the New Control in a Dialog
Finally I modified the About dialog to use the new dialog template based control. The first thing was to add the static windows to the About dialog template to provide containers for the new control which provided the placement for where I wanted the control instances to be.
Next I added the handling source code to use the new dialog template based control in the modified About dialog.
// other source code from the Windows Desktop Application main window handler
// is above this. We are only modifying the About dialog code which is at the
// bottom of the source file.
#include "CDialogChild.h"
CDialogChild myAbout1;
CDialogChild myAbout2;
// Message handler for about box.
INT_PTR CALLBACK About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
{
UNREFERENCED_PARAMETER(lParam);
switch (message)
{
case WM_INITDIALOG:
myAbout1.Create(GetDlgItem(hDlg, IDC_STATIC4), hInst);
myAbout1.SetCheckInitial();
myAbout2.Create(GetDlgItem(hDlg, IDC_STATIC5), hInst);
myAbout2.SetCheckInitial();
return (INT_PTR)TRUE;
case WM_COMMAND:
if (LOWORD(wParam) == IDOK || LOWORD(wParam) == IDCANCEL)
{
if (LOWORD(wParam) == IDOK) {
myAbout1.GetCheckFinal();
myAbout2.GetCheckFinal();
}
EndDialog(hDlg, LOWORD(wParam));
return (INT_PTR)TRUE;
}
break;
}
return (INT_PTR)FALSE;
}

SDL2 Window turns black on resize

I have started to work with SDL2 and am not experienced with it. I am working on a Mac system. Almost everything has been good, but I have a problem that when a resizeable window is resized, while the handle is being dragged, the window turns black, and I can only repaint it after releasing. And I have checked that while the window is being resized, no event is being produced and I have no means to interfere or detect this, as the event loop is just paused. Is there any possible solutions?
Here is the code (Almost replica of a tutorial on handling resize event):
SDL_Event event;
SDL_Rect nativeSize;
SDL_Rect newWindowSize;
float scaleRatioW;//This is to change anything that might rely on something like mouse coords
float scaleRatioH; //(such as a button on screen) over to the new coordinate system scaling would create
SDL_Window * window; //Our beautiful window
SDL_Renderer * renderer; //The renderer for our window
SDL_Texture * backBuffer; //The back buffer that we will be rendering everything to before scaling up
SDL_Texture * ballImage; //A nice picture to demonstrate the scaling;
bool resize;
void InitValues(); //Initialize all the variables needed
void InitSDL(); //Initialize the window, renderer, backBuffer, and image;
bool HandleEvents(); //Handle the window changed size event
void Render(); //Switches the render target back to the window and renders the back buffer, then switches back.
void Resize(); //The important part for stretching. Changes the viewPort, changes the scale ratios
void InitValues()
{
nativeSize.x = 0;
nativeSize.y = 0;
nativeSize.w = 256;
nativeSize.h = 224; //A GameBoy size window width and height
scaleRatioW = 1.0f;
scaleRatioH = 1.0f;
newWindowSize.x = 0;
newWindowSize.y = 0;
newWindowSize.w = nativeSize.w;
newWindowSize.h = nativeSize.h;
window = NULL;
renderer = NULL;
backBuffer = NULL;
ballImage = NULL;
resize = false;
}
void InitSDL()
{
if(SDL_Init(SDL_INIT_EVERYTHING) < 0)
{
//cout << "Failed to initialize SDL" << endl;
printf("%d\r\n", __LINE__);
}
//Set the scaling quality to nearest-pixel
if(SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "0") < 0)
{
//cout << "Failed to set Render Scale Quality" << endl;
printf("%d\r\n", __LINE__);
}
//Window needs to be resizable
window = SDL_CreateWindow("Rescaling Windows!",
SDL_WINDOWPOS_CENTERED,
SDL_WINDOWPOS_CENTERED,
256,
224,
SDL_WINDOW_RESIZABLE);
//You must use the SDL_RENDERER_TARGETTEXTURE flag in order to target the backbuffer
renderer = SDL_CreateRenderer(window,
-1,
SDL_RENDERER_ACCELERATED |
SDL_RENDERER_TARGETTEXTURE);
//Set to blue so it's noticeable if it doesn't do right.
SDL_SetRenderDrawColor(renderer, 0, 0, 200, 255);
//Similarly, you must use SDL_TEXTUREACCESS_TARGET when you create the texture
backBuffer = SDL_CreateTexture(renderer,
SDL_GetWindowPixelFormat(window),
SDL_TEXTUREACCESS_TARGET,
nativeSize.w,
nativeSize.h);
//IMPORTANT Set the back buffer as the target
SDL_SetRenderTarget(renderer, backBuffer);
//Load an image yay
SDL_Surface * image = SDL_LoadBMP("Ball.bmp");
ballImage = SDL_CreateTextureFromSurface(renderer, image);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 4);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 1);
SDL_FreeSurface(image);
}
bool HandleEvents()
{
while(SDL_PollEvent(&event) )
{
printf("%d\r\n", __LINE__);
if(event.type == SDL_QUIT)
{
printf("%d\r\n", __LINE__);
return true;
}
else if(event.type == SDL_WINDOWEVENT)
{
if(event.window.event == SDL_WINDOWEVENT_SIZE_CHANGED)
{
resize = true;
printf("%d\r\n", __LINE__);
}
}
return false;
}
return false;
}
void Render()
{
SDL_RenderCopy(renderer, ballImage, NULL, NULL); //Render the entire ballImage to the backBuffer at (0, 0)
printf("%d\r\n", __LINE__);
SDL_SetRenderTarget(renderer, NULL); //Set the target back to the window
if(resize)
{
Resize();
resize = false;
}
printf("%d\r\n", __LINE__);
SDL_RenderCopy(renderer, backBuffer, &nativeSize, &newWindowSize); //Render the backBuffer onto the screen at (0,0)
SDL_RenderPresent(renderer);
SDL_RenderClear(renderer); //Clear the window buffer
SDL_SetRenderTarget(renderer, backBuffer); //Set the target back to the back buffer
SDL_RenderClear(renderer); //Clear the back buffer
printf("%d\r\n", __LINE__);
}
void Resize()
{
int w, h;
printf("%d\r\n", __LINE__);
SDL_GetWindowSize(window, &w, &h);
scaleRatioW = w / nativeSize.w;
scaleRatioH = h / nativeSize.h; //The ratio from the native size to the new size
newWindowSize.w = w;
newWindowSize.h = h;
//In order to do a resize, you must destroy the back buffer. Try without it, it doesn't work
SDL_DestroyTexture(backBuffer);
backBuffer = SDL_CreateTexture(renderer,
SDL_GetWindowPixelFormat(window),
SDL_TEXTUREACCESS_TARGET, //Again, must be created using this
nativeSize.w,
nativeSize.h);
SDL_Rect viewPort;
SDL_RenderGetViewport(renderer, &viewPort);
if(viewPort.w != newWindowSize.w || viewPort.h != newWindowSize.h)
{
//VERY IMPORTANT - Change the viewport over to the new size. It doesn't do this for you.
SDL_RenderSetViewport(renderer, &newWindowSize);
}
}
int main(int argc, char * argv[])
{
InitValues();
InitSDL();
bool quit = false;
printf("%d\r\n", __LINE__);
while(!quit)
{
printf("%d\r\n", __LINE__);
quit = HandleEvents();
Render();
}
return 0;
}
Ok, after fighting a bit with SDL2, I got it working with macOS 10.12.
Here is the problem:
SDL2 catches resize events and resends only the last 3 when you poll events using SDL_PollEvent(&event) for example.
During this time (you pressed left mouse click on resize area and hold the mouse) the SDL_PollEvent is blocking.
Here is the workaround:
Luckily, you can hook into the event handler using SDL_SetEventFilter. This will fire every time a event is being received. So for all resize events as they occur.
So what you can do is to register your own event filter callback that basically allows every event (by returning 1), listens on resize events, and sends them to your draw loop.
Example:
//register this somewhere
int filterEvent(void *userdata, SDL_Event * event) {
if (event->type == SDL_WINDOWEVENT && event->window.event == SDL_WINDOWEVENT_RESIZED) {
//convert userdata pointer to yours and trigger your own draw function
//this is called very often now
//IMPORTANT: Might be called from a different thread, see SDL_SetEventFilter docs
((MyApplicationClass *)userdata)->myDrawFunction();
//return 0 if you don't want to handle this event twice
return 0;
}
//important to allow all events, or your SDL_PollEvent doesn't get any event
return 1;
}
///after SDL_Init
SDL_SetEventFilter(filterEvent, this) //this is instance of MyApplicationClass for example
Important: Do not call SDL_PollEvent within your filterEvent callback, as this will result in weird behaviour of stuck events. (resize will not stop sometimes for example)
Turns out that this is not specific to my code, and it is a part of a wider problem with all the OpenGL libraries in MacOSX. Although recent patches in GLFW has fixed it, and in the GLUT version which is provided with XCode itself, it is quite better and you just observe a flicker in the window while resizing.
https://github.com/openframeworks/openFrameworks/issues/2800
https://github.com/openframeworks/openFrameworks/issues/2456
The problem is because of the blocking nature of the OSX window manager, which blocks all the events until the mouse is released.
To solve this, you should manipulate the library you are using and recompile it. You should add these or something similar (depending on your development environment) to the resize handler to bypass the block:
ofNotifyUpdate();
instance->display();
which is disastrous, if you are a novice, and want the ability to use library updates without much difficulty. Another solution is to override the SDL behaviour, by writing another event handler that does that. It is better as it doesn't need editing the SDL code, but adds a pile of platform specific code that I personally don't tend to and would cause a lot of problems that I don't want to spend time fixing.
After 2 days of searching, because I had just started the project and not implemented much relying on SDL, I decided to switch to GLFW which has the smoothest resize handling and I observe no flicker.

Win32 opengl Window creation

What is the best way to set up a window in win32 that has OpenGl(and glsl if the needs extra code to work) integrated into it?
I have done some research and found numerous ways of accomplishing this task i was wondering what the best way is or what way you like the most if the best way doesn't have an answer.
I have looked at nehe`s design and also the one supplied by the OpenGl super bible which both have completely different ways of accomplishing it (also the super bibles one gives me errors :().
any help would be appreciated including tutorials etc.
thanks
All your "different ways" aren't so different. You have to:
create your window (in the usual Win32 way, with RegisterClass(Ex) and CreateWindow(Ex))
create a GDI device context corresponding to your window (GetDC)
pick a pixel format which is supported by the display (optional DescribePixelFormat, then ChoosePixelFormat)
create your OpenGL context (wglCreateContext)
(optional, but required to use GLSL) link OpenGL extension functions (GLee or GLEW helpers, or glGetString(GL_EXTENSIONS) then wglGetProcAddress)
(optional) create an OpenGL 3.x context, free the compatibility context (wglCreateContextAttribs)
make the context active (wglMakeCurrent)
start using OpenGL (set up shader programs, load textures, draw stuff, etc.)
An excerpt of code showing these steps in action (not suitable for copy+paste, a bunch of RAII wrappers are used):
bool Context::attach( HWND hwnd )
{
PIXELFORMATDESCRIPTOR pfd = { sizeof(pfd), 1 };
if (!m_dc) {
scoped_window_hdc(hwnd).swap(m_dc);
pfd.dwFlags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_SUPPORT_COMPOSITION | PFD_DOUBLEBUFFER;
pfd.iPixelType = PFD_TYPE_RGBA;
pfd.cColorBits = 32;
pfd.cAlphaBits = 8;
pfd.iLayerType = PFD_MAIN_PLANE;
auto format_index = ::ChoosePixelFormat(m_dc.get(), &pfd);
if (!format_index)
return false;
if (!::SetPixelFormat(m_dc.get(), format_index, &pfd))
return false;
}
auto active_format_index = ::GetPixelFormat(m_dc.get());
if (!active_format_index)
return false;
if (!::DescribePixelFormat(m_dc.get(), active_format_index, sizeof pfd, &pfd))
return false;
if ((pfd.dwFlags & PFD_SUPPORT_OPENGL) != PFD_SUPPORT_OPENGL)
return false;
m_render_thread = ::CreateThread(NULL, 0, &RenderThreadProc, this, 0, NULL);
return m_render_thread != NULL;
}
DWORD WINAPI Context::RenderThreadProc( LPVOID param )
{
Context* const ctx = static_cast<Context*>(param);
HDC dc = ctx->m_dc.get();
SIZE canvas_size;
ctx->m_dc.check_resize(&canvas_size);
scoped_hglrc glrc(wglCreateContext(dc));
if (!glrc)
return EXIT_FAILURE;
if (!glrc.make_current(dc))
return EXIT_FAILURE;
if (ctx->m_major_version > 2 && GLEE_WGL_ARB_create_context) {
int const create_attribs[] = {
WGL_CONTEXT_MAJOR_VERSION_ARB, ctx->m_major_version,
WGL_CONTEXT_MINOR_VERSION_ARB, ctx->m_minor_version,
0
};
scoped_hglrc advrc(wglCreateContextAttribsARB(dc, 0, create_attribs));
if (advrc) {
if (!advrc.make_current(dc))
return EXIT_FAILURE;
advrc.swap(glrc);
}
}
{
const char* ver = reinterpret_cast<const char*>(glGetString(GL_VERSION));
if (ver) {
OutputDebugStringA("GL_VERSION = \"");
OutputDebugStringA(ver);
OutputDebugStringA("\"\n");
}
}
glDisable(GL_DITHER);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glClearColor(0.f, 0.f, 0.f, 1.f);
if (GLEE_WGL_EXT_swap_control)
wglSwapIntervalEXT(1);
GLuint vao;
glGenVertexArrays(1, &vao);
glBindVertexArray(vao);
while (!::InterlockedExchange(&ctx->m_stop_render, 0)) {
ctx->process_queued_tasks();
if (ctx->m_dc.check_resize(&canvas_size)) {
glViewport(0, 0, canvas_size.cx, canvas_size.cy);
ctx->process_on_resize();
}
glClear(GL_COLOR_BUFFER_BIT);
glBindVertexArray(vao);
ctx->process_on_render();
BOOL swapped = ::SwapBuffers(dc);
if (!swapped)
std::cout << "::SwapBuffers failure, GetLastError() returns " << std::hex << ::GetLastError() << std::endl;
}
ctx->m_program_db.clear();
return EXIT_SUCCESS;
}
It also doesn't cover window creation, it enables OpenGL on an existing window.
This pretty much did it for me: http://nehe.gamedev.net/tutorial/creating_an_opengl_window_%28win32%29/13001/
EDIT: Related code:
/*
* This Code Was Created By Jeff Molofee 2000
* A HUGE Thanks To Fredric Echols For Cleaning Up
* And Optimizing This Code, Making It More Flexible!
* If You've Found This Code Useful, Please Let Me Know.
* Visit My Site At nehe.gamedev.net
*/
#include <windows.h> // Header File For Windows
#include <gl\gl.h> // Header File For The OpenGL32 Library
#include <gl\glu.h> // Header File For The GLu32 Library
#include <gl\glaux.h> // Header File For The Glaux Library
HDC hDC=NULL; // Private GDI Device Context
HGLRC hRC=NULL; // Permanent Rendering Context
HWND hWnd=NULL; // Holds Our Window Handle
HINSTANCE hInstance; // Holds The Instance Of The Application
bool keys[256]; // Array Used For The Keyboard Routine
bool active=TRUE; // Window Active Flag Set To TRUE By Default
bool fullscreen=TRUE; // Fullscreen Flag Set To Fullscreen Mode By Default
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); // Declaration For WndProc
GLvoid ReSizeGLScene(GLsizei width, GLsizei height) // Resize And Initialize The GL Window
{
if (height==0) // Prevent A Divide By Zero By
{
height=1; // Making Height Equal One
}
glViewport(0,0,width,height); // Reset The Current Viewport
glMatrixMode(GL_PROJECTION); // Select The Projection Matrix
glLoadIdentity(); // Reset The Projection Matrix
// Calculate The Aspect Ratio Of The Window
gluPerspective(45.0f,(GLfloat)width/(GLfloat)height,0.1f,100.0f);
glMatrixMode(GL_MODELVIEW); // Select The Modelview Matrix
glLoadIdentity(); // Reset The Modelview Matrix
}
int InitGL(GLvoid) // All Setup For OpenGL Goes Here
{
glShadeModel(GL_SMOOTH); // Enable Smooth Shading
glClearColor(0.0f, 0.0f, 0.0f, 0.5f); // Black Background
glClearDepth(1.0f); // Depth Buffer Setup
glEnable(GL_DEPTH_TEST); // Enables Depth Testing
glDepthFunc(GL_LEQUAL); // The Type Of Depth Testing To Do
glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST); // Really Nice Perspective Calculations
return TRUE; // Initialization Went OK
}
int DrawGLScene(GLvoid) // Here's Where We Do All The Drawing
{
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // Clear Screen And Depth Buffer
glLoadIdentity(); // Reset The Current Modelview Matrix
return TRUE; // Everything Went OK
}
GLvoid KillGLWindow(GLvoid) // Properly Kill The Window
{
if (fullscreen) // Are We In Fullscreen Mode?
{
ChangeDisplaySettings(NULL,0); // If So Switch Back To The Desktop
ShowCursor(TRUE); // Show Mouse Pointer
}
if (hRC) // Do We Have A Rendering Context?
{
if (!wglMakeCurrent(NULL,NULL)) // Are We Able To Release The DC And RC Contexts?
{
MessageBox(NULL,"Release Of DC And RC Failed.","SHUTDOWN ERROR",MB_OK | MB_ICONINFORMATION);
}
if (!wglDeleteContext(hRC)) // Are We Able To Delete The RC?
{
MessageBox(NULL,"Release Rendering Context Failed.","SHUTDOWN ERROR",MB_OK | MB_ICONINFORMATION);
}
hRC=NULL; // Set RC To NULL
}
if (hDC && !ReleaseDC(hWnd,hDC)) // Are We Able To Release The DC
{
MessageBox(NULL,"Release Device Context Failed.","SHUTDOWN ERROR",MB_OK | MB_ICONINFORMATION);
hDC=NULL; // Set DC To NULL
}
if (hWnd && !DestroyWindow(hWnd)) // Are We Able To Destroy The Window?
{
MessageBox(NULL,"Could Not Release hWnd.","SHUTDOWN ERROR",MB_OK | MB_ICONINFORMATION);
hWnd=NULL; // Set hWnd To NULL
}
if (!UnregisterClass("OpenGL",hInstance)) // Are We Able To Unregister Class
{
MessageBox(NULL,"Could Not Unregister Class.","SHUTDOWN ERROR",MB_OK | MB_ICONINFORMATION);
hInstance=NULL; // Set hInstance To NULL
}
}
/* This Code Creates Our OpenGL Window. Parameters Are: *
* title - Title To Appear At The Top Of The Window *
* width - Width Of The GL Window Or Fullscreen Mode *
* height - Height Of The GL Window Or Fullscreen Mode *
* bits - Number Of Bits To Use For Color (8/16/24/32) *
* fullscreenflag - Use Fullscreen Mode (TRUE) Or Windowed Mode (FALSE) */
BOOL CreateGLWindow(char* title, int width, int height, int bits, bool fullscreenflag)
{
GLuint PixelFormat; // Holds The Results After Searching For A Match
WNDCLASS wc; // Windows Class Structure
DWORD dwExStyle; // Window Extended Style
DWORD dwStyle; // Window Style
RECT WindowRect; // Grabs Rectangle Upper Left / Lower Right Values
WindowRect.left=(long)0; // Set Left Value To 0
WindowRect.right=(long)width; // Set Right Value To Requested Width
WindowRect.top=(long)0; // Set Top Value To 0
WindowRect.bottom=(long)height; // Set Bottom Value To Requested Height
fullscreen=fullscreenflag; // Set The Global Fullscreen Flag
hInstance = GetModuleHandle(NULL); // Grab An Instance For Our Window
wc.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC; // Redraw On Size, And Own DC For Window.
wc.lpfnWndProc = (WNDPROC) WndProc; // WndProc Handles Messages
wc.cbClsExtra = 0; // No Extra Window Data
wc.cbWndExtra = 0; // No Extra Window Data
wc.hInstance = hInstance; // Set The Instance
wc.hIcon = LoadIcon(NULL, IDI_WINLOGO); // Load The Default Icon
wc.hCursor = LoadCursor(NULL, IDC_ARROW); // Load The Arrow Pointer
wc.hbrBackground = NULL; // No Background Required For GL
wc.lpszMenuName = NULL; // We Don't Want A Menu
wc.lpszClassName = "OpenGL"; // Set The Class Name
if (!RegisterClass(&wc)) // Attempt To Register The Window Class
{
MessageBox(NULL,"Failed To Register The Window Class.","ERROR",MB_OK|MB_ICONEXCLAMATION);
return FALSE; // Return FALSE
}
if (fullscreen) // Attempt Fullscreen Mode?
{
DEVMODE dmScreenSettings; // Device Mode
memset(&dmScreenSettings,0,sizeof(dmScreenSettings)); // Makes Sure Memory's Cleared
dmScreenSettings.dmSize=sizeof(dmScreenSettings); // Size Of The Devmode Structure
dmScreenSettings.dmPelsWidth = width; // Selected Screen Width
dmScreenSettings.dmPelsHeight = height; // Selected Screen Height
dmScreenSettings.dmBitsPerPel = bits; // Selected Bits Per Pixel
dmScreenSettings.dmFields=DM_BITSPERPEL|DM_PELSWIDTH|DM_PELSHEIGHT;
// Try To Set Selected Mode And Get Results. NOTE: CDS_FULLSCREEN Gets Rid Of Start Bar.
if (ChangeDisplaySettings(&dmScreenSettings,CDS_FULLSCREEN)!=DISP_CHANGE_SUCCESSFUL)
{
// If The Mode Fails, Offer Two Options. Quit Or Use Windowed Mode.
if (MessageBox(NULL,"The Requested Fullscreen Mode Is Not Supported By\nYour Video Card. Use Windowed Mode Instead?","NeHe GL",MB_YESNO|MB_ICONEXCLAMATION)==IDYES)
{
fullscreen=FALSE; // Windowed Mode Selected. Fullscreen = FALSE
}
else
{
// Pop Up A Message Box Letting User Know The Program Is Closing.
MessageBox(NULL,"Program Will Now Close.","ERROR",MB_OK|MB_ICONSTOP);
return FALSE; // Return FALSE
}
}
}
if (fullscreen) // Are We Still In Fullscreen Mode?
{
dwExStyle=WS_EX_APPWINDOW; // Window Extended Style
dwStyle=WS_POPUP; // Windows Style
ShowCursor(FALSE); // Hide Mouse Pointer
}
else
{
dwExStyle=WS_EX_APPWINDOW | WS_EX_WINDOWEDGE; // Window Extended Style
dwStyle=WS_OVERLAPPEDWINDOW; // Windows Style
}
AdjustWindowRectEx(&WindowRect, dwStyle, FALSE, dwExStyle); // Adjust Window To True Requested Size
// Create The Window
if (!(hWnd=CreateWindowEx( dwExStyle, // Extended Style For The Window
"OpenGL", // Class Name
title, // Window Title
dwStyle | // Defined Window Style
WS_CLIPSIBLINGS | // Required Window Style
WS_CLIPCHILDREN, // Required Window Style
0, 0, // Window Position
WindowRect.right-WindowRect.left, // Calculate Window Width
WindowRect.bottom-WindowRect.top, // Calculate Window Height
NULL, // No Parent Window
NULL, // No Menu
hInstance, // Instance
NULL))) // Dont Pass Anything To WM_CREATE
{
KillGLWindow(); // Reset The Display
MessageBox(NULL,"Window Creation Error.","ERROR",MB_OK|MB_ICONEXCLAMATION);
return FALSE; // Return FALSE
}
static PIXELFORMATDESCRIPTOR pfd= // pfd Tells Windows How We Want Things To Be
{
sizeof(PIXELFORMATDESCRIPTOR), // Size Of This Pixel Format Descriptor
1, // Version Number
PFD_DRAW_TO_WINDOW | // Format Must Support Window
PFD_SUPPORT_OPENGL | // Format Must Support OpenGL
PFD_DOUBLEBUFFER, // Must Support Double Buffering
PFD_TYPE_RGBA, // Request An RGBA Format
bits, // Select Our Color Depth
0, 0, 0, 0, 0, 0, // Color Bits Ignored
0, // No Alpha Buffer
0, // Shift Bit Ignored
0, // No Accumulation Buffer
0, 0, 0, 0, // Accumulation Bits Ignored
16, // 16Bit Z-Buffer (Depth Buffer)
0, // No Stencil Buffer
0, // No Auxiliary Buffer
PFD_MAIN_PLANE, // Main Drawing Layer
0, // Reserved
0, 0, 0 // Layer Masks Ignored
};
if (!(hDC=GetDC(hWnd))) // Did We Get A Device Context?
{
KillGLWindow(); // Reset The Display
MessageBox(NULL,"Can't Create A GL Device Context.","ERROR",MB_OK|MB_ICONEXCLAMATION);
return FALSE; // Return FALSE
}
if (!(PixelFormat=ChoosePixelFormat(hDC,&pfd))) // Did Windows Find A Matching Pixel Format?
{
KillGLWindow(); // Reset The Display
MessageBox(NULL,"Can't Find A Suitable PixelFormat.","ERROR",MB_OK|MB_ICONEXCLAMATION);
return FALSE; // Return FALSE
}
if(!SetPixelFormat(hDC,PixelFormat,&pfd)) // Are We Able To Set The Pixel Format?
{
KillGLWindow(); // Reset The Display
MessageBox(NULL,"Can't Set The PixelFormat.","ERROR",MB_OK|MB_ICONEXCLAMATION);
return FALSE; // Return FALSE
}
if (!(hRC=wglCreateContext(hDC))) // Are We Able To Get A Rendering Context?
{
KillGLWindow(); // Reset The Display
MessageBox(NULL,"Can't Create A GL Rendering Context.","ERROR",MB_OK|MB_ICONEXCLAMATION);
return FALSE; // Return FALSE
}
if(!wglMakeCurrent(hDC,hRC)) // Try To Activate The Rendering Context
{
KillGLWindow(); // Reset The Display
MessageBox(NULL,"Can't Activate The GL Rendering Context.","ERROR",MB_OK|MB_ICONEXCLAMATION);
return FALSE; // Return FALSE
}
ShowWindow(hWnd,SW_SHOW); // Show The Window
SetForegroundWindow(hWnd); // Slightly Higher Priority
SetFocus(hWnd); // Sets Keyboard Focus To The Window
ReSizeGLScene(width, height); // Set Up Our Perspective GL Screen
if (!InitGL()) // Initialize Our Newly Created GL Window
{
KillGLWindow(); // Reset The Display
MessageBox(NULL,"Initialization Failed.","ERROR",MB_OK|MB_ICONEXCLAMATION);
return FALSE; // Return FALSE
}
return TRUE; // Success
}
LRESULT CALLBACK WndProc( HWND hWnd, // Handle For This Window
UINT uMsg, // Message For This Window
WPARAM wParam, // Additional Message Information
LPARAM lParam) // Additional Message Information
{
switch (uMsg) // Check For Windows Messages
{
case WM_ACTIVATE: // Watch For Window Activate Message
{
if (!HIWORD(wParam)) // Check Minimization State
{
active=TRUE; // Program Is Active
}
else
{
active=FALSE; // Program Is No Longer Active
}
return 0; // Return To The Message Loop
}
case WM_SYSCOMMAND: // Intercept System Commands
{
switch (wParam) // Check System Calls
{
case SC_SCREENSAVE: // Screensaver Trying To Start?
case SC_MONITORPOWER: // Monitor Trying To Enter Powersave?
return 0; // Prevent From Happening
}
break; // Exit
}
case WM_CLOSE: // Did We Receive A Close Message?
{
PostQuitMessage(0); // Send A Quit Message
return 0; // Jump Back
}
case WM_KEYDOWN: // Is A Key Being Held Down?
{
keys[wParam] = TRUE; // If So, Mark It As TRUE
return 0; // Jump Back
}
case WM_KEYUP: // Has A Key Been Released?
{
keys[wParam] = FALSE; // If So, Mark It As FALSE
return 0; // Jump Back
}
case WM_SIZE: // Resize The OpenGL Window
{
ReSizeGLScene(LOWORD(lParam),HIWORD(lParam)); // LoWord=Width, HiWord=Height
return 0; // Jump Back
}
}
// Pass All Unhandled Messages To DefWindowProc
return DefWindowProc(hWnd,uMsg,wParam,lParam);
}
int WINAPI WinMain( HINSTANCE hInstance, // Instance
HINSTANCE hPrevInstance, // Previous Instance
LPSTR lpCmdLine, // Command Line Parameters
int nCmdShow) // Window Show State
{
MSG msg; // Windows Message Structure
BOOL done=FALSE; // Bool Variable To Exit Loop
// Ask The User Which Screen Mode They Prefer
if (MessageBox(NULL,"Would You Like To Run In Fullscreen Mode?", "Start FullScreen?",MB_YESNO|MB_ICONQUESTION)==IDNO)
{
fullscreen=FALSE; // Windowed Mode
}
// Create Our OpenGL Window
if (!CreateGLWindow("NeHe's OpenGL Framework",640,480,16,fullscreen))
{
return 0; // Quit If Window Was Not Created
}
while(!done) // Loop That Runs While done=FALSE
{
if (PeekMessage(&msg,NULL,0,0,PM_REMOVE)) // Is There A Message Waiting?
{
if (msg.message==WM_QUIT) // Have We Received A Quit Message?
{
done=TRUE; // If So done=TRUE
}
else // If Not, Deal With Window Messages
{
TranslateMessage(&msg); // Translate The Message
DispatchMessage(&msg); // Dispatch The Message
}
}
else // If There Are No Messages
{
// Draw The Scene. Watch For ESC Key And Quit Messages From DrawGLScene()
if (active) // Program Active?
{
if (keys[VK_ESCAPE]) // Was ESC Pressed?
{
done=TRUE; // ESC Signalled A Quit
}
else // Not Time To Quit, Update Screen
{
DrawGLScene(); // Draw The Scene
SwapBuffers(hDC); // Swap Buffers (Double Buffering)
}
}
if (keys[VK_F1]) // Is F1 Being Pressed?
{
keys[VK_F1]=FALSE; // If So Make Key FALSE
KillGLWindow(); // Kill Our Current Window
fullscreen=!fullscreen; // Toggle Fullscreen / Windowed Mode
// Recreate Our OpenGL Window
if (!CreateGLWindow("NeHe's OpenGL Framework",640,480,16,fullscreen))
{
return 0; // Quit If Window Was Not Created
}
}
}
}
// Shutdown
KillGLWindow(); // Kill The Window
return (msg.wParam); // Exit The Program
}
If you really, really want to do it yourself, you can still have a look at how GLFW does it. In its source directory, you have a win32-specific directory, in which you have a windowing-specic .c file. You should find everything you need in this file.
This page of the OpenGL Wiki could help you, too.
The best way is NOT to do it yourself. OpenGL context creation is a bottomless abyss of complexity. You have lots of tools that can do the dirty work for you, and what's more, it'll work on Linux and Mac too
GLFW, FreeGlut, SDL, SFML are probably the most common.
See GLFW's user guide, for instance, to see how easy these libs make it to create a window :
glfwInit();
glfwOpenWindow( 300,300, 0,0,0,0,0,0, GLFW_WINDOW );
// bam, you're done

Resources