FreeRDP
wf_event.c
1 
22 #include <freerdp/config.h>
23 
24 #include <stdio.h>
25 
26 #include <winpr/assert.h>
27 #include <winpr/sysinfo.h>
28 #include <freerdp/freerdp.h>
29 
30 #include "wf_client.h"
31 
32 #include "wf_gdi.h"
33 #include "wf_event.h"
34 
35 #include <freerdp/event.h>
36 
37 #include <windowsx.h>
38 
39 static HWND g_focus_hWnd = NULL;
40 static HWND g_main_hWnd = NULL;
41 static HWND g_parent_hWnd = NULL;
42 
43 #define RESIZE_MIN_DELAY 200 /* minimum delay in ms between two resizes */
44 
45 static BOOL wf_scale_blt(wfContext* wfc, HDC hdc, int x, int y, int w, int h, HDC hdcSrc, int x1,
46  int y1, DWORD rop);
47 static BOOL wf_scale_mouse_event(wfContext* wfc, UINT16 flags, INT32 x, INT32 y);
48 #if (_WIN32_WINNT >= 0x0500)
49 static BOOL wf_scale_mouse_event_ex(wfContext* wfc, UINT16 flags, UINT16 buttonMask, INT32 x,
50  INT32 y);
51 #endif
52 
53 static BOOL g_flipping_in = FALSE;
54 static BOOL g_flipping_out = FALSE;
55 
56 static BOOL g_keystates[256] = { 0 };
57 
58 static BOOL ctrl_down(void)
59 {
60  return g_keystates[VK_CONTROL] || g_keystates[VK_LCONTROL] || g_keystates[VK_RCONTROL];
61 }
62 
63 static BOOL alt_ctrl_down(void)
64 {
65  const BOOL altDown = g_keystates[VK_MENU] || g_keystates[VK_LMENU] || g_keystates[VK_RMENU];
66  return altDown && ctrl_down();
67 }
68 
69 LRESULT CALLBACK wf_ll_kbd_proc(int nCode, WPARAM wParam, LPARAM lParam)
70 {
71  DWORD ext_proc_id = 0;
72 
73  wfContext* wfc = NULL;
74  DWORD rdp_scancode;
75  BOOL keystate;
76  rdpInput* input;
77  PKBDLLHOOKSTRUCT p;
78 
79  DEBUG_KBD("Low-level keyboard hook, hWnd %X nCode %X wParam %X", g_focus_hWnd, nCode, wParam);
80 
81  if (g_flipping_in)
82  {
83  if (!alt_ctrl_down())
84  g_flipping_in = FALSE;
85 
86  return CallNextHookEx(NULL, nCode, wParam, lParam);
87  }
88 
89  if (g_parent_hWnd && g_main_hWnd)
90  {
91  wfc = (wfContext*)GetWindowLongPtr(g_main_hWnd, GWLP_USERDATA);
92  GUITHREADINFO gui_thread_info;
93  gui_thread_info.cbSize = sizeof(GUITHREADINFO);
94  HWND fg_win_hwnd = GetForegroundWindow();
95  DWORD fg_win_thread_id = GetWindowThreadProcessId(fg_win_hwnd, &ext_proc_id);
96  BOOL result = GetGUIThreadInfo(fg_win_thread_id, &gui_thread_info);
97  if (gui_thread_info.hwndFocus != wfc->hWndParent)
98  {
99  g_focus_hWnd = NULL;
100  return CallNextHookEx(NULL, nCode, wParam, lParam);
101  }
102 
103  g_focus_hWnd = g_main_hWnd;
104  }
105 
106  if (g_focus_hWnd && (nCode == HC_ACTION))
107  {
108  switch (wParam)
109  {
110  case WM_KEYDOWN:
111  case WM_SYSKEYDOWN:
112  case WM_KEYUP:
113  case WM_SYSKEYUP:
114  if (!wfc)
115  wfc = (wfContext*)GetWindowLongPtr(g_focus_hWnd, GWLP_USERDATA);
116  p = (PKBDLLHOOKSTRUCT)lParam;
117 
118  if (!wfc || !p)
119  return 1;
120 
121  input = wfc->common.context.input;
122  rdp_scancode = MAKE_RDP_SCANCODE((BYTE)p->scanCode, p->flags & LLKHF_EXTENDED);
123  keystate = g_keystates[p->scanCode & 0xFF];
124 
125  switch (wParam)
126  {
127  case WM_KEYDOWN:
128  case WM_SYSKEYDOWN:
129  g_keystates[p->scanCode & 0xFF] = TRUE;
130  break;
131  case WM_KEYUP:
132  case WM_SYSKEYUP:
133  default:
134  g_keystates[p->scanCode & 0xFF] = FALSE;
135  break;
136  }
137  DEBUG_KBD("keydown %d scanCode 0x%08lX flags 0x%08lX vkCode 0x%08lX",
138  (wParam == WM_KEYDOWN), p->scanCode, p->flags, p->vkCode);
139 
140  if (wfc->fullscreen_toggle && (p->vkCode == VK_RETURN || p->vkCode == VK_CANCEL))
141  {
142  if (alt_ctrl_down())
143  {
144  if (wParam == WM_KEYDOWN)
145  {
146  wf_toggle_fullscreen(wfc);
147  return 1;
148  }
149  }
150  }
151 
152  if (rdp_scancode == RDP_SCANCODE_NUMLOCK_EXTENDED)
153  {
154  /* Windows sends NumLock as extended - rdp doesn't */
155  DEBUG_KBD("hack: NumLock (x45) should not be extended");
156  rdp_scancode = RDP_SCANCODE_NUMLOCK;
157  }
158  else if (rdp_scancode == RDP_SCANCODE_NUMLOCK)
159  {
160  /* Windows sends Pause as if it was a RDP NumLock (handled above).
161  * It must however be sent as a one-shot Ctrl+NumLock */
162  if (wParam == WM_KEYDOWN)
163  {
164  DEBUG_KBD("Pause, sent as Ctrl+NumLock");
165  freerdp_input_send_keyboard_event_ex(input, TRUE, FALSE,
166  RDP_SCANCODE_LCONTROL);
167  freerdp_input_send_keyboard_event_ex(input, TRUE, FALSE,
168  RDP_SCANCODE_NUMLOCK);
169  freerdp_input_send_keyboard_event_ex(input, FALSE, FALSE,
170  RDP_SCANCODE_LCONTROL);
171  freerdp_input_send_keyboard_event_ex(input, FALSE, FALSE,
172  RDP_SCANCODE_NUMLOCK);
173  }
174  else
175  {
176  DEBUG_KBD("Pause up");
177  }
178 
179  return 1;
180  }
181  else if (rdp_scancode == RDP_SCANCODE_RSHIFT_EXTENDED)
182  {
183  DEBUG_KBD("right shift (x36) should not be extended");
184  rdp_scancode = RDP_SCANCODE_RSHIFT;
185  }
186 
187  freerdp_input_send_keyboard_event_ex(input, !(p->flags & LLKHF_UP), keystate,
188  rdp_scancode);
189 
190  if (p->vkCode == VK_NUMLOCK || p->vkCode == VK_CAPITAL || p->vkCode == VK_SCROLL ||
191  p->vkCode == VK_KANA)
192  DEBUG_KBD(
193  "lock keys are processed on client side too to toggle their indicators");
194  else
195  return 1;
196 
197  break;
198  default:
199  break;
200  }
201  }
202 
203  if (g_flipping_out)
204  {
205  if (!alt_ctrl_down())
206  {
207  g_flipping_out = FALSE;
208  g_focus_hWnd = NULL;
209  }
210  }
211 
212  return CallNextHookEx(NULL, nCode, wParam, lParam);
213 }
214 
215 void wf_event_focus_in(wfContext* wfc)
216 {
217  UINT16 syncFlags;
218  rdpInput* input;
219  POINT pt;
220  RECT rc;
221  input = wfc->common.context.input;
222  syncFlags = 0;
223 
224  if (GetKeyState(VK_NUMLOCK))
225  syncFlags |= KBD_SYNC_NUM_LOCK;
226 
227  if (GetKeyState(VK_CAPITAL))
228  syncFlags |= KBD_SYNC_CAPS_LOCK;
229 
230  if (GetKeyState(VK_SCROLL))
231  syncFlags |= KBD_SYNC_SCROLL_LOCK;
232 
233  if (GetKeyState(VK_KANA))
234  syncFlags |= KBD_SYNC_KANA_LOCK;
235 
236  input->FocusInEvent(input, syncFlags);
237  /* send pointer position if the cursor is currently inside our client area */
238  GetCursorPos(&pt);
239  ScreenToClient(wfc->hwnd, &pt);
240  GetClientRect(wfc->hwnd, &rc);
241 
242  if (pt.x >= rc.left && pt.x < rc.right && pt.y >= rc.top && pt.y < rc.bottom)
243  input->MouseEvent(input, PTR_FLAGS_MOVE, (UINT16)pt.x, (UINT16)pt.y);
244 }
245 
246 static BOOL wf_event_process_WM_MOUSEWHEEL(wfContext* wfc, HWND hWnd, UINT Msg, WPARAM wParam,
247  LPARAM lParam, BOOL horizontal, INT32 x, INT32 y)
248 {
249  int delta;
250  UINT16 flags = 0;
251  rdpInput* input;
252 
253  WINPR_ASSERT(wfc);
254 
255  input = wfc->common.context.input;
256  WINPR_ASSERT(input);
257 
258  DefWindowProc(hWnd, Msg, wParam, lParam);
259  delta = ((signed short)HIWORD(wParam)); /* GET_WHEEL_DELTA_WPARAM(wParam); */
260 
261  if (horizontal)
262  flags |= PTR_FLAGS_HWHEEL;
263  else
264  flags |= PTR_FLAGS_WHEEL;
265 
266  if (delta < 0)
267  {
268  flags |= PTR_FLAGS_WHEEL_NEGATIVE;
269  /* 9bit twos complement, delta already negative */
270  delta = 0x100 + delta;
271  }
272 
273  flags |= delta;
274  return wf_scale_mouse_event(wfc, flags, x, y);
275 }
276 
277 static void wf_sizing(wfContext* wfc, WPARAM wParam, LPARAM lParam)
278 {
279  rdpSettings* settings = wfc->common.context.settings;
280  // Holding the CTRL key down while resizing the window will force the desktop aspect ratio.
281  LPRECT rect;
282 
283  if ((freerdp_settings_get_bool(settings, FreeRDP_SmartSizing) ||
284  freerdp_settings_get_bool(settings, FreeRDP_DynamicResolutionUpdate)) &&
285  ctrl_down())
286  {
287  rect = (LPRECT)wParam;
288 
289  switch (lParam)
290  {
291  case WMSZ_LEFT:
292  case WMSZ_RIGHT:
293  case WMSZ_BOTTOMRIGHT:
294  // Adjust height
295  rect->bottom =
296  rect->top + freerdp_settings_get_uint32(settings, FreeRDP_DesktopHeight) *
297  (rect->right - rect->left) /
298  freerdp_settings_get_uint32(settings, FreeRDP_DesktopWidth);
299  break;
300 
301  case WMSZ_TOP:
302  case WMSZ_BOTTOM:
303  case WMSZ_TOPRIGHT:
304  // Adjust width
305  rect->right =
306  rect->left + freerdp_settings_get_uint32(settings, FreeRDP_DesktopWidth) *
307  (rect->bottom - rect->top) /
308  freerdp_settings_get_uint32(settings, FreeRDP_DesktopHeight);
309  break;
310 
311  case WMSZ_BOTTOMLEFT:
312  case WMSZ_TOPLEFT:
313  // adjust width
314  rect->left =
315  rect->right - (freerdp_settings_get_uint32(settings, FreeRDP_DesktopWidth) *
316  (rect->bottom - rect->top) /
317  freerdp_settings_get_uint32(settings, FreeRDP_DesktopHeight));
318  break;
319  }
320  }
321 }
322 
323 static void wf_send_resize(wfContext* wfc)
324 {
325  RECT windowRect;
326  int targetWidth = wfc->client_width;
327  int targetHeight = wfc->client_height;
328  rdpSettings* settings = wfc->common.context.settings;
329 
330  if (freerdp_settings_get_bool(settings, FreeRDP_DynamicResolutionUpdate) && wfc->disp != NULL)
331  {
332  if (GetTickCount64() - wfc->lastSentDate > RESIZE_MIN_DELAY)
333  {
334  if (wfc->fullscreen)
335  {
336  GetWindowRect(wfc->hwnd, &windowRect);
337  targetWidth = windowRect.right - windowRect.left;
338  targetHeight = windowRect.bottom - windowRect.top;
339  }
340  if (freerdp_settings_get_uint32(settings, FreeRDP_SmartSizingWidth) != targetWidth ||
341  freerdp_settings_get_uint32(settings, FreeRDP_SmartSizingHeight) != targetHeight)
342  {
343  DISPLAY_CONTROL_MONITOR_LAYOUT layout = { 0 };
344 
345  layout.Flags = DISPLAY_CONTROL_MONITOR_PRIMARY;
346  layout.Top = layout.Left = 0;
347  layout.Width = targetWidth;
348  layout.Height = targetHeight;
349  layout.Orientation =
350  freerdp_settings_get_uint16(settings, FreeRDP_DesktopOrientation);
351  layout.DesktopScaleFactor =
352  freerdp_settings_get_uint32(settings, FreeRDP_DesktopScaleFactor);
353  layout.DeviceScaleFactor =
354  freerdp_settings_get_uint32(settings, FreeRDP_DeviceScaleFactor);
355  layout.PhysicalWidth = targetWidth;
356  layout.PhysicalHeight = targetHeight;
357 
358  if (IFCALLRESULT(CHANNEL_RC_OK, wfc->disp->SendMonitorLayout, wfc->disp, 1,
359  &layout) != CHANNEL_RC_OK)
360  {
361  WLog_ERR("", "SendMonitorLayout failed.");
362  }
363  (void)freerdp_settings_set_uint32(settings, FreeRDP_SmartSizingWidth, targetWidth);
364  (void)freerdp_settings_set_uint32(settings, FreeRDP_SmartSizingHeight,
365  targetHeight);
366  }
367  wfc->lastSentDate = GetTickCount64();
368  }
369  }
370 }
371 
372 LRESULT CALLBACK wf_event_proc(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam)
373 {
374  HDC hdc = { 0 };
375  PAINTSTRUCT ps = { 0 };
376  BOOL processed = FALSE;
377  RECT windowRect = { 0 };
378  MINMAXINFO* minmax = NULL;
379  SCROLLINFO si = { 0 };
380  processed = TRUE;
381  LONG_PTR ptr = GetWindowLongPtr(hWnd, GWLP_USERDATA);
382  wfContext* wfc = (wfContext*)ptr;
383 
384  if (wfc != NULL)
385  {
386  rdpInput* input = wfc->common.context.input;
387  rdpSettings* settings = wfc->common.context.settings;
388 
389  if (!g_parent_hWnd && wfc->hWndParent)
390  g_parent_hWnd = wfc->hWndParent;
391 
392  if (!g_main_hWnd)
393  g_main_hWnd = wfc->hwnd;
394 
395  switch (Msg)
396  {
397  case WM_MOVE:
398  if (!wfc->disablewindowtracking)
399  {
400  int x = (int)(short)LOWORD(lParam);
401  int y = (int)(short)HIWORD(lParam);
402  wfc->client_x = x;
403  wfc->client_y = y;
404  }
405 
406  break;
407 
408  case WM_GETMINMAXINFO:
409  if (freerdp_settings_get_bool(settings, FreeRDP_SmartSizing) ||
410  (freerdp_settings_get_bool(settings, FreeRDP_DynamicResolutionUpdate)))
411  {
412  processed = FALSE;
413  }
414  else
415  {
416  // Set maximum window size for resizing
417  minmax = (MINMAXINFO*)lParam;
418 
419  // always use the last determined canvas diff, because it could be
420  // that the window is minimized when this gets called
421  // wf_update_canvas_diff(wfc);
422 
423  if (!wfc->fullscreen)
424  {
425  // add window decoration
426  minmax->ptMaxTrackSize.x =
427  freerdp_settings_get_uint32(settings, FreeRDP_DesktopWidth) +
428  wfc->diff.x;
429  minmax->ptMaxTrackSize.y =
430  freerdp_settings_get_uint32(settings, FreeRDP_DesktopHeight) +
431  wfc->diff.y;
432  }
433  }
434 
435  break;
436 
437  case WM_SIZING:
438  wf_sizing(wfc, lParam, wParam);
439  break;
440 
441  case WM_SIZE:
442  GetWindowRect(wfc->hwnd, &windowRect);
443 
444  if (!wfc->fullscreen)
445  {
446  wfc->client_width = LOWORD(lParam);
447  wfc->client_height = HIWORD(lParam);
448  wfc->client_x = windowRect.left;
449  wfc->client_y = windowRect.top;
450  }
451  else
452  {
453  wfc->wasMaximized = TRUE;
454  wf_send_resize(wfc);
455  }
456 
457  if (wfc->client_width && wfc->client_height)
458  {
459  wf_size_scrollbars(wfc, LOWORD(lParam), HIWORD(lParam));
460 
461  // Workaround: when the window is maximized, the call to "ShowScrollBars"
462  // returns TRUE but has no effect.
463  if (wParam == SIZE_MAXIMIZED && !wfc->fullscreen)
464  {
465  SetWindowPos(wfc->hwnd, HWND_TOP, 0, 0, windowRect.right - windowRect.left,
466  windowRect.bottom - windowRect.top,
467  SWP_NOMOVE | SWP_FRAMECHANGED);
468  wfc->wasMaximized = TRUE;
469  wf_send_resize(wfc);
470  }
471  else if (wParam == SIZE_RESTORED && !wfc->fullscreen && wfc->wasMaximized)
472  {
473  wfc->wasMaximized = FALSE;
474  wf_send_resize(wfc);
475  }
476  else if (wParam == SIZE_MINIMIZED)
477  {
478  g_focus_hWnd = NULL;
479  }
480  }
481 
482  break;
483 
484  case WM_EXITSIZEMOVE:
485  wf_size_scrollbars(wfc, wfc->client_width, wfc->client_height);
486  wf_send_resize(wfc);
487  break;
488 
489  case WM_ERASEBKGND:
490  /* Say we handled it - prevents flickering */
491  return (LRESULT)1;
492 
493  case WM_PAINT:
494  {
495  hdc = BeginPaint(hWnd, &ps);
496  const int x = ps.rcPaint.left;
497  const int y = ps.rcPaint.top;
498  const int w = ps.rcPaint.right - ps.rcPaint.left + 1;
499  const int h = ps.rcPaint.bottom - ps.rcPaint.top + 1;
500  wf_scale_blt(wfc, hdc, x, y, w, h, wfc->primary->hdc,
501  x - wfc->offset_x + wfc->xCurrentScroll,
502  y - wfc->offset_y + wfc->yCurrentScroll, SRCCOPY);
503  EndPaint(hWnd, &ps);
504  }
505  break;
506 #if (_WIN32_WINNT >= 0x0500)
507 
508  case WM_XBUTTONDOWN:
509  wf_scale_mouse_event_ex(wfc, PTR_XFLAGS_DOWN, GET_XBUTTON_WPARAM(wParam),
510  GET_X_LPARAM(lParam) - wfc->offset_x,
511  GET_Y_LPARAM(lParam) - wfc->offset_y);
512  break;
513 
514  case WM_XBUTTONUP:
515  wf_scale_mouse_event_ex(wfc, 0, GET_XBUTTON_WPARAM(wParam),
516  GET_X_LPARAM(lParam) - wfc->offset_x,
517  GET_Y_LPARAM(lParam) - wfc->offset_y);
518  break;
519 #endif
520 
521  case WM_MBUTTONDOWN:
522  wf_scale_mouse_event(wfc, PTR_FLAGS_DOWN | PTR_FLAGS_BUTTON3,
523  GET_X_LPARAM(lParam) - wfc->offset_x,
524  GET_Y_LPARAM(lParam) - wfc->offset_y);
525  break;
526 
527  case WM_MBUTTONUP:
528  wf_scale_mouse_event(wfc, PTR_FLAGS_BUTTON3, GET_X_LPARAM(lParam) - wfc->offset_x,
529  GET_Y_LPARAM(lParam) - wfc->offset_y);
530  break;
531 
532  case WM_LBUTTONDOWN:
533  wf_scale_mouse_event(wfc, PTR_FLAGS_DOWN | PTR_FLAGS_BUTTON1,
534  GET_X_LPARAM(lParam) - wfc->offset_x,
535  GET_Y_LPARAM(lParam) - wfc->offset_y);
536  SetCapture(wfc->hwnd);
537  break;
538 
539  case WM_LBUTTONUP:
540  wf_scale_mouse_event(wfc, PTR_FLAGS_BUTTON1, GET_X_LPARAM(lParam) - wfc->offset_x,
541  GET_Y_LPARAM(lParam) - wfc->offset_y);
542  ReleaseCapture();
543  break;
544 
545  case WM_RBUTTONDOWN:
546  wf_scale_mouse_event(wfc, PTR_FLAGS_DOWN | PTR_FLAGS_BUTTON2,
547  GET_X_LPARAM(lParam) - wfc->offset_x,
548  GET_Y_LPARAM(lParam) - wfc->offset_y);
549  break;
550 
551  case WM_RBUTTONUP:
552  wf_scale_mouse_event(wfc, PTR_FLAGS_BUTTON2, GET_X_LPARAM(lParam) - wfc->offset_x,
553  GET_Y_LPARAM(lParam) - wfc->offset_y);
554  break;
555 
556  case WM_MOUSEMOVE:
557  wf_scale_mouse_event(wfc, PTR_FLAGS_MOVE, GET_X_LPARAM(lParam) - wfc->offset_x,
558  GET_Y_LPARAM(lParam) - wfc->offset_y);
559  break;
560 #if (_WIN32_WINNT >= 0x0400) || (_WIN32_WINDOWS > 0x0400)
561 
562  case WM_MOUSEWHEEL:
563  wf_event_process_WM_MOUSEWHEEL(wfc, hWnd, Msg, wParam, lParam, FALSE,
564  GET_X_LPARAM(lParam) - wfc->offset_x,
565  GET_Y_LPARAM(lParam) - wfc->offset_y);
566  break;
567 #endif
568 #if (_WIN32_WINNT >= 0x0600)
569 
570  case WM_MOUSEHWHEEL:
571  wf_event_process_WM_MOUSEWHEEL(wfc, hWnd, Msg, wParam, lParam, TRUE,
572  GET_X_LPARAM(lParam) - wfc->offset_x,
573  GET_Y_LPARAM(lParam) - wfc->offset_y);
574  break;
575 #endif
576 
577  case WM_SETCURSOR:
578  if (LOWORD(lParam) == HTCLIENT)
579  SetCursor(wfc->cursor);
580  else
581  DefWindowProc(hWnd, Msg, wParam, lParam);
582 
583  break;
584 
585  case WM_HSCROLL:
586  {
587  int xDelta; // xDelta = new_pos - current_pos
588  int xNewPos; // new position
589  int yDelta = 0;
590 
591  switch (LOWORD(wParam))
592  {
593  // User clicked the scroll bar shaft left of the scroll box.
594  case SB_PAGEUP:
595  xNewPos = wfc->xCurrentScroll - 50;
596  break;
597 
598  // User clicked the scroll bar shaft right of the scroll box.
599  case SB_PAGEDOWN:
600  xNewPos = wfc->xCurrentScroll + 50;
601  break;
602 
603  // User clicked the left arrow.
604  case SB_LINEUP:
605  xNewPos = wfc->xCurrentScroll - 5;
606  break;
607 
608  // User clicked the right arrow.
609  case SB_LINEDOWN:
610  xNewPos = wfc->xCurrentScroll + 5;
611  break;
612 
613  // User dragged the scroll box.
614  case SB_THUMBPOSITION:
615  xNewPos = HIWORD(wParam);
616  break;
617 
618  // user is dragging the scrollbar
619  case SB_THUMBTRACK:
620  xNewPos = HIWORD(wParam);
621  break;
622 
623  default:
624  xNewPos = wfc->xCurrentScroll;
625  }
626 
627  // New position must be between 0 and the screen width.
628  xNewPos = MAX(0, xNewPos);
629  xNewPos = MIN(wfc->xMaxScroll, xNewPos);
630 
631  // If the current position does not change, do not scroll.
632  if (xNewPos == wfc->xCurrentScroll)
633  break;
634 
635  // Determine the amount scrolled (in pixels).
636  xDelta = xNewPos - wfc->xCurrentScroll;
637  // Reset the current scroll position.
638  wfc->xCurrentScroll = xNewPos;
639  // Scroll the window. (The system repaints most of the
640  // client area when ScrollWindowEx is called; however, it is
641  // necessary to call UpdateWindow in order to repaint the
642  // rectangle of pixels that were invalidated.)
643  ScrollWindowEx(wfc->hwnd, -xDelta, -yDelta, (CONST RECT*)NULL, (CONST RECT*)NULL,
644  (HRGN)NULL, (PRECT)NULL, SW_INVALIDATE);
645  UpdateWindow(wfc->hwnd);
646  // Reset the scroll bar.
647  si.cbSize = sizeof(si);
648  si.fMask = SIF_POS;
649  si.nPos = wfc->xCurrentScroll;
650  SetScrollInfo(wfc->hwnd, SB_HORZ, &si, TRUE);
651  }
652  break;
653 
654  case WM_VSCROLL:
655  {
656  int xDelta = 0;
657  int yDelta; // yDelta = new_pos - current_pos
658  int yNewPos; // new position
659 
660  switch (LOWORD(wParam))
661  {
662  // User clicked the scroll bar shaft above the scroll box.
663  case SB_PAGEUP:
664  yNewPos = wfc->yCurrentScroll - 50;
665  break;
666 
667  // User clicked the scroll bar shaft below the scroll box.
668  case SB_PAGEDOWN:
669  yNewPos = wfc->yCurrentScroll + 50;
670  break;
671 
672  // User clicked the top arrow.
673  case SB_LINEUP:
674  yNewPos = wfc->yCurrentScroll - 5;
675  break;
676 
677  // User clicked the bottom arrow.
678  case SB_LINEDOWN:
679  yNewPos = wfc->yCurrentScroll + 5;
680  break;
681 
682  // User dragged the scroll box.
683  case SB_THUMBPOSITION:
684  yNewPos = HIWORD(wParam);
685  break;
686 
687  // user is dragging the scrollbar
688  case SB_THUMBTRACK:
689  yNewPos = HIWORD(wParam);
690  break;
691 
692  default:
693  yNewPos = wfc->yCurrentScroll;
694  }
695 
696  // New position must be between 0 and the screen height.
697  yNewPos = MAX(0, yNewPos);
698  yNewPos = MIN(wfc->yMaxScroll, yNewPos);
699 
700  // If the current position does not change, do not scroll.
701  if (yNewPos == wfc->yCurrentScroll)
702  break;
703 
704  // Determine the amount scrolled (in pixels).
705  yDelta = yNewPos - wfc->yCurrentScroll;
706  // Reset the current scroll position.
707  wfc->yCurrentScroll = yNewPos;
708  // Scroll the window. (The system repaints most of the
709  // client area when ScrollWindowEx is called; however, it is
710  // necessary to call UpdateWindow in order to repaint the
711  // rectangle of pixels that were invalidated.)
712  ScrollWindowEx(wfc->hwnd, -xDelta, -yDelta, (CONST RECT*)NULL, (CONST RECT*)NULL,
713  (HRGN)NULL, (PRECT)NULL, SW_INVALIDATE);
714  UpdateWindow(wfc->hwnd);
715  // Reset the scroll bar.
716  si.cbSize = sizeof(si);
717  si.fMask = SIF_POS;
718  si.nPos = wfc->yCurrentScroll;
719  SetScrollInfo(wfc->hwnd, SB_VERT, &si, TRUE);
720  }
721  break;
722 
723  case WM_SYSCOMMAND:
724  {
725  if (wParam == SYSCOMMAND_ID_SMARTSIZING)
726  {
727  HMENU hMenu = GetSystemMenu(wfc->hwnd, FALSE);
728  const BOOL rc = freerdp_settings_get_bool(settings, FreeRDP_SmartSizing);
729  (void)freerdp_settings_set_bool(settings, FreeRDP_SmartSizing, !rc);
730  CheckMenuItem(hMenu, SYSCOMMAND_ID_SMARTSIZING,
731  freerdp_settings_get_bool(settings, FreeRDP_SmartSizing)
732  ? MF_CHECKED
733  : MF_UNCHECKED);
734  if (!freerdp_settings_get_bool(settings, FreeRDP_SmartSizing))
735  {
736  SetWindowPos(wfc->hwnd, HWND_TOP, -1, -1,
737  freerdp_settings_get_uint32(settings, FreeRDP_DesktopWidth) +
738  wfc->diff.x,
739  freerdp_settings_get_uint32(settings, FreeRDP_DesktopHeight) +
740  wfc->diff.y,
741  SWP_NOMOVE);
742  }
743  else
744  {
745  wf_size_scrollbars(wfc, wfc->client_width, wfc->client_height);
746  wf_send_resize(wfc);
747  }
748  }
749  else if (wParam == SYSCOMMAND_ID_REQUEST_CONTROL)
750  {
751  freerdp_client_encomsp_set_control(wfc->common.encomsp, TRUE);
752  }
753  else
754  {
755  processed = FALSE;
756  }
757  }
758  break;
759 
760  default:
761  processed = FALSE;
762  break;
763  }
764  }
765  else
766  {
767  processed = FALSE;
768  }
769 
770  if (processed)
771  return 0;
772 
773  switch (Msg)
774  {
775  case WM_DESTROY:
776  PostQuitMessage(WM_QUIT);
777  break;
778 
779  case WM_SETFOCUS:
780  DEBUG_KBD("getting focus %X", hWnd);
781 
782  (void)freerdp_settings_set_bool(wfc->common.context.settings, FreeRDP_SuspendInput,
783  FALSE);
784 
785  if (alt_ctrl_down())
786  g_flipping_in = TRUE;
787 
788  g_focus_hWnd = hWnd;
789  freerdp_set_focus(wfc->common.context.instance);
790  break;
791 
792  case WM_KILLFOCUS:
793  (void)freerdp_settings_set_bool(wfc->common.context.settings, FreeRDP_SuspendInput,
794  TRUE);
795 
796  if (g_focus_hWnd == hWnd && wfc && !wfc->fullscreen)
797  {
798  DEBUG_KBD("loosing focus %X", hWnd);
799 
800  if (alt_ctrl_down())
801  g_flipping_out = TRUE;
802  else
803  g_focus_hWnd = NULL;
804  }
805 
806  break;
807 
808  case WM_ACTIVATE:
809  {
810  int activate = (int)(short)LOWORD(wParam);
811  BOOL minimized_flag = (BOOL)HIWORD(wParam);
812 
813  if (activate != WA_INACTIVE && !minimized_flag)
814  {
815  if (alt_ctrl_down())
816  g_flipping_in = TRUE;
817 
818  g_focus_hWnd = hWnd;
819  }
820  else
821  {
822  if (alt_ctrl_down())
823  g_flipping_out = TRUE;
824  else
825  g_focus_hWnd = NULL;
826  }
827  }
828 
829  default:
830  return DefWindowProc(hWnd, Msg, wParam, lParam);
831  break;
832  }
833 
834  return 0;
835 }
836 
837 BOOL wf_scale_blt(wfContext* wfc, HDC hdc, int x, int y, int w, int h, HDC hdcSrc, int x1, int y1,
838  DWORD rop)
839 {
840  UINT32 ww, wh, dw, dh;
841  WINPR_ASSERT(wfc);
842 
843  rdpSettings* settings = wfc->common.context.settings;
844  WINPR_ASSERT(settings);
845 
846  if (!wfc->client_width)
847  wfc->client_width = freerdp_settings_get_uint32(settings, FreeRDP_DesktopWidth);
848 
849  if (!wfc->client_height)
850  wfc->client_height = freerdp_settings_get_uint32(settings, FreeRDP_DesktopHeight);
851 
852  ww = wfc->client_width;
853  wh = wfc->client_height;
854  dw = freerdp_settings_get_uint32(settings, FreeRDP_DesktopWidth);
855  dh = freerdp_settings_get_uint32(settings, FreeRDP_DesktopHeight);
856 
857  if (!ww)
858  ww = dw;
859 
860  if (!wh)
861  wh = dh;
862 
863  if (wfc->fullscreen || !freerdp_settings_get_bool(settings, FreeRDP_SmartSizing) ||
864  (ww == dw && wh == dh))
865  {
866  return BitBlt(hdc, x, y, w, h, wfc->primary->hdc, x1, y1, SRCCOPY);
867  }
868  else
869  {
870  SetStretchBltMode(hdc, HALFTONE);
871  SetBrushOrgEx(hdc, 0, 0, NULL);
872  return StretchBlt(hdc, 0, 0, ww, wh, wfc->primary->hdc, 0, 0, dw, dh, SRCCOPY);
873  }
874 
875  return TRUE;
876 }
877 
878 static BOOL wf_scale_mouse_pos(wfContext* wfc, INT32 x, INT32 y, UINT16* px, UINT16* py)
879 {
880  int ww, wh, dw, dh;
881  rdpSettings* settings;
882 
883  if (!wfc || !px || !py)
884  return FALSE;
885 
886  settings = wfc->common.context.settings;
887 
888  if (!settings)
889  return FALSE;
890 
891  if (!wfc->client_width)
892  wfc->client_width = freerdp_settings_get_uint32(settings, FreeRDP_DesktopWidth);
893 
894  if (!wfc->client_height)
895  wfc->client_height = freerdp_settings_get_uint32(settings, FreeRDP_DesktopHeight);
896 
897  ww = wfc->client_width;
898  wh = wfc->client_height;
899  dw = freerdp_settings_get_uint32(settings, FreeRDP_DesktopWidth);
900  dh = freerdp_settings_get_uint32(settings, FreeRDP_DesktopHeight);
901 
902  if (!freerdp_settings_get_bool(settings, FreeRDP_SmartSizing) || ((ww == dw) && (wh == dh)))
903  {
904  x += wfc->xCurrentScroll;
905  y += wfc->yCurrentScroll;
906  }
907  else
908  {
909  x = x * dw / ww + wfc->xCurrentScroll;
910  y = y * dh / wh + wfc->yCurrentScroll;
911  }
912 
913  *px = MIN(UINT16_MAX, MAX(0, x));
914  *py = MIN(UINT16_MAX, MAX(0, y));
915 
916  return TRUE;
917 }
918 
919 static BOOL wf_pub_mouse_event(wfContext* wfc, UINT16 flags, UINT16 x, UINT16 y)
920 {
921  MouseEventEventArgs eventArgs = { 0 };
922 
923  eventArgs.flags = flags;
924  eventArgs.x = x;
925  eventArgs.y = y;
926  PubSub_OnMouseEvent(wfc->common.context.pubSub, &wfc->common.context, &eventArgs);
927  return TRUE;
928 }
929 
930 static BOOL wf_scale_mouse_event(wfContext* wfc, UINT16 flags, INT32 x, INT32 y)
931 {
932  UINT16 px, py;
933 
934  WINPR_ASSERT(wfc);
935 
936  if (!wf_scale_mouse_pos(wfc, x, y, &px, &py))
937  return FALSE;
938 
939  if (freerdp_client_send_button_event(&wfc->common, FALSE, flags, px, py))
940  return FALSE;
941 
942  return wf_pub_mouse_event(wfc, flags, px, py);
943 }
944 
945 #if (_WIN32_WINNT >= 0x0500)
946 static BOOL wf_scale_mouse_event_ex(wfContext* wfc, UINT16 flags, UINT16 buttonMask, INT32 x,
947  INT32 y)
948 {
949  UINT16 px, py;
950 
951  WINPR_ASSERT(wfc);
952 
953  if (buttonMask & XBUTTON1)
954  flags |= PTR_XFLAGS_BUTTON1;
955 
956  if (buttonMask & XBUTTON2)
957  flags |= PTR_XFLAGS_BUTTON2;
958 
959  if (!wf_scale_mouse_pos(wfc, x, y, &px, &py))
960  return FALSE;
961 
962  if (freerdp_client_send_extended_button_event(&wfc->common, FALSE, flags, px, py))
963  return FALSE;
964 
965  return wf_pub_mouse_event(wfc, flags, px, py);
966 }
967 #endif
FREERDP_API UINT32 freerdp_settings_get_uint32(const rdpSettings *settings, FreeRDP_Settings_Keys_UInt32 id)
Returns a UINT32 settings value.
FREERDP_API BOOL freerdp_settings_get_bool(const rdpSettings *settings, FreeRDP_Settings_Keys_Bool id)
Returns a boolean settings value.
FREERDP_API UINT16 freerdp_settings_get_uint16(const rdpSettings *settings, FreeRDP_Settings_Keys_UInt16 id)
Returns a UINT16 settings value.
FREERDP_API BOOL freerdp_settings_set_uint32(rdpSettings *settings, FreeRDP_Settings_Keys_UInt32 id, UINT32 param)
Sets a UINT32 settings value.
FREERDP_API BOOL freerdp_settings_set_bool(rdpSettings *settings, FreeRDP_Settings_Keys_Bool id, BOOL param)
Sets a BOOL settings value.