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(&notification_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, &notification_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