Skip to content

Commit a6ab527

Browse files
committed
ISSUE-21: Moved most Win32 message handling to the Win32RenderWindow class.
1 parent 2e29dd4 commit a6ab527

File tree

3 files changed

+426
-328
lines changed

3 files changed

+426
-328
lines changed
Lines changed: 223 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -1,44 +1,26 @@
11

22
#include "Win32RenderWindow.h"
3+
#include "resource.h"
4+
#include <Unknwn.h>
5+
#include <ShObjIdl.h>
6+
#include <KnownFolders.h>
7+
#include <shellapi.h>
8+
#include "throw_helpers.h"
9+
#include "cairo.h"
10+
#include "cairo-win32.h"
11+
#include <memory>
12+
#include <functional>
13+
#include <atomic>
14+
#include <wrl.h>
315

4-
LRESULT CALLBACK InternalWindowProc( HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam )
5-
{
6-
LONG_PTR ObjPtr = GetWindowLongPtr(hwnd, 0);
7-
8-
if (ObjPtr == 0) {
9-
return( DefWindowProc( hwnd, msg, wparam, lparam ) );
10-
} else {
11-
return( reinterpret_cast<Win32RenderWindow*>(ObjPtr)->WindowProc(hwnd, msg, wparam, lparam) );
12-
}
13-
}
14-
15-
16+
using namespace std;
17+
using namespace std::experimental;
18+
using namespace std::experimental::drawing;
19+
using namespace Microsoft::WRL;
1620

1721
Win32RenderWindow::Win32RenderWindow(unsigned int width, unsigned int height, const std::wstring& caption) :
1822
handle( 0 )
1923
{
20-
WNDCLASSEX wc;
21-
22-
// Setup the window class
23-
memset(&wc, 0, sizeof(wc));
24-
25-
wc.cbSize = sizeof(WNDCLASSEX);
26-
wc.style = CS_HREDRAW | CS_VREDRAW;
27-
wc.lpfnWndProc = InternalWindowProc;
28-
wc.cbClsExtra = 0;
29-
wc.cbWndExtra = sizeof(this);
30-
wc.hInstance = 0;
31-
wc.hIcon = LoadIcon(NULL, IDI_APPLICATION);
32-
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
33-
wc.hbrBackground = (HBRUSH)GetStockObject(BLACK_BRUSH);
34-
wc.lpszMenuName = NULL;
35-
wc.lpszClassName = L"N3888_RefImpl";
36-
wc.hIconSm = LoadIcon(NULL, IDI_ASTERISK);
37-
38-
39-
// Register the window class
40-
RegisterClassEx(&wc);
41-
4224
// Record the desired client window size
4325
RECT rc;
4426
rc.top = rc.left = 0;
@@ -58,7 +40,7 @@ Win32RenderWindow::Win32RenderWindow(unsigned int width, unsigned int height, co
5840
// Create an instance of the window
5941
handle = CreateWindowEx(
6042
NULL, // extended style
61-
wc.lpszClassName, // class name
43+
L"N3888_RefImpl", // class name
6244
caption.c_str(), // instance title
6345
(WS_OVERLAPPEDWINDOW | WS_VISIBLE), // window style
6446
lleft, ltop, // initial x, y
@@ -79,7 +61,7 @@ Win32RenderWindow::Win32RenderWindow(unsigned int width, unsigned int height, co
7961
UpdateWindow(handle);
8062
}
8163

82-
64+
g_psurface = unique_ptr<surface>(new surface(move(make_surface(format::argb32, lwidth, lheight))));
8365
}
8466

8567
Win32RenderWindow::~Win32RenderWindow()
@@ -92,9 +74,54 @@ Win32RenderWindow::~Win32RenderWindow()
9274
}
9375

9476

95-
LRESULT Win32RenderWindow::WindowProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam)
77+
HWND Win32RenderWindow::GetHandle()
9678
{
79+
return(handle);
80+
}
81+
82+
83+
// Message handler for about box.
84+
INT_PTR CALLBACK About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam) {
85+
UNREFERENCED_PARAMETER(lParam);
86+
switch (message) {
87+
case WM_INITDIALOG:
88+
return (INT_PTR)TRUE;
89+
90+
case WM_COMMAND:
91+
if (LOWORD(wParam) == IDOK || LOWORD(wParam) == IDCANCEL) {
92+
EndDialog(hDlg, LOWORD(wParam));
93+
return (INT_PTR)TRUE;
94+
}
95+
break;
96+
case WM_NOTIFY:
97+
{
98+
PNMLINK pnmLink = reinterpret_cast<PNMLINK>(lParam);
99+
if ((pnmLink->hdr.idFrom == IDC_SYSLINK1) || (pnmLink->hdr.idFrom == IDC_SYSLINK2)) {
100+
switch (pnmLink->hdr.code)
101+
{
102+
case NM_CLICK:
103+
// Intentional fall-through.
104+
case NM_RETURN:
105+
{
106+
auto shExecResult = reinterpret_cast<int>(ShellExecute(nullptr, L"open", pnmLink->item.szUrl, nullptr, nullptr, SW_SHOW));
107+
if (shExecResult <= 32) {
108+
wstringstream err;
109+
err << L"Error calling ShellExecute while trying to open the link. Return code: " << to_wstring(shExecResult) << "." << endl;
110+
MessageBox(hDlg, err.str().c_str(), L"Error opening link", MB_OK | MB_ICONEXCLAMATION);
111+
}
112+
}
113+
return (INT_PTR)TRUE;
114+
}
115+
}
116+
}
117+
break;
118+
}
119+
return (INT_PTR)FALSE;
120+
}
121+
97122

123+
LRESULT Win32RenderWindow::WindowProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam)
124+
{
98125
switch (msg)
99126
{
100127
case WM_CREATE:
@@ -104,14 +131,6 @@ LRESULT Win32RenderWindow::WindowProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM
104131
return(0);
105132
} break;
106133

107-
case WM_PAINT:
108-
{
109-
// This message is handled by the default handler to avoid a
110-
// repeated sending of the message. This results in the ability
111-
// to process all pending messages at once without getting stuck
112-
// in an eternal loop.
113-
} break;
114-
115134
case WM_CLOSE:
116135
{
117136
// This message is sent when a window or an application should
@@ -127,12 +146,168 @@ LRESULT Win32RenderWindow::WindowProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM
127146

128147
case WM_SIZE:
129148
{
130-
//EvtWindowResizePtr pEvent = EvtWindowResizePtr(new EvtWindowResize(hwnd, wparam, lparam));
131-
//EvtManager.ProcessEvent(pEvent);
149+
auto width = lparam & 0xFFFF;
150+
auto height = (lparam & 0xFFFF0000) >> 16;
151+
152+
g_psurface = unique_ptr<surface>(new surface(move(make_surface(format::argb32, width, height))));
153+
154+
} break;
155+
156+
case WM_COMMAND:
157+
{
158+
int wmId, wmEvent;
159+
160+
wmId = LOWORD(wparam);
161+
wmEvent = HIWORD(wparam);
162+
// Parse the menu selections:
163+
switch (wmId)
164+
{
165+
case IDM_ABOUT:
166+
{
167+
auto aboutResult = DialogBox(0, MAKEINTRESOURCE(IDD_ABOUTBOX), handle, About);
168+
if (aboutResult <= 0) {
169+
throw_get_last_error<logic_error>("Failed call to DialogBox.");
170+
}
171+
} break;
172+
173+
case ID_EDIT_SCREENCAPTURE:
174+
ShowSaveAsPNGDialog();
175+
break;
176+
177+
case IDM_EXIT:
178+
DestroyWindow(handle);
179+
break;
180+
181+
default:
182+
return DefWindowProc(handle, msg, wparam, lparam);
183+
} break;
184+
} break;
185+
186+
case WM_ENTERSIZEMOVE:
187+
{
188+
//g_doNotPaint = true; // Don't paint while resizing to avoid flicker.
189+
return DefWindowProc(handle, msg, wparam, lparam);
190+
} break;
191+
192+
case WM_EXITSIZEMOVE:
193+
{
194+
//g_doNotPaint = false;
195+
return DefWindowProc(handle, msg, wparam, lparam);
132196
} break;
133197

198+
case WM_PAINT:
199+
{
200+
//if (!g_doNotPaint) {
201+
// OnPaint(handle, msg, wparam, lparam);
202+
//}
203+
PAINTSTRUCT ps;
204+
HDC hdc;
205+
206+
// Flush to ensure that it is drawn to the window.
207+
hdc = BeginPaint(handle, &ps);
208+
g_psurface->flush();
209+
210+
auto surface = make_surface(cairo_win32_surface_create(hdc));
211+
auto ctxt = context(surface);
212+
ctxt.set_source_surface(*g_psurface, 0.0, 0.0);
213+
ctxt.paint();
214+
surface.flush();
215+
EndPaint(handle, &ps);
216+
} break;
134217
}
135218

136219
return(DefWindowProc(hwnd, msg, wparam, lparam));
137220

138-
}
221+
}
222+
223+
void Win32RenderWindow::ShowSaveAsPNGDialog() {
224+
225+
ComPtr<IFileSaveDialog> fsd;
226+
throw_if_failed_hresult<logic_error>(
227+
CoCreateInstance(CLSID_FileSaveDialog, nullptr, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&fsd)), "Failed call to CoCreateInstance for IFileSaveDialog.");
228+
229+
FILEOPENDIALOGOPTIONS fodOptions{};
230+
throw_if_failed_hresult<logic_error>(
231+
fsd->GetOptions(&fodOptions), "Failed call to IFileDialog::GetOptions.");
232+
throw_if_failed_hresult<logic_error>(
233+
fsd->SetOptions(fodOptions | FOS_FORCEFILESYSTEM | FOS_OVERWRITEPROMPT | FOS_PATHMUSTEXIST), "Failed call to IFileDialog::SetOptions.");
234+
235+
const COMDLG_FILTERSPEC filterSpec[] = {
236+
{ L"PNG", L".png" }
237+
};
238+
throw_if_failed_hresult<logic_error>(
239+
fsd->SetFileTypes(ARRAYSIZE(filterSpec), filterSpec), "Failed call to IFileDialog::SetFileTypes.");
240+
throw_if_failed_hresult<logic_error>(
241+
fsd->SetFileTypeIndex(1U), "Failed call to IFileDialog::SetFileTypeIndex.");
242+
throw_if_failed_hresult<logic_error>(
243+
fsd->SetDefaultExtension(L"png"), "Failed call to IFileDialog::SetDefaultExtension.");
244+
245+
ComPtr<IKnownFolderManager> kfm;
246+
throw_if_failed_hresult<logic_error>(
247+
CoCreateInstance(CLSID_KnownFolderManager, nullptr, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&kfm)), "Failed call to CoCreateInstance for IKnownFolderManager.");
248+
ComPtr<IKnownFolder> picturesKnownFolder;
249+
throw_if_failed_hresult<logic_error>(
250+
kfm->GetFolder(FOLDERID_Pictures, &picturesKnownFolder), "Failed call to IKnownFolderManager::GetFolder.");
251+
ComPtr<IShellItem> picturesShellItem;
252+
throw_if_failed_hresult<logic_error>(
253+
picturesKnownFolder->GetShellItem(0, IID_PPV_ARGS(&picturesShellItem)), "Failed call to IKnownFolder::GetShellItem.");
254+
255+
throw_if_failed_hresult<logic_error>(
256+
fsd->SetDefaultFolder(picturesShellItem.Get()), "Failed call to IFileDialog::SetDefaultFolder.");
257+
258+
HRESULT hr;
259+
hr = fsd->Show(nullptr);
260+
if (SUCCEEDED(hr)) {
261+
// The user picked a file.
262+
ComPtr<IShellItem> result;
263+
throw_if_failed_hresult<logic_error>(
264+
fsd->GetResult(&result), "Failed call to IFileDialog::GetResult.");
265+
wstring wfilename;
266+
PWSTR pwstrFilename = nullptr;
267+
throw_if_failed_hresult<logic_error>(
268+
result->GetDisplayName(SIGDN_FILESYSPATH, &pwstrFilename), "Failed call to IShellItem::GetDisplayName.");
269+
try {
270+
wfilename = pwstrFilename;
271+
CoTaskMemFree(pwstrFilename);
272+
}
273+
catch (...) {
274+
CoTaskMemFree(pwstrFilename);
275+
throw;
276+
}
277+
HANDLE hFile = CreateFileW(wfilename.c_str(), GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, nullptr, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, nullptr);
278+
if (hFile == INVALID_HANDLE_VALUE) {
279+
throw_get_last_error<runtime_error>("Failed call to CreateFileW.");
280+
}
281+
if (CloseHandle(hFile) == 0) {
282+
throw_get_last_error<runtime_error>("Failed call to CloseHandle.");
283+
}
284+
auto bufferSize = GetShortPathNameW(wfilename.c_str(), nullptr, 0);
285+
wstring wshortfilename;
286+
wshortfilename.resize(bufferSize);
287+
if (GetShortPathNameW(wfilename.c_str(), &wshortfilename[0], bufferSize) == 0) {
288+
throw_get_last_error<runtime_error>("Failed call to GetShortPathNameW.");
289+
}
290+
char defaultChar = '*';
291+
BOOL usedDefault = FALSE;
292+
auto mbBufferSize = WideCharToMultiByte(CP_ACP, WC_NO_BEST_FIT_CHARS, wshortfilename.c_str(), -1, nullptr, 0, &defaultChar, &usedDefault);
293+
string mbFileName;
294+
usedDefault = FALSE;
295+
mbFileName.resize(mbBufferSize);
296+
if (WideCharToMultiByte(CP_ACP, WC_NO_BEST_FIT_CHARS, wshortfilename.c_str(), -1, &mbFileName[0], mbBufferSize, &defaultChar, &usedDefault) == 0) {
297+
throw_get_last_error<runtime_error>("Failed call to WideCharToMultiByte.");
298+
}
299+
if (usedDefault != FALSE) {
300+
throw runtime_error("Could not convert short filename string to multibyte from wide character.");
301+
}
302+
g_psurface->write_to_png(mbFileName);
303+
}
304+
else {
305+
if (hr == HRESULT_FROM_WIN32(ERROR_CANCELLED)) {
306+
// Do nothing. The user clicked cancel.
307+
}
308+
else {
309+
throw_if_failed_hresult<logic_error>(hr, "Failed call to IModalWindow::Show.");
310+
}
311+
}
312+
313+
}

win32/N3888_RefImpl/Win32RenderWindow.h

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@
55
#define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers
66
#include <windows.h>
77
#include <string>
8+
#include "drawing.h"
9+
#include <memory>
810

911
class Win32RenderWindow
1012
{
@@ -13,10 +15,19 @@ class Win32RenderWindow
1315
Win32RenderWindow( unsigned int width, unsigned int height, const std::wstring& caption );
1416
~Win32RenderWindow();
1517

18+
HWND GetHandle();
19+
20+
1621
LRESULT WindowProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam);
22+
void ShowSaveAsPNGDialog();
23+
24+
std::unique_ptr<std::experimental::drawing::surface> g_psurface;
1725

1826
private:
1927
HWND handle;
28+
29+
RECT g_previousClientRect;
30+
//ref_counted_bool g_doNotPaint;
2031
};
2132

2233
#endif // _WIN32RENDERWINDOW_

0 commit comments

Comments
 (0)