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
1721Win32RenderWindow::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
8567Win32RenderWindow::~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+ }
0 commit comments