1 /* ______ ___ ___
2 * /\ _ \ /\_ \ /\_ \
3 * \ \ \L\ \\//\ \ \//\ \ __ __ _ __ ___
4 * \ \ __ \ \ \ \ \ \ \ /'__`\ /'_ `\/\`'__\/ __`\
5 * \ \ \/\ \ \_\ \_ \_\ \_/\ __//\ \L\ \ \ \//\ \L\ \
6 * \ \_\ \_\/\____\/\____\ \____\ \____ \ \_\\ \____/
7 * \/_/\/_/\/____/\/____/\/____/\/___L\ \/_/ \/___/
8 * /\____/
9 * \_/__/
10 *
11 * New Windows window handling
12 *
13 * By Trent Gamblin.
14 *
15 */
16
17 /* Raw input */
18 #define _WIN32_WINNT 0x0501
19 #ifndef WINVER
20 #define WINVER 0x0501
21 #endif
22
23 #include <windows.h>
24 #include <windowsx.h>
25 #include <winuser.h>
26 #include <dinput.h>
27 #include <dbt.h>
28
29 /* Only used for Vista and up. */
30 #ifndef WM_MOUSEHWHEEL
31 #define WM_MOUSEHWHEEL 0x020E
32 #endif
33
34 #include <allegro5/allegro.h>
35 #include <process.h>
36
37 #include "allegro5/allegro_windows.h"
38 #include "allegro5/internal/aintern.h"
39 #include "allegro5/internal/aintern_bitmap.h"
40 #include "allegro5/internal/aintern_vector.h"
41 #include "allegro5/internal/aintern_display.h"
42 #include "allegro5/internal/aintern_wunicode.h"
43 #include "allegro5/internal/aintern_joystick.h"
44 #include "allegro5/internal/aintern_wjoydxnu.h"
45 #include "allegro5/platform/aintwin.h"
46
47
48 ALLEGRO_DEBUG_CHANNEL("wwindow")
49
50 static WNDCLASS window_class;
51
52 static bool resize_postponed = false;
53 static bool we_hid_the_mouse = false;
54
55
56 UINT _al_win_msg_call_proc = 0;
57 UINT _al_win_msg_suicide = 0;
58
59
60 #ifndef WM_DPICHANGED
61 #define WM_DPICHANGED 0x02E0
62 #endif
63
64
display_flags_to_window_styles(int flags,DWORD * style,DWORD * ex_style)65 static void display_flags_to_window_styles(int flags,
66 DWORD *style, DWORD *ex_style)
67 {
68 if (flags & ALLEGRO_FULLSCREEN) {
69 *style = WS_POPUP;
70 *ex_style = WS_EX_APPWINDOW;
71 }
72 else if (flags & ALLEGRO_MAXIMIZED) {
73 *style = WS_OVERLAPPEDWINDOW;
74 *ex_style = WS_EX_APPWINDOW;
75 }
76 else if (flags & ALLEGRO_RESIZABLE) {
77 *style = WS_OVERLAPPEDWINDOW;
78 *ex_style = WS_EX_APPWINDOW | WS_EX_OVERLAPPEDWINDOW;
79 }
80 else {
81 *style = WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX;
82 *ex_style = WS_EX_APPWINDOW;
83 }
84 }
85
86 // Clears a window to black
clear_window(HWND window,int w,int h)87 static void clear_window(HWND window, int w, int h)
88 {
89 PAINTSTRUCT ps;
90 HDC hdc;
91
92 hdc = BeginPaint(window, &ps);
93
94 SelectObject(hdc, GetStockObject(DC_BRUSH));
95 SetDCBrushColor(hdc, RGB(0, 0, 0));
96
97 Rectangle(hdc, 0, 0, w, h);
98 }
99
_al_win_create_hidden_window()100 HWND _al_win_create_hidden_window()
101 {
102 HWND window = CreateWindowEx(0,
103 TEXT("ALEX"), TEXT("hidden"), WS_POPUP,
104 -5000, -5000, 0, 0,
105 NULL,NULL,window_class.hInstance,0);
106 return window;
107 }
108
_al_win_get_window_center(ALLEGRO_DISPLAY_WIN * win_display,int width,int height,int * out_x,int * out_y)109 static void _al_win_get_window_center(
110 ALLEGRO_DISPLAY_WIN *win_display, int width, int height, int *out_x, int *out_y)
111 {
112 int a = win_display->adapter;
113 bool *is_fullscreen;
114 ALLEGRO_MONITOR_INFO info;
115 RECT win_size;
116
117 ALLEGRO_SYSTEM *sys = al_get_system_driver();
118 unsigned int num;
119 unsigned int i;
120 unsigned int fullscreen_found = 0;
121 num = al_get_num_video_adapters();
122 is_fullscreen = al_calloc(num, sizeof(bool));
123 for (i = 0; i < sys->displays._size; i++) {
124 ALLEGRO_DISPLAY **dptr = _al_vector_ref(&sys->displays, i);
125 ALLEGRO_DISPLAY *d = *dptr;
126 if (d->flags & ALLEGRO_FULLSCREEN) {
127 ALLEGRO_DISPLAY_WIN *win_display = (ALLEGRO_DISPLAY_WIN *)d;
128 is_fullscreen[win_display->adapter] = true;
129 fullscreen_found++;
130 }
131 }
132 if (fullscreen_found && fullscreen_found < num) {
133 for (i = 0; i < num; i++) {
134 if (is_fullscreen[i] == false) {
135 a = i;
136 break;
137 }
138 }
139 }
140 al_free(is_fullscreen);
141
142 al_get_monitor_info(a, &info);
143
144 win_size.left = info.x1 + (info.x2 - info.x1 - width) / 2;
145 win_size.top = info.y1 + (info.y2 - info.y1 - height) / 2;
146
147 *out_x = win_size.left;
148 *out_y = win_size.top;
149 }
150
_al_win_create_window(ALLEGRO_DISPLAY * display,int width,int height,int flags)151 HWND _al_win_create_window(ALLEGRO_DISPLAY *display, int width, int height, int flags)
152 {
153 HWND my_window;
154 DWORD style;
155 DWORD ex_style;
156 DEV_BROADCAST_DEVICEINTERFACE notification_filter;
157 int pos_x, pos_y;
158 bool center = false;
159 ALLEGRO_DISPLAY_WIN *win_display = (ALLEGRO_DISPLAY_WIN *)display;
160 WINDOWINFO wi;
161 int lsize, rsize, tsize, bsize; // left, right, top, bottom border sizes
162 TCHAR* window_title;
163
164 wi.cbSize = sizeof(WINDOWINFO);
165
166 display_flags_to_window_styles(flags, &style, &ex_style);
167
168 al_get_new_window_position(&pos_x, &pos_y);
169 if ((flags & ALLEGRO_FULLSCREEN) || (flags & ALLEGRO_FULLSCREEN_WINDOW)) {
170 pos_x = pos_y = 0;
171 }
172 else if (pos_x == INT_MAX) {
173 pos_x = pos_y = 0;
174 center = true;
175 }
176
177 if (center) {
178 _al_win_get_window_center(win_display, width, height, &pos_x, &pos_y);
179 }
180
181 window_title = _twin_utf8_to_tchar(al_get_new_window_title());
182 my_window = CreateWindowEx(ex_style,
183 TEXT("ALEX"), window_title, style,
184 pos_x, pos_y, width, height,
185 NULL,NULL,window_class.hInstance,0);
186 al_free(window_title);
187
188 if (_al_win_register_touch_window)
189 _al_win_register_touch_window(my_window, 0);
190
191 ZeroMemory(¬ification_filter, sizeof(notification_filter));
192 notification_filter.dbcc_size = sizeof(DEV_BROADCAST_DEVICEINTERFACE);
193 notification_filter.dbcc_devicetype = DBT_DEVTYP_DEVICEINTERFACE;
194 RegisterDeviceNotification(my_window, ¬ification_filter, DEVICE_NOTIFY_WINDOW_HANDLE | DEVICE_NOTIFY_ALL_INTERFACE_CLASSES);
195
196 GetWindowInfo(my_window, &wi);
197
198 lsize = (wi.rcClient.left - wi.rcWindow.left);
199 tsize = (wi.rcClient.top - wi.rcWindow.top);
200 rsize = (wi.rcWindow.right - wi.rcClient.right);
201 bsize = (wi.rcWindow.bottom - wi.rcClient.bottom);
202
203 SetWindowPos(my_window, 0, 0, 0,
204 width+lsize+rsize,
205 height+tsize+bsize,
206 SWP_NOZORDER | SWP_NOMOVE);
207 SetWindowPos(my_window, 0, pos_x-lsize, pos_y-tsize,
208 0, 0,
209 SWP_NOZORDER | SWP_NOSIZE);
210
211 if (flags & ALLEGRO_FRAMELESS) {
212 SetWindowLong(my_window, GWL_STYLE, WS_VISIBLE);
213 SetWindowLong(my_window, GWL_EXSTYLE, WS_EX_APPWINDOW);
214 SetWindowPos(my_window, 0, pos_x, pos_y, width, height, SWP_NOZORDER | SWP_FRAMECHANGED);
215 }
216
217 ShowWindow(my_window, SW_SHOW);
218
219 clear_window(my_window, width, height);
220
221 if (!(flags & ALLEGRO_RESIZABLE) && !(flags & ALLEGRO_FULLSCREEN)) {
222 /* Make the window non-resizable */
223 HMENU menu;
224 menu = GetSystemMenu(my_window, false);
225 DeleteMenu(menu, SC_SIZE, MF_BYCOMMAND);
226 DeleteMenu(menu, SC_MAXIMIZE, MF_BYCOMMAND);
227 DrawMenuBar(my_window);
228 }
229
230 _al_vector_init(&win_display->msg_callbacks, sizeof(ALLEGRO_DISPLAY_WIN_CALLBACK));
231
232 return my_window;
233 }
234
235
_al_win_create_faux_fullscreen_window(LPCTSTR devname,ALLEGRO_DISPLAY * display,int x1,int y1,int width,int height,int refresh_rate,int flags)236 HWND _al_win_create_faux_fullscreen_window(LPCTSTR devname, ALLEGRO_DISPLAY *display,
237 int x1, int y1, int width, int height, int refresh_rate, int flags)
238 {
239 HWND my_window;
240 DWORD style;
241 DWORD ex_style;
242 DEVMODE mode;
243 LONG temp;
244 TCHAR* window_title;
245
246 ALLEGRO_DISPLAY_WIN *win_display = (ALLEGRO_DISPLAY_WIN *)display;
247 (void)flags;
248
249 _al_vector_init(&win_display->msg_callbacks, sizeof(ALLEGRO_DISPLAY_WIN_CALLBACK));
250
251 style = WS_VISIBLE;
252 ex_style = WS_EX_TOPMOST;
253
254 window_title = _twin_utf8_to_tchar(al_get_new_window_title());
255 my_window = CreateWindowEx(ex_style,
256 TEXT("ALEX"), window_title, style,
257 x1, y1, width, height,
258 NULL,NULL,window_class.hInstance,0);
259 al_free(window_title);
260
261 if (_al_win_register_touch_window)
262 _al_win_register_touch_window(my_window, 0);
263
264 temp = GetWindowLong(my_window, GWL_STYLE);
265 temp &= ~WS_CAPTION;
266 SetWindowLong(my_window, GWL_STYLE, temp);
267 SetWindowPos(my_window, HWND_TOP, 0, 0, width, height, SWP_NOMOVE | SWP_FRAMECHANGED);
268
269 /* Go fullscreen */
270 memset(&mode, 0, sizeof(DEVMODE));
271 mode.dmSize = sizeof(DEVMODE);
272 mode.dmDriverExtra = 0;
273 mode.dmBitsPerPel = al_get_new_display_option(ALLEGRO_COLOR_SIZE, NULL);
274 mode.dmPelsWidth = width;
275 mode.dmPelsHeight = height;
276 mode.dmDisplayFlags = 0;
277 mode.dmDisplayFrequency = refresh_rate;
278 mode.dmPosition.x = x1;
279 mode.dmPosition.y = y1;
280 mode.dmFields = DM_BITSPERPEL|DM_PELSWIDTH|DM_PELSHEIGHT|DM_DISPLAYFLAGS|
281 DM_DISPLAYFREQUENCY|DM_POSITION;
282
283 ChangeDisplaySettingsEx(devname, &mode, NULL, 0, NULL/*CDS_FULLSCREEN*/);
284
285 clear_window(my_window, width, height);
286
287 return my_window;
288 }
289
290
291 /* _al_win_grab_input:
292 * Makes the passed display grab the input. All consequent input events will be
293 * generated the this display's window. The display's window must the the
294 * foreground window.
295 */
_al_win_grab_input(ALLEGRO_DISPLAY_WIN * win_disp)296 void _al_win_grab_input(ALLEGRO_DISPLAY_WIN *win_disp)
297 {
298 _al_win_wnd_schedule_proc(win_disp->window,
299 _al_win_joystick_dinput_grab,
300 win_disp);
301 }
302
303 /* Generate a resize event if the size has changed. We cannot asynchronously
304 * change the display size here yet, since the user will only know about a
305 * changed size after receiving the resize event. Here we merely add the
306 * event to the queue.
307 */
win_generate_resize_event(ALLEGRO_DISPLAY_WIN * win_display)308 static void win_generate_resize_event(ALLEGRO_DISPLAY_WIN *win_display)
309 {
310 ALLEGRO_DISPLAY *display = (ALLEGRO_DISPLAY *)win_display;
311 ALLEGRO_EVENT_SOURCE *es = &display->es;
312 WINDOWINFO wi;
313 int x, y, w, h;
314
315 wi.cbSize = sizeof(WINDOWINFO);
316 GetWindowInfo(win_display->window, &wi);
317 x = wi.rcClient.left;
318 y = wi.rcClient.top;
319 w = wi.rcClient.right - wi.rcClient.left;
320 h = wi.rcClient.bottom - wi.rcClient.top;
321
322 /* Don't generate events when restoring after minimise. */
323 if (w == 0 && h == 0 && x == -32000 && y == -32000)
324 return;
325
326 /* Always generate resize event when constraints are used.
327 * This is needed because d3d_acknowledge_resize() updates d->w, d->h
328 * before this function will be called.
329 */
330 if (display->use_constraints || display->w != w || display->h != h) {
331 _al_event_source_lock(es);
332 if (_al_event_source_needs_to_generate_event(es)) {
333 ALLEGRO_EVENT event;
334 event.display.type = ALLEGRO_EVENT_DISPLAY_RESIZE;
335 event.display.timestamp = al_get_time();
336 event.display.x = x;
337 event.display.y = y;
338 event.display.width = w;
339 event.display.height = h;
340 event.display.source = display;
341 _al_event_source_emit_event(es, &event);
342
343 /* Generate an expose event. */
344 /* This seems a bit redundant after a resize. */
345 if (win_display->display.flags & ALLEGRO_GENERATE_EXPOSE_EVENTS) {
346 event.display.type = ALLEGRO_EVENT_DISPLAY_EXPOSE;
347 _al_event_source_emit_event(es, &event);
348 }
349 }
350 _al_event_source_unlock(es);
351 }
352 }
353
postpone_thread_proc(void * arg)354 static void postpone_thread_proc(void *arg)
355 {
356 ALLEGRO_DISPLAY *display = (ALLEGRO_DISPLAY *)arg;
357 ALLEGRO_DISPLAY_WIN *win_display = (ALLEGRO_DISPLAY_WIN *)display;
358
359 Sleep(50);
360
361 if (win_display->ignore_resize) {
362 win_display->ignore_resize = false;
363 }
364 else {
365 win_generate_resize_event(win_display);
366 }
367
368 resize_postponed = false;
369 win_display->can_acknowledge = true;
370 }
371
372
handle_mouse_capture(bool down,HWND hWnd)373 static void handle_mouse_capture(bool down, HWND hWnd)
374 {
375 int i;
376 bool any_button_down = false;
377 ALLEGRO_MOUSE_STATE state;
378
379 if (!al_is_mouse_installed())
380 return;
381
382 al_get_mouse_state(&state);
383 for (i = 1; i <= 5; i++) {
384 any_button_down |= al_mouse_button_down(&state, i);
385 }
386
387 if (down && GetCapture() != hWnd) {
388 SetCapture(hWnd);
389 }
390 else if (!any_button_down) {
391 ReleaseCapture();
392 }
393 }
394
break_window_message_pump(ALLEGRO_DISPLAY_WIN * win_display,HWND hWnd)395 static void break_window_message_pump(ALLEGRO_DISPLAY_WIN *win_display, HWND hWnd)
396 {
397 /* Get the ID of the thread which created the HWND and is processing its messages */
398 DWORD wnd_thread_id = GetWindowThreadProcessId(hWnd, NULL);
399
400 /* Set the "end_thread" flag to stop the message pump */
401 win_display->end_thread = true;
402
403 /* Wake-up the message pump so the thread can read the new value of "end_thread" */
404 PostThreadMessage(wnd_thread_id, WM_NULL, 0, 0);
405 }
406
407 /* Windows Touch Input emulate WM_MOUSE* events. If we are using touch input explicitly,
408 * we do not want to use this, because Allegro driver provide emulation already. This
409 * way we can be consistent across platforms.
410 */
accept_mouse_event(void)411 static bool accept_mouse_event(void)
412 {
413 if (!al_is_touch_input_installed())
414 return true;
415
416 return !((GetMessageExtraInfo() & _AL_MOUSEEVENTF_FROMTOUCH) == _AL_MOUSEEVENTF_FROMTOUCH);
417 }
418
window_callback(HWND hWnd,UINT message,WPARAM wParam,LPARAM lParam)419 static LRESULT CALLBACK window_callback(HWND hWnd, UINT message,
420 WPARAM wParam, LPARAM lParam)
421 {
422 ALLEGRO_DISPLAY *d = NULL;
423 ALLEGRO_DISPLAY_WIN *win_display = NULL;
424 //WINDOWINFO wi;
425 unsigned int i;
426 ALLEGRO_EVENT_SOURCE *es = NULL;
427 ALLEGRO_SYSTEM *system = al_get_system_driver();
428
429 //wi.cbSize = sizeof(WINDOWINFO);
430
431 if (message == _al_win_msg_call_proc) {
432 ((void (*)(void*))wParam) ((void*)lParam);
433 return 0;
434 }
435
436 if (!system) {
437 return DefWindowProc(hWnd,message,wParam,lParam);
438 }
439
440 if (message == _al_win_msg_suicide && wParam) {
441 win_display = (ALLEGRO_DISPLAY_WIN*)wParam;
442 break_window_message_pump(win_display, hWnd);
443 if (_al_win_unregister_touch_window)
444 _al_win_unregister_touch_window(hWnd);
445 DestroyWindow(hWnd);
446 return 0;
447 }
448
449 for (i = 0; i < system->displays._size; i++) {
450 ALLEGRO_DISPLAY **dptr = _al_vector_ref(&system->displays, i);
451 d = *dptr;
452 win_display = (void*)d;
453 if (win_display->window == hWnd) {
454 es = &d->es;
455 break;
456 }
457 }
458
459 if (i == system->displays._size)
460 return DefWindowProc(hWnd,message,wParam,lParam);
461
462 if (message == _al_win_msg_suicide) {
463 break_window_message_pump(win_display, hWnd);
464 if (_al_win_unregister_touch_window)
465 _al_win_unregister_touch_window(hWnd);
466 DestroyWindow(hWnd);
467 return 0;
468 }
469
470 for (i = 0; i < _al_vector_size(&win_display->msg_callbacks); ++i) {
471 LRESULT result = TRUE;
472 ALLEGRO_DISPLAY_WIN_CALLBACK *ptr = _al_vector_ref(&win_display->msg_callbacks, i);
473 if ((ptr->proc)(d, message, wParam, lParam, &result, ptr->userdata))
474 return result;
475 }
476
477 switch (message) {
478 case WM_INPUT:
479 {
480 /* RAW Input is currently unused. */
481 UINT dwSize;
482 LPBYTE lpb;
483 RAWINPUT* raw;
484
485 /* We can't uninstall WM_INPUT mesages. */
486 if (!al_is_mouse_installed())
487 break;
488
489 GetRawInputData((HRAWINPUT)lParam, RID_INPUT, NULL, &dwSize,
490 sizeof(RAWINPUTHEADER));
491 lpb = al_malloc(sizeof(BYTE)*dwSize);
492 if (lpb == NULL)
493 break;
494
495 GetRawInputData((HRAWINPUT)lParam, RID_INPUT, lpb, &dwSize, sizeof(RAWINPUTHEADER));
496 raw = (RAWINPUT*)lpb;
497
498 if (raw->header.dwType != RIM_TYPEMOUSE) {
499 al_free(lpb);
500 break;
501 }
502
503 {
504 RAWMOUSE *rm = &raw->data.mouse;
505 int x = raw->data.mouse.lLastX;
506 int y = raw->data.mouse.lLastY;
507 bool abs = (rm->usFlags & (MOUSE_MOVE_ABSOLUTE
508 | MOUSE_VIRTUAL_DESKTOP)) != 0;
509 if (abs || x || y)
510 _al_win_mouse_handle_move(x, y, abs, win_display);
511
512 if (rm->usButtonFlags & RI_MOUSE_BUTTON_1_DOWN)
513 _al_win_mouse_handle_button(1, true, x, y, abs, win_display);
514 if (rm->usButtonFlags & RI_MOUSE_BUTTON_1_UP)
515 _al_win_mouse_handle_button(1, false, x, y, abs, win_display);
516 if (rm->usButtonFlags & RI_MOUSE_BUTTON_2_DOWN)
517 _al_win_mouse_handle_button(2, true, x, y, abs, win_display);
518 if (rm->usButtonFlags & RI_MOUSE_BUTTON_2_UP)
519 _al_win_mouse_handle_button(2, false, x, y, abs, win_display);
520 if (rm->usButtonFlags & RI_MOUSE_BUTTON_3_DOWN)
521 _al_win_mouse_handle_button(3, true, x, y, abs, win_display);
522 if (rm->usButtonFlags & RI_MOUSE_BUTTON_3_UP)
523 _al_win_mouse_handle_button(3, false, x, y, abs, win_display);
524 if (rm->usButtonFlags & RI_MOUSE_BUTTON_4_DOWN)
525 _al_win_mouse_handle_button(4, true, x, y, abs, win_display);
526 if (rm->usButtonFlags & RI_MOUSE_BUTTON_4_UP)
527 _al_win_mouse_handle_button(4, false, x, y, abs, win_display);
528 if (rm->usButtonFlags & RI_MOUSE_BUTTON_5_DOWN)
529 _al_win_mouse_handle_button(5, true, x, y, abs, win_display);
530 if (rm->usButtonFlags & RI_MOUSE_BUTTON_5_UP)
531 _al_win_mouse_handle_button(5, false, x, y, abs, win_display);
532
533 if (rm->usButtonFlags & RI_MOUSE_WHEEL) {
534 SHORT z = (SHORT)rm->usButtonData;
535 _al_win_mouse_handle_wheel(z, false, win_display);
536 }
537 }
538
539 al_free(lpb);
540 break;
541 }
542 case WM_LBUTTONDOWN:
543 case WM_LBUTTONUP: {
544 if (accept_mouse_event()) {
545 int mx = GET_X_LPARAM(lParam);
546 int my = GET_Y_LPARAM(lParam);
547 bool down = (message == WM_LBUTTONDOWN);
548 _al_win_mouse_handle_button(1, down, mx, my, true, win_display);
549 handle_mouse_capture(down, hWnd);
550 }
551 break;
552 }
553 case WM_MBUTTONDOWN:
554 case WM_MBUTTONUP: {
555 if (accept_mouse_event()) {
556 int mx = GET_X_LPARAM(lParam);
557 int my = GET_Y_LPARAM(lParam);
558 bool down = (message == WM_MBUTTONDOWN);
559 _al_win_mouse_handle_button(3, down, mx, my, true, win_display);
560 handle_mouse_capture(down, hWnd);
561 }
562 break;
563 }
564 case WM_RBUTTONDOWN:
565 case WM_RBUTTONUP: {
566 if (accept_mouse_event()) {
567 int mx = GET_X_LPARAM(lParam);
568 int my = GET_Y_LPARAM(lParam);
569 bool down = (message == WM_RBUTTONDOWN);
570 _al_win_mouse_handle_button(2, down, mx, my, true, win_display);
571 handle_mouse_capture(down, hWnd);
572 }
573 break;
574 }
575 case WM_XBUTTONDOWN:
576 case WM_XBUTTONUP: {
577 if (accept_mouse_event()) {
578 int mx = GET_X_LPARAM(lParam);
579 int my = GET_Y_LPARAM(lParam);
580 int button = HIWORD(wParam);
581 bool down = (message == WM_XBUTTONDOWN);
582 if (button == XBUTTON1)
583 _al_win_mouse_handle_button(4, down, mx, my, true, win_display);
584 else if (button == XBUTTON2)
585 _al_win_mouse_handle_button(5, down, mx, my, true, win_display);
586 handle_mouse_capture(down, hWnd);
587 return TRUE;
588 }
589 break;
590 }
591 case WM_MOUSEWHEEL: {
592 if (accept_mouse_event()) {
593 int d = GET_WHEEL_DELTA_WPARAM(wParam);
594 _al_win_mouse_handle_wheel(d, false, win_display);
595 return TRUE;
596 }
597 break;
598 }
599 case WM_MOUSEHWHEEL: {
600 if (accept_mouse_event()) {
601 int d = GET_WHEEL_DELTA_WPARAM(wParam);
602 _al_win_mouse_handle_hwheel(d, false, win_display);
603 return TRUE;
604 }
605 break;
606 }
607 case WM_MOUSEMOVE: {
608 if (accept_mouse_event()) {
609 TRACKMOUSEEVENT tme;
610 int mx = GET_X_LPARAM(lParam);
611 int my = GET_Y_LPARAM(lParam);
612
613 if (win_display->mouse_cursor_shown && we_hid_the_mouse) {
614 we_hid_the_mouse = false;
615 win_display->display.vt->hide_mouse_cursor((void*)win_display);
616 }
617
618 _al_win_mouse_handle_move(mx, my, true, win_display);
619 if (mx >= 0 && my >= 0 && mx < d->w && my < d->h) {
620 tme.cbSize = sizeof(tme);
621 tme.dwFlags = TME_QUERY;
622 if (TrackMouseEvent(&tme) && !tme.hwndTrack) {
623 tme.dwFlags = TME_LEAVE;
624 tme.hwndTrack = hWnd;
625 tme.dwHoverTime = 0;
626 TrackMouseEvent(&tme);
627 _al_win_mouse_handle_enter(win_display);
628 }
629 }
630 }
631
632 /* WM_SETCURSOR messages are not received while the mouse is
633 * captured. We call SetCursor here so that changing the mouse
634 * cursor has an effect while the user is holding down the mouse
635 * button.
636 */
637 if (GetCapture() == hWnd && win_display->mouse_cursor_shown) {
638 SetCursor(win_display->mouse_selected_hcursor);
639 }
640
641 break;
642 }
643 case WM_MOUSELEAVE: {
644 if (accept_mouse_event()) {
645 _al_win_mouse_handle_leave(win_display);
646 }
647 break;
648 }
649 case _AL_WM_TOUCH: {
650 if (_al_win_get_touch_input_info && _al_win_close_touch_input_handle) {
651 int number_of_touches = LOWORD(wParam);
652
653 TOUCHINPUT* touches = al_malloc(number_of_touches * sizeof(TOUCHINPUT));
654
655 if (_al_win_get_touch_input_info((HANDLE)lParam, number_of_touches, touches, sizeof(TOUCHINPUT))) {
656
657 if (al_is_touch_input_installed()) {
658
659 int i;
660
661 POINT origin = { 0, 0 };
662
663 ClientToScreen(hWnd, &origin);
664
665 _al_win_touch_input_set_time_stamp((touches + number_of_touches - 1)->dwTime);
666
667 for (i = 0; i < number_of_touches; ++i) {
668
669 TOUCHINPUT* touch = touches + i;
670
671 float x = touch->x / 100.0f - (float)origin.x;
672 float y = touch->y / 100.0f - (float)origin.y;
673
674 bool primary = touch->dwFlags & _AL_TOUCHEVENTF_PRIMARY ? true : false;
675
676 if (touch->dwFlags & _AL_TOUCHEVENTF_DOWN)
677 _al_win_touch_input_handle_begin((int)touch->dwID, (size_t)touch->dwTime, x, y, primary, win_display);
678 else if (touch->dwFlags & _AL_TOUCHEVENTF_UP)
679 _al_win_touch_input_handle_end((int)touch->dwID, (size_t)touch->dwTime, x, y, primary, win_display);
680 else if (touch->dwFlags & _AL_TOUCHEVENTF_MOVE)
681 _al_win_touch_input_handle_move((int)touch->dwID, (size_t)touch->dwTime, x, y, primary, win_display);
682 }
683 }
684
685 _al_win_close_touch_input_handle((HANDLE)lParam);
686 }
687
688 al_free(touches);
689 }
690 break;
691 }
692 case WM_CAPTURECHANGED: {
693 if (al_is_mouse_installed()) {
694 int i;
695 ALLEGRO_MOUSE_STATE state;
696 if (!lParam || (HWND)lParam == hWnd)
697 break;
698 al_get_mouse_state(&state);
699 for (i = 1; i <= 5; i++) {
700 if (al_mouse_button_down(&state, i))
701 _al_win_mouse_handle_button(i, 0, 0, 0, true, win_display);
702 }
703 }
704 break;
705 }
706 case WM_NCMOUSEMOVE: {
707 if (!win_display->mouse_cursor_shown) {
708 we_hid_the_mouse = true;
709 win_display->display.vt->show_mouse_cursor((void*)win_display);
710 }
711 break;
712 }
713 case WM_SYSKEYDOWN: {
714 int vcode = wParam;
715 bool extended = (lParam >> 24) & 0x1;
716 bool repeated = (lParam >> 30) & 0x1;
717 _al_win_kbd_handle_key_press(0, vcode, extended, repeated, win_display);
718 break;
719 }
720 case WM_KEYDOWN: {
721 int vcode = wParam;
722 int scode = (lParam >> 16) & 0xff;
723 bool extended = (lParam >> 24) & 0x1;
724 bool repeated = (lParam >> 30) & 0x1;
725 /* We can't use TranslateMessage() because we don't know if it will
726 produce a WM_CHAR or not. */
727 _al_win_kbd_handle_key_press(scode, vcode, extended, repeated, win_display);
728 break;
729 }
730 case WM_SYSKEYUP:
731 case WM_KEYUP: {
732 int vcode = wParam;
733 int scode = (lParam >> 16) & 0xff;
734 bool extended = (lParam >> 24) & 0x1;
735 _al_win_kbd_handle_key_release(scode, vcode, extended, win_display);
736 break;
737 }
738 case WM_SYSCOMMAND: {
739 if (_al_win_disable_screensaver &&
740 ((wParam & 0xfff0) == SC_MONITORPOWER || (wParam & 0xfff0) == SC_SCREENSAVE)) {
741 return 0;
742 }
743 else if ((wParam & 0xfff0) == SC_KEYMENU) {
744 /* Prevent Windows from intercepting the ALT key.
745 (Disables opening menus via the ALT key.) */
746 return 0;
747 }
748 /* This is used by WM_GETMINMAXINFO to set constraints. */
749 else if ((wParam & 0xfff0) == SC_MAXIMIZE) {
750 d->flags |= ALLEGRO_MAXIMIZED;
751 SetWindowLong(hWnd, GWL_EXSTYLE, WS_EX_APPWINDOW);
752 }
753 else if ((wParam & 0xfff0) == SC_RESTORE) {
754 d->flags &= ~ALLEGRO_MAXIMIZED;
755 SetWindowLong(hWnd, GWL_EXSTYLE, WS_EX_APPWINDOW | WS_EX_OVERLAPPEDWINDOW);
756 }
757 break;
758 }
759 case WM_PAINT: {
760 if (win_display->display.flags & ALLEGRO_GENERATE_EXPOSE_EVENTS) {
761 RECT r;
762 HRGN hrgn;
763 GetWindowRect(win_display->window, &r);
764 hrgn = CreateRectRgn(r.left, r.top, r.right, r.bottom);
765 if (GetUpdateRgn(win_display->window, hrgn, false) != ERROR) {
766 PAINTSTRUCT ps;
767 DWORD size;
768 LPRGNDATA rgndata;
769 int n;
770 int i;
771 RECT *rects;
772 BeginPaint(win_display->window, &ps);
773 size = GetRegionData(hrgn, 0, NULL);
774 rgndata = al_malloc(size);
775 GetRegionData(hrgn, size, rgndata);
776 n = rgndata->rdh.nCount;
777 rects = (RECT *)rgndata->Buffer;
778 //GetWindowInfo(win_display->window, &wi);
779 _al_event_source_lock(es);
780 if (_al_event_source_needs_to_generate_event(es)) {
781 ALLEGRO_EVENT event;
782 event.display.type = ALLEGRO_EVENT_DISPLAY_EXPOSE;
783 event.display.timestamp = al_get_time();
784 for (i = 0; i < n; i++) {
785 event.display.x = rects[i].left;
786 event.display.y = rects[i].top;
787 event.display.width = rects[i].right - rects[i].left;
788 event.display.height = rects[i].bottom - rects[i].top;
789 _al_event_source_emit_event(es, &event);
790 }
791 }
792 _al_event_source_unlock(es);
793 al_free(rgndata);
794 EndPaint(win_display->window, &ps);
795 DeleteObject(hrgn);
796 }
797 return 0;
798 }
799 break;
800 }
801
802 case WM_SETCURSOR:
803 switch (LOWORD(lParam)) {
804 case HTLEFT:
805 case HTRIGHT:
806 SetCursor(LoadCursor(NULL, IDC_SIZEWE));
807 break;
808 case HTBOTTOM:
809 case HTTOP:
810 SetCursor(LoadCursor(NULL, IDC_SIZENS));
811 break;
812 case HTBOTTOMLEFT:
813 case HTTOPRIGHT:
814 SetCursor(LoadCursor(NULL, IDC_SIZENESW));
815 break;
816 case HTBOTTOMRIGHT:
817 case HTTOPLEFT:
818 SetCursor(LoadCursor(NULL, IDC_SIZENWSE));
819 break;
820 default:
821 if (win_display->mouse_cursor_shown) {
822 SetCursor(win_display->mouse_selected_hcursor);
823 }
824 else {
825 SetCursor(NULL);
826 }
827 break;
828 }
829 return 1;
830 case WM_ACTIVATE:
831 if (HIWORD(wParam) && LOWORD(wParam) != WA_INACTIVE)
832 break;
833
834 if (HIWORD(wParam))
835 d->flags |= ALLEGRO_MINIMIZED;
836 else
837 d->flags &= ~ALLEGRO_MINIMIZED;
838
839 if (LOWORD(wParam) != WA_INACTIVE) {
840 // Make fullscreen windows TOPMOST again
841 if (d->flags & ALLEGRO_FULLSCREEN_WINDOW) {
842 SetWindowPos(win_display->window, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE);
843 }
844 if (d->vt->switch_in)
845 d->vt->switch_in(d);
846 _al_win_fix_modifiers();
847 _al_event_source_lock(es);
848 if (_al_event_source_needs_to_generate_event(es)) {
849 ALLEGRO_EVENT event;
850 memset(&event, 0, sizeof(event));
851 event.display.type = ALLEGRO_EVENT_DISPLAY_SWITCH_IN;
852 event.display.timestamp = al_get_time();
853 _al_event_source_emit_event(es, &event);
854 }
855 _al_event_source_unlock(es);
856 _al_win_grab_input(win_display);
857 return 0;
858 }
859 else {
860 // Remove TOPMOST flag from fullscreen windows so we can alt-tab. Also must raise the new activated window
861 if (d->flags & ALLEGRO_FULLSCREEN_WINDOW) {
862 SetWindowPos(win_display->window, HWND_NOTOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE);
863 SetWindowPos(GetForegroundWindow(), HWND_TOP, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE);
864 }
865 if (d->flags & ALLEGRO_FULLSCREEN) {
866 d->vt->switch_out(d);
867 }
868 _al_event_source_lock(es);
869 if (_al_event_source_needs_to_generate_event(es)) {
870 ALLEGRO_EVENT event;
871 memset(&event, 0, sizeof(event));
872 event.display.type = ALLEGRO_EVENT_DISPLAY_SWITCH_OUT;
873 event.display.timestamp = al_get_time();
874 _al_event_source_emit_event(es, &event);
875 }
876 _al_event_source_unlock(es);
877 return 0;
878 }
879 break;
880 case WM_MENUCHAR :
881 return (MNC_CLOSE << 16) | (wParam & 0xffff);
882 case WM_CLOSE:
883 _al_event_source_lock(es);
884 if (_al_event_source_needs_to_generate_event(es)) {
885 ALLEGRO_EVENT event;
886 memset(&event, 0, sizeof(event));
887 event.display.type = ALLEGRO_EVENT_DISPLAY_CLOSE;
888 event.display.timestamp = al_get_time();
889 _al_event_source_emit_event(es, &event);
890 }
891 _al_event_source_unlock(es);
892 return 0;
893 case WM_GETMINMAXINFO:
894 /* Set window constraints only when needed. */
895 if (d->use_constraints) {
896 LPMINMAXINFO p_info = (LPMINMAXINFO)lParam;
897 RECT wRect;
898 RECT cRect;
899 int wWidth;
900 int wHeight;
901 int cWidth;
902 int cHeight;
903
904 GetWindowRect(hWnd, &wRect);
905 GetClientRect(hWnd, &cRect);
906 wWidth = wRect.right - wRect.left;
907 wHeight = wRect.bottom - wRect.top;
908 cWidth = cRect.right - cRect.left;
909 cHeight = cRect.bottom - cRect.top;
910
911 /* Client size is zero when the window is restored. */
912 if (cWidth != 0 && cHeight != 0) {
913 int total_border_width = wWidth - cWidth;
914 int total_border_height = wHeight - cHeight;
915 POINT wmin, wmax;
916
917 wmin.x = (d->min_w > 0) ? d->min_w + total_border_width : p_info->ptMinTrackSize.x;
918 wmin.y = (d->min_h > 0) ? d->min_h + total_border_height : p_info->ptMinTrackSize.y;
919
920 /* don't use max_w & max_h constraints when window maximized */
921 if (d->flags & ALLEGRO_MAXIMIZED) {
922 wmax.x = p_info->ptMaxTrackSize.x;
923 wmax.y = p_info->ptMaxTrackSize.y;
924 }
925 else {
926 wmax.x = (d->max_w > 0) ? d->max_w + total_border_width : p_info->ptMaxTrackSize.x;
927 wmax.y = (d->max_h > 0) ? d->max_h + total_border_height : p_info->ptMaxTrackSize.y;
928 }
929
930 p_info->ptMinTrackSize = wmin;
931 p_info->ptMaxTrackSize = wmax;
932 }
933 }
934 break;
935 case WM_SIZE:
936 if (wParam == SIZE_RESTORED || wParam == SIZE_MAXIMIZED || wParam == SIZE_MINIMIZED) {
937 /*
938 * Delay the resize event so we don't get bogged down with them
939 */
940 if (!resize_postponed) {
941 resize_postponed = true;
942 _beginthread(postpone_thread_proc, 0, (void *)d);
943 }
944 d->flags &= ~ALLEGRO_MAXIMIZED;
945 if (wParam == SIZE_MAXIMIZED) {
946 d->flags |= ALLEGRO_MAXIMIZED;
947 }
948 }
949 else if (d->use_constraints) {
950 /* al_apply_window_constraints() resizes a window if the current
951 * width & height do not fit in the constraint's range.
952 * We have to create the resize event, so the application could
953 * redraw its content.
954 */
955 if (!resize_postponed) {
956 resize_postponed = true;
957 _beginthread(postpone_thread_proc, 0, (void *)d);
958 }
959 }
960 return 0;
961 case WM_ENTERSIZEMOVE:
962 /* DefWindowProc for WM_ENTERSIZEMOVE enters a modal loop, which also
963 * ends up blocking the loop in d3d_display_thread_proc (which is
964 * where we are called from, if using D3D). Rather than batching up
965 * intermediate resize events which the user cannot acknowledge in the
966 * meantime anyway, make it so only a single resize event is generated
967 * at WM_EXITSIZEMOVE.
968 */
969 if (d->flags & ALLEGRO_DIRECT3D_INTERNAL) {
970 resize_postponed = true;
971 }
972 break;
973 case WM_EXITSIZEMOVE:
974 if (resize_postponed) {
975 win_generate_resize_event(win_display);
976 win_display->ignore_resize = false;
977 resize_postponed = false;
978 win_display->can_acknowledge = true;
979 }
980 break;
981 case WM_DPICHANGED:
982 if ((d->flags & ALLEGRO_RESIZABLE) && !(d->flags & ALLEGRO_FULLSCREEN)) {
983 RECT* rect = (RECT*)lParam;
984 // XXX: This doesn't seem to actually move the window... why?
985 SetWindowPos(
986 hWnd,
987 0,
988 rect->left,
989 rect->top,
990 rect->right - rect->left,
991 rect->bottom - rect->top,
992 SWP_NOZORDER | SWP_NOACTIVATE);
993 win_generate_resize_event(win_display);
994 }
995 break;
996 case WM_DEVICECHANGE:
997 _al_win_joystick_dinput_trigger_enumeration();
998 break;
999 }
1000
1001 return DefWindowProc(hWnd,message,wParam,lParam);
1002 }
1003
_al_win_init_window()1004 int _al_win_init_window()
1005 {
1006 // Create A Window Class Structure
1007 window_class.cbClsExtra = 0;
1008 window_class.cbWndExtra = 0;
1009 window_class.hbrBackground = NULL;
1010 window_class.hCursor = NULL;
1011 window_class.hIcon = NULL;
1012 window_class.hInstance = GetModuleHandle(NULL);
1013 window_class.lpfnWndProc = window_callback;
1014 window_class.lpszClassName = TEXT("ALEX");
1015 window_class.lpszMenuName = NULL;
1016 window_class.style = CS_VREDRAW|CS_HREDRAW|CS_OWNDC;
1017
1018 RegisterClass(&window_class);
1019
1020 if (_al_win_msg_call_proc == 0 && _al_win_msg_suicide == 0) {
1021 _al_win_msg_call_proc = RegisterWindowMessage(TEXT("Allegro call proc"));
1022 _al_win_msg_suicide = RegisterWindowMessage(TEXT("Allegro window suicide"));
1023 }
1024
1025 return true;
1026 }
1027
1028
win_choose_icon_bitmap(const int sys_w,const int sys_h,const int num_icons,ALLEGRO_BITMAP * bmps[])1029 static int win_choose_icon_bitmap(const int sys_w, const int sys_h,
1030 const int num_icons, ALLEGRO_BITMAP *bmps[])
1031 {
1032 int best_i = 0;
1033 int best_score = INT_MAX;
1034 int i;
1035
1036 for (i = 0; i < num_icons; i++) {
1037 int bmp_w = al_get_bitmap_width(bmps[i]);
1038 int bmp_h = al_get_bitmap_height(bmps[i]);
1039 int score;
1040
1041 if (bmp_w == sys_w && bmp_h == sys_h)
1042 return i;
1043
1044 /* We prefer to scale up smaller bitmaps to the desired size than to
1045 * scale down larger bitmaps. At these resolutions, scaled up bitmaps
1046 * look blocky, but scaled down bitmaps can look even worse due to to
1047 * dropping crucial pixels.
1048 */
1049 if (bmp_w * bmp_h <= sys_w * sys_h)
1050 score = (sys_w * sys_h) - (bmp_w * bmp_h);
1051 else
1052 score = bmp_w * bmp_h;
1053
1054 if (score < best_score) {
1055 best_score = score;
1056 best_i = i;
1057 }
1058 }
1059
1060 return best_i;
1061 }
1062
win_set_display_icon(ALLEGRO_DISPLAY_WIN * win_display,const WPARAM icon_type,const int sys_w,const int sys_h,const int num_icons,ALLEGRO_BITMAP * bmps[])1063 static void win_set_display_icon(ALLEGRO_DISPLAY_WIN *win_display,
1064 const WPARAM icon_type, const int sys_w, const int sys_h,
1065 const int num_icons, ALLEGRO_BITMAP *bmps[])
1066 {
1067 HICON icon;
1068 HICON old_icon;
1069 ALLEGRO_BITMAP *bmp;
1070 int bmp_w;
1071 int bmp_h;
1072 int i;
1073
1074 i = win_choose_icon_bitmap(sys_w, sys_h, num_icons, bmps);
1075 bmp = bmps[i];
1076 bmp_w = al_get_bitmap_width(bmp);
1077 bmp_h = al_get_bitmap_height(bmp);
1078
1079 if (bmp_w == sys_w && bmp_h == sys_h) {
1080 icon = _al_win_create_icon(win_display->window, bmp, 0, 0, false, false);
1081 }
1082 else {
1083 ALLEGRO_BITMAP *tmp_bmp;
1084 ALLEGRO_STATE backup;
1085
1086 tmp_bmp = al_create_bitmap(sys_w, sys_h);
1087 if (!tmp_bmp)
1088 return;
1089
1090 al_store_state(&backup, ALLEGRO_STATE_BITMAP | ALLEGRO_STATE_BLENDER);
1091 al_set_new_bitmap_flags(ALLEGRO_MEMORY_BITMAP);
1092 al_set_new_bitmap_format(ALLEGRO_PIXEL_FORMAT_ARGB_8888);
1093
1094 al_set_target_bitmap(tmp_bmp);
1095 al_set_blender(ALLEGRO_ADD, ALLEGRO_ONE, ALLEGRO_ZERO);
1096 al_draw_scaled_bitmap(bmp, 0, 0, bmp_w, bmp_h, 0, 0, sys_w, sys_h, 0);
1097
1098 al_restore_state(&backup);
1099
1100 icon = _al_win_create_icon(win_display->window, tmp_bmp, 0, 0, false,
1101 false);
1102
1103 al_destroy_bitmap(tmp_bmp);
1104 }
1105
1106 old_icon = (HICON)SendMessage(win_display->window, WM_SETICON,
1107 icon_type, (LPARAM)icon);
1108
1109 if (old_icon)
1110 DestroyIcon(old_icon);
1111 }
1112
_al_win_set_display_icons(ALLEGRO_DISPLAY * display,int num_icons,ALLEGRO_BITMAP * bmps[])1113 void _al_win_set_display_icons(ALLEGRO_DISPLAY *display,
1114 int num_icons, ALLEGRO_BITMAP *bmps[])
1115 {
1116 ALLEGRO_DISPLAY_WIN *win_display = (ALLEGRO_DISPLAY_WIN *)display;
1117 int sys_w;
1118 int sys_h;
1119
1120 sys_w = GetSystemMetrics(SM_CXSMICON);
1121 sys_h = GetSystemMetrics(SM_CYSMICON);
1122 win_set_display_icon(win_display, ICON_SMALL, sys_w, sys_h,
1123 num_icons, bmps);
1124
1125 sys_w = GetSystemMetrics(SM_CXICON);
1126 sys_h = GetSystemMetrics(SM_CYICON);
1127 win_set_display_icon(win_display, ICON_BIG, sys_w, sys_h,
1128 num_icons, bmps);
1129 }
1130
_al_win_destroy_display_icons(ALLEGRO_DISPLAY * display)1131 void _al_win_destroy_display_icons(ALLEGRO_DISPLAY *display)
1132 {
1133 ALLEGRO_DISPLAY_WIN *win_display = (ALLEGRO_DISPLAY_WIN *)display;
1134 HICON old_icon;
1135
1136 old_icon = (HICON)SendMessage(win_display->window, WM_SETICON, ICON_SMALL, 0);
1137 if (old_icon)
1138 DestroyIcon(old_icon);
1139 old_icon = (HICON)SendMessage(win_display->window, WM_SETICON, ICON_BIG, 0);
1140 if (old_icon)
1141 DestroyIcon(old_icon);
1142 }
1143
_al_win_set_window_position(HWND window,int x,int y)1144 void _al_win_set_window_position(HWND window, int x, int y)
1145 {
1146 SetWindowPos(
1147 window,
1148 HWND_TOP,
1149 x,
1150 y,
1151 0,
1152 0,
1153 SWP_NOSIZE | SWP_NOZORDER);
1154 }
1155
_al_win_get_window_position(HWND window,int * x,int * y)1156 void _al_win_get_window_position(HWND window, int *x, int *y)
1157 {
1158 RECT r;
1159 GetWindowRect(window, &r);
1160
1161 if (x) {
1162 *x = r.left;
1163 }
1164 if (y) {
1165 *y = r.top;
1166 }
1167 }
1168
1169
_al_win_set_window_frameless(ALLEGRO_DISPLAY * display,HWND hWnd,bool frameless)1170 void _al_win_set_window_frameless(ALLEGRO_DISPLAY *display, HWND hWnd,
1171 bool frameless)
1172 {
1173 int w = display->w;
1174 int h = display->h;
1175
1176 if (frameless) {
1177 SetWindowLong(hWnd, GWL_STYLE, WS_VISIBLE);
1178 SetWindowLong(hWnd, GWL_EXSTYLE, WS_EX_APPWINDOW);
1179 SetWindowPos(hWnd, 0, 0, 0, w, h, SWP_NOMOVE | SWP_NOZORDER | SWP_FRAMECHANGED);
1180 }
1181 else {
1182 RECT r;
1183 DWORD style;
1184 DWORD exStyle;
1185
1186 display_flags_to_window_styles(display->flags, &style, &exStyle);
1187 style |= WS_VISIBLE;
1188
1189 GetWindowRect(hWnd, &r);
1190 AdjustWindowRectEx(&r, style, GetMenu(hWnd) ? TRUE : FALSE, exStyle);
1191
1192 w = r.right - r.left;
1193 h = r.bottom - r.top;
1194
1195 SetWindowLong(hWnd, GWL_STYLE, style);
1196 SetWindowLong(hWnd, GWL_EXSTYLE, exStyle);
1197 SetWindowPos(hWnd, 0, 0, 0, w, h, SWP_NOMOVE | SWP_NOZORDER | SWP_FRAMECHANGED);
1198 }
1199 }
1200
1201
_al_win_set_display_flag(ALLEGRO_DISPLAY * display,int flag,bool onoff)1202 bool _al_win_set_display_flag(ALLEGRO_DISPLAY *display, int flag, bool onoff)
1203 {
1204 ALLEGRO_DISPLAY_WIN *win_display = (void*)display;
1205 //double timeout;
1206 ALLEGRO_MONITOR_INFO mi;
1207
1208 memset(&mi, 0, sizeof(mi));
1209
1210 switch (flag) {
1211 case ALLEGRO_FRAMELESS: {
1212 if (onoff) {
1213 display->flags |= ALLEGRO_FRAMELESS;
1214 }
1215 else {
1216 display->flags &= ~ALLEGRO_FRAMELESS;
1217 }
1218 _al_win_set_window_frameless(display, win_display->window,
1219 (display->flags & ALLEGRO_FRAMELESS));
1220 return true;
1221 }
1222
1223 case ALLEGRO_FULLSCREEN_WINDOW:
1224 if ((display->flags & ALLEGRO_FULLSCREEN_WINDOW) && onoff) {
1225 ALLEGRO_DEBUG("Already a fullscreen window\n");
1226 return true;
1227 }
1228 if (!(display->flags & ALLEGRO_FULLSCREEN_WINDOW) && !onoff) {
1229 ALLEGRO_DEBUG("Already a non-fullscreen window\n");
1230 return true;
1231 }
1232
1233 if (onoff) {
1234 /* Switch off frame in fullscreen window mode. */
1235 _al_win_set_window_frameless(display, win_display->window, true);
1236 }
1237 else {
1238 /* Respect display flag in windowed mode. */
1239 _al_win_set_window_frameless(display, win_display->window,
1240 (display->flags & ALLEGRO_FRAMELESS));
1241 }
1242
1243 if (onoff) {
1244 int adapter = win_display->adapter;
1245 al_get_monitor_info(adapter, &mi);
1246 display->flags |= ALLEGRO_FULLSCREEN_WINDOW;
1247 display->w = mi.x2 - mi.x1;
1248 display->h = mi.y2 - mi.y1;
1249 }
1250 else {
1251 display->flags &= ~ALLEGRO_FULLSCREEN_WINDOW;
1252 display->w = win_display->toggle_w;
1253 display->h = win_display->toggle_h;
1254 }
1255
1256 ASSERT(!!(display->flags & ALLEGRO_FULLSCREEN_WINDOW) == onoff);
1257
1258 // Hide the window temporarily
1259 SetWindowPos(win_display->window, 0, 0, 0, 0, 0, SWP_HIDEWINDOW | SWP_NOSIZE | SWP_NOZORDER | SWP_NOMOVE);
1260
1261 al_resize_display(display, display->w, display->h);
1262
1263 if (onoff) {
1264 // Re-set the TOPMOST flag and move to position
1265 SetWindowPos(win_display->window, HWND_TOPMOST, mi.x1, mi.y1, 0, 0, SWP_NOSIZE);
1266 }
1267 else {
1268 int pos_x = 0;
1269 int pos_y = 0;
1270 WINDOWINFO wi;
1271 int bw, bh;
1272
1273 // Unset the topmost flag
1274 SetWindowPos(win_display->window, HWND_NOTOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE);
1275
1276 // Center the window
1277 _al_win_get_window_center(win_display, display->w, display->h, &pos_x, &pos_y);
1278 GetWindowInfo(win_display->window, &wi);
1279 bw = (wi.rcClient.left - wi.rcWindow.left) + (wi.rcWindow.right - wi.rcClient.right),
1280 bh = (wi.rcClient.top - wi.rcWindow.top) + (wi.rcWindow.bottom - wi.rcClient.bottom),
1281 SetWindowPos(
1282 win_display->window, HWND_TOP, 0, 0, display->w+bw, display->h+bh, SWP_NOMOVE
1283 );
1284 SetWindowPos(
1285 win_display->window, HWND_TOP, pos_x-bw/2, pos_y-bh/2, 0, 0, SWP_NOSIZE
1286 );
1287 }
1288
1289 // Show the window again
1290 SetWindowPos(win_display->window, 0, 0, 0, 0, 0, SWP_SHOWWINDOW | SWP_NOSIZE | SWP_NOZORDER | SWP_NOMOVE);
1291
1292 // Clear the window to black
1293 clear_window(win_display->window, display->w, display->h);
1294
1295 ASSERT(!!(display->flags & ALLEGRO_FULLSCREEN_WINDOW) == onoff);
1296 return true;
1297 case ALLEGRO_MAXIMIZED:
1298 if ((!!(display->flags & ALLEGRO_MAXIMIZED)) == onoff)
1299 return true;
1300 if (onoff) {
1301 ShowWindow(win_display->window, SW_SHOWMAXIMIZED);
1302 }
1303 else {
1304 ShowWindow(win_display->window, SW_RESTORE);
1305 }
1306 return true;
1307 }
1308 return false;
1309 }
1310
1311
_al_win_set_window_title(ALLEGRO_DISPLAY * display,const char * title)1312 void _al_win_set_window_title(ALLEGRO_DISPLAY *display, const char *title)
1313 {
1314 ALLEGRO_DISPLAY_WIN *win_display = (ALLEGRO_DISPLAY_WIN *)display;
1315 TCHAR *ttitle = _twin_utf8_to_tchar(title);
1316 SetWindowText(win_display->window, ttitle);
1317 al_free(ttitle);
1318 }
1319
_al_win_set_window_constraints(ALLEGRO_DISPLAY * display,int min_w,int min_h,int max_w,int max_h)1320 bool _al_win_set_window_constraints(ALLEGRO_DISPLAY *display,
1321 int min_w, int min_h, int max_w, int max_h)
1322 {
1323 display->min_w = min_w;
1324 display->min_h = min_h;
1325 display->max_w = max_w;
1326 display->max_h = max_h;
1327
1328 return true;
1329 }
1330
_al_win_apply_window_constraints(ALLEGRO_DISPLAY * display,bool onoff)1331 void _al_win_apply_window_constraints(ALLEGRO_DISPLAY *display, bool onoff)
1332 {
1333 if (!(display->flags & ALLEGRO_MAXIMIZED))
1334 al_resize_display(display, display->w, display->h);
1335 }
1336
_al_win_post_create_window(ALLEGRO_DISPLAY * display)1337 void _al_win_post_create_window(ALLEGRO_DISPLAY *display)
1338 {
1339 /* Ideally the d3d/wgl window creation would already create the
1340 * window maximized - but that code looks too messy to me to touch
1341 * right now.
1342 */
1343 if (display->flags & ALLEGRO_MAXIMIZED) {
1344 display->flags &= ~ALLEGRO_MAXIMIZED;
1345 al_set_display_flag(display, ALLEGRO_MAXIMIZED, true);
1346 }
1347 }
1348
_al_win_get_window_constraints(ALLEGRO_DISPLAY * display,int * min_w,int * min_h,int * max_w,int * max_h)1349 bool _al_win_get_window_constraints(ALLEGRO_DISPLAY *display,
1350 int *min_w, int *min_h, int *max_w, int *max_h)
1351 {
1352 ALLEGRO_DISPLAY_WIN *win_display = (ALLEGRO_DISPLAY_WIN *)display;
1353 *min_w = win_display->display.min_w;
1354 *min_h = win_display->display.min_h;
1355 *max_w = win_display->display.max_w;
1356 *max_h = win_display->display.max_h;
1357
1358 return true;
1359 }
1360
1361
1362 /* _al_win_wnd_call_proc:
1363 * instructs the specifed window thread to call the specified procedure. Waits
1364 * until the procedure has returned.
1365 */
_al_win_wnd_call_proc(HWND wnd,void (* proc)(void *),void * param)1366 void _al_win_wnd_call_proc(HWND wnd, void (*proc) (void*), void* param)
1367 {
1368 ASSERT(_al_win_msg_call_proc);
1369 SendMessage(wnd, _al_win_msg_call_proc, (WPARAM)proc, (LPARAM)param);
1370 }
1371
1372
1373 /* _al_win_wnd_schedule_proc:
1374 * instructs the specifed window thread to call the specified procedure but
1375 * doesn't wait until the procedure has returned.
1376 */
_al_win_wnd_schedule_proc(HWND wnd,void (* proc)(void *),void * param)1377 void _al_win_wnd_schedule_proc(HWND wnd, void (*proc) (void*), void* param)
1378 {
1379 ASSERT(_al_win_msg_call_proc);
1380 if (!PostMessage(wnd, _al_win_msg_call_proc, (WPARAM)proc, (LPARAM)param)) {
1381 ALLEGRO_ERROR("_al_win_wnd_schedule_proc failed.\n");
1382 }
1383 }
1384
1385
1386 /* Function: al_get_win_window_handle
1387 */
al_get_win_window_handle(ALLEGRO_DISPLAY * display)1388 HWND al_get_win_window_handle(ALLEGRO_DISPLAY *display)
1389 {
1390 if (!display)
1391 return NULL;
1392 return ((ALLEGRO_DISPLAY_WIN *)display)->window;
1393 }
1394
1395
_al_win_determine_adapter(void)1396 int _al_win_determine_adapter(void)
1397 {
1398 int a = al_get_new_display_adapter();
1399 if (a == -1) {
1400 int num_screens = al_get_num_video_adapters();
1401 int cScreen = 0;
1402 ALLEGRO_MONITOR_INFO temp_info;
1403 for (cScreen = 0; cScreen < num_screens; cScreen++) {
1404 al_get_monitor_info(cScreen, &temp_info);
1405 if (temp_info.x1 == 0 && temp_info.y1 == 0) { // ..probably found primary display
1406 return cScreen;
1407 }
1408 }
1409 return 0; // safety measure, probably not necessary
1410 }
1411 return a;
1412 }
1413
1414 /* Function: al_win_add_window_callback
1415 */
al_win_add_window_callback(ALLEGRO_DISPLAY * display,bool (* callback)(ALLEGRO_DISPLAY * display,UINT message,WPARAM wparam,LPARAM lparam,LRESULT * result,void * userdata),void * userdata)1416 bool al_win_add_window_callback(ALLEGRO_DISPLAY *display,
1417 bool (*callback)(ALLEGRO_DISPLAY *display, UINT message, WPARAM wparam,
1418 LPARAM lparam, LRESULT *result, void *userdata), void *userdata)
1419 {
1420 ALLEGRO_DISPLAY_WIN *win_display = (ALLEGRO_DISPLAY_WIN *) display;
1421 ALLEGRO_DISPLAY_WIN_CALLBACK *ptr;
1422
1423 if (!display || !callback) {
1424 return false;
1425 }
1426 else {
1427 size_t i;
1428 for (i = 0; i < _al_vector_size(&win_display->msg_callbacks); ++i) {
1429 ALLEGRO_DISPLAY_WIN_CALLBACK *ptr = _al_vector_ref(&win_display->msg_callbacks, i);
1430 if (ptr->proc == callback && ptr->userdata == userdata)
1431 return false;
1432 }
1433 }
1434
1435 if (!(ptr = _al_vector_alloc_back(&win_display->msg_callbacks)))
1436 return false;
1437
1438 ptr->proc = callback;
1439 ptr->userdata = userdata;
1440 return true;
1441 }
1442
1443 /* Function: al_win_remove_window_callback
1444 */
al_win_remove_window_callback(ALLEGRO_DISPLAY * display,bool (* callback)(ALLEGRO_DISPLAY * display,UINT message,WPARAM wparam,LPARAM lparam,LRESULT * result,void * userdata),void * userdata)1445 bool al_win_remove_window_callback(ALLEGRO_DISPLAY *display,
1446 bool (*callback)(ALLEGRO_DISPLAY *display, UINT message, WPARAM wparam,
1447 LPARAM lparam, LRESULT *result, void *userdata), void *userdata)
1448 {
1449 ALLEGRO_DISPLAY_WIN *win_display = (ALLEGRO_DISPLAY_WIN *) display;
1450
1451 if (display && callback) {
1452 size_t i;
1453 for (i = 0; i < _al_vector_size(&win_display->msg_callbacks); ++i) {
1454 ALLEGRO_DISPLAY_WIN_CALLBACK *ptr = _al_vector_ref(&win_display->msg_callbacks, i);
1455 if (ptr->proc == callback && ptr->userdata == userdata) {
1456 _al_vector_delete_at(&win_display->msg_callbacks, i);
1457 return true;
1458 }
1459 }
1460 }
1461
1462 return false;
1463 }
1464
1465 /* vi: set ts=8 sts=3 sw=3 et: */
1466