Pyrogenesis  13997
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
wsdl.cpp
Go to the documentation of this file.
1 /* Copyright (c) 2012 Wildfire Games
2  *
3  * Permission is hereby granted, free of charge, to any person obtaining
4  * a copy of this software and associated documentation files (the
5  * "Software"), to deal in the Software without restriction, including
6  * without limitation the rights to use, copy, modify, merge, publish,
7  * distribute, sublicense, and/or sell copies of the Software, and to
8  * permit persons to whom the Software is furnished to do so, subject to
9  * the following conditions:
10  *
11  * The above copyright notice and this permission notice shall be included
12  * in all copies or substantial portions of the Software.
13  *
14  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
17  * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
18  * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
19  * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
20  * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21  */
22 
23 /*
24  * emulate SDL on Windows.
25  */
26 
27 #include "precompiled.h"
29 
30 #if CONFIG2_WSDL
31 
32 #include <stdio.h>
33 #include <math.h>
34 #include <queue>
35 #include <algorithm>
36 
37 #include "lib/sysdep/os/win/win.h"
38 #include <process.h> // _beginthreadex
39 #include <WindowsX.h> // message crackers
40 
41 #include "lib/module_init.h"
43 #include "lib/sysdep/sysdep.h"
46 #include "lib/sysdep/os/win/wmi.h" // for SDL_GetVideoInfo
47 
48 #if MSC_VERSION
49 #pragma comment(lib, "user32.lib")
50 #pragma comment(lib, "gdi32.lib")
51 #endif
52 
53 #include "lib/ogl.h" // needed to pull in the delay-loaded opengl32.dll
54 
55 
58 
59 // in fullscreen mode, i.e. not windowed.
60 // video mode will be restored when app is deactivated.
61 static bool fullscreen;
62 
63 // the app is shutting down.
64 // if set, ignore further Windows messages for clean shutdown.
65 static bool is_quitting;
66 
67 static HWND g_hWnd = (HWND)INVALID_HANDLE_VALUE;
68 
69 // usable at any time (required by SDL_SetGamma); this is made
70 // mostly safe via CS_OWNDC.
71 static HDC g_hDC = (HDC)INVALID_HANDLE_VALUE;
72 
73 
74 //----------------------------------------------------------------------------
75 // gamma
76 
77 class GammaRamp
78 {
79 public:
81  : m_hasChanged(false)
82  {
83  }
84 
85  bool Change(HDC hDC, float gamma_r, float gamma_g, float gamma_b)
86  {
87  // get current ramp (once) so we can later restore it.
88  if(!m_hasChanged)
89  {
91  if(!GetDeviceGammaRamp(hDC, m_original))
92  return false;
93  }
94 
95  Compute(gamma_r, m_changed+0*256);
96  Compute(gamma_g, m_changed+1*256);
97  Compute(gamma_b, m_changed+2*256);
98  if(!Upload(m_changed))
99  return false;
100 
101  m_hasChanged = true;
102  return true;
103  }
104 
105  void Latch()
106  {
107  if(m_hasChanged)
108  {
109  if(!Upload(m_changed))
110  m_hasChanged = false;
111  }
112 
113  }
114 
116  {
117  if(m_hasChanged)
118  {
119  if(!Upload(m_original))
120  m_hasChanged = false;
121  }
122  }
123 
124 private:
125  static void Compute(float gamma, u16* ramp)
126  {
127  // assume identity if invalid
128  if(gamma <= 0.0f)
129  gamma = 1.0f;
130 
131  // identity: special-case to make sure we get exact values
132  if(gamma == 1.0f)
133  {
134  for(u16 i = 0; i < 256; i++)
135  ramp[i] = u16(i << 8);
136  return;
137  }
138 
139  for(int i = 0; i < 256; i++)
140  {
141  const double val = pow(i/255.0, (double)gamma);
142  const double clamped = std::max(0.0, std::min(val, 1.0-DBL_EPSILON));
143  ramp[i] = u16_from_double(clamped);
144  }
145  ENSURE(ramp[0] == 0);
146  ENSURE(ramp[255] == 0xFFFF);
147  }
148 
149  bool Upload(u16* ramps)
150  {
152  SetLastError(0);
154  BOOL ok = SetDeviceGammaRamp(g_hDC, ramps);
155  // on multi-monitor NVidia systems, the first call after a reboot
156  // fails, but subsequent ones succeed.
157  // see http://icculus.org/pipermail/quake3-bugzilla/2009-October/001316.html
158  if(ok == FALSE)
159  {
160  ok = SetDeviceGammaRamp(g_hDC, ramps);
161  // at least one 32-bit XP system STILL fails here,
162  // so don't raise an error dialog.
163  if(!ok)
164  debug_printf(L"SetDeviceGammaRamp failed twice. Oh well.\n");
165  }
166  return (ok == TRUE);
167  }
168 
170 
171  // values are 8.8 fixed point
172  u16 m_original[3*256];
173  u16 m_changed[3*256];
174 };
175 
177 
178 
179 // note: any component gamma = 0 is assumed to be identity.
180 int SDL_SetGamma(float r, float g, float b)
181 {
182  return gammaRamp.Change(g_hDC, r, g, b)? 0 : -1;
183 }
184 
185 
186 //----------------------------------------------------------------------------
187 // window
188 //----------------------------------------------------------------------------
189 
190 static DWORD wnd_ChooseWindowStyle(bool fullscreen, HWND previousWindow = (HWND)INVALID_HANDLE_VALUE)
191 {
192  DWORD windowStyle = fullscreen? WS_POPUP : WS_POPUPWINDOW|WS_CAPTION|WS_MINIMIZEBOX;
193  windowStyle |= WS_VISIBLE;
194  windowStyle |= WS_CLIPCHILDREN|WS_CLIPSIBLINGS; // MSDN SetPixelFormat says this is required
195 
196  if(!fullscreen) // windowed
197  {
198  // support resizing
199  windowStyle |= WS_SIZEBOX|WS_MAXIMIZEBOX;
200 
201  // remember the previous maximized state
202  // (else subsequent attempts to maximize will fail)
203  if(wutil_IsValidHandle(previousWindow))
204  {
205  const DWORD previousWindowState = GetWindowLongW(previousWindow, GWL_STYLE);
206  windowStyle |= (previousWindowState & WS_MAXIMIZE);
207  }
208  }
209 
210  return windowStyle;
211 }
212 
213 
214 // @param w,h value-return (in: desired, out: actual pixel count)
215 static void wnd_UpdateWindowDimensions(DWORD windowStyle, int& w, int& h)
216 {
217  // Calculate the size of the outer window, so that the client area has
218  // the desired dimensions.
219  RECT r;
220  r.left = r.top = 0;
221  r.right = w; r.bottom = h;
222  if(AdjustWindowRectEx(&r, windowStyle, FALSE, 0))
223  {
224  w = r.right - r.left;
225  h = r.bottom - r.top;
226  }
227 }
228 
229 
230 static LRESULT CALLBACK OnMessage(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
231 
232 static HWND wnd_CreateWindow(int w, int h)
233 {
234  // (create new window every time (instead of once at startup), because
235  // pixel format isn't supposed to be changed more than once)
236 
237  // app instance.
238  // returned by GetModuleHandle and used in keyboard hook and window creation.
239  const HINSTANCE hInst = GetModuleHandle(0);
240 
241  // register window class
242  WNDCLASSW wc;
243  memset(&wc, 0, sizeof(wc));
244  // no CS_VREDRAW and CS_HREDRAW - avoids redrawing when resized.
245  wc.style = CS_OWNDC; // (see g_hDC definition)
246  wc.lpfnWndProc = OnMessage;
247  wc.lpszClassName = L"WSDL{55752F43-0241-492C-8648-C7243397FCE4}";
248  wc.hInstance = hInst;
249  ATOM class_atom = RegisterClassW(&wc);
250  // ignore failure, which is probably caused by not unregistering the class
251  // (does not happen automatically when called from a DLL). just re-use
252  // the existing class, which is safe because our class name is unique.
253 
254  const DWORD windowStyle = wnd_ChooseWindowStyle(fullscreen);
255  wnd_UpdateWindowDimensions(windowStyle, w, h);
256 
257  // note: you can override the hard-coded window name via SDL_WM_SetCaption.
258  HWND hWnd = CreateWindowExW(WS_EX_APPWINDOW, (LPCWSTR)(uintptr_t)class_atom, L"wsdl", windowStyle, 0, 0, w, h, 0, 0, hInst, 0);
259  if(!wutil_IsValidHandle(hWnd))
261  return hWnd;
262 }
263 
264 
265 static RECT ClientRect(HWND hWnd)
266 {
267  RECT rect;
269  WARN_IF_FALSE(GetClientRect(hWnd, &rect));
270  return rect;
271 }
272 
273 
274 //----------------------------------------------------------------------------
275 // video
276 //----------------------------------------------------------------------------
277 
278 static DEVMODE dm; // current video mode
279 static HGLRC hGLRC = (HGLRC)INVALID_HANDLE_VALUE;
280 
281 // set via SDL_GL_SetAttribute:
282 static int depthBufferBits = 24;
283 static int stencilBufferBits = 0;
284 static int vsyncEnabled = 1;
285 
286 int SDL_GL_SetAttribute(SDL_GLattr attr, int value)
287 {
288  switch(attr)
289  {
290  case SDL_GL_DEPTH_SIZE:
291  depthBufferBits = value;
292  break;
293 
294  case SDL_GL_STENCIL_SIZE:
295  stencilBufferBits = value;
296  break;
297 
298  case SDL_GL_SWAP_CONTROL:
299  vsyncEnabled = value;
300  break;
301 
302  case SDL_GL_DOUBLEBUFFER:
303  // (always enabled)
304  break;
305  }
306 
307  return 0;
308 }
309 
310 
311 // check if resolution needs to be changed
312 static bool video_NeedsChange(int w, int h, int cur_w, int cur_h, bool fullscreen)
313 {
314  // invalid: keep current settings
315  if(w <= 0 || h <= 0)
316  return false;
317 
318  // higher resolution mode needed
319  if(w > cur_w || h > cur_h)
320  return true;
321 
322  // fullscreen requested and not exact same mode set
323  if(fullscreen && (w != cur_w || h != cur_h))
324  return true;
325 
326  return false;
327 }
328 
329 
330 static void video_SetPixelFormat(HDC hDC, int bpp)
331 {
332  const DWORD dwFlags = PFD_SUPPORT_OPENGL|PFD_DRAW_TO_WINDOW|PFD_DOUBLEBUFFER;
333  BYTE cColourBits = (BYTE)bpp;
334  BYTE cAlphaBits = 0;
335  if(bpp == 32)
336  {
337  cColourBits = 24;
338  cAlphaBits = 8;
339  }
340  const BYTE cAccumBits = 0;
341  const BYTE cDepthBits = (BYTE)depthBufferBits;
342  const BYTE cStencilBits = (BYTE)stencilBufferBits;
343  const BYTE cAuxBuffers = 0;
344 
345  PIXELFORMATDESCRIPTOR pfd =
346  {
347  sizeof(PIXELFORMATDESCRIPTOR),
348  1, // version
349  dwFlags,
350  PFD_TYPE_RGBA,
351  cColourBits, 0, 0, 0, 0, 0, 0, // c*Bits, c*Shift are unused
352  cAlphaBits, 0, // cAlphaShift is unused
353  cAccumBits, 0, 0, 0, 0, // cAccum*Bits are unused
354  cDepthBits,
355  cStencilBits,
356  cAuxBuffers,
357  PFD_MAIN_PLANE,
358  0, 0, 0, 0 // bReserved, dw*Mask are unused
359  };
360 
361  // note: the GDI pixel format functions require opengl32.dll to be loaded.
362  // a deadlock on the next line is probably due to VLD's LdrLoadDll hook.
363 
364  const int pf = ChoosePixelFormat(hDC, &pfd);
365  ENSURE(pf >= 1);
366  WARN_IF_FALSE(SetPixelFormat(hDC, pf, &pfd));
367 }
368 
369 
370 // set video mode width x height : bpp (or leave unchanged if already adequate).
371 // w = h = bpp = 0 => no change.
372 SDL_Surface* SDL_SetVideoMode(int w, int h, int bpp, Uint32 flags)
373 {
374  WinScopedPreserveLastError s; // OpenGL and GDI
375 
376  fullscreen = (flags & SDL_FULLSCREEN) != 0;
377 
378  // get current mode settings
379  memset(&dm, 0, sizeof(dm));
380  dm.dmSize = sizeof(dm);
381  WARN_IF_FALSE(EnumDisplaySettings(0, ENUM_CURRENT_SETTINGS, &dm));
382  const int cur_w = (int)dm.dmPelsWidth, cur_h = (int)dm.dmPelsHeight;
383 
384  // independent of resolution; app must always get bpp it wants
385  dm.dmBitsPerPel = bpp;
386  dm.dmFields = DM_BITSPERPEL;
387 
388  if(video_NeedsChange(w,h, cur_w,cur_h, fullscreen))
389  {
390  dm.dmPelsWidth = (DWORD)w;
391  dm.dmPelsHeight = (DWORD)h;
392  dm.dmFields |= DM_PELSWIDTH|DM_PELSHEIGHT;
393  }
394  // the (possibly changed) mode will be (re)set at next WM_ACTIVATE
395 
397  {
398  g_hWnd = wnd_CreateWindow(w, h);
400  return 0;
401 
402  g_hDC = GetDC(g_hWnd);
403 
405 
406  hGLRC = wglCreateContext(g_hDC);
407  if(!hGLRC)
408  return 0;
409 
410  if(!wglMakeCurrent(g_hDC, hGLRC))
411  return 0;
412  }
413  else // update the existing window
414  {
415  const DWORD windowStyle = wnd_ChooseWindowStyle(fullscreen, g_hWnd);
416  wnd_UpdateWindowDimensions(windowStyle, w, h);
417 
418  UINT swp_flags = SWP_FRAMECHANGED|SWP_NOZORDER|SWP_NOACTIVATE;
419  if(!fullscreen) // windowed: preserve the top-left corner
420  swp_flags |= SWP_NOMOVE;
421 
422  WARN_IF_FALSE(SetWindowLongW(g_hWnd, GWL_STYLE, windowStyle));
423  WARN_IF_FALSE(SetWindowPos(g_hWnd, 0, 0, 0, w, h, swp_flags));
424 
425  LONG status;
426  if(fullscreen)
427  {
428  ShowWindow(g_hWnd, SW_RESTORE);
429  status = ChangeDisplaySettings(&dm, CDS_FULLSCREEN);
430  }
431  else
432  {
433  status = ChangeDisplaySettings(0, 0);
434  // don't ShowWindow with SW_MINIMIZE (we just want to update)
435  }
436  ENSURE(status == DISP_CHANGE_SUCCESSFUL);
437  }
438 
439  // (required for ogl_HaveExtension, but callers should also invoke
440  // ogl_Init in case the real SDL is being used.)
441  ogl_Init();
442  if(ogl_HaveExtension("WGL_EXT_swap_control") && pwglSwapIntervalEXT)
443  pwglSwapIntervalEXT(vsyncEnabled);
444 
445  const RECT rect = ClientRect(g_hWnd); // updated window size
446 
447  static SDL_PixelFormat format;
448  format.BitsPerPixel = bpp;
449 
450  static SDL_Surface screen;
451  screen.w = rect.right;
452  screen.h = rect.bottom;
453  screen.format = &format;
454  return &screen;
455 }
456 
457 
458 static void video_Shutdown()
459 {
460  if(fullscreen)
461  {
462  LONG status = ChangeDisplaySettings(0, 0);
463  ENSURE(status == DISP_CHANGE_SUCCESSFUL);
464  }
465 
466  if(wutil_IsValidHandle(hGLRC))
467  {
470  hGLRC = (HGLRC)INVALID_HANDLE_VALUE;
471  }
472 
473  g_hWnd = (HWND)INVALID_HANDLE_VALUE;
474  g_hDC = (HDC)INVALID_HANDLE_VALUE;
475 }
476 
477 
479 {
480  WARN_IF_FALSE(SwapBuffers(g_hDC));
481 }
482 
483 
485 {
486  static SDL_VideoInfo video_info;
487 
488  if(video_info.video_mem == 0)
489  {
490  WmiInstances instances;
491  if(wmi_GetClassInstances(L"Win32_VideoController", instances) == INFO::OK)
492  {
493  for(WmiInstances::iterator it = instances.begin(); it != instances.end(); ++it)
494  {
495  if((*it)[L"Availability"].intVal != 8) // not offline
496  {
497  video_info.video_mem = std::max<Uint32>(video_info.video_mem, (*it)[L"AdapterRAM"].lVal / KiB);
498  break;
499  }
500  }
501  }
502  }
503 
504  return &video_info;
505 }
506 
507 
509 {
510  return 0;
511 }
512 
513 
514 //----------------------------------------------------------------------------
515 // event queue
516 
517 // note: we only use winit to redirect stdout; this queue won't be used
518 // before _cinit.
519 typedef std::queue<SDL_Event> Queue;
520 static Queue g_queue;
521 
522 static void QueueEvent(const SDL_Event& ev)
523 {
524  g_queue.push(ev);
525 }
526 
527 static bool DequeueEvent(SDL_Event& ev)
528 {
529  if(g_queue.empty())
530  return false;
531  ev = g_queue.front();
532  g_queue.pop();
533  return true;
534 }
535 
536 
537 //----------------------------------------------------------------------------
538 // keyboard
539 
540 // note: keysym.unicode is only returned for SDL_KEYDOWN, and is otherwise 0.
541 static void QueueKeyEvent(Uint8 type, SDLKey sdlk, WCHAR unicode_char)
542 {
543  SDL_Event ev;
544  ev.type = type;
545  ev.key.keysym.sym = sdlk;
546  ev.key.keysym.unicode = (Uint16)unicode_char;
547  QueueEvent(ev);
548 }
549 
550 
551 static Uint8 keys[SDLK_LAST];
552 
553 static SDLKey g_SDLKeyForVK[256]; // g_SDLKeyForVK[vk] == SDLK
554 
555 static void key_Init()
556 {
557  // Map the VK keysyms
558  for(size_t i = 0; i < ARRAY_SIZE(g_SDLKeyForVK); i++)
559  g_SDLKeyForVK[i] = SDLK_UNKNOWN;
560 
561  g_SDLKeyForVK[VK_BACK] = SDLK_BACKSPACE;
562  g_SDLKeyForVK[VK_TAB] = SDLK_TAB;
563  g_SDLKeyForVK[VK_CLEAR] = SDLK_CLEAR;
564  g_SDLKeyForVK[VK_RETURN] = SDLK_RETURN;
565  g_SDLKeyForVK[VK_PAUSE] = SDLK_PAUSE;
566  g_SDLKeyForVK[VK_ESCAPE] = SDLK_ESCAPE;
567  g_SDLKeyForVK[VK_SPACE] = SDLK_SPACE;
568  g_SDLKeyForVK[VK_OEM_7] = SDLK_QUOTE;
569  g_SDLKeyForVK[VK_OEM_COMMA] = SDLK_COMMA;
570  g_SDLKeyForVK[VK_OEM_MINUS] = SDLK_MINUS;
571  g_SDLKeyForVK[VK_OEM_PERIOD] = SDLK_PERIOD;
572  g_SDLKeyForVK[VK_OEM_2] = SDLK_SLASH;
573  g_SDLKeyForVK[VK_OEM_1] = SDLK_SEMICOLON;
574  g_SDLKeyForVK[VK_OEM_PLUS] = SDLK_EQUALS;
575  g_SDLKeyForVK[VK_OEM_4] = SDLK_LEFTBRACKET;
576  g_SDLKeyForVK[VK_OEM_5] = SDLK_BACKSLASH;
577  g_SDLKeyForVK[VK_OEM_6] = SDLK_RIGHTBRACKET;
578  g_SDLKeyForVK[VK_OEM_3] = SDLK_BACKQUOTE;
579  g_SDLKeyForVK[VK_OEM_8] = SDLK_BACKQUOTE;
580 
581  // winuser.h guarantees A..Z and 0..9 match their ASCII values:
582  const int VK_0 = '0';
583  for(int i = 0; i < 10; i++)
584  g_SDLKeyForVK[VK_0+i] = (SDLKey)(SDLK_0+i);
585  const int VK_A = 'A';
586  for(int i = 0; i < 26; i++)
587  g_SDLKeyForVK[VK_A+i] = (SDLKey)(SDLK_a+i);
588 
589  g_SDLKeyForVK[VK_DELETE] = SDLK_DELETE;
590 
591  for(int i = 0; i < 10; i++)
592  g_SDLKeyForVK[VK_NUMPAD0+i] = (SDLKey)(SDLK_KP0+i);
593 
594  g_SDLKeyForVK[VK_DECIMAL] = SDLK_KP_PERIOD;
595  g_SDLKeyForVK[VK_DIVIDE] = SDLK_KP_DIVIDE;
596  g_SDLKeyForVK[VK_MULTIPLY] = SDLK_KP_MULTIPLY;
597  g_SDLKeyForVK[VK_SUBTRACT] = SDLK_KP_MINUS;
598  g_SDLKeyForVK[VK_ADD] = SDLK_KP_PLUS;
599 
600  g_SDLKeyForVK[VK_UP] = SDLK_UP;
601  g_SDLKeyForVK[VK_DOWN] = SDLK_DOWN;
602  g_SDLKeyForVK[VK_RIGHT] = SDLK_RIGHT;
603  g_SDLKeyForVK[VK_LEFT] = SDLK_LEFT;
604  g_SDLKeyForVK[VK_INSERT] = SDLK_INSERT;
605  g_SDLKeyForVK[VK_HOME] = SDLK_HOME;
606  g_SDLKeyForVK[VK_END] = SDLK_END;
607  g_SDLKeyForVK[VK_PRIOR] = SDLK_PAGEUP;
608  g_SDLKeyForVK[VK_NEXT] = SDLK_PAGEDOWN;
609 
610  for(int i = 0; i < 12; i++)
611  g_SDLKeyForVK[VK_F1+i] = (SDLKey)(SDLK_F1+i);
612 
613  g_SDLKeyForVK[VK_NUMLOCK] = SDLK_NUMLOCK;
614  g_SDLKeyForVK[VK_CAPITAL] = SDLK_CAPSLOCK;
615  g_SDLKeyForVK[VK_SCROLL] = SDLK_SCROLLOCK;
616  g_SDLKeyForVK[VK_RSHIFT] = SDLK_RSHIFT;
617  g_SDLKeyForVK[VK_LSHIFT] = SDLK_LSHIFT;
618  g_SDLKeyForVK[VK_SHIFT] = SDLK_LSHIFT; // XXX: Not quite
619  g_SDLKeyForVK[VK_RCONTROL] = SDLK_RCTRL;
620  g_SDLKeyForVK[VK_LCONTROL] = SDLK_LCTRL;
621  g_SDLKeyForVK[VK_CONTROL] = SDLK_LCTRL; // XXX: Not quite
622  g_SDLKeyForVK[VK_RMENU] = SDLK_RALT;
623  g_SDLKeyForVK[VK_LMENU] = SDLK_LALT;
624  g_SDLKeyForVK[VK_MENU] = SDLK_LALT; // XXX: Not quite
625  g_SDLKeyForVK[VK_RWIN] = SDLK_RSUPER;
626  g_SDLKeyForVK[VK_LWIN] = SDLK_LSUPER;
627 
628  g_SDLKeyForVK[VK_HELP] = SDLK_HELP;
629 #ifdef VK_PRINT
630  g_SDLKeyForVK[VK_PRINT] = SDLK_PRINT;
631 #endif
632  g_SDLKeyForVK[VK_SNAPSHOT] = SDLK_PRINT;
633  g_SDLKeyForVK[VK_CANCEL] = SDLK_BREAK;
634  g_SDLKeyForVK[VK_APPS] = SDLK_MENU;
635 }
636 
637 
638 static inline SDLKey SDLKeyFromVK(int vk)
639 {
640  if(!(0 <= vk && vk < 256))
641  {
642  DEBUG_WARN_ERR(ERR::LOGIC); // invalid vk
643  return SDLK_UNKNOWN;
644  }
645  return g_SDLKeyForVK[vk];
646 }
647 
648 
649 static void key_ResetAll()
650 {
651  SDL_Event spoofed_up_event;
652  spoofed_up_event.type = SDL_KEYUP;
653  spoofed_up_event.key.keysym.unicode = 0;
654 
655  for(size_t i = 0; i < ARRAY_SIZE(keys); i++)
656  {
657  if(keys[i])
658  {
659  spoofed_up_event.key.keysym.sym = (SDLKey)i;
660  QueueEvent(spoofed_up_event);
661  }
662  }
663 }
664 
665 
666 static LRESULT OnKey(HWND UNUSED(hWnd), UINT vk, BOOL fDown, int UNUSED(cRepeat), UINT flags)
667 {
668  // TODO Mappings for left/right modifier keys
669  // TODO Modifier statekeeping
670 
671  const SDLKey sdlk = SDLKeyFromVK(vk);
672  if(sdlk != SDLK_UNKNOWN)
673  keys[sdlk] = (Uint8)fDown;
674 
675  if(!fDown)
676  QueueKeyEvent(SDL_KEYUP, sdlk, 0);
677  else
678  {
679  // note: flags is HIWORD(lParam) from WM_KEYDOWN, which includes
680  // scancode. ToUnicode also uses its bit 15 to determine if the
681  // key is currently pressed.
682  const UINT scancode = flags;
683  u8 key_states[256];
684  WARN_IF_FALSE(GetKeyboardState(key_states));
685  WCHAR wchars[8];
686  int output_count = ToUnicode(vk, scancode, key_states, wchars, ARRAY_SIZE(wchars), 0);
687  // translation succeeded; queue each produced character
688  if(output_count > 0)
689  {
690  for(int i = 0; i < output_count; i++)
691  QueueKeyEvent(SDL_KEYDOWN, sdlk, wchars[i]);
692  }
693  // dead-char; do nothing
694  else if(output_count == -1)
695  {
696  }
697  // translation failed; just generate a regular (non-unicode) event
698  else if(output_count == 0)
699  QueueKeyEvent(SDL_KEYDOWN, sdlk, 0);
700  else
701  UNREACHABLE;
702  }
703 
704  return 0;
705 }
706 
707 
708 Uint8* SDL_GetKeyState(int* num_keys)
709 {
710  if(num_keys)
711  *num_keys = SDLK_LAST;
712  return keys;
713 }
714 
715 
716 // always on (we don't care about the extra overhead)
717 int SDL_EnableUNICODE(int UNUSED(enable))
718 {
719  return 1;
720 }
721 
722 
723 //----------------------------------------------------------------------------
724 // joystick
725 
727 {
728  return 0;
729 }
730 
732 {
733  return 0;
734 }
735 
736 const char* SDL_JoystickName(int UNUSED(device_index))
737 {
738  return NULL;
739 }
740 
742 {
743  return NULL;
744 }
745 
747 {
748  return 0;
749 }
750 
752 {
753  return 0;
754 }
755 
756 
757 //----------------------------------------------------------------------------
758 // app activation
759 
760 enum SdlActivationType { LOSE = 0, GAIN = 1 };
761 
762 static inline void QueueActiveEvent(SdlActivationType type, size_t changed_app_state)
763 {
764  // SDL says this event is not generated when the window is created,
765  // but skipping the first event may confuse things.
766 
767  SDL_Event ev;
768  ev.type = SDL_ACTIVEEVENT;
769  ev.active.state = (u8)changed_app_state;
770  ev.active.gain = (u8)((type == GAIN)? 1 : 0);
771  QueueEvent(ev);
772 }
773 
774 
775 // SDL_APP* bitflags indicating whether we are active.
776 // note: responsibility for yielding lies with SDL apps -
777 // they control the main loop.
779 
780 static void active_ChangeState(SdlActivationType type, Uint8 changed_app_state)
781 {
782  Uint8 old_app_state = app_state;
783 
784  if(type == GAIN)
785  app_state = Uint8(app_state | changed_app_state);
786  else
787  app_state = Uint8(app_state & ~changed_app_state);
788 
789  // generate an event - but only if the given state flags actually changed.
790  if((old_app_state & changed_app_state) != (app_state & changed_app_state))
791  QueueActiveEvent(type, changed_app_state);
792 }
793 
794 
795 static LRESULT OnActivate(HWND hWnd, UINT state, HWND UNUSED(hWndActDeact), BOOL fMinimized)
796 {
797  SdlActivationType type;
798  Uint8 changed_app_state;
799 
800  // went active and not minimized
801  if(state != WA_INACTIVE && !fMinimized)
802  {
803  type = GAIN;
804  changed_app_state = SDL_APPINPUTFOCUS|SDL_APPACTIVE;
805 
806  // grab keyboard focus (we previously had DefWindowProc do this).
807  SetFocus(hWnd);
808 
809  gammaRamp.Latch();
810  if(fullscreen)
811  {
812  ShowWindow(g_hWnd, SW_RESTORE);
813  const LONG ret = ChangeDisplaySettings(&dm, CDS_FULLSCREEN);
814  ENSURE(ret == DISP_CHANGE_SUCCESSFUL);
815  }
816 
817  // re-assert mouse grab state
819  }
820  // deactivated (Alt+Tab out) or minimized
821  else
822  {
823  type = LOSE;
824  changed_app_state = SDL_APPINPUTFOCUS;
825  if(fMinimized)
826  changed_app_state |= SDL_APPACTIVE;
827 
828  key_ResetAll();
829 
830  gammaRamp.RestoreOriginal();
831  if(fullscreen)
832  {
833  const LONG ret = ChangeDisplaySettings(0, 0);
834  ENSURE(ret == DISP_CHANGE_SUCCESSFUL);
835  WARN_IF_FALSE(ShowWindow(g_hWnd, SW_MINIMIZE));
836  }
837  }
838 
839  active_ChangeState(type, changed_app_state);
840  return 0;
841 }
842 
843 
845 {
846  return app_state;
847 }
848 
849 
850 static void QueueQuitEvent()
851 {
852  SDL_Event ev;
853  ev.type = SDL_QUIT;
854  QueueEvent(ev);
855 }
856 
857 
858 //----------------------------------------------------------------------------
859 // mouse
860 
861 // background: there are several types of coordinates.
862 // - screen coords are relative to the primary desktop and may therefore be
863 // negative on multi-monitor systems (e.g. if secondary monitor is left of
864 // primary). they are prefixed with screen_*.
865 // - "client" coords are simply relative to the parent window's origin and
866 // can also be negative (e.g. in the window's NC area).
867 // these are prefixed with client_*.
868 // - "idealized" coords are what the app sees. these range from 0 to
869 // windowDimensions-1. they are returned by mouse_GetCoords and have no prefix.
870 
871 static void QueueMouseEvent(int x, int y)
872 {
873  SDL_Event ev;
874  ev.type = SDL_MOUSEMOTION;
875  ENSURE(unsigned(x|y) <= USHRT_MAX);
876  ev.motion.x = (Uint16)x;
877  ev.motion.y = (Uint16)y;
878  QueueEvent(ev);
879 }
880 
881 static void QueueButtonEvent(int button, int state, int x, int y)
882 {
883  SDL_Event ev;
885  ev.button.button = (u8)button;
886  ev.button.state = (u8)state;
887  ENSURE(unsigned(x|y) <= USHRT_MAX);
888  ev.button.x = (Uint16)x;
889  ev.button.y = (Uint16)y;
890  QueueEvent(ev);
891 }
892 
893 
894 static int mouse_x, mouse_y;
895 
896 // generate a mouse move message and update our notion of the mouse position.
897 // x, y are client pixel coordinates.
898 // notes:
899 // - does not actually move the OS cursor;
900 // - called from mouse_Update and SDL_WarpMouse.
901 static void mouse_UpdatePosition(int x, int y)
902 {
903  // nothing to do if it hasn't changed since last time
904  if(mouse_x == x && mouse_y == y)
905  return;
906 
907  mouse_x = x;
908  mouse_y = y;
909  QueueMouseEvent(x, y);
910 }
911 
912 static POINT mouse_ScreenFromClient(int client_x, int client_y)
913 {
914  POINT screen_pt;
915  screen_pt.x = (LONG)client_x;
916  screen_pt.y = (LONG)client_y;
917  WARN_IF_FALSE(ClientToScreen(g_hWnd, &screen_pt));
918  return screen_pt;
919 }
920 
921 // get idealized client coordinates or return false if outside our window.
922 static bool mouse_GetCoords(int screen_x, int screen_y, int& x, int& y)
923 {
925 
926  POINT screen_pt;
927  screen_pt.x = (LONG)screen_x;
928  screen_pt.y = (LONG)screen_y;
929 
930  POINT client_pt;
931  {
932  // note: MapWindowPoints has a really stupid interface, returning 0
933  // on failure or if no shift was needed (i.e. window is fullscreen).
934  // we must use GetLastError to detect error conditions.
936  SetLastError(0);
937  client_pt = screen_pt; // translated below
938  const int ret = MapWindowPoints(HWND_DESKTOP, g_hWnd, &client_pt, 1);
939  ENSURE(ret != 0 || GetLastError() == 0);
940  }
941 
942  {
943  const RECT client_rect = ClientRect(g_hWnd);
944  if(!PtInRect(&client_rect, client_pt))
945  return false;
946  }
947 
948  if(WindowFromPoint(screen_pt) != g_hWnd)
949  return false;
950 
951  x = client_pt.x;
952  y = client_pt.y;
953  ENSURE(x >= 0 && y >= 0);
954  return true;
955 }
956 
957 
958 static void mouse_Update()
959 {
960  // window not created yet or already shut down. no sense reporting
961  // mouse position, and bail now to avoid ScreenToClient failing.
963  return;
964 
965  // don't use DirectInput, because we want to respect the user's mouse
966  // sensitivity settings. Windows messages are prone to lag,
967  // so query current position directly.
968  // note: GetCursorPos fails if the desktop is switched (e.g. after
969  // pressing Ctrl+Alt+Del), which can be ignored.
970  POINT screen_pt;
971  if(!GetCursorPos(&screen_pt))
972  return;
973  int x, y;
974  if(mouse_GetCoords(screen_pt.x, screen_pt.y, x, y))
975  {
977  mouse_UpdatePosition(x, y);
978  }
979  // moved outside of window
980  else
982 }
983 
984 
985 static unsigned mouse_buttons;
986 
987 // (we define a new function signature since the windowsx.h message crackers
988 // don't provide for passing uMsg)
989 static LRESULT OnMouseButton(HWND UNUSED(hWnd), UINT uMsg, int client_x, int client_y, UINT flags)
990 {
991  int button;
992  int state;
993  switch(uMsg)
994  {
995  case WM_LBUTTONDOWN:
996  button = SDL_BUTTON_LEFT;
997  state = SDL_PRESSED;
998  break;
999  case WM_LBUTTONUP:
1000  button = SDL_BUTTON_LEFT;
1001  state = SDL_RELEASED;
1002  break;
1003  case WM_RBUTTONDOWN:
1004  button = SDL_BUTTON_RIGHT;
1005  state = SDL_PRESSED;
1006  break;
1007  case WM_RBUTTONUP:
1008  button = SDL_BUTTON_RIGHT;
1009  state = SDL_RELEASED;
1010  break;
1011  case WM_MBUTTONDOWN:
1012  button = SDL_BUTTON_MIDDLE;
1013  state = SDL_PRESSED;
1014  break;
1015  case WM_MBUTTONUP:
1016  button = SDL_BUTTON_MIDDLE;
1017  state = SDL_RELEASED;
1018  break;
1019  case WM_XBUTTONDOWN:
1020  button = SDL_BUTTON_X1 + GET_XBUTTON_WPARAM(flags) - 1;
1021  state = SDL_PRESSED;
1022  break;
1023  case WM_XBUTTONUP:
1024  button = SDL_BUTTON_X1 + GET_XBUTTON_WPARAM(flags) - 1;
1025  state = SDL_RELEASED;
1026  break;
1027  NODEFAULT;
1028  }
1029 
1030  // mouse capture
1031  static int outstanding_press_events;
1032  if(state == SDL_PRESSED)
1033  {
1034  // grab mouse to ensure we get up events
1035  if(++outstanding_press_events > 0)
1036  (void)SetCapture(g_hWnd); // (returns previous window)
1037  }
1038  else
1039  {
1040  // release after all up events received
1041  if(--outstanding_press_events <= 0)
1042  {
1043  WARN_IF_FALSE(ReleaseCapture());
1044  outstanding_press_events = 0;
1045  }
1046  }
1047 
1048  // update button bitfield
1049  if(state == SDL_PRESSED)
1050  mouse_buttons |= SDL_BUTTON(button);
1051  else
1052  mouse_buttons &= ~SDL_BUTTON(button);
1053 
1054  const POINT screen_pt = mouse_ScreenFromClient(client_x, client_y);
1055  int x, y;
1056  if(mouse_GetCoords(screen_pt.x, screen_pt.y, x, y))
1057  QueueButtonEvent(button, state, x, y);
1058 
1059  // Per MSDN, return TRUE for XBUTTON events
1060  if (uMsg == WM_XBUTTONDOWN || uMsg == WM_XBUTTONUP)
1061  return TRUE;
1062 
1063  return 0;
1064 }
1065 
1066 
1067 // (note: this message is sent even if the cursor is outside our window)
1068 static LRESULT OnMouseWheel(HWND UNUSED(hWnd), int screen_x, int screen_y, int zDelta, UINT UNUSED(fwKeys))
1069 {
1070  int x, y;
1071  if(mouse_GetCoords(screen_x, screen_y, x, y))
1072  {
1073  int button = (zDelta < 0)? SDL_BUTTON_WHEELDOWN : SDL_BUTTON_WHEELUP;
1074  // SDL says this sends a down message followed by up.
1075  QueueButtonEvent(button, SDL_PRESSED, x, y);
1076  QueueButtonEvent(button, SDL_RELEASED, x, y);
1077  }
1078 
1079  return 0; // handled
1080 }
1081 
1082 
1083 Uint8 SDL_GetMouseState(int* x, int* y)
1084 {
1085  if(x)
1086  *x = (int)mouse_x;
1087  if(y)
1088  *y = (int)mouse_y;
1089  return (Uint8)mouse_buttons;
1090 }
1091 
1092 
1093 void SDL_WarpMouse(int x, int y)
1094 {
1095  // SDL interface provides for int, but the values should be
1096  // idealized client coords (>= 0)
1097  ENSURE(x >= 0 && y >= 0);
1098  mouse_UpdatePosition(x, y);
1099 
1100  const int client_x = x, client_y = y;
1101  const POINT screen_pt = mouse_ScreenFromClient(client_x, client_y);
1102  WARN_IF_FALSE(SetCursorPos(screen_pt.x, screen_pt.y));
1103 }
1104 
1105 
1106 int SDL_ShowCursor(int toggle)
1107 {
1108  static int cursor_visible = SDL_ENABLE;
1109  if(toggle != SDL_QUERY)
1110  {
1111  // only call Windows ShowCursor if changing the visibility -
1112  // it maintains a counter.
1113  if(cursor_visible != toggle)
1114  {
1115  (void)ShowCursor(toggle); // (returns display counter)
1116  cursor_visible = toggle;
1117  }
1118  }
1119  return cursor_visible;
1120 }
1121 
1122 
1124 {
1125  static SDL_GrabMode prevMode;
1126  if(mode == SDL_GRAB_QUERY)
1127  return prevMode;
1128  prevMode = mode;
1129 
1130  if(mode == SDL_GRAB_OFF)
1131  WARN_IF_FALSE(ClipCursor(0));
1132  else
1133  {
1134  const RECT clientRect = ClientRect(g_hWnd);
1135  POINT upperLeft = { clientRect.left, clientRect.top };
1136  WARN_IF_FALSE(ClientToScreen(g_hWnd, &upperLeft));
1137  POINT lowerRight = { clientRect.right, clientRect.bottom };
1138  WARN_IF_FALSE(ClientToScreen(g_hWnd, &lowerRight));
1139  const RECT screenRect = { upperLeft.x, upperLeft.y, lowerRight.x, lowerRight.y };
1140  WARN_IF_FALSE(ClipCursor(&screenRect));
1141  }
1142 
1143  return mode;
1144 }
1145 
1146 
1147 //----------------------------------------------------------------------------
1148 // video resizing/expose
1149 
1150 static bool ResizeEventEnabled(int clientWidth, int clientHeight)
1151 {
1152  // when the app receives a resize event, it must call SDL_SetVideoMode.
1153  // avoid that if the size hasn't actually changed. this also
1154  // prevents infinite recursion, since SDL_SetVideoMode might
1155  // trigger resize events. note that this logic is safer than a
1156  // skipResize flag, because that would remain set if SDL_SetVideoMode
1157  // doesn't trigger a resize, and we would miss an actual resize.
1158  static int lastClientWidth, lastClientHeight;
1159  if(lastClientWidth == clientWidth && lastClientHeight == clientHeight)
1160  return false;
1161  lastClientWidth = clientWidth;
1162  lastClientHeight = clientHeight;
1163 
1164  // if fullscreen, interaction with other topmost windows causes
1165  // minimization and a spurious resize. however, the app only
1166  // expects resizing events if !fullscreen.
1167  if(fullscreen)
1168  return false;
1169 
1170  // this happens during minimization, which results in an
1171  // app-active event anyway, and the app might have
1172  // trouble with size=0.
1173  if(clientWidth == 0 && clientHeight == 0)
1174  return false;
1175 
1176  return true;
1177 }
1178 
1179 // note: this is called continuously during resizing. since SDL doesn't
1180 // discard any SDL_VIDEORESIZE events, the application must deal with
1181 // the flood (and only call SDL_SetVideoMode once a frame or similar).
1182 // note: SDL uses WM_WINDOWPOSCHANGING, which requires calling
1183 // GetClientRect and suffers from false alarms.
1184 static void OnSize(HWND UNUSED(hWnd), UINT UNUSED(state), int clientWidth, int clientHeight)
1185 {
1186  if(!ResizeEventEnabled(clientWidth, clientHeight))
1187  return;
1188 
1189  SDL_Event ev;
1190  ev.type = SDL_VIDEORESIZE;
1191  ev.resize.w = clientWidth;
1192  ev.resize.h = clientHeight;
1193  QueueEvent(ev);
1194 }
1195 
1196 
1197 static BOOL OnEraseBkgnd(HWND UNUSED(hWnd), HDC UNUSED(hDC))
1198 {
1199  SDL_Event ev;
1200  ev.type = SDL_VIDEOEXPOSE;
1201  QueueEvent(ev);
1202 
1203  // prevent GDI from erasing the background by claiming we did so.
1204  // PAINTSTRUCT.fErase will later be FALSE.
1205  return TRUE;
1206 }
1207 
1208 
1209 //----------------------------------------------------------------------------
1210 
1211 static LRESULT OnPaint(HWND hWnd)
1212 {
1213  // BeginPaint/EndPaint is unnecessary (see http://opengl.czweb.org/ch04/082-084.html)
1214  // however, we at least need to validate the window to prevent
1215  // continuous WM_PAINT messages.
1216  WARN_IF_FALSE(ValidateRect(hWnd, 0));
1217  return 0;
1218 }
1219 
1220 static LRESULT OnDestroy(HWND hWnd)
1221 {
1222  ENSURE(hWnd == g_hWnd);
1223  WARN_IF_FALSE(ReleaseDC(g_hWnd, g_hDC));
1224  g_hDC = (HDC)INVALID_HANDLE_VALUE;
1225  g_hWnd = (HWND)INVALID_HANDLE_VALUE;
1226  QueueQuitEvent();
1227 
1228 #ifdef _DEBUG
1229  // see http://www.adrianmccarthy.com/blog/?p=51
1230  // with WM_QUIT in the message queue, MessageBox will immediately
1231  // return IDABORT. to ensure any subsequent CRT error reports are
1232  // at least somewhat visible, we redirect them to debug output.
1233  _CrtSetReportMode(_CRT_ASSERT, _CRTDBG_MODE_DEBUG);
1234 #endif
1235 
1236  PostQuitMessage(0);
1237  return 0;
1238 }
1239 
1240 
1242 {
1245 };
1246 
1248 {
1249  switch(wParam)
1250  {
1251  case SC_SCREENSAVE:
1252  // disable screen-saver in fullscreen mode (other applications
1253  // may interfere with us, and we have set the system-wide gamma)
1254  if(fullscreen)
1255  return kSkipDefWindowProc;
1256  break;
1257 
1258  // Alt+F4 or system menu double-click / exit
1259  case SC_CLOSE:
1260  QueueQuitEvent();
1261  break;
1262  }
1263 
1264  return kRunDefWindowProc;
1265 }
1266 
1267 
1268 static LRESULT CALLBACK OnMessage(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
1269 {
1270  if(is_quitting)
1271  return DefWindowProcW(hWnd, uMsg, wParam, lParam);
1272 
1273  switch(uMsg)
1274  {
1275  case WM_PAINT:
1276  return OnPaint(hWnd);
1277 
1278  HANDLE_MSG(hWnd, WM_ERASEBKGND, OnEraseBkgnd);
1279 
1280  // prevent selecting menu in fullscreen mode
1281  case WM_NCHITTEST:
1282  if(fullscreen)
1283  return HTCLIENT;
1284  break;
1285 
1286  HANDLE_MSG(hWnd, WM_ACTIVATE, OnActivate);
1287  HANDLE_MSG(hWnd, WM_DESTROY, OnDestroy);
1288 
1289  case WM_SYSCOMMAND:
1290  if(OnSysCommand(wParam) == kSkipDefWindowProc)
1291  return 0;
1292  break;
1293 
1294  HANDLE_MSG(hWnd, WM_SYSKEYUP , OnKey);
1295  HANDLE_MSG(hWnd, WM_KEYUP , OnKey);
1296  HANDLE_MSG(hWnd, WM_SYSKEYDOWN, OnKey);
1297  HANDLE_MSG(hWnd, WM_KEYDOWN , OnKey);
1298 
1299  HANDLE_MSG(hWnd, WM_MOUSEWHEEL, OnMouseWheel);
1300 
1301  HANDLE_MSG(hWnd, WM_SIZE, OnSize);
1302 
1303  // (can't use message crackers: they do not provide for passing uMsg)
1304  case WM_LBUTTONDOWN:
1305  case WM_LBUTTONUP:
1306  case WM_RBUTTONDOWN:
1307  case WM_RBUTTONUP:
1308  case WM_MBUTTONDOWN:
1309  case WM_MBUTTONUP:
1310  case WM_XBUTTONDOWN:
1311  case WM_XBUTTONUP:
1312  return OnMouseButton(hWnd, uMsg, GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam), (UINT)wParam);
1313 
1314  default:
1315  // (DefWindowProc must be called outside the switch because some
1316  // messages are only conditionally 'grabbed', e.g. NCHITTEST)
1317  break;
1318  }
1319 
1320  return DefWindowProcW(hWnd, uMsg, wParam, lParam);
1321 }
1322 
1323 
1325 {
1326  // rationale: we would like to reduce CPU usage automatically if
1327  // possible. blocking here until a message arrives would accomplish
1328  // that, but might potentially freeze the app too long.
1329  // instead, they should check active state and call SDL_Delay etc.
1330  // if our window is minimized.
1331 
1332  mouse_Update(); // polled
1333 
1334  MSG msg;
1335  while(PeekMessageW(&msg, 0, 0, 0, PM_REMOVE))
1336  {
1337  (void)DispatchMessageW(&msg); // (returns window procedure's return value)
1338  }
1339 }
1340 
1341 
1342 
1344 {
1345  SDL_PumpEvents();
1346 
1347  if(DequeueEvent(*ev))
1348  return 1;
1349 
1350  return 0;
1351 }
1352 
1353 
1355 {
1356  QueueEvent(*ev);
1357  return 0;
1358 }
1359 
1360 
1361 //-----------------------------------------------------------------------------
1362 // byte swapping
1363 
1364 // implement only if the header hasn't mapped SDL_Swap* to intrinsics
1365 
1366 #ifndef SDL_Swap16
1368 {
1369  return (u16)(((x & 0xff) << 8) | (x >> 8));
1370 }
1371 #endif
1372 
1373 #ifndef SDL_Swap32
1375 {
1376  return (x << 24) |
1377  (x >> 24) |
1378  ((x << 8) & 0x00ff0000) |
1379  ((x >> 8) & 0x0000ff00);
1380 }
1381 #endif
1382 
1383 #ifndef SDL_Swap64
1385 {
1386  const u32 lo = (u32)(x & 0xFFFFFFFF);
1387  const u32 hi = (u32)(x >> 32);
1388  u64 ret = SDL_Swap32(lo);
1389  ret <<= 32;
1390  // careful: must shift var of type u64, not u32
1391  ret |= SDL_Swap32(hi);
1392  return ret;
1393 }
1394 #endif
1395 
1396 
1397 //-----------------------------------------------------------------------------
1398 // multithread support
1399 
1400 // semaphores
1401 // note: implementing these in terms of pthread sem_t doesn't help;
1402 // this wrapper is very close to the Win32 routines.
1403 
1405 {
1406  return (HANDLE)s;
1407 }
1408 
1410 {
1411  return (SDL_sem*)h;
1412 }
1413 
1415 {
1416  HANDLE h = CreateSemaphore(0, cnt, std::numeric_limits<LONG>::max(), 0);
1417  return sem_from_HANDLE(h);
1418 }
1419 
1421 {
1422  HANDLE h = HANDLE_from_sem(sem);
1423  WARN_IF_FALSE(CloseHandle(h));
1424 }
1425 
1427 {
1428  HANDLE h = HANDLE_from_sem(sem);
1429  return ReleaseSemaphore(h, 1, 0);
1430 }
1431 
1433 {
1434  HANDLE h = HANDLE_from_sem(sem);
1435  return WaitForSingleObject(h, INFINITE);
1436 }
1437 
1438 
1439 //-----------------------------------------------------------------------------
1440 // misc API
1441 
1442 void SDL_WM_SetCaption(const char* title, const char* icon)
1443 {
1444  WARN_IF_FALSE(SetWindowTextA(g_hWnd, title));
1445 
1446  // real SDL ignores this parameter, so we will follow suit.
1447  UNUSED2(icon);
1448 }
1449 
1450 
1452 {
1453  return GetTickCount();
1454 }
1455 
1456 
1458 {
1459  Sleep(ms);
1460 }
1461 
1462 
1463 void* SDL_GL_GetProcAddress(const char* name)
1464 {
1465  return wglGetProcAddress(name);
1466 }
1467 
1468 
1469 //-----------------------------------------------------------------------------
1470 // init/shutdown
1471 
1472 // defend against calling SDL_Quit twice (GameSetup does this to work
1473 // around ATI driver breakage)
1475 
1476 static Status Init()
1477 {
1478  key_Init();
1479  return INFO::OK;
1480 }
1481 
1482 static void Shutdown()
1483 {
1484  is_quitting = true;
1485 
1487  gammaRamp.RestoreOriginal();
1488 
1490  WARN_IF_FALSE(DestroyWindow(g_hWnd));
1491 
1492  video_Shutdown();
1493 }
1494 
1496 {
1497  return (ModuleInit(&initState, Init) < 0)? -1 : 0;
1498 }
1499 
1501 {
1502  return 0;
1503 }
1504 
1505 void SDL_Quit()
1506 {
1507  ModuleShutdown(&initState, Shutdown);
1508 }
1509 
1510 
1511 static void RedirectStdout()
1512 {
1513  // this process is apparently attached to a console, and users might be
1514  // surprised to find that we redirected the output to a file, so don't.
1515  if(wutil_IsValidHandle(GetStdHandle(STD_OUTPUT_HANDLE)))
1516  return;
1517 
1518  WinScopedPreserveLastError s; // ChangeExtension
1519 
1520  // this code may be included in multiple executables sharing the same
1521  // directory, so include the executable's name in the filename. use its
1522  // full path since the current directory is unreliable.
1523  const OsPath pathname = sys_ExecutablePathname().ChangeExtension(L"_stdout.txt");
1524 
1525  // ignore BoundsChecker warnings here. subsystem is set to "Windows"
1526  // to prevent the OS from opening a console on startup (ugly).
1527  // that means stdout isn't associated with a lowio handle; _close is
1528  // called with fd = -1. oh well, there's nothing we can do.
1529  FILE* f = 0;
1530  // ignore return value - it might indicate 'file already exists' even
1531  // if f is valid, which is what actually counts.
1532  (void)_wfreopen_s(&f, OsString(pathname).c_str(), L"wt", stdout);
1533  if(GetLastError() == ERROR_ALREADY_EXISTS)
1534  SetLastError(0);
1535  // executable directory (probably Program Files) is read-only for
1536  // non-Administrators. we can't pick another directory because
1537  // ah_log_dir might not be valid until the app's init has run,
1538  // nor should we pollute the (root) wutil_AppdataPath directory.
1539  // since stdout usually isn't critical and is seen if launching the
1540  // app from a console, just skip the redirection in this case.
1541  if(f == 0)
1542  return;
1543 
1544 #if CONFIG_ENABLE_CHECKS
1545  // disable buffering, so that no writes are lost even if the program
1546  // crashes. only enabled in full debug mode because this is really slow!
1547  setvbuf(stdout, 0, _IONBF, 0);
1548  #endif
1549 }
1550 
1552 {
1553  // note: SDL does this in its WinMain hook. doing so in SDL_Init would be
1554  // too late (we might miss some output), so we use winit.
1555  // (this is possible because _cinit has already been called)
1556  RedirectStdout();
1557 
1558  return INFO::OK;
1559 }
1560 
1562 {
1563  // was redirected to stdout.txt; closing avoids a BoundsChecker warning.
1564  fclose(stdout);
1565 
1566  return INFO::OK;
1567 }
1568 
1569 #endif // #if CONFIG2_WSDL
int SDL_PollEvent(SDL_Event *ev)
Definition: wsdl.cpp:1343
static unsigned mouse_buttons
Definition: wsdl.cpp:985
static Uint8 app_state
Definition: wsdl.cpp:778
void SDL_sem
Definition: wsdl.h:109
static HANDLE HANDLE_from_sem(SDL_sem *s)
Definition: wsdl.cpp:1404
#define u8
Definition: types.h:39
const Status LOGIC
Definition: status.h:409
u16 m_original[3 *256]
Definition: wsdl.cpp:172
u16 SDL_Swap16(const u16 x)
Definition: wsdl.cpp:1367
static bool fullscreen
Definition: wsdl.cpp:61
static LRESULT OnMouseButton(HWND hWnd, UINT uMsg, int client_x, int client_y, UINT flags)
Definition: wsdl.cpp:989
static SDLKey SDLKeyFromVK(int vk)
Definition: wsdl.cpp:638
#define UNUSED(param)
mark a function parameter as unused and avoid the corresponding compiler warning. ...
int SDL_JoystickNumAxes(SDL_Joystick *joystick)
Definition: wsdl.cpp:746
SDL_GrabMode
Definition: wsdl.h:326
int SDL_ShowCursor(int toggle)
Definition: wsdl.cpp:1106
#define SDL_BUTTON(b)
Definition: wsdl.h:233
Uint8 SDL_GetMouseState(int *x, int *y)
Definition: wsdl.cpp:1083
static void key_Init()
Definition: wsdl.cpp:555
int SDL_JoystickEventState(int state)
Definition: wsdl.cpp:731
static void QueueMouseEvent(int x, int y)
Definition: wsdl.cpp:871
SDL_KeyboardEvent key
Definition: wsdl.h:305
Definition: wsdl.cpp:760
const Status OK
Definition: status.h:386
some WinAPI functions SetLastError(0) on success, which is bad because it can hide previous errors...
Definition: wutil.h:119
void SDL_WarpMouse(int x, int y)
Definition: wsdl.cpp:1093
static ModuleInitState initState
Definition: wsdl.cpp:1474
static Uint8 keys[SDLK_LAST]
Definition: wsdl.cpp:551
void SDL_Delay(Uint32 ms)
Definition: wsdl.cpp:1457
SDL_GrabMode SDL_WM_GrabInput(SDL_GrabMode mode)
Definition: wsdl.cpp:1123
bool wutil_IsValidHandle(H h)
Definition: wutil.h:34
static void mouse_UpdatePosition(int x, int y)
Definition: wsdl.cpp:901
Definition: wsdl.h:294
static bool is_quitting
Definition: wsdl.cpp:65
void SDL_PumpEvents()
Definition: wsdl.cpp:1324
DefWindowProcDisposition
Definition: wsdl.cpp:1241
SDL_VideoInfo * SDL_GetVideoInfo()
Definition: wsdl.cpp:484
static bool DequeueEvent(SDL_Event &ev)
Definition: wsdl.cpp:527
static Status Init()
Definition: h_mgr.cpp:744
static SDL_sem * sem_from_HANDLE(HANDLE h)
Definition: wsdl.cpp:1409
Uint8 BitsPerPixel
Definition: wsdl.h:77
void * SDL_GL_GetProcAddress(const char *name)
Definition: wsdl.cpp:1463
u16 u16_from_double(double in)
convert double to u16; verifies number is in range.
Definition: lib.cpp:97
static void Shutdown()
Definition: h_mgr.cpp:762
void * SDL_Joystick
Definition: wsdl.h:157
Uint8 SDL_GetAppState()
Definition: wsdl.cpp:844
bool Change(HDC hDC, float gamma_r, float gamma_g, float gamma_b)
Definition: wsdl.cpp:85
static void Compute(float gamma, u16 *ramp)
Definition: wsdl.cpp:125
SDL_GLattr
Definition: wsdl.h:59
WINGDIAPI BOOL WINAPI wglDeleteContext(HGLRC)
void SDL_GL_SwapBuffers()
Definition: wsdl.cpp:478
bool m_hasChanged
Definition: wsdl.cpp:169
int SDL_GL_SetAttribute(SDL_GLattr attr, int value)
Definition: wsdl.cpp:286
int BOOL
Definition: wgl.h:51
GammaRamp()
Definition: wsdl.cpp:80
void SDL_Quit()
Definition: wsdl.cpp:1505
u8 Uint8
Definition: wsdl.h:36
static int vsyncEnabled
Definition: wsdl.cpp:284
std::vector< WmiInstance > WmiInstances
Definition: wmi.h:42
#define ARRAY_SIZE(name)
SDL_MouseMotionEvent motion
Definition: wsdl.h:307
static void QueueEvent(const SDL_Event &ev)
Definition: wsdl.cpp:522
static void active_ChangeState(SdlActivationType type, Uint8 changed_app_state)
Definition: wsdl.cpp:780
static Status wsdl_Init()
Definition: wsdl.cpp:1551
LONG left
Definition: wgl.h:66
void SDL_DestroySemaphore(SDL_sem *sem)
Definition: wsdl.cpp:1420
static void QueueActiveEvent(SdlActivationType type, size_t changed_app_state)
Definition: wsdl.cpp:762
LONG top
Definition: wgl.h:67
static LRESULT OnPaint(HWND hWnd)
Definition: wsdl.cpp:1211
SdlActivationType
Definition: wsdl.cpp:760
#define ENSURE(expr)
ensure the expression &lt;expr&gt; evaluates to non-zero.
Definition: debug.h:282
void SDL_WM_SetCaption(const char *title, const char *icon)
Definition: wsdl.cpp:1442
#define UNUSED2(param)
mark a function local variable or parameter as unused and avoid the corresponding compiler warning...
#define CALLBACK
Definition: wgl.h:39
intptr_t ModuleInitState
initialization state of a module (class, source file, etc.) must be initialized to zero (e...
Definition: module_init.h:35
static LRESULT OnMouseWheel(HWND hWnd, int screen_x, int screen_y, int zDelta, UINT fwKeys)
Definition: wsdl.cpp:1068
static void wnd_UpdateWindowDimensions(DWORD windowStyle, int &w, int &h)
Definition: wsdl.cpp:215
Uint8 type
Definition: wsdl.h:302
WINGDIAPI HGLRC WINAPI wglCreateContext(HDC)
static int stencilBufferBits
Definition: wsdl.cpp:283
SDL_MouseButtonEvent button
Definition: wsdl.h:308
SDL_ActiveEvent active
Definition: wsdl.h:303
Definition: path.h:75
void * HANDLE
Definition: wgl.h:62
static DEVMODE dm
Definition: wsdl.cpp:278
static int depthBufferBits
Definition: wsdl.cpp:282
Status wmi_GetClassInstances(const wchar_t *className, WmiInstances &instances)
get all instances of the requested class.
Definition: wmi.cpp:106
static GammaRamp gammaRamp
Definition: wsdl.cpp:176
static void QueueQuitEvent()
Definition: wsdl.cpp:850
u64 SDL_Swap64(const u64 x)
Definition: wsdl.cpp:1384
#define UNREACHABLE
&quot;unreachable code&quot; helpers
void Latch()
Definition: wsdl.cpp:105
Uint8 * SDL_GetKeyState(int *num_keys)
Definition: wsdl.cpp:708
static const size_t KiB
Definition: alignment.h:71
unsigned long DWORD
Definition: wgl.h:56
Sint16 SDL_JoystickGetAxis(SDL_Joystick *joystick, int axis)
Definition: wsdl.cpp:751
static HWND wnd_CreateWindow(int w, int h)
Definition: wsdl.cpp:232
i16 Sint16
Definition: wsdl.h:38
static HDC g_hDC
Definition: wsdl.cpp:71
static bool mouse_GetCoords(int screen_x, int screen_y, int &x, int &y)
Definition: wsdl.cpp:922
int h
Definition: wsdl.h:83
i64 Status
Error handling system.
Definition: status.h:171
SDL_PixelFormat * format
Definition: wsdl.h:82
u16 unicode
Definition: wsdl.h:189
LONG right
Definition: wgl.h:68
WINIT_REGISTER_EARLY_SHUTDOWN(wsdl_Shutdown)
Uint32 video_mem
Definition: wsdl.h:92
SDL_Joystick * SDL_JoystickOpen(int device_index)
Definition: wsdl.cpp:741
SDL_Surface * SDL_GetVideoSurface()
Definition: wsdl.cpp:508
void RestoreOriginal()
Definition: wsdl.cpp:115
WINGDIAPI PROC WINAPI wglGetProcAddress(LPCSTR)
bool Upload(u16 *ramps)
Definition: wsdl.cpp:149
#define DEBUG_WARN_ERR(status)
display the error dialog with text corresponding to the given error code.
Definition: debug.h:331
int w
Definition: wsdl.h:83
static void QueueKeyEvent(Uint8 type, SDLKey sdlk, WCHAR unicode_char)
Definition: wsdl.cpp:541
static LRESULT OnKey(HWND hWnd, UINT vk, BOOL fDown, int cRepeat, UINT flags)
Definition: wsdl.cpp:666
int SDL_Init(Uint32 flags)
Definition: wsdl.cpp:1495
static HGLRC hGLRC
Definition: wsdl.cpp:279
#define u16
Definition: types.h:40
int SDL_EnableUNICODE(int enable)
Definition: wsdl.cpp:717
SDL_sem * SDL_CreateSemaphore(int cnt)
Definition: wsdl.cpp:1414
#define u64
Definition: types.h:42
Status ModuleShutdown(volatile ModuleInitState *initState, void(*shutdown)())
calls a user-defined shutdown function if initState is &quot;initialized&quot;.
Definition: module_init.cpp:65
WINIT_REGISTER_LATE_INIT(wsdl_Init)
const char * SDL_JoystickName(int device_index)
Definition: wsdl.cpp:736
std::queue< SDL_Event > Queue
Definition: wsdl.cpp:519
bool ogl_HaveExtension(const char *ext)
check if an extension is supported by the OpenGL implementation.
Definition: ogl.cpp:187
static LRESULT OnDestroy(HWND hWnd)
Definition: wsdl.cpp:1220
static bool video_NeedsChange(int w, int h, int cur_w, int cur_h, bool fullscreen)
Definition: wsdl.cpp:312
u32 SDL_Swap32(const u32 x)
Definition: wsdl.cpp:1374
#define u32
Definition: types.h:41
Path ChangeExtension(Path extension) const
Definition: path.h:185
static void video_Shutdown()
Definition: wsdl.cpp:458
Definition: wgl.h:64
static int mouse_x
Definition: wsdl.cpp:894
LONG bottom
Definition: wgl.h:69
OsPath sys_ExecutablePathname()
Definition: bsd.cpp:33
static Queue g_queue
Definition: wsdl.cpp:520
#define WARN_IF_FALSE(expression)
Definition: status.h:360
void ogl_Init()
initialization: import extension function pointers and do feature detect.
Definition: ogl.cpp:473
static LRESULT OnActivate(HWND hWnd, UINT state, HWND hWndActDeact, BOOL fMinimized)
Definition: wsdl.cpp:795
u32 Uint32
Definition: wsdl.h:39
static Status wsdl_Shutdown()
Definition: wsdl.cpp:1561
static DefWindowProcDisposition OnSysCommand(WPARAM wParam)
Definition: wsdl.cpp:1247
unsigned int UINT
Definition: wgl.h:54
const Status FAIL
Definition: status.h:406
int SDL_PushEvent(SDL_Event *ev)
Definition: wsdl.cpp:1354
int SDL_InitSubSystem(Uint32 flags)
Definition: wsdl.cpp:1500
SDL_keysym keysym
Definition: wsdl.h:196
static BOOL OnEraseBkgnd(HWND hWnd, HDC hDC)
Definition: wsdl.cpp:1197
static void QueueButtonEvent(int button, int state, int x, int y)
Definition: wsdl.cpp:881
#define NODEFAULT
convenient specialization of UNREACHABLE for switch statements whose default can never be reached...
u32 SDL_GetTicks()
Definition: wsdl.cpp:1451
u16 m_changed[3 *256]
Definition: wsdl.cpp:173
int SDL_SetGamma(float r, float g, float b)
Definition: wsdl.cpp:180
static int mouse_y
Definition: wsdl.cpp:894
static void video_SetPixelFormat(HDC hDC, int bpp)
Definition: wsdl.cpp:330
static bool ResizeEventEnabled(int clientWidth, int clientHeight)
Definition: wsdl.cpp:1150
long LONG
Definition: wgl.h:55
int SDL_SemPost(SDL_sem *sem)
Definition: wsdl.cpp:1426
SDL_Surface * SDL_SetVideoMode(int w, int h, int bpp, Uint32 flags)
Definition: wsdl.cpp:372
static void OnSize(HWND hWnd, UINT state, int clientWidth, int clientHeight)
Definition: wsdl.cpp:1184
static POINT mouse_ScreenFromClient(int client_x, int client_y)
Definition: wsdl.cpp:912
SDLKey sym
Definition: wsdl.h:188
static void mouse_Update()
Definition: wsdl.cpp:958
Status ModuleInit(volatile ModuleInitState *initState, Status(*init)())
calls a user-defined init function if initState is zero.
Definition: module_init.cpp:40
WINGDIAPI BOOL WINAPI wglMakeCurrent(HDC, HGLRC)
u16 Uint16
Definition: wsdl.h:37
int SDL_SemWait(SDL_sem *sem)
Definition: wsdl.cpp:1432
static LRESULT CALLBACK OnMessage(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
Definition: wsdl.cpp:1268
static SDLKey g_SDLKeyForVK[256]
Definition: wsdl.cpp:553
static void RedirectStdout()
Definition: wsdl.cpp:1511
static void key_ResetAll()
Definition: wsdl.cpp:649
SDL_ResizeEvent resize
Definition: wsdl.h:309
void debug_printf(const wchar_t *fmt,...)
write a formatted string to the debug channel, subject to filtering (see below).
Definition: debug.cpp:142
#define SDL_FULLSCREEN
Definition: wsdl.h:72
static enum @41 state
static std::string OsString(const OsPath &path)
Definition: os_path.h:42
static RECT ClientRect(HWND hWnd)
Definition: wsdl.cpp:265
int SDL_NumJoysticks()
Definition: wsdl.cpp:726
Definition: wsdl.cpp:760
static DWORD wnd_ChooseWindowStyle(bool fullscreen, HWND previousWindow=(HWND) INVALID_HANDLE_VALUE)
Definition: wsdl.cpp:190
static HWND g_hWnd
Definition: wsdl.cpp:67