FreeBASIC  0.91.0
gfx_win32.c
Go to the documentation of this file.
1 /* common win32 routines and drivers list */
2 
3 #include "../fb_gfx.h"
4 #include "fb_gfx_win32.h"
5 #include "../../rtlib/win32/fb_private_console.h"
6 #include <process.h>
7 
8 #define WM_MOUSEENTER WM_USER
9 
10 #ifndef WM_XBUTTONDOWN
11 #define WM_XBUTTONDOWN 523
12 #define WM_XBUTTONUP 524
13 #define WM_XBUTTONDBLCLK 525
14 #endif
15 
16 #ifndef WM_MOUSEHWHEEL
17 #define WM_MOUSEHWHEEL 526
18 #endif
19 
20 #ifndef MK_XBUTTON1
21 #define MK_XBUTTON1 32
22 #define MK_XBUTTON2 64
23 #endif
24 
25 #ifndef MONITOR_DEFAULTTONEAREST
26 #define MONITOR_DEFAULTTONEAREST 0x00000002
27 #endif
28 
30 
32 #ifndef HOST_CYGWIN
34 #endif
37  NULL
38 };
39 
40 static const struct { const char *name; FARPROC *proc; } user32_procs[] = {
41  {"SetLayeredWindowAttributes", (FARPROC *)&fb_win32.SetLayeredWindowAttributes},
42  {"MonitorFromWindow", (FARPROC *)&fb_win32.MonitorFromWindow },
43  {"MonitorFromPoint", (FARPROC *)&fb_win32.MonitorFromPoint },
44  {"FlashWindowEx", (FARPROC *)&fb_win32.FlashWindowEx },
45  {"TrackMouseEvent", (FARPROC *)&fb_win32.TrackMouseEvent },
46  {"GetMonitorInfoA", (FARPROC *)&fb_win32.GetMonitorInfo },
47  {"ChangeDisplaySettingsExA", (FARPROC *)&fb_win32.ChangeDisplaySettingsEx }
48 };
49 
50 static CRITICAL_SECTION update_lock;
51 static HANDLE handle;
55 static POINT last_mouse_pos;
56 
57 struct keyconvinfo {
58  union {
59  void *v;
60  WCHAR *w;
61  char *a;
62  };
63  int size;
64 };
65 typedef struct keyconvinfo KEYCONVINFO;
66 static KEYCONVINFO keyconv1 = {{NULL}, 0};
67 static KEYCONVINFO keyconv2 = {{NULL}, 0};
68 
69 static void keyconv_clear( KEYCONVINFO *k );
70 static void keyconv_grow( KEYCONVINFO *k, int nchars, int charsize );
71 
72 static unsigned int hIntlConvertChar( int key, int source_cp, int dest_cp );
73 
74 static void fb_hSetMouseClip( void )
75 {
76  RECT rc;
77  POINT point;
78  GetClientRect(fb_win32.wnd, &rc);
79  point.x = rc.left;
80  point.y = rc.top;
81  ClientToScreen(fb_win32.wnd, &point);
82  rc.left = point.x;
83  rc.top = point.y;
84  point.x = rc.right;
85  point.y = rc.bottom;
86  ClientToScreen(fb_win32.wnd, &point);
87  rc.right = point.x;
88  rc.bottom = point.y;
89  ClipCursor(&rc);
90 }
91 
92 static void ToggleFullScreen( void )
93 {
94  if (fb_win32.flags & DRIVER_NO_SWITCH)
95  return;
96 
97  fb_win32.monitor = fb_win32.MonitorFromWindow ? fb_win32.MonitorFromWindow(fb_win32.wnd, MONITOR_DEFAULTTONEAREST) : NULL;
98 
99  if (fb_win32.mouse_clip)
100  ClipCursor(NULL);
101 
102  fb_win32.exit();
103  fb_win32.flags ^= DRIVER_FULLSCREEN;
104  if (fb_win32.init()) {
105  fb_win32.exit();
106  fb_win32.flags ^= DRIVER_FULLSCREEN;
107  fb_win32.init();
108  }
110  fb_hMemSet(__fb_gfx->dirty, TRUE, fb_win32.h);
111  fb_win32.is_active = TRUE;
112  has_focus = FALSE;
113 }
114 
115 static VOID CALLBACK fb_hTrackMouseTimerProc(HWND hWnd, UINT uMsg, UINT idEvent, DWORD dwTime)
116 {
117  POINT pt, rect_pt[2];
118  RECT *rect = (RECT *)rect_pt;
119 
120  GetClientRect(fb_win32.wnd, rect);
121  MapWindowPoints(fb_win32.wnd, NULL, rect_pt, 2);
122  GetCursorPos(&pt);
123  if ((!PtInRect(rect, pt)) || (WindowFromPoint(pt) != fb_win32.wnd)) {
124  KillTimer(fb_win32.wnd, idEvent);
125  PostMessage(fb_win32.wnd, WM_MOUSELEAVE, 0, 0);
126  }
127 }
128 
129 static BOOL WINAPI fb_hTrackMouseEvent(TRACKMOUSEEVENT *e)
130 {
131  if (e->dwFlags == TME_LEAVE)
132  return SetTimer(e->hwndTrack, e->dwFlags, 100, (TIMERPROC)fb_hTrackMouseTimerProc);
133  return FALSE;
134 }
135 
136 LRESULT CALLBACK fb_hWin32WinProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
137 {
138  BYTE key_state[256];
139  TRACKMOUSEEVENT track_e;
140  POINT mouse_pos, rect_pt[2];
141  RECT *rect = (RECT *)rect_pt;
142  PAINTSTRUCT ps;
143  EVENT e;
144  BOOL is_minimized;
145  MINMAXINFO *mmi;
146 
147  e.type = 0;
148 
149  GetClientRect(fb_win32.wnd, rect);
150  MapWindowPoints(fb_win32.wnd, NULL, rect_pt, 2);
151  GetCursorPos(&mouse_pos);
152  mouse_on = PtInRect(rect, mouse_pos);
153 
154  switch (message)
155  {
156  case WM_ACTIVATE:
157  is_minimized = HIWORD(wParam);
158  fb_win32.is_active = ((!is_minimized) && (LOWORD(wParam) != WA_INACTIVE));
159 
160  /*
161  If the window is minimized or inactive, then lower the thread
162  priority to avoid a very slow fast-user-switch.
163  */
164  if (handle) {
165  if(fb_win32.flags & DRIVER_HIGH_PRIORITY)
166  {
167  if (fb_win32.is_active)
168  SetThreadPriority(handle, THREAD_PRIORITY_ABOVE_NORMAL );
169  else
170  SetThreadPriority(handle, THREAD_PRIORITY_NORMAL );
171  }
172  }
173 
174  fb_hMemSet(__fb_gfx->key, FALSE, 128);
175  mouse_buttons = 0;
176  fb_hMemSet(__fb_gfx->dirty, TRUE, fb_win32.h);
177  if ((!fb_win32.is_active) && (has_focus)) {
179  fb_hPostEvent(&e);
180  has_focus = FALSE;
181  KillTimer(fb_win32.wnd, TME_LEAVE);
182  }
183  if (!((LOWORD(wParam)) && (is_minimized))) {
185  fb_hPostEvent(&e);
186  }
187  if ((fb_win32.is_active) && (mouse_on)) {
188  message = WM_MOUSEENTER;
189  break;
190  }
191  if (fb_win32.mouse_clip) {
192  if (!fb_win32.is_active)
193  ClipCursor(NULL);
194  else
196  }
197  return 0;
198 
199  case WM_SETCURSOR:
200  if ((mouse_on) && (!cursor_shown))
201  SetCursor(NULL);
202  else
203  SetCursor(LoadCursor(NULL, IDC_ARROW));
204  return TRUE;
205 
206  case WM_MOUSEMOVE:
208  mouse_x = e.x = lParam & 0xFFFF;
209  mouse_y = e.y = (lParam >> 16) & 0xFFFF;
210  if (last_mouse_pos.x == 0xFFFF) {
211  e.dx = e.dy = 0;
212  }
213  else {
214  e.dx = mouse_pos.x - last_mouse_pos.x;
215  e.dy = mouse_pos.y - last_mouse_pos.y;
216  }
217  last_mouse_pos = mouse_pos;
218  if (((!e.dx) && (!e.dy)) || (!fb_win32.is_active))
219  e.type = 0;
220  break;
221 
222  case WM_MOUSELEAVE:
223  if (!fb_win32.is_active)
224  break;
226  fb_hPostEvent(&e);
227  has_focus = FALSE;
228  return 0;
229 
230  case WM_LBUTTONDOWN:
231  case WM_LBUTTONDBLCLK:
232  SetCapture( hWnd );
234  e.type = (message == WM_LBUTTONDOWN ? EVENT_MOUSE_BUTTON_PRESS : EVENT_MOUSE_DOUBLE_CLICK);
235  e.button = BUTTON_LEFT;
236  break;
237 
238  case WM_LBUTTONUP:
240  if(!mouse_buttons && GetCapture() == hWnd)
241  ReleaseCapture();
243  e.button = BUTTON_LEFT;
244  break;
245 
246  case WM_RBUTTONDOWN:
247  case WM_RBUTTONDBLCLK:
248  SetCapture( hWnd );
250  e.type = (message == WM_RBUTTONDOWN ? EVENT_MOUSE_BUTTON_PRESS : EVENT_MOUSE_DOUBLE_CLICK);
251  e.button = BUTTON_RIGHT;
252  break;
253 
254  case WM_RBUTTONUP:
256  if(!mouse_buttons && GetCapture() == hWnd)
257  ReleaseCapture();
259  e.button = BUTTON_RIGHT;
260  break;
261 
262  case WM_MBUTTONDOWN:
263  case WM_MBUTTONDBLCLK:
264  SetCapture( hWnd );
266  e.type = (message == WM_MBUTTONDOWN ? EVENT_MOUSE_BUTTON_PRESS : EVENT_MOUSE_DOUBLE_CLICK);
267  e.button = BUTTON_MIDDLE;
268  break;
269 
270  case WM_MBUTTONUP:
272  if(!mouse_buttons && GetCapture() == hWnd)
273  ReleaseCapture();
275  e.button = BUTTON_MIDDLE;
276  break;
277 
278  case WM_XBUTTONDOWN:
279  case WM_XBUTTONDBLCLK:
280  if (fb_win32.version < 0x500)
281  break;
282  SetCapture( hWnd );
284  mouse_buttons |= ((LOWORD(wParam) & MK_XBUTTON1) ? BUTTON_X1 : 0 )
285  | ((LOWORD(wParam) & MK_XBUTTON2) ? BUTTON_X2 : 0 );
288  break;
289 
290  case WM_XBUTTONUP:
291  if (fb_win32.version < 0x500)
292  break;
294  mouse_buttons &= ~((LOWORD(wParam) & MK_XBUTTON1) ? 0 : BUTTON_X1 )
295  & ~((LOWORD(wParam) & MK_XBUTTON2) ? 0 : BUTTON_X2 );
296  if(!mouse_buttons && GetCapture() == hWnd)
297  ReleaseCapture();
300  break;
301 
302  case WM_MOUSEWHEEL:
303  if (fb_win32.version < 0x40A)
304  break;
305  if ((signed)wParam > 0)
306  mouse_wheel++;
307  else
308  mouse_wheel--;
310  e.z = mouse_wheel;
311  break;
312 
313  case WM_MOUSEHWHEEL:
314  if (fb_win32.version < 0x500)
315  break;
316  if ((signed)wParam > 0)
317  mouse_hwheel++;
318  else
319  mouse_hwheel--;
321  e.w = mouse_hwheel;
322  break;
323 
324  case WM_SIZE:
325  case WM_SYSKEYDOWN:
326  if (!fb_win32.is_active)
327  break;
328 
329  {
330  int is_alt_enter = ((message == WM_SYSKEYDOWN) && (wParam == VK_RETURN) && (lParam & 0x20000000));
331  int is_maximize = ((message == WM_SIZE) && (wParam == SIZE_MAXIMIZED));
332  if ( is_maximize || is_alt_enter) {
333  if (has_focus) {
335  fb_hPostEvent(&e);
336  }
338  return FALSE;
339  }
340  if( message!=WM_SYSKEYDOWN )
341  break;
342  }
343 
344  case WM_KEYDOWN:
345  case WM_KEYUP:
346  {
347  WORD wVkCode = (WORD) wParam;
348  WORD wVsCode = (WORD) (( lParam & 0xFF0000 ) >> 16);
349  int is_ext_keycode = ( lParam & 0x1000000 )!=0;
350  size_t repeat_count = ( lParam & 0xFFFF );
351  int is_repeated = (lParam & 0x40000000);
352  DWORD dwControlKeyState = 0;
353  char chAsciiChar;
354  int is_dead_key;
355  int key;
356 
357  GetKeyboardState(key_state);
358 
359  if( (key_state[VK_SHIFT] | key_state[VK_LSHIFT] | key_state[VK_RSHIFT]) & 0x80 )
360  dwControlKeyState ^= SHIFT_PRESSED;
361  if( (key_state[VK_LCONTROL] | key_state[VK_CONTROL]) & 0x80 )
362  dwControlKeyState ^= LEFT_CTRL_PRESSED;
363  if( key_state[VK_RCONTROL] & 0x80 )
364  dwControlKeyState ^= RIGHT_CTRL_PRESSED;
365  if( (key_state[VK_LMENU] | key_state[VK_MENU]) & 0x80 )
366  dwControlKeyState ^= LEFT_ALT_PRESSED;
367  if( key_state[VK_RMENU] & 0x80 )
368  dwControlKeyState ^= RIGHT_ALT_PRESSED;
369  if( is_ext_keycode )
370  dwControlKeyState |= ENHANCED_KEY;
371 
372  is_dead_key = (MapVirtualKey( wVkCode, 2 ) & 0x80000000)!=0;
373  if( !is_dead_key ) {
374  WORD wKey = 0;
375  if( ToAscii( wVkCode, wVsCode, key_state, &wKey, 0 )==1 ) {
376  chAsciiChar = (char) wKey;
377  } else {
378  chAsciiChar = 0;
379  }
380  } else {
381  /* Never use ToAscii when a dead key is used - otherwise
382  * we don't get the combined character (for accents) */
383  chAsciiChar = 0;
384  }
385  key = fb_hConsoleTranslateKey( chAsciiChar,
386  wVsCode,
387  wVkCode,
388  dwControlKeyState,
389  FALSE );
390  if (message == WM_KEYDOWN) {
391  if (is_repeated)
393  else
394  e.type = EVENT_KEY_PRESS;
395  if (key > 0xFF) {
396  while( repeat_count-- ) {
397  fb_hPostKey(key);
398  }
399  }
400  }
401  else
403  e.scancode = fb_hVirtualToScancode(wVkCode);
404 
405  /* Don't return extended keycodes in the ascii field */
406  e.ascii = ((key < 0) || (key > 0xFF)) ? 0 : key;
407 
408  /* We don't want to enter the menu ... */
409  if( wVkCode == VK_F10 || wVkCode == VK_MENU || key == KEY_QUIT )
410  return FALSE;
411  }
412  break;
413 
414  case WM_CHAR:
415  {
416  size_t repeat_count = ( lParam & 0xFFFF );
417  int key = (int) wParam;
418  if( key < 256 ) {
419  int target_cp = FB_GFX_GET_CODEPAGE();
420  if( target_cp!=-1 )
421  key = hIntlConvertChar( key, CP_ACP, target_cp );
422 
423  while( repeat_count-- ) {
424  fb_hPostKey(key);
425  }
426  }
427  }
428 
429  break;
430 
431  case WM_CLOSE:
432  fb_hPostKey( KEY_QUIT ); /* ALT + F4 */
434  fb_hPostEvent(&e);
435  return FALSE;
436 
437  case WM_PAINT:
438  BeginPaint(fb_win32.wnd, &ps);
439  fb_win32.paint();
440  EndPaint(fb_win32.wnd, &ps);
441  break;
442 
443  case WM_DISPLAYCHANGE:
444  fb_win32.paint();
446  break;
447 
448  case WM_MOVING:
449  if (fb_win32.mouse_clip)
450  ClipCursor(NULL);
451  break;
452 
453  case WM_GETMINMAXINFO:
454  /* Don't let the window size be truncated to the screen size */
455  mmi = (MINMAXINFO *)lParam;
456  mmi->ptMaxSize.x = fb_win32.fullw;
457  mmi->ptMaxSize.y = fb_win32.fullh;
458  mmi->ptMinTrackSize.x = fb_win32.fullw;
459  mmi->ptMinTrackSize.y = fb_win32.fullh;
460  mmi->ptMaxTrackSize.x = fb_win32.fullw;
461  mmi->ptMaxTrackSize.y = fb_win32.fullh;
462  return 0;
463  }
464 
465  if ((message == WM_MOUSEMOVE) || (message == WM_MOUSEENTER)) {
466  if ((!has_focus) && (fb_win32.is_active)) {
467  track_e.cbSize = sizeof(TRACKMOUSEEVENT);
468  track_e.dwFlags = TME_LEAVE;
469  track_e.hwndTrack = hWnd;
470  fb_win32.TrackMouseEvent(&track_e);
471  has_focus = TRUE;
473  if (fb_win32.mouse_clip) {
475  }
476  }
477  }
478 
479  if (e.type)
480  fb_hPostEvent(&e);
481 
482  if (message == WM_MOUSEENTER)
483  return 0;
484  else
485  return DefWindowProc(hWnd, message, wParam, lParam);
486 }
487 
489 {
490  MSG message;
491  while (PeekMessage(&message, fb_win32.wnd, 0, 0, PM_REMOVE)) {
492  TranslateMessage(&message);
493  DispatchMessage(&message);
494  }
495 }
496 
497 int fb_hInitWindow(DWORD style, DWORD ex_style, int x, int y, int w, int h)
498 {
499  fb_win32.fullw = w;
500  fb_win32.fullh = h;
501 
502  fb_win32.wnd = CreateWindowEx(ex_style, fb_win32.window_class, fb_win32.window_title, style,
503  x, y, w, h, HWND_DESKTOP, NULL, fb_win32.hinstance, NULL);
504  if (!fb_win32.wnd)
505  return -1;
506 
507  if (fb_win32.flags & DRIVER_ALWAYS_ON_TOP)
508  SetWindowPos(fb_win32.wnd, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOSENDCHANGING);
509 
510  SetForegroundWindow(fb_win32.wnd);
511  return 0;
512 }
513 
514 int fb_hWin32Init(char *title, int w, int h, int depth, int refresh_rate, int flags)
515 {
516  OSVERSIONINFO info;
517  HMODULE module;
518  HANDLE events[2];
519  long result;
520  int i;
521 
522  info.dwOSVersionInfoSize = sizeof(info);
523  GetVersionEx(&info);
524  fb_win32.version = (info.dwMajorVersion << 8) | info.dwMinorVersion;
525 
526  module = GetModuleHandle("USER32");
527  for (i = 0; i < sizeof(user32_procs) / sizeof(user32_procs[0]); i++) {
528  *user32_procs[i].proc = GetProcAddress(module, user32_procs[i].name);
529  }
530 
531  if (fb_win32.MonitorFromPoint) {
532  POINT pt;
533  GetCursorPos(&pt);
534  fb_win32.monitor = fb_win32.MonitorFromPoint(pt, MONITOR_DEFAULTTONEAREST);
535  } else {
536  fb_win32.monitor = NULL;
537  }
538 
539  cursor_shown = TRUE;
540  last_mouse_pos.x = 0xFFFF;
541  fb_win32.mouse_clip = FALSE;
542 
543  if (!fb_win32.TrackMouseEvent) {
545  }
546 
547  SystemParametersInfo(SPI_GETSCREENSAVEACTIVE, 0, &screensaver_active, 0);
548  SystemParametersInfo(SPI_SETSCREENSAVEACTIVE, FALSE, NULL, 0);
549 
550  fb_win32.hinstance = (HINSTANCE)GetModuleHandle(NULL);
551  fb_win32.window_title = title;
552 
553  /* Make a unique name for the window class, by encoding the address of the global context into it,
554  so that the window class won't be re-used by other instances of gfxlib2 (e.g. from DLLs). */
555  snprintf( fb_win32.window_class, WINDOW_CLASS_SIZE-1, "%s%p", WINDOW_CLASS_PREFIX, &fb_win32 );
556  fb_win32.window_class[WINDOW_CLASS_SIZE-1] = '\0';
557 
558  fb_win32.w = w;
559  fb_win32.h = h;
560  fb_win32.depth = depth;
561  fb_win32.flags = flags;
562  fb_win32.refresh_rate = refresh_rate;
563  fb_win32.wndclass.lpfnWndProc = fb_hWin32WinProc;
564  fb_win32.wndclass.lpszClassName = fb_win32.window_class;
565  fb_win32.wndclass.hInstance = fb_win32.hinstance;
566  fb_win32.wndclass.hCursor = LoadCursor(0, IDC_ARROW);
567  fb_win32.wndclass.hIcon = LoadIcon(fb_win32.hinstance, "FB_PROGRAM_ICON");
568  if (!fb_win32.wndclass.hIcon)
569  fb_win32.wndclass.hIcon = LoadIcon(NULL, IDI_APPLICATION);
570  fb_win32.wndclass.style = CS_DBLCLKS | ((flags & DRIVER_OPENGL) ? 0 : CS_VREDRAW | CS_HREDRAW);
571  RegisterClass(&fb_win32.wndclass);
572 
574  fb_win32.is_running = TRUE;
575 
576  keyconv_clear( &keyconv1 );
577  keyconv_clear( &keyconv2 );
578 
579  if (!(flags & DRIVER_OPENGL)) {
580  InitializeCriticalSection(&update_lock);
581 
582  events[0] = CreateEvent( NULL, FALSE, FALSE, NULL );
583  if( events[0] == NULL ) {
584  return -1;
585  }
586 
587 #ifdef HOST_MINGW
588  /* Note: _beginthreadex()'s last parameter cannot be NULL,
589  or else the function fails on Windows 9x */
590  unsigned int thrdaddr;
591  events[1] = (HANDLE)_beginthreadex( NULL, 0, fb_win32.thread, events[0], 0, &thrdaddr );
592 #else
593  DWORD dwThreadId;
594  events[1] = CreateThread( NULL, 0, fb_win32.thread, events[0], 0, &dwThreadId );
595 #endif
596  if( events[1] == NULL ) {
597  CloseHandle(events[0]);
598  return -1;
599  }
600 
601  result = WaitForMultipleObjects(2, events, FALSE, INFINITE);
602  CloseHandle(events[0]);
603  handle = events[1];
604  if (result != WAIT_OBJECT_0)
605  return -1;
606 
607  if(flags & DRIVER_HIGH_PRIORITY)
608  SetThreadPriority(handle, THREAD_PRIORITY_ABOVE_NORMAL);
609  } else {
610  handle = NULL;
611  }
612 
613  return 0;
614 }
615 
616 void fb_hWin32Exit(void)
617 {
618  if (!fb_win32.is_running)
619  return;
620 
621  fb_win32.is_running = FALSE;
622 
623  if (__fb_gfx->lock_count != 0) {
624  __fb_gfx->lock_count = 0;
625  __fb_gfx->driver->unlock();
626  }
627 
628  if (handle) {
629  WaitForSingleObject(handle, INFINITE);
630  CloseHandle( handle );
631  handle = NULL;
632  DeleteCriticalSection(&update_lock);
633  }
634 
635  keyconv_clear( &keyconv1 );
636  keyconv_clear( &keyconv2 );
637 
638  fb_win32.exit();
639 
640  SystemParametersInfo(SPI_SETSCREENSAVEACTIVE, screensaver_active, NULL, 0);
641  UnregisterClass(fb_win32.window_class, fb_win32.hinstance);
642 
643  if (fb_win32.mouse_clip) {
644  ClipCursor(NULL);
645  fb_win32.mouse_clip = FALSE;
646  }
647 }
648 
649 void fb_hWin32Lock(void)
650 {
651  EnterCriticalSection(&update_lock);
652 }
653 
654 void fb_hWin32Unlock(void)
655 {
656  LeaveCriticalSection(&update_lock);
657 }
658 
659 void fb_hWin32SetPalette(int index, int r, int g, int b)
660 {
661  fb_win32.palette[index].peRed = r;
662  fb_win32.palette[index].peGreen = g;
663  fb_win32.palette[index].peBlue = b;
664  fb_win32.palette[index].peFlags = PC_NOCOLLAPSE;
665  fb_win32.is_palette_changed = TRUE;
666 }
667 
669 {
670  Sleep(1000 / (__fb_gfx->refresh_rate ? __fb_gfx->refresh_rate : 60));
671 }
672 
673 int fb_hWin32GetMouse(int *x, int *y, int *z, int *buttons, int *clip)
674 {
675  if ((!fb_win32.is_active) || (!mouse_on))
676  return -1;
677 
678  *x = mouse_x;
679  *y = mouse_y;
680  *z = mouse_wheel;
681  *buttons = mouse_buttons;
682  *clip = fb_win32.mouse_clip;
683 
684  return 0;
685 }
686 
687 void fb_hWin32SetMouse(int x, int y, int cursor, int clip)
688 {
689  POINT point;
690 
691  if (x >= 0) {
692  point.x = MID(0, x, fb_win32.w - 1);
693  point.y = MID(0, y, fb_win32.h - 1);
694  if (!(fb_win32.flags & DRIVER_FULLSCREEN))
695  ClientToScreen(fb_win32.wnd, &point);
696  SetCursorPos(point.x, point.y);
697  mouse_x = x;
698  mouse_y = y;
699  }
700 
701  if ((cursor == 0) && (cursor_shown)) {
703  PostMessage(fb_win32.wnd, WM_SETCURSOR, 0, 0);
704  }
705  else if ((cursor > 0) && (!cursor_shown)) {
706  cursor_shown = TRUE;
707  PostMessage(fb_win32.wnd, WM_SETCURSOR, 0, 0);
708  }
709 
710  if (clip == 0) {
711  fb_win32.mouse_clip = FALSE;
712  ClipCursor(NULL);
713  }
714  else if (clip > 0) {
715  fb_win32.mouse_clip = TRUE;
717  }
718 }
719 
720 void fb_hWin32SetWindowTitle(char *title)
721 {
722  if (__fb_gfx->lock_count != 0)
723  LeaveCriticalSection(&update_lock);
724  fb_win32.window_title = title;
725  SetWindowText(fb_win32.wnd, title);
726  if (__fb_gfx->lock_count != 0)
727  EnterCriticalSection(&update_lock);
728 }
729 
730 int fb_hWin32SetWindowPos(int x, int y)
731 {
732  if (fb_win32.flags & DRIVER_FULLSCREEN)
733  return 0;
734 
735  if( (x == 0x80000000) && (y == 0x80000000) ) {
736  /* Querying window position */
737  RECT rc;
738  if( GetWindowRect( fb_win32.wnd, &rc ) ) {
739  x = rc.left;
740  y = rc.top;
741  return (x & 0xFFFF) | (y << 16);
742  }
743  } else {
744  /* Setting window position */
745  SetWindowPos( fb_win32.wnd, HWND_TOP, x, y, 0, 0,
746  SWP_ASYNCWINDOWPOS | SWP_NOOWNERZORDER | SWP_NOSIZE | SWP_NOZORDER );
747  }
748 
749  return 0;
750 }
751 
752 void fb_hScreenInfo(ssize_t *width, ssize_t *height, ssize_t *depth, ssize_t *refresh)
753 {
754  HDC hdc;
755 
756  hdc = GetDC(NULL);
757  *width = GetDeviceCaps(hdc, HORZRES);
758  *height = GetDeviceCaps(hdc, VERTRES);
759  *depth = GetDeviceCaps(hdc, BITSPIXEL);
760  *refresh = GetDeviceCaps(hdc, VREFRESH);
761  ReleaseDC(NULL, hdc);
762 }
763 
764 ssize_t fb_hGetWindowHandle(void)
765 {
766  return (ssize_t)fb_win32.wnd;
767 }
768 
769 static void keyconv_clear( KEYCONVINFO *k )
770 {
771  if( k->v ) {
772  free(k->v);
773  k->v = NULL;
774  }
775  k->size = 0;
776 }
777 
778 static void keyconv_grow( KEYCONVINFO *k, int nchars, int charsize )
779 {
780  void * p = realloc( k->v, (nchars+1) * charsize );
781  if( p ) {
782  k->v = p;
783  k->size = nchars;
784  }
785  if( k->v )
786  {
787  if( charsize == 2 )
788  k->a[0] = 0;
789  else
790  k->w[0] = 0;
791  }
792 }
793 
794 static unsigned int hIntlConvertChar( int key, int source_cp, int dest_cp )
795 {
796  /*
797  Do key translation between code pages (from WM_CHAR message).
798 
799  keyconv1 and keyconv2 are allocated as needed, and cleaned
800  up in the exit routine. Ideally we would know the max buffer sizes
801  needed but docs for WideCharToMultiByte/MultiByteToWideChar
802  indicate that the safe (only?) thing to do is call them twice
803  getting the size needed from the first call. Seems excessive
804  when we know we are always converting only one character. (jeffm)
805  */
806 
807  char ch[2] = {
808  (char) key,
809  0
810  };
811  int length1, length2;
812 
813  if( !key )
814  return 0;
815 
816  /* convert source (key) to wide character */
817  length1 = MultiByteToWideChar( source_cp, 0,
818  (LPCSTR)ch, 1,
819  NULL, 0 );
820 
821  if( length1 > keyconv1.size )
822  keyconv_grow( &keyconv1, length1, sizeof(WCHAR) );
823 
824  if(!keyconv1.w)
825  return 0;
826 
827  length1 = MultiByteToWideChar( source_cp, 0,
828  (LPCSTR)ch, 1,
829  keyconv1.w, keyconv1.size );
830 
831  keyconv1.w[length1] = 0;
832 
833  /* convert wide character to code page character */
834  length2 = WideCharToMultiByte( dest_cp, 0,
835  (LPCWSTR)keyconv1.w, length1,
836  (LPSTR)NULL, 0,
837  NULL, NULL );
838 
839  if( length2 > keyconv2.size )
840  keyconv_grow( &keyconv2, length2, sizeof(char) );
841 
842  if(!keyconv2.a)
843  return 0;
844 
845  length2 = WideCharToMultiByte( dest_cp, 0,
846  (LPCWSTR)keyconv1.w, length1,
847  (LPSTR)keyconv2.a, keyconv2.size,
848  NULL,NULL );
849 
850  keyconv2.a[length2] = 0;
851 
852  return keyconv2.a[0];
853 }