win32window.cpp

main 1237 lines · 37.7 KB Raw
Alex Alex Commit Client Upload Part 2 19/10/2026 00:06
1/*
2 * Copyright (c) 2010-2017 OTClient <https://github.com/edubart/otclient>
3 *
4 * Permission is hereby granted, free of charge, to any person obtaining a copy
5 * of this software and associated documentation files (the "Software"), to deal
6 * in the Software without restriction, including without limitation the rights
7 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8 * copies of the Software, and to permit persons to whom the Software is
9 * furnished to do so, subject to the following conditions:
10 *
11 * The above copyright notice and this permission notice shall be included in
12 * all copies or substantial portions of the Software.
13 *
14 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
20 * THE SOFTWARE.
21 */
22
23#ifdef WIN32
24
25#include "win32window.h"
26#include <framework/graphics/image.h>
27#include <framework/core/application.h>
28#include <framework/core/resourcemanager.h>
29#include <framework/core/eventdispatcher.h>
30#include <framework/util/stats.h>
31#include <framework/util/extras.h>
32
33#define HSB_BIT_SET(p, n) (p[(n)/8] |= (128 >>((n)%8)))
34
35WIN32Window::WIN32Window()
36{
37 m_window = 0;
38 m_instance = 0;
39 m_deviceContext = 0;
40 m_cursor = 0;
41 m_minimumSize = Size(600,480);
42 m_size = Size(600,480);
43 m_hidden = true;
44
45#ifdef OPENGL_ES
46 m_eglConfig = 0;
47 m_eglContext = 0;
48 m_eglDisplay = 0;
49 m_eglSurface = 0;
50#else
51 m_wglContext = 0;
52#endif
53
54 m_keyMap[VK_ESCAPE] = Fw::KeyEscape;
55 m_keyMap[VK_TAB] = Fw::KeyTab;
56 m_keyMap[VK_RETURN] = Fw::KeyEnter;
57 m_keyMap[VK_BACK] = Fw::KeyBackspace;
58 m_keyMap[VK_SPACE] = Fw::KeySpace;
59
60 m_keyMap[VK_PRIOR] = Fw::KeyPageUp;
61 m_keyMap[VK_NEXT] = Fw::KeyPageDown;
62 m_keyMap[VK_HOME] = Fw::KeyHome;
63 m_keyMap[VK_END] = Fw::KeyEnd;
64 m_keyMap[VK_INSERT] = Fw::KeyInsert;
65 m_keyMap[VK_DELETE] = Fw::KeyDelete;
66
67 m_keyMap[VK_UP] = Fw::KeyUp;
68 m_keyMap[VK_DOWN] = Fw::KeyDown;
69 m_keyMap[VK_LEFT] = Fw::KeyLeft;
70 m_keyMap[VK_RIGHT] = Fw::KeyRight;
71
72 m_keyMap[VK_NUMLOCK] = Fw::KeyNumLock;
73 m_keyMap[VK_SCROLL] = Fw::KeyScrollLock;
74 m_keyMap[VK_CAPITAL] = Fw::KeyCapsLock;
75 m_keyMap[VK_SNAPSHOT] = Fw::KeyPrintScreen;
76 m_keyMap[VK_PAUSE] = Fw::KeyPause;
77
78 m_keyMap[VK_CONTROL] = Fw::KeyCtrl;
79 m_keyMap[VK_LCONTROL] = Fw::KeyCtrl;
80 m_keyMap[VK_RCONTROL] = Fw::KeyCtrl;
81 m_keyMap[VK_SHIFT] = Fw::KeyShift;
82 m_keyMap[VK_LSHIFT] = Fw::KeyShift;
83 m_keyMap[VK_RSHIFT] = Fw::KeyShift;
84 m_keyMap[VK_MENU] = Fw::KeyAlt;
85 m_keyMap[VK_LMENU] = Fw::KeyAlt;
86 m_keyMap[VK_RMENU] = Fw::KeyAlt;
87 m_keyMap[VK_LWIN] = Fw::KeyMeta;
88 m_keyMap[VK_RWIN] = Fw::KeyMeta;
89 m_keyMap[VK_APPS] = Fw::KeyMenu;
90
91 // ascii characters
92 /*
93 m_keyMap['!'] = Fw::KeyExclamation;
94 m_keyMap['"'] = Fw::KeyQuote;
95 m_keyMap['#'] = Fw::KeyNumberSign;
96 m_keyMap['$'] = Fw::KeyDollar;
97 m_keyMap['%'] = Fw::KeyPercent;
98 m_keyMap['$'] = Fw::KeyAmpersand;
99 m_keyMap['\''] = Fw::KeyApostrophe;
100 m_keyMap['('] = Fw::KeyLeftParen;
101 m_keyMap[')'] = Fw::KeyRightParen;
102 m_keyMap['*'] = Fw::KeyAsterisk;
103 m_keyMap['+'] = Fw::KeyPlus;
104 m_keyMap['.'] = Fw::KeyComma;
105 m_keyMap['-'] = Fw::KeyMinus;
106 m_keyMap['.'] = Fw::KeyPeriod;
107 m_keyMap['/'] = Fw::KeySlash;
108 */
109
110 m_keyMap['0'] = Fw::Key0;
111 m_keyMap['1'] = Fw::Key1;
112 m_keyMap['2'] = Fw::Key2;
113 m_keyMap['3'] = Fw::Key3;
114 m_keyMap['4'] = Fw::Key4;
115 m_keyMap['5'] = Fw::Key5;
116 m_keyMap['6'] = Fw::Key6;
117 m_keyMap['7'] = Fw::Key7;
118 m_keyMap['8'] = Fw::Key8;
119 m_keyMap['9'] = Fw::Key9;
120
121 m_keyMap[VK_NUMPAD0] = Fw::Key0;
122 m_keyMap[VK_NUMPAD1] = Fw::Key1;
123 m_keyMap[VK_NUMPAD2] = Fw::Key2;
124 m_keyMap[VK_NUMPAD3] = Fw::Key3;
125 m_keyMap[VK_NUMPAD4] = Fw::Key4;
126 m_keyMap[VK_NUMPAD5] = Fw::Key5;
127 m_keyMap[VK_NUMPAD6] = Fw::Key6;
128 m_keyMap[VK_NUMPAD7] = Fw::Key7;
129 m_keyMap[VK_NUMPAD8] = Fw::Key8;
130 m_keyMap[VK_NUMPAD9] = Fw::Key9;
131
132 /*
133 m_keyMap[':'] = Fw::KeyColon;
134 m_keyMap[';'] = Fw::KeySemicolon;
135 m_keyMap['<'] = Fw::KeyLess;
136 m_keyMap['='] = Fw::KeyEqual;
137 m_keyMap['>'] = Fw::KeyGreater;
138 m_keyMap['?'] = Fw::KeyQuestion;
139 m_keyMap['@'] = Fw::KeyAtSign;
140 */
141
142 m_keyMap['A'] = Fw::KeyA;
143 m_keyMap['B'] = Fw::KeyB;
144 m_keyMap['C'] = Fw::KeyC;
145 m_keyMap['D'] = Fw::KeyD;
146 m_keyMap['E'] = Fw::KeyE;
147 m_keyMap['F'] = Fw::KeyF;
148 m_keyMap['G'] = Fw::KeyG;
149 m_keyMap['H'] = Fw::KeyH;
150 m_keyMap['I'] = Fw::KeyI;
151 m_keyMap['J'] = Fw::KeyJ;
152 m_keyMap['K'] = Fw::KeyK;
153 m_keyMap['L'] = Fw::KeyL;
154 m_keyMap['M'] = Fw::KeyM;
155 m_keyMap['N'] = Fw::KeyN;
156 m_keyMap['O'] = Fw::KeyO;
157 m_keyMap['P'] = Fw::KeyP;
158 m_keyMap['Q'] = Fw::KeyQ;
159 m_keyMap['R'] = Fw::KeyR;
160 m_keyMap['S'] = Fw::KeyS;
161 m_keyMap['T'] = Fw::KeyT;
162 m_keyMap['U'] = Fw::KeyU;
163 m_keyMap['V'] = Fw::KeyV;
164 m_keyMap['W'] = Fw::KeyW;
165 m_keyMap['X'] = Fw::KeyX;
166 m_keyMap['Y'] = Fw::KeyY;
167 m_keyMap['Z'] = Fw::KeyZ;
168
169 m_keyMap['['] = Fw::KeyLeftBracket;
170 m_keyMap['\\'] = Fw::KeyBackslash;
171 m_keyMap[']'] = Fw::KeyRightBracket;
172 m_keyMap['^'] = Fw::KeyCaret;
173 m_keyMap['_'] = Fw::KeyUnderscore;
174 m_keyMap['`'] = Fw::KeyGrave;
175 m_keyMap['{'] = Fw::KeyLeftCurly;
176 m_keyMap['|'] = Fw::KeyBar;
177 m_keyMap['}'] = Fw::KeyRightCurly;
178 m_keyMap['~'] = Fw::KeyTilde;
179
180 // keypad
181 m_keyMap[VK_ADD] = Fw::KeyPlus;
182 m_keyMap[VK_SUBTRACT] = Fw::KeyMinus;
183 m_keyMap[VK_DECIMAL] = Fw::KeyPeriod;
184 m_keyMap[VK_DIVIDE] = Fw::KeySlash;
185 m_keyMap[VK_MULTIPLY] = Fw::KeyAsterisk;
186
187 m_keyMap[VK_OEM_1] = Fw::KeySemicolon;
188 m_keyMap[VK_OEM_2] = Fw::KeySlash;
189 m_keyMap[VK_OEM_3] = Fw::KeyGrave;
190 m_keyMap[VK_OEM_4] = Fw::KeyLeftBracket;
191 m_keyMap[VK_OEM_5] = Fw::KeyBackslash;
192 m_keyMap[VK_OEM_6] = Fw::KeyRightBracket;
193 m_keyMap[VK_OEM_7] = Fw::KeyApostrophe;
194 m_keyMap[VK_OEM_MINUS] = Fw::KeyMinus;
195 m_keyMap[VK_OEM_PLUS] = Fw::KeyEqual;
196 m_keyMap[VK_OEM_COMMA] = Fw::KeyComma;
197 m_keyMap[VK_OEM_PERIOD] = Fw::KeyPeriod;
198
199 m_keyMap[VK_F1] = Fw::KeyF1;
200 m_keyMap[VK_F2] = Fw::KeyF2;
201 m_keyMap[VK_F3] = Fw::KeyF3;
202 m_keyMap[VK_F4] = Fw::KeyF4;
203 m_keyMap[VK_F5] = Fw::KeyF5;
204 m_keyMap[VK_F6] = Fw::KeyF6;
205 m_keyMap[VK_F7] = Fw::KeyF7;
206 m_keyMap[VK_F8] = Fw::KeyF8;
207 m_keyMap[VK_F9] = Fw::KeyF9;
208 m_keyMap[VK_F10] = Fw::KeyF10;
209 m_keyMap[VK_F11] = Fw::KeyF11;
210 m_keyMap[VK_F12] = Fw::KeyF12;
211}
212
213void WIN32Window::init()
214{
215 m_instance = GetModuleHandle(NULL);
216
217 internalSetupTimerAccuracy();
218 internalCreateWindow();
219 internalCreateGLContext();
220 internalRestoreGLContext();
221}
222
223void WIN32Window::terminate()
224{
225 SetCursor(NULL);
226 if(m_defaultCursor) {
227 DestroyCursor(m_defaultCursor);
228 m_defaultCursor = NULL;
229 }
230
231 for(HCURSOR& cursor : m_cursors)
232 DestroyCursor(cursor);
233 m_cursors.clear();
234
235 internalDestroyGLContext();
236
237 if(m_deviceContext) {
238 if(!ReleaseDC(m_window, m_deviceContext))
239 g_logger.error("Release device context failed.");
240 m_deviceContext = NULL;
241 }
242
243 if(m_window) {
244 if(!DestroyWindow(m_window))
245 g_logger.error("ERROR: Destroy window failed.");
246 m_window = NULL;
247 }
248
249 if(m_instance) {
250 if(!UnregisterClassA(g_app.getCompactName().c_str(), m_instance))
251 g_logger.error("UnregisterClassA failed");
252 m_instance = NULL;
253 }
254
255 if (m_timerRes) {
256 timeEndPeriod(m_timerRes);
257 }
258}
259
260void WIN32Window::internalSetupTimerAccuracy() {
261 TIMECAPS tc;
262
263 if (timeGetDevCaps(&tc, sizeof(TIMECAPS)) != TIMERR_NOERROR)
264 {
265 return;
266 }
267
268 m_timerRes = std::min<UINT>(std::max<UINT>(tc.wPeriodMin, 1), tc.wPeriodMax);
269 timeBeginPeriod(m_timerRes);
270}
271
272struct WindowProcProxy {
273 static LRESULT CALLBACK call(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
274 WIN32Window *window = (WIN32Window*)&g_window;
275 return window->windowProc(hWnd, uMsg, wParam, lParam);
276 }
277};
278
279void WIN32Window::internalCreateWindow()
280{
281 m_defaultCursor = LoadCursor(NULL, IDC_ARROW);
282 WNDCLASSA wc;
283 wc.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC;
284 wc.lpfnWndProc = (WNDPROC)WindowProcProxy::call;
285 wc.cbClsExtra = 0;
286 wc.cbWndExtra = 0;
287 wc.hInstance = m_instance;
288 wc.hIcon = LoadIcon(NULL, IDI_WINLOGO);
289 wc.hCursor = m_defaultCursor;
290 wc.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
291 wc.lpszMenuName = NULL;
292 wc.lpszClassName = g_app.getCompactName().c_str();
293
294 if(!RegisterClassA(&wc))
295 g_logger.fatal("Failed to register the window class.");
296 DWORD dwExStyle = WS_EX_APPWINDOW | WS_EX_WINDOWEDGE;
297 DWORD dwStyle = WS_OVERLAPPEDWINDOW;
298
299 // initialize in the center of the screen
300 m_position = ((getDisplaySize() - m_size) / 2).toPoint();
301
302 Rect screenRect = adjustWindowRect(Rect(m_position, m_size));
303
304 updateUnmaximizedCoords();
305 m_window = CreateWindowExA(dwExStyle,
306 g_app.getCompactName().c_str(),
307 NULL,
308 dwStyle,
309 screenRect.left(),
310 screenRect.top(),
311 screenRect.width(),
312 screenRect.height(),
313 NULL,
314 NULL,
315 m_instance,
316 NULL);
317
318 if(!m_window)
319 g_logger.fatal("Unable to create window");
320
321 ShowWindow(m_window, SW_HIDE);
322
323 m_deviceContext = GetDC(m_window);
324 if(!m_deviceContext)
325 g_logger.fatal("GetDC failed");
326}
327
328void WIN32Window::internalCreateGLContext()
329{
330#ifdef OPENGL_ES
331 PFNEGLGETPLATFORMDISPLAYEXTPROC eglGetPlatformDisplayEXT = reinterpret_cast<PFNEGLGETPLATFORMDISPLAYEXTPROC>(eglGetProcAddress("eglGetPlatformDisplayEXT"));
332
333 EGLint displayAttributes[3][5] =
334 {
335 {
336 EGL_PLATFORM_ANGLE_TYPE_ANGLE, EGL_PLATFORM_ANGLE_TYPE_D3D11_ANGLE,
337 EGL_PLATFORM_ANGLE_DEVICE_TYPE_ANGLE, EGL_PLATFORM_ANGLE_DEVICE_TYPE_HARDWARE_ANGLE,
338 EGL_NONE,
339 },
340 {
341 EGL_PLATFORM_ANGLE_TYPE_ANGLE, EGL_PLATFORM_ANGLE_TYPE_D3D9_ANGLE,
342 EGL_PLATFORM_ANGLE_DEVICE_TYPE_ANGLE, EGL_PLATFORM_ANGLE_DEVICE_TYPE_HARDWARE_ANGLE,
343 EGL_NONE,
344 },
345 {
346 EGL_PLATFORM_ANGLE_TYPE_ANGLE, EGL_PLATFORM_ANGLE_TYPE_D3D11_ANGLE,
347 EGL_PLATFORM_ANGLE_DEVICE_TYPE_ANGLE, EGL_PLATFORM_ANGLE_DEVICE_TYPE_D3D_WARP_ANGLE,
348 EGL_NONE,
349 },
350 };
351
352 auto setupDisplay = [&](EGLDisplay display) -> bool {
353 if (!display) return false;
354 if (eglInitialize(display, NULL, NULL)) {
355 m_eglDisplay = display;
356 return true;
357 }
358 return false;
359 };
360
361 if (eglGetPlatformDisplayEXT) {
362 std::string args(GetCommandLineA());
363 if (args.find("-dx11") != std::string::npos) {
364 setupDisplay(eglGetPlatformDisplayEXT(EGL_PLATFORM_ANGLE_ANGLE, EGL_DEFAULT_DISPLAY, displayAttributes[0]));
365 } else if (args.find("-dx9") != std::string::npos) {
366 setupDisplay(eglGetPlatformDisplayEXT(EGL_PLATFORM_ANGLE_ANGLE, EGL_DEFAULT_DISPLAY, displayAttributes[1]));
367 } else if (args.find("-warp") != std::string::npos) {
368 setupDisplay(eglGetPlatformDisplayEXT(EGL_PLATFORM_ANGLE_ANGLE, EGL_DEFAULT_DISPLAY, displayAttributes[2]));
369 } else {
370 for (EGLint* attributes : displayAttributes) {
371 if (setupDisplay(eglGetPlatformDisplayEXT(EGL_PLATFORM_ANGLE_ANGLE, EGL_DEFAULT_DISPLAY, attributes)))
372 break;
373 }
374 }
375 }
376
377 if (!m_eglDisplay && !setupDisplay(eglGetDisplay(m_deviceContext))) {
378 g_logger.fatal("DirectX is not supported, try to use OpenGL version or install latest directx drivers. Also, make sure that your folder contains libEGL.dll, libGLESv2.dll and d3dcompiler_47.dll.");
379 }
380
381 static int configList[] = {
382 EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
383 EGL_RED_SIZE, 8,
384 EGL_GREEN_SIZE, 8,
385 EGL_BLUE_SIZE, 8,
386 EGL_ALPHA_SIZE, 8,
387 EGL_NONE
388 };
389
390 EGLint numConfig;
391
392 if(!eglGetConfigs(m_eglDisplay, NULL, 0xFFFF, &numConfig)) // it must have 0xFFFF to work
393 g_logger.fatal("No valid GL configurations");
394
395 if(!eglChooseConfig(m_eglDisplay, configList, &m_eglConfig, 1, &numConfig))
396 g_logger.fatal("Failed to choose EGL config");
397
398 EGLint contextAtrrList[] = {
399 EGL_CONTEXT_CLIENT_VERSION, 2,
400 EGL_NONE
401 };
402
403 m_eglSurface = eglCreateWindowSurface(m_eglDisplay, m_eglConfig, m_window, NULL);
404 if(m_eglSurface == EGL_NO_SURFACE)
405 g_logger.fatal(stdext::format("Unable to create EGL surface: %i", eglGetError()));
406
407 m_eglContext = eglCreateContext(m_eglDisplay, m_eglConfig, EGL_NO_CONTEXT, contextAtrrList);
408 if(m_eglContext == EGL_NO_CONTEXT )
409 g_logger.fatal(stdext::format("Unable to create EGL context: %i", eglGetError()));
410
411#else
412 uint pixelFormat;
413 static PIXELFORMATDESCRIPTOR pfd = { sizeof(PIXELFORMATDESCRIPTOR),
414 1,
415 PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER,
416 PFD_TYPE_RGBA,
417 32, // Select Our Color Depth
418 8, 0, 8, 0, 8, 0, // Color Bits Ignored
419 8, // Alpha Buffer Bits
420 0, // Shift Bit Ignored
421 0, // No Accumulation Buffer
422 0, 0, 0, 0, // Accumulation Bits Ignored
423 0, // Z-Buffer (Depth Buffer)
424 0, // No Stencil Buffer
425 0, // No Auxiliary Buffer
426 PFD_MAIN_PLANE, // Main Drawing Layer
427 0, // Reserved
428 0, 0, 0 }; // Layer Masks Ignored
429
430 pixelFormat = ChoosePixelFormat(m_deviceContext, &pfd);
431 if(!pixelFormat)
432 g_logger.fatal("Could not find a suitable pixel format\nTry to update your graphics drivers or use DirectX version if available.");
433
434 if(!SetPixelFormat(m_deviceContext, pixelFormat, &pfd))
435 g_logger.fatal("Could not set the pixel format\nTry to update your graphics drivers or use DirectX version if available.");
436
437 if(!(m_wglContext = wglCreateContext(m_deviceContext)))
438 g_logger.fatal("Unable to create GL context.\nTry to update your graphics drivers or use DirectX version if available.");
439#endif
440}
441
442void WIN32Window::internalDestroyGLContext()
443{
444#ifdef OPENGL_ES
445 if(m_eglDisplay) {
446 if(m_eglContext) {
447 eglDestroyContext(m_eglDisplay, m_eglContext);
448 m_eglContext = 0;
449 }
450 if(m_eglSurface) {
451 eglDestroySurface(m_eglDisplay, m_eglSurface);
452 m_eglSurface = 0;
453 }
454 eglTerminate(m_eglDisplay);
455 m_eglDisplay = 0;
456 }
457#else
458 if(m_wglContext) {
459 if(!wglMakeCurrent(NULL, NULL))
460 g_logger.error("Release of dc and rc failed.");
461 if(!wglDeleteContext(m_wglContext))
462 g_logger.error("Release rendering context failed.");
463 m_wglContext = NULL;
464 }
465#endif
466}
467
468void WIN32Window::internalRestoreGLContext()
469{
470#ifdef OPENGL_ES
471 if(!eglMakeCurrent(m_eglDisplay, m_eglSurface, m_eglSurface, m_eglContext))
472 g_logger.fatal(stdext::format("Unable to make current EGL context (error: %i, report it)", (int)eglGetError()));
473#else
474 if(!wglMakeCurrent(m_deviceContext, m_wglContext))
475 g_logger.fatal(stdext::format("Unable to make current WGL context (error: %i, report it)", GetLastError()));
476#endif
477}
478
479bool WIN32Window::isExtensionSupported(const char *ext)
480{
481#ifdef OPENGL_ES
482 //TODO
483 return false;
484#else
485 typedef const char* (WINAPI * wglGetExtensionsStringProc)();
486 wglGetExtensionsStringProc wglGetExtensionsString = (wglGetExtensionsStringProc)getExtensionProcAddress("wglGetExtensionsStringEXT");
487 if(!wglGetExtensionsString)
488 return false;
489
490 const char *exts = wglGetExtensionsString();
491 if(exts && strstr(exts, ext))
492 return true;
493
494 return false;
495#endif
496}
497
498void *WIN32Window::getExtensionProcAddress(const char *ext)
499{
500#ifdef OPENGL_ES
501 //TODO
502 return NULL;
503#else
504 return (void*)wglGetProcAddress(ext);
505#endif
506}
507
508void WIN32Window::move(const Point& pos)
509{
510 g_graphicsDispatcher.addEvent([&, pos] {
511 Rect clientRect(pos, getClientRect().size());
512 Rect windowRect = adjustWindowRect(clientRect);
513 MoveWindow(m_window, windowRect.x(), windowRect.y(), windowRect.width(), windowRect.height(), TRUE);
514 if (m_hidden)
515 ShowWindow(m_window, SW_HIDE);
516 });
517}
518
519void WIN32Window::resize(const Size& size)
520{
521 g_graphicsDispatcher.addEvent([&, size] {
522 if(size.width() < m_minimumSize.width() || size.height() < m_minimumSize.height())
523 return;
524 Rect clientRect(getClientRect().topLeft(), size);
525 Rect windowRect = adjustWindowRect(clientRect);
526 MoveWindow(m_window, windowRect.x(), windowRect.y(), windowRect.width(), windowRect.height(), TRUE);
527 if (m_hidden)
528 ShowWindow(m_window, SW_HIDE);
529 });
530}
531
532void WIN32Window::show()
533{
534 g_graphicsDispatcher.addEvent([&] {
535 m_hidden = false;
536 if (m_maximized)
537 ShowWindow(m_window, SW_MAXIMIZE);
538 else
539 ShowWindow(m_window, SW_SHOW);
540 });
541}
542
543void WIN32Window::hide()
544{
545 g_graphicsDispatcher.addEvent([&] {
546 m_hidden = true;
547 ShowWindow(m_window, SW_HIDE);
548 });
549}
550
551void WIN32Window::maximize()
552{
553 g_graphicsDispatcher.addEvent([&] {
554 if (!m_hidden)
555 ShowWindow(m_window, SW_MAXIMIZE);
556 else
557 m_maximized = true;
558 });
559}
560
561void WIN32Window::minimize()
562{
563 g_graphicsDispatcher.addEvent([&] {
564 if (!m_hidden && m_maximized) {
565 ShowWindow(m_window, SW_MINIMIZE);
566 m_maximized = true;
567 }
568 });
569}
570
571void WIN32Window::poll()
572{
573 AutoStat s(STATS_RENDER, "PollWindow");
574
575 MSG msg;
576 while(PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
577 TranslateMessage(&msg);
578 DispatchMessage(&msg);
579 }
580
581 g_dispatcher.addEvent([&] {
582 fireKeysPress();
583 updateUnmaximizedCoords();
584 });
585}
586
587Fw::Key WIN32Window::retranslateVirtualKey(WPARAM wParam, LPARAM lParam)
588{
589 // ignore numpad keys when numlock is on
590 // if((wParam >= VK_NUMPAD0 && wParam <= VK_NUMPAD9) || wParam == VK_SEPARATOR)
591 // return Fw::KeyUnknown;
592
593 // lParam will have this state when receiving insert,end,down,etc presses from numpad
594 if(!(((HIWORD(lParam) >> 8) & 0xFF) & 1)) {
595 bool numlockOn = GetKeyState(VK_NUMLOCK);
596 // retranslate numpad keys
597 switch(wParam) {
598 case VK_INSERT:
599 return numlockOn ? Fw::Key0 : Fw::KeyNumpad0;
600 case VK_END:
601 return numlockOn ? Fw::Key1 : Fw::KeyNumpad1;
602 case VK_DOWN:
603 return numlockOn ? Fw::Key2 : Fw::KeyNumpad2;
604 case VK_NEXT:
605 return numlockOn ? Fw::Key3 : Fw::KeyNumpad3;
606 case VK_LEFT:
607 return numlockOn ? Fw::Key4 : Fw::KeyNumpad4;
608 case VK_CLEAR:
609 return numlockOn ? Fw::Key5 : Fw::KeyNumpad5;
610 case VK_RIGHT:
611 return numlockOn ? Fw::Key6 : Fw::KeyNumpad6;
612 case VK_HOME:
613 return numlockOn ? Fw::Key7 : Fw::KeyNumpad7;
614 case VK_UP:
615 return numlockOn ? Fw::Key8 : Fw::KeyNumpad8;
616 case VK_PRIOR:
617 return numlockOn ? Fw::Key9 : Fw::KeyNumpad9;
618 }
619 }
620
621 Fw::Key key = Fw::KeyUnknown;
622 if(m_keyMap.find(wParam) != m_keyMap.end())
623 key = m_keyMap[wParam];
624
625 // actually ignore alt/ctrl/shift keys, they is states are already stored in m_inputEvent.keyboardModifiers
626 if(key == Fw::KeyAlt || key == Fw::KeyCtrl || key == Fw::KeyShift)
627 key = Fw::KeyUnknown;
628
629 return key;
630}
631
632#define IsKeyDown(a) (GetKeyState(a) & 0x80)
633
634LRESULT WIN32Window::windowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
635{
636 g_dispatcher.addEvent(std::bind(&WIN32Window::dispatcherWindowProc, this, hWnd, uMsg, wParam, lParam));
637
638 switch(uMsg)
639 {
640 case WM_SETCURSOR: {
641 if(m_cursor)
642 SetCursor(m_cursor);
643 else
644 return DefWindowProc(hWnd, uMsg, wParam, lParam);
645 break;
646 }
647 case WM_ACTIVATE:
648 case WM_SETFOCUS:
649 case WM_KILLFOCUS:
650 case WM_CHAR:
651 case WM_CLOSE:
652 case WM_KEYDOWN:
653 case WM_KEYUP:
654 case WM_SYSKEYUP:
655 break;
656 case WM_SYSKEYDOWN:
657 {
658 if (wParam == VK_F4 && m_inputEvent.keyboardModifiers & Fw::KeyboardAltModifier)
659 return DefWindowProc(hWnd, uMsg, wParam, lParam);
660 break;
661 }
662 case WM_LBUTTONDOWN: {
663 SetCapture(m_window);
664 break;
665 }
666 case WM_LBUTTONUP: {
667 SetCapture(NULL);
668 break;
669 }
670 case WM_MBUTTONDOWN: {
671 SetCapture(m_window);
672 break;
673 }
674 case WM_MBUTTONUP: {
675 SetCapture(NULL);
676 break;
677 }
678 case WM_RBUTTONDOWN: {
679 SetCapture(m_window);
680 break;
681 }
682 case WM_RBUTTONUP: {
683 SetCapture(NULL);
684 break;
685 }
686 case WM_MOUSEMOVE:
687 case WM_MOUSEWHEEL:
688 case WM_MOVE:
689 break;
690 case WM_GETMINMAXINFO: {
691 LPMINMAXINFO pMMI = (LPMINMAXINFO)lParam;
692 Rect adjustedRect = adjustWindowRect(Rect(0, 0, m_minimumSize));
693 pMMI->ptMinTrackSize.x = adjustedRect.width();
694 pMMI->ptMinTrackSize.y = adjustedRect.height();
695 break;
696 }
697 case WM_SIZE: {
698 bool forceResize = false;
699 switch(wParam) {
700 case SIZE_MAXIMIZED:
701 m_maximized = true;
702 m_visible = true;
703 forceResize = true;
704 break;
705 case SIZE_RESTORED:
706 m_maximized = false;
707 m_visible = true;
708 forceResize = true;
709 break;
710 case SIZE_MINIMIZED:
711 m_visible = false;
712 break;
713 }
714
715 if(m_visible && m_deviceContext)
716 internalRestoreGLContext();
717
718 Size size = Size(LOWORD(lParam), HIWORD(lParam));
719 size.setWidth(std::max<int32>(std::min<int32>(size.width(), 7680), 32));
720 size.setHeight(std::max<int32>(std::min<int32>(size.height(), 4320), 32));
721
722 if(m_visible && (forceResize || m_size != size)) {
723 m_size = size;
724 m_onResize(m_size);
725 }
726 break;
727 }
728 default:
729 return DefWindowProc(hWnd, uMsg, wParam, lParam);
730 }
731
732 return 0;
733}
734
735LRESULT WIN32Window::dispatcherWindowProc(HWND, UINT uMsg, WPARAM wParam, LPARAM lParam)
736{
737 // called from dispatcher
738 m_inputEvent.keyboardModifiers = 0;
739 if (IsKeyDown(VK_CONTROL))
740 m_inputEvent.keyboardModifiers |= Fw::KeyboardCtrlModifier;
741 if (IsKeyDown(VK_SHIFT))
742 m_inputEvent.keyboardModifiers |= Fw::KeyboardShiftModifier;
743 if (IsKeyDown(VK_MENU))
744 m_inputEvent.keyboardModifiers |= Fw::KeyboardAltModifier;
745
746 switch (uMsg) {
747 case WM_ACTIVATE:
748 {
749 m_focused = !(wParam == WA_INACTIVE);
750 releaseAllKeys();
751 break;
752 }
753 case WM_SETFOCUS:
754 case WM_KILLFOCUS:
755 {
756 releaseAllKeys();
757 break;
758 }
759 case WM_CHAR:
760 {
761 if (wParam >= 32 && wParam <= 255) {
762 m_inputEvent.reset(Fw::KeyTextInputEvent);
763 m_inputEvent.keyText = wParam;
764 if (m_onInputEvent)
765 m_onInputEvent(m_inputEvent);
766 }
767 break;
768 }
769 case WM_CLOSE:
770 {
771 m_onClose();
772 break;
773 }
774 case WM_KEYDOWN:
775 {
776 processKeyDown(retranslateVirtualKey(wParam, lParam));
777 break;
778 }
779 case WM_KEYUP:
780 {
781 processKeyUp(retranslateVirtualKey(wParam, lParam));
782 break;
783 }
784 case WM_SYSKEYUP:
785 {
786 processKeyUp(retranslateVirtualKey(wParam, lParam));
787 break;
788 }
789 case WM_SYSKEYDOWN:
790 {
791 processKeyDown(retranslateVirtualKey(wParam, lParam));
792 break;
793 }
794 case WM_LBUTTONDOWN:
795 {
796 m_inputEvent.reset(Fw::MousePressInputEvent);
797 m_inputEvent.mouseButton = Fw::MouseLeftButton;
798 m_mouseButtonStates[Fw::MouseLeftButton] = true;
799 if (m_onInputEvent)
800 m_onInputEvent(m_inputEvent);
801 break;
802 }
803 case WM_LBUTTONUP:
804 {
805 m_inputEvent.reset(Fw::MouseReleaseInputEvent);
806 m_inputEvent.mouseButton = Fw::MouseLeftButton;
807 m_mouseButtonStates[Fw::MouseLeftButton] = false;
808 if (m_onInputEvent)
809 m_onInputEvent(m_inputEvent);
810 break;
811 }
812 case WM_MBUTTONDOWN:
813 {
814 m_inputEvent.reset(Fw::MousePressInputEvent);
815 m_inputEvent.mouseButton = Fw::MouseMidButton;
816 m_mouseButtonStates[Fw::MouseMidButton] = true;
817 if (m_onInputEvent)
818 m_onInputEvent(m_inputEvent);
819 break;
820 }
821 case WM_MBUTTONUP:
822 {
823 m_inputEvent.reset(Fw::MouseReleaseInputEvent);
824 m_inputEvent.mouseButton = Fw::MouseMidButton;
825 m_mouseButtonStates[Fw::MouseMidButton] = false;
826 if (m_onInputEvent)
827 m_onInputEvent(m_inputEvent);
828 break;
829 }
830 case WM_RBUTTONDOWN:
831 {
832 m_inputEvent.reset(Fw::MousePressInputEvent);
833 m_inputEvent.mouseButton = Fw::MouseRightButton;
834 m_mouseButtonStates[Fw::MouseRightButton] = true;
835 if (m_onInputEvent)
836 m_onInputEvent(m_inputEvent);
837 break;
838 }
839 case WM_RBUTTONUP:
840 {
841 m_inputEvent.reset(Fw::MouseReleaseInputEvent);
842 m_inputEvent.mouseButton = Fw::MouseRightButton;
843 m_mouseButtonStates[Fw::MouseRightButton] = false;
844 if (m_onInputEvent)
845 m_onInputEvent(m_inputEvent);
846 break;
847 }
848#if (_WIN32_WINNT >= 0x0500)
849 case WM_XBUTTONDOWN:
850 {
851 m_inputEvent.reset(Fw::MousePressInputEvent);
852 if (GET_XBUTTON_WPARAM(wParam) == XBUTTON1) {
853 m_inputEvent.mouseButton = Fw::MouseButton4;
854 m_mouseButtonStates[Fw::MouseButton4] = true;
855 }
856 else if(GET_XBUTTON_WPARAM(wParam) == XBUTTON2) {
857 m_inputEvent.mouseButton = Fw::MouseButton5;
858 m_mouseButtonStates[Fw::MouseButton5] = true;
859 }
860 if (m_onInputEvent)
861 m_onInputEvent(m_inputEvent);
862 break;
863 }
864 case WM_XBUTTONUP:
865 {
866 m_inputEvent.reset(Fw::MouseReleaseInputEvent);
867 if (GET_XBUTTON_WPARAM(wParam) == XBUTTON1) {
868 m_inputEvent.mouseButton = Fw::MouseButton4;
869 m_mouseButtonStates[Fw::MouseButton4] = false;
870 }
871 else if (GET_XBUTTON_WPARAM(wParam) == XBUTTON2) {
872 m_inputEvent.mouseButton = Fw::MouseButton5;
873 m_mouseButtonStates[Fw::MouseButton5] = false;
874 }
875 if (m_onInputEvent)
876 m_onInputEvent(m_inputEvent);
877 break;
878 }
879#endif
880 case WM_MOUSEMOVE:
881 {
882 m_inputEvent.reset(Fw::MouseMoveInputEvent);
883
884 Point newMousePos(LOWORD(lParam), HIWORD(lParam));
885 if (newMousePos.x >= 32767)
886 newMousePos.x = 0;
887 else
888 newMousePos.x = std::min<int32>(newMousePos.x, m_size.width());
889
890 if (newMousePos.y >= 32767)
891 newMousePos.y = 0;
892 else
893 newMousePos.y = std::min<int32>(newMousePos.y, m_size.height());
894
895 newMousePos = Point(newMousePos.x / m_scaling, newMousePos.y / m_scaling);
896 m_inputEvent.mouseMoved = newMousePos - m_inputEvent.mousePos;
897 m_inputEvent.mousePos = newMousePos;
898 if (m_onInputEvent)
899 m_onInputEvent(m_inputEvent);
900 break;
901 }
902 case WM_MOUSEWHEEL:
903 {
904 m_inputEvent.reset(Fw::MouseWheelInputEvent);
905 m_inputEvent.mouseButton = Fw::MouseMidButton;
906 m_inputEvent.wheelDirection = ((short)HIWORD(wParam)) > 0 ? Fw::MouseWheelUp : Fw::MouseWheelDown;
907 if (m_onInputEvent)
908 m_onInputEvent(m_inputEvent);
909 break;
910 }
911 case WM_MOVE:
912 {
913 m_position.x = (short)LOWORD(lParam);
914 m_position.y = (short)HIWORD(lParam);
915 releaseAllKeys();
916 break;
917 }
918 case WM_SIZE:
919 {
920 releaseAllKeys();
921 break;
922 }
923 }
924
925 return 0;
926}
927
928void WIN32Window::swapBuffers()
929{
930#ifdef OPENGL_ES
931 eglSwapBuffers(m_eglDisplay, m_eglSurface);
932#else
933 SwapBuffers(m_deviceContext);
934#endif
935}
936
937void WIN32Window::showMouse()
938{
939 ShowCursor(true);
940}
941
942void WIN32Window::hideMouse()
943{
944 ShowCursor(false);
945}
946
947void WIN32Window::displayFatalError(const std::string& message)
948{
949 MessageBoxW(m_window, stdext::latin1_to_utf16(message).c_str(), L"FATAL ERROR", MB_OK | MB_ICONERROR | MB_TOPMOST);
950}
951
952int WIN32Window::internalLoadMouseCursor(const ImagePtr& image, const Point& hotSpot)
953{
954 int width = image->getWidth();
955 int height = image->getHeight();
956 int numbits = width * height;
957 int numbytes = (width * height)/8;
958
959 std::vector<uchar> andMask(numbytes, 0);
960 std::vector<uchar> xorMask(numbytes, 0);
961
962 for(int i=0;i<numbits;++i) {
963 uint32 rgba = stdext::readULE32(image->getPixelData() + i*4);
964 if(rgba == 0xffffffff) { //white
965 HSB_BIT_SET(xorMask, i);
966 } else if(rgba == 0x00000000) { //alpha
967 HSB_BIT_SET(andMask, i);
968 } // otherwise 0xff000000 => black
969 }
970
971 HCURSOR cursor = CreateCursor(m_instance, hotSpot.x, hotSpot.y, width, height, &andMask[0], &xorMask[0]);
972 m_cursors.push_back(cursor);
973 return m_cursors.size()-1;
974}
975
976void WIN32Window::setMouseCursor(int cursorId)
977{
978 g_graphicsDispatcher.addEvent([&, cursorId] {
979 if (cursorId >= (int)m_cursors.size() || cursorId < 0)
980 return;
981
982 m_cursor = m_cursors[cursorId];
983 SetCursor(m_cursor);
984 ShowCursor(true);
985 });
986}
987
988void WIN32Window::restoreMouseCursor()
989{
990 g_graphicsDispatcher.addEvent([&] {
991 if (m_cursor) {
992 m_cursor = NULL;
993 SetCursor(m_defaultCursor);
994 ShowCursor(true);
995 }
996 });
997}
998
999void WIN32Window::setTitle(const std::string& title)
1000{
1001 g_graphicsDispatcher.addEvent([&, title] {
1002 SetWindowTextW(m_window, stdext::latin1_to_utf16(title).c_str());
1003 });
1004}
1005
1006void WIN32Window::setMinimumSize(const Size& minimumSize)
1007{
1008 g_graphicsDispatcher.addEvent([&, minimumSize] {
1009 m_minimumSize = minimumSize;
1010 });
1011}
1012
1013void WIN32Window::setFullscreen(bool fullscreen)
1014{
1015 g_graphicsDispatcher.addEvent([&, fullscreen] {
1016 if (m_fullscreen == fullscreen)
1017 return;
1018
1019 m_fullscreen = fullscreen;
1020
1021 DWORD dwStyle = GetWindowLong(m_window, GWL_STYLE);
1022 static WINDOWPLACEMENT wpPrev;
1023 wpPrev.length = sizeof(wpPrev);
1024
1025 if (fullscreen) {
1026 Size size = getDisplaySize();
1027 GetWindowPlacement(m_window, &wpPrev);
1028 SetWindowLong(m_window, GWL_STYLE, (dwStyle & ~WS_OVERLAPPEDWINDOW) | WS_POPUP | WS_EX_TOPMOST);
1029 SetWindowPos(m_window, HWND_TOPMOST, 0, 0, size.width(), size.height(), SWP_FRAMECHANGED);
1030 } else {
1031 SetWindowLong(m_window, GWL_STYLE, (dwStyle & ~(WS_POPUP | WS_EX_TOPMOST)) | WS_OVERLAPPEDWINDOW);
1032 SetWindowPlacement(m_window, &wpPrev);
1033 SetWindowPos(m_window, HWND_NOTOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_FRAMECHANGED);
1034 }
1035 });
1036}
1037
1038void WIN32Window::setVerticalSync(bool enable)
1039{
1040 m_verticalSync = enable;
1041#ifdef OPENGL_ES
1042 g_graphicsDispatcher.addEvent([&, enable] {
1043 if (eglSwapInterval(m_eglDisplay, enable ? 1 : 0) != EGL_TRUE) {
1044 g_logger.error("Error while setting vsync");
1045 }
1046 });
1047#else
1048 g_graphicsDispatcher.addEvent([&, enable] {
1049 typedef BOOL(WINAPI* wglSwapIntervalProc)(int);
1050 if (isExtensionSupported("WGL_EXT_swap_control") && isExtensionSupported("EXT_swap_control_tear")) {
1051 wglSwapIntervalProc wglSwapInterval = (wglSwapIntervalProc)getExtensionProcAddress("wglSwapIntervalEXT");
1052 if (wglSwapInterval) {
1053 wglSwapInterval(enable ? -1 : 0);
1054 return;
1055 }
1056 }
1057
1058 if (!isExtensionSupported("WGL_EXT_swap_control"))
1059 return;
1060
1061 wglSwapIntervalProc wglSwapInterval = (wglSwapIntervalProc)getExtensionProcAddress("wglSwapIntervalEXT");
1062 if (!wglSwapInterval)
1063 return;
1064
1065 wglSwapInterval(enable ? 1 : 0);
1066 });
1067#endif
1068}
1069
1070void WIN32Window::setIcon(const std::string& file)
1071{
1072 g_graphicsDispatcher.addEvent([&, file] {
1073 ImagePtr image = Image::load(file);
1074
1075 if (!image) {
1076 g_logger.traceError(stdext::format("unable to load icon file %s", file));
1077 return;
1078 }
1079
1080 if (image->getBpp() != 4) {
1081 g_logger.error("the app icon must have 4 channels");
1082 return;
1083 }
1084
1085 int n = image->getWidth() * image->getHeight();
1086 std::vector<uint32> iconData(n);
1087 for (int i = 0; i < n; ++i) {
1088 uint8* pixel = (uint8*)&iconData[i];
1089 pixel[2] = *(image->getPixelData() + (i * 4) + 0);
1090 pixel[1] = *(image->getPixelData() + (i * 4) + 1);
1091 pixel[0] = *(image->getPixelData() + (i * 4) + 2);
1092 pixel[3] = *(image->getPixelData() + (i * 4) + 3);
1093 }
1094
1095 HBITMAP hbmColor = CreateBitmap(image->getWidth(), image->getHeight(), 1, 32, &iconData[0]);
1096 HBITMAP hbmMask = CreateCompatibleBitmap(GetDC(NULL), image->getWidth(), image->getHeight());
1097
1098 ICONINFO ii;
1099 ii.fIcon = TRUE;
1100 ii.hbmColor = hbmColor;
1101 ii.hbmMask = hbmMask;
1102 ii.xHotspot = 0;
1103 ii.yHotspot = 0;
1104
1105 HICON icon = CreateIconIndirect(&ii);
1106 DeleteObject(hbmMask);
1107 DeleteObject(hbmColor);
1108
1109 SendMessage(m_window, WM_SETICON, ICON_SMALL, (LPARAM)icon);
1110 SendMessage(m_window, WM_SETICON, ICON_BIG, (LPARAM)icon);
1111 });
1112}
1113
1114void WIN32Window::setClipboardText(const std::string& text)
1115{
1116 if(!OpenClipboard(m_window))
1117 return;
1118
1119 HGLOBAL hglb = GlobalAlloc(GMEM_MOVEABLE, (text.length() + 1));
1120 if(!hglb)
1121 return;
1122
1123 LPSTR lpstr = (LPSTR)GlobalLock(hglb);
1124 memcpy(lpstr, (char*)&text[0], text.length());
1125 lpstr[text.length()] = 0;
1126 GlobalUnlock(hglb);
1127
1128 EmptyClipboard();
1129 SetClipboardData(CF_TEXT, hglb);
1130 CloseClipboard();
1131}
1132
1133Size WIN32Window::getDisplaySize()
1134{
1135 return Size(GetSystemMetrics(SM_CXSCREEN), GetSystemMetrics(SM_CYSCREEN));
1136}
1137
1138std::string WIN32Window::getClipboardText()
1139{
1140 std::string text;
1141
1142 if(!OpenClipboard(m_window))
1143 return text;
1144
1145 HGLOBAL hglb = GetClipboardData(CF_TEXT);
1146 if(hglb) {
1147 LPSTR lpstr = (LPSTR)GlobalLock(hglb);
1148 if(lpstr) {
1149 text = std::string(lpstr);// stdext::utf16_to_utf8(lpwstr);
1150 GlobalUnlock(hglb);
1151 }
1152 }
1153 CloseClipboard();
1154 return text;
1155}
1156
1157
1158std::string WIN32Window::getPlatformType()
1159{
1160#ifdef __GNUC__
1161#ifndef OPENGL_ES
1162 return "WIN32-WGL-GCC";
1163#else
1164 return "WIN32-EGL-GCC";
1165#endif
1166#else
1167#ifndef OPENGL_ES
1168 return "WIN32-WGL";
1169#else
1170 return "WIN32-EGL";
1171#endif
1172#endif
1173}
1174
1175Rect WIN32Window::getClientRect()
1176{
1177 if(m_window) {
1178 RECT clientRect = {0,0,0,0};
1179 int ret = GetClientRect(m_window, &clientRect);
1180 VALIDATE(ret != 0);
1181 return Rect(Point(clientRect.left, clientRect.top), Point(clientRect.right, clientRect.bottom));
1182 } else {
1183 return Rect(m_position, m_size);
1184 }
1185}
1186
1187Rect WIN32Window::getWindowRect()
1188{
1189 if(m_window) {
1190 RECT windowRect = {0,0,0,0};
1191 int ret = GetWindowRect(m_window, &windowRect);
1192 VALIDATE(ret != 0);
1193 return Rect(Point(windowRect.left, windowRect.top), Point(windowRect.right, windowRect.bottom));
1194 } else {
1195 return adjustWindowRect(getClientRect());
1196 }
1197}
1198
1199Rect WIN32Window::adjustWindowRect(const Rect& clientRect)
1200{
1201 Rect rect;
1202 DWORD dwStyle;
1203 DWORD dwExStyle;
1204 RECT windowRect = { clientRect.left(), clientRect.top(), clientRect.right(), clientRect.bottom() };
1205 if(m_window) {
1206 dwStyle = GetWindowLong(m_window, GWL_STYLE);
1207 dwExStyle = GetWindowLong(m_window, GWL_EXSTYLE);
1208 } else {
1209 dwStyle = WS_OVERLAPPEDWINDOW;
1210 dwExStyle = WS_EX_APPWINDOW | WS_EX_WINDOWEDGE;
1211 }
1212 if(AdjustWindowRectEx(&windowRect, dwStyle, FALSE, dwExStyle) != 0) {
1213 if (windowRect.top < 0) {
1214 windowRect.bottom -= windowRect.top;
1215 windowRect.top = 0;
1216 }
1217 rect = Rect(Point(windowRect.left, windowRect.top), Point(windowRect.right, windowRect.bottom));
1218 } else {
1219 g_logger.traceError("AdjustWindowRectEx failed");
1220 rect = Rect(0,0, m_minimumSize);
1221 }
1222 return rect;
1223}
1224
1225void WIN32Window::flash()
1226{
1227 FLASHWINFO fInfo;
1228 fInfo.cbSize = sizeof(fInfo);
1229 fInfo.dwFlags = FLASHW_TRAY | FLASHW_TIMERNOFG;
1230 fInfo.hwnd = m_window;
1231 fInfo.uCount = 0;
1232 fInfo.dwTimeout = 1000;
1233 FlashWindowEx(&fInfo);
1234}
1235
1236#endif
1237
Top