PageRenderTime 63ms CodeModel.GetById 24ms RepoModel.GetById 0ms app.codeStats 0ms

/src/win/wwindow.c

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