Merge pull request #26287 from CrystalP/ref-savefilestatejob
[xbmc.git] / xbmc / windowing / windows / WinEventsWin32.cpp
blobe3a2fd03f1d7cdd6300a103cd9286af9c6449f19
1 /*
2 * Copyright (C) 2005-2018 Team Kodi
3 * This file is part of Kodi - https://kodi.tv
5 * SPDX-License-Identifier: GPL-2.0-or-later
6 * See LICENSES/README.md for more information.
7 */
9 #ifndef _USE_MATH_DEFINES
10 #define _USE_MATH_DEFINES
11 #endif
12 #include "WinEventsWin32.h"
14 #include "ServiceBroker.h"
15 #include "Util.h"
16 #include "WinKeyMap.h"
17 #include "application/AppInboundProtocol.h"
18 #include "application/Application.h"
19 #include "application/ApplicationComponents.h"
20 #include "application/ApplicationPowerHandling.h"
21 #include "guilib/GUIComponent.h"
22 #include "guilib/GUIControl.h" // for EVENT_RESULT
23 #include "guilib/GUIWindowManager.h"
24 #include "input/InputManager.h"
25 #include "input/actions/Action.h"
26 #include "input/keyboard/Key.h"
27 #include "input/keyboard/KeyIDs.h"
28 #include "input/mouse/MouseStat.h"
29 #include "input/touch/generic/GenericTouchActionHandler.h"
30 #include "input/touch/generic/GenericTouchSwipeDetector.h"
31 #include "messaging/ApplicationMessenger.h"
32 #include "network/Zeroconf.h"
33 #include "network/ZeroconfBrowser.h"
34 #include "peripherals/Peripherals.h"
35 #include "rendering/dx/RenderContext.h"
36 #include "storage/MediaManager.h"
37 #include "utils/JobManager.h"
38 #include "utils/StringUtils.h"
39 #include "utils/log.h"
41 #include "platform/win32/CharsetConverter.h"
42 #include "platform/win32/WIN32Util.h"
43 #include "platform/win32/powermanagement/Win32PowerSyscall.h"
44 #include "platform/win32/storage/Win32StorageProvider.h"
46 #include <array>
47 #include <math.h>
49 #include <Shlobj.h>
50 #include <dbt.h>
52 HWND g_hWnd = nullptr;
54 #ifndef LODWORD
55 #define LODWORD(longval) ((DWORD)((DWORDLONG)(longval)))
56 #endif
58 #define ROTATE_ANGLE_DEGREE(arg) GID_ROTATE_ANGLE_FROM_ARGUMENT(LODWORD(arg)) * 180 / M_PI
60 #define GET_X_LPARAM(lp) ((int)(short)LOWORD(lp))
61 #define GET_Y_LPARAM(lp) ((int)(short)HIWORD(lp))
63 /* Masks for processing the windows KEYDOWN and KEYUP messages */
64 #define REPEATED_KEYMASK (1<<30)
65 #define EXTENDED_KEYMASK (1<<24)
66 #define EXTKEYPAD(keypad) ((scancode & 0x100)?(mvke):(keypad))
68 static GUID USB_HID_GUID = { 0x4D1E55B2, 0xF16F, 0x11CF, { 0x88, 0xCB, 0x00, 0x11, 0x11, 0x00, 0x00, 0x30 } };
70 uint32_t g_uQueryCancelAutoPlay = 0;
71 bool g_sizeMoveSizing = false;
72 bool g_sizeMoveMoving = false;
73 int g_sizeMoveWidth = 0;
74 int g_sizeMoveHight = 0;
75 int g_sizeMoveX = -10000;
76 int g_sizeMoveY = -10000;
78 int CWinEventsWin32::m_originalZoomDistance = 0;
79 Pointer CWinEventsWin32::m_touchPointer;
80 CGenericTouchSwipeDetector* CWinEventsWin32::m_touchSwipeDetector = nullptr;
82 // register to receive SD card events (insert/remove)
83 // seen at http://www.codeproject.com/Messages/2897423/Re-No-message-triggered-on-SD-card-insertion-remov.aspx
84 #define WM_MEDIA_CHANGE (WM_USER + 666)
85 SHChangeNotifyEntry shcne;
87 static int XBMC_MapVirtualKey(int scancode, WPARAM vkey)
89 int mvke = MapVirtualKeyEx(scancode & 0xFF, 1, nullptr);
91 switch (vkey)
92 { /* These are always correct */
93 case VK_DIVIDE:
94 case VK_MULTIPLY:
95 case VK_SUBTRACT:
96 case VK_ADD:
97 case VK_LWIN:
98 case VK_RWIN:
99 case VK_APPS:
100 /* These are already handled */
101 case VK_LCONTROL:
102 case VK_RCONTROL:
103 case VK_LSHIFT:
104 case VK_RSHIFT:
105 case VK_LMENU:
106 case VK_RMENU:
107 case VK_SNAPSHOT:
108 case VK_PAUSE:
109 /* Multimedia keys are already handled */
110 case VK_BROWSER_BACK:
111 case VK_BROWSER_FORWARD:
112 case VK_BROWSER_REFRESH:
113 case VK_BROWSER_STOP:
114 case VK_BROWSER_SEARCH:
115 case VK_BROWSER_FAVORITES:
116 case VK_BROWSER_HOME:
117 case VK_VOLUME_MUTE:
118 case VK_VOLUME_DOWN:
119 case VK_VOLUME_UP:
120 case VK_MEDIA_NEXT_TRACK:
121 case VK_MEDIA_PREV_TRACK:
122 case VK_MEDIA_STOP:
123 case VK_MEDIA_PLAY_PAUSE:
124 case VK_LAUNCH_MAIL:
125 case VK_LAUNCH_MEDIA_SELECT:
126 case VK_LAUNCH_APP1:
127 case VK_LAUNCH_APP2:
128 return static_cast<int>(vkey);
129 default:;
131 switch (mvke)
132 { /* Distinguish between keypad and extended keys */
133 case VK_INSERT: return EXTKEYPAD(VK_NUMPAD0);
134 case VK_DELETE: return EXTKEYPAD(VK_DECIMAL);
135 case VK_END: return EXTKEYPAD(VK_NUMPAD1);
136 case VK_DOWN: return EXTKEYPAD(VK_NUMPAD2);
137 case VK_NEXT: return EXTKEYPAD(VK_NUMPAD3);
138 case VK_LEFT: return EXTKEYPAD(VK_NUMPAD4);
139 case VK_CLEAR: return EXTKEYPAD(VK_NUMPAD5);
140 case VK_RIGHT: return EXTKEYPAD(VK_NUMPAD6);
141 case VK_HOME: return EXTKEYPAD(VK_NUMPAD7);
142 case VK_UP: return EXTKEYPAD(VK_NUMPAD8);
143 case VK_PRIOR: return EXTKEYPAD(VK_NUMPAD9);
144 default:;
146 return mvke ? mvke : static_cast<int>(vkey);
150 static XBMC_keysym *TranslateKey(WPARAM vkey, UINT scancode, XBMC_keysym *keysym, int pressed)
152 using namespace KODI::WINDOWING::WINDOWS;
154 uint8_t keystate[256];
156 /* Set the keysym information */
157 keysym->scancode = static_cast<uint32_t>(scancode);
158 keysym->unicode = 0;
160 if ((vkey == VK_RETURN) && (scancode & 0x100))
162 /* No VK_ code for the keypad enter key */
163 keysym->sym = XBMCK_KP_ENTER;
165 else
167 keysym->sym = VK_keymap[XBMC_MapVirtualKey(scancode, vkey)];
170 // Attempt to convert the keypress to a UNICODE character
171 if (GetKeyboardState(keystate) == FALSE)
173 CLog::LogF(LOGERROR, "GetKeyboardState error {}", GetLastError());
174 return keysym;
177 if (pressed)
179 std::array<uint16_t, 2> wchars;
181 /* Numlock isn't taken into account in ToUnicode,
182 * so we handle it as a special case here */
183 if ((keystate[VK_NUMLOCK] & 1) && vkey >= VK_NUMPAD0 && vkey <= VK_NUMPAD9)
185 keysym->unicode = static_cast<uint16_t>(vkey - VK_NUMPAD0 + '0');
187 else if (ToUnicode(static_cast<UINT>(vkey), scancode, keystate,
188 reinterpret_cast<LPWSTR>(wchars.data()), static_cast<int>(wchars.size()),
189 0) > 0)
191 keysym->unicode = wchars[0];
195 // Set the modifier bitmap
197 uint16_t mod = static_cast<uint16_t>(XBMCKMOD_NONE);
199 // If left control and right alt are down this usually means that
200 // AltGr is down
201 if ((keystate[VK_LCONTROL] & 0x80) && (keystate[VK_RMENU] & 0x80))
203 mod |= XBMCKMOD_MODE;
205 else
207 if (keystate[VK_LCONTROL] & 0x80) mod |= XBMCKMOD_LCTRL;
208 if (keystate[VK_RMENU] & 0x80) mod |= XBMCKMOD_RALT;
211 // Check the remaining modifiers
212 if (keystate[VK_LSHIFT] & 0x80) mod |= XBMCKMOD_LSHIFT;
213 if (keystate[VK_RSHIFT] & 0x80) mod |= XBMCKMOD_RSHIFT;
214 if (keystate[VK_RCONTROL] & 0x80) mod |= XBMCKMOD_RCTRL;
215 if (keystate[VK_LMENU] & 0x80) mod |= XBMCKMOD_LALT;
216 if (keystate[VK_LWIN] & 0x80) mod |= XBMCKMOD_LSUPER;
217 if (keystate[VK_RWIN] & 0x80) mod |= XBMCKMOD_LSUPER;
218 keysym->mod = static_cast<XBMCMod>(mod);
220 // Return the updated keysym
221 return(keysym);
225 bool CWinEventsWin32::MessagePump()
227 MSG msg;
228 while( PeekMessage( &msg, nullptr, 0U, 0U, PM_REMOVE ) )
230 TranslateMessage( &msg );
231 DispatchMessage( &msg );
233 return true;
236 LRESULT CALLBACK CWinEventsWin32::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
238 using KODI::PLATFORM::WINDOWS::FromW;
240 XBMC_Event newEvent = {};
241 static HDEVNOTIFY hDeviceNotify;
243 if (uMsg == WM_CREATE)
245 g_hWnd = hWnd;
246 // need to set windows handle before WM_SIZE processing
247 DX::Windowing()->SetWindow(hWnd);
249 KODI::WINDOWING::WINDOWS::DIB_InitOSKeymap();
251 g_uQueryCancelAutoPlay = RegisterWindowMessage(TEXT("QueryCancelAutoPlay"));
252 shcne.pidl = nullptr;
253 shcne.fRecursive = TRUE;
254 long fEvents = SHCNE_DRIVEADD | SHCNE_DRIVEREMOVED | SHCNE_MEDIAREMOVED | SHCNE_MEDIAINSERTED;
255 SHChangeNotifyRegister(hWnd, SHCNRF_ShellLevel | SHCNRF_NewDelivery, fEvents, WM_MEDIA_CHANGE, 1, &shcne);
256 RegisterDeviceInterfaceToHwnd(USB_HID_GUID, hWnd, &hDeviceNotify);
257 return 0;
260 if (uMsg == WM_DESTROY)
261 g_hWnd = nullptr;
263 if(g_uQueryCancelAutoPlay != 0 && uMsg == g_uQueryCancelAutoPlay)
264 return S_FALSE;
266 std::shared_ptr<CAppInboundProtocol> appPort = CServiceBroker::GetAppPort();
268 switch (uMsg)
270 case WM_CLOSE:
271 case WM_QUIT:
272 case WM_DESTROY:
273 if (hDeviceNotify)
275 if (UnregisterDeviceNotification(hDeviceNotify))
276 hDeviceNotify = nullptr;
277 else
278 CLog::LogF(LOGINFO, "UnregisterDeviceNotification failed ({})", GetLastError());
280 newEvent.type = XBMC_QUIT;
281 if (appPort)
282 appPort->OnEvent(newEvent);
283 break;
284 case WM_SHOWWINDOW:
286 const auto& components = CServiceBroker::GetAppComponents();
287 const auto appPower = components.GetComponent<CApplicationPowerHandling>();
288 bool active = appPower->GetRenderGUI();
289 if (appPort)
290 appPort->SetRenderGUI(wParam != 0);
291 if (appPower->GetRenderGUI() != active)
292 DX::Windowing()->NotifyAppActiveChange(appPower->GetRenderGUI());
293 CLog::LogFC(LOGDEBUG, LOGWINDOWING, "WM_SHOWWINDOW -> window is {}",
294 wParam != 0 ? "shown" : "hidden");
296 break;
297 case WM_ACTIVATE:
299 CLog::LogFC(LOGDEBUG, LOGWINDOWING, "WM_ACTIVATE -> window is {}",
300 LOWORD(wParam) != WA_INACTIVE ? "active" : "inactive");
301 const auto& components = CServiceBroker::GetAppComponents();
302 const auto appPower = components.GetComponent<CApplicationPowerHandling>();
303 bool active = appPower->GetRenderGUI();
304 if (HIWORD(wParam))
306 if (appPort)
307 appPort->SetRenderGUI(false);
309 else
311 WINDOWPLACEMENT lpwndpl;
312 lpwndpl.length = sizeof(lpwndpl);
313 if (LOWORD(wParam) != WA_INACTIVE)
315 if (GetWindowPlacement(hWnd, &lpwndpl))
317 if (appPort)
318 appPort->SetRenderGUI(lpwndpl.showCmd != SW_HIDE);
321 else
326 if (appPower->GetRenderGUI() != active)
327 DX::Windowing()->NotifyAppActiveChange(appPower->GetRenderGUI());
328 CLog::LogFC(LOGDEBUG, LOGWINDOWING, "window is {}",
329 appPower->GetRenderGUI() ? "active" : "inactive");
331 break;
332 case WM_SETFOCUS:
333 case WM_KILLFOCUS:
334 g_application.m_AppFocused = uMsg == WM_SETFOCUS;
335 CLog::LogFC(LOGDEBUG, LOGWINDOWING, "window focus {}",
336 g_application.m_AppFocused ? "set" : "lost");
338 DX::Windowing()->NotifyAppFocusChange(g_application.m_AppFocused);
339 if (uMsg == WM_KILLFOCUS)
341 std::string procfile;
342 if (CWIN32Util::GetFocussedProcess(procfile))
343 CLog::LogFC(LOGDEBUG, LOGWINDOWING, "Focus switched to process {}", procfile);
345 break;
346 /* needs to be reviewed after frodo. we reset the system idle timer
347 and the display timer directly now (see m_screenSaverTimer).
348 case WM_SYSCOMMAND:
349 switch( wParam&0xFFF0 )
351 case SC_MONITORPOWER:
352 if (g_application.GetAppPlayer().IsPlaying() || g_application.GetAppPlayer().IsPausedPlayback())
353 return 0;
354 else if(CServiceBroker::GetSettingsComponent()->GetSettings()->GetInt(CSettings::SETTING_POWERMANAGEMENT_DISPLAYSOFF) == 0)
355 return 0;
356 break;
357 case SC_SCREENSAVE:
358 return 0;
360 break;*/
361 case WM_SYSKEYDOWN:
362 switch (wParam)
364 case VK_F4: //alt-f4, default event quit.
365 return(DefWindowProc(hWnd, uMsg, wParam, lParam));
366 case VK_RETURN: //alt-return
367 if ((lParam & REPEATED_KEYMASK) == 0)
368 CServiceBroker::GetAppMessenger()->PostMsg(TMSG_TOGGLEFULLSCREEN);
369 return 0;
370 default:;
372 [[fallthrough]];
373 case WM_KEYDOWN:
375 switch (wParam)
377 case VK_CONTROL:
378 if ( lParam & EXTENDED_KEYMASK )
379 wParam = VK_RCONTROL;
380 else
381 wParam = VK_LCONTROL;
382 break;
383 case VK_SHIFT:
384 /* EXTENDED trick doesn't work here */
385 if (GetKeyState(VK_LSHIFT) & 0x8000)
386 wParam = VK_LSHIFT;
387 else if (GetKeyState(VK_RSHIFT) & 0x8000)
388 wParam = VK_RSHIFT;
389 break;
390 case VK_MENU:
391 if ( lParam & EXTENDED_KEYMASK )
392 wParam = VK_RMENU;
393 else
394 wParam = VK_LMENU;
395 break;
396 default:;
398 XBMC_keysym keysym;
399 TranslateKey(wParam, HIWORD(lParam), &keysym, 1);
401 newEvent.type = XBMC_KEYDOWN;
402 newEvent.key.keysym = keysym;
403 if (appPort)
404 appPort->OnEvent(newEvent);
406 return(0);
408 case WM_SYSKEYUP:
409 case WM_KEYUP:
411 switch (wParam)
413 case VK_CONTROL:
414 if ( lParam&EXTENDED_KEYMASK )
415 wParam = VK_RCONTROL;
416 else
417 wParam = VK_LCONTROL;
418 break;
419 case VK_SHIFT:
421 uint32_t scanCodeL = MapVirtualKey(VK_LSHIFT, MAPVK_VK_TO_VSC);
422 uint32_t scanCodeR = MapVirtualKey(VK_RSHIFT, MAPVK_VK_TO_VSC);
423 uint32_t keyCode = static_cast<uint32_t>((lParam & 0xFF0000) >> 16);
424 if (keyCode == scanCodeL)
425 wParam = VK_LSHIFT;
426 else if (keyCode == scanCodeR)
427 wParam = VK_RSHIFT;
429 break;
430 case VK_MENU:
431 if ( lParam&EXTENDED_KEYMASK )
432 wParam = VK_RMENU;
433 else
434 wParam = VK_LMENU;
435 break;
436 default:;
438 XBMC_keysym keysym;
439 TranslateKey(wParam, HIWORD(lParam), &keysym, 1);
441 if (wParam == VK_SNAPSHOT)
442 newEvent.type = XBMC_KEYDOWN;
443 else
444 newEvent.type = XBMC_KEYUP;
445 newEvent.key.keysym = keysym;
446 if (appPort)
447 appPort->OnEvent(newEvent);
449 return(0);
450 case WM_APPCOMMAND: // MULTIMEDIA keys are mapped to APPCOMMANDS
452 const unsigned int appcmd = GET_APPCOMMAND_LPARAM(lParam);
454 CLog::LogFC(LOGDEBUG, LOGWINDOWING, "APPCOMMAND {}", appcmd);
456 // Reset the screen saver
457 auto& components = CServiceBroker::GetAppComponents();
458 const auto appPower = components.GetComponent<CApplicationPowerHandling>();
459 appPower->ResetScreenSaver();
461 // If we were currently in the screen saver wake up and don't process the
462 // appcommand
463 if (appPower->WakeUpScreenSaverAndDPMS())
464 return true;
466 // Retrieve the action associated with this appcommand from the mapping table
467 CKey key(appcmd | KEY_APPCOMMAND, 0U);
468 int iWin = CServiceBroker::GetGUI()->GetWindowManager().GetActiveWindowOrDialog();
470 CAction appcmdaction = CServiceBroker::GetInputManager().GetAction(iWin, key);
471 if (appcmdaction.GetID())
473 CLog::LogFC(LOGDEBUG, LOGWINDOWING, "appcommand {}, action {}", appcmd,
474 appcmdaction.GetName());
475 CServiceBroker::GetInputManager().QueueAction(appcmdaction);
476 return true;
479 CLog::LogFC(LOGDEBUG, LOGWINDOWING, "unknown appcommand {}", appcmd);
480 return DefWindowProc(hWnd, uMsg, wParam, lParam);
482 case WM_GESTURENOTIFY:
484 OnGestureNotify(hWnd, lParam);
485 return DefWindowProc(hWnd, WM_GESTURENOTIFY, wParam, lParam);
487 case WM_GESTURE:
489 OnGesture(hWnd, lParam);
490 return 0;
492 case WM_SYSCHAR:
493 if (wParam == VK_RETURN) //stop system beep on alt-return
494 return 0;
495 break;
496 case WM_SETCURSOR:
497 if (HTCLIENT != LOWORD(lParam))
498 DX::Windowing()->ShowOSMouse(true);
499 break;
500 case WM_MOUSEMOVE:
501 newEvent.type = XBMC_MOUSEMOTION;
502 newEvent.motion.x = GET_X_LPARAM(lParam);
503 newEvent.motion.y = GET_Y_LPARAM(lParam);
504 if (appPort)
505 appPort->OnEvent(newEvent);
506 return(0);
507 case WM_LBUTTONDOWN:
508 case WM_MBUTTONDOWN:
509 case WM_RBUTTONDOWN:
510 newEvent.type = XBMC_MOUSEBUTTONDOWN;
511 newEvent.button.x = GET_X_LPARAM(lParam);
512 newEvent.button.y = GET_Y_LPARAM(lParam);
513 newEvent.button.button = 0;
514 if (uMsg == WM_LBUTTONDOWN) newEvent.button.button = XBMC_BUTTON_LEFT;
515 else if (uMsg == WM_MBUTTONDOWN) newEvent.button.button = XBMC_BUTTON_MIDDLE;
516 else if (uMsg == WM_RBUTTONDOWN) newEvent.button.button = XBMC_BUTTON_RIGHT;
517 if (appPort)
518 appPort->OnEvent(newEvent);
519 return(0);
520 case WM_LBUTTONUP:
521 case WM_MBUTTONUP:
522 case WM_RBUTTONUP:
523 newEvent.type = XBMC_MOUSEBUTTONUP;
524 newEvent.button.x = GET_X_LPARAM(lParam);
525 newEvent.button.y = GET_Y_LPARAM(lParam);
526 newEvent.button.button = 0;
527 if (uMsg == WM_LBUTTONUP) newEvent.button.button = XBMC_BUTTON_LEFT;
528 else if (uMsg == WM_MBUTTONUP) newEvent.button.button = XBMC_BUTTON_MIDDLE;
529 else if (uMsg == WM_RBUTTONUP) newEvent.button.button = XBMC_BUTTON_RIGHT;
530 if (appPort)
531 appPort->OnEvent(newEvent);
532 return(0);
533 case WM_MOUSEWHEEL:
535 // SDL, which our events system is based off, sends a MOUSEBUTTONDOWN message
536 // followed by a MOUSEBUTTONUP message. As this is a momentary event, we just
537 // react on the MOUSEBUTTONUP message, resetting the state after processing.
538 newEvent.type = XBMC_MOUSEBUTTONDOWN;
539 // the coordinates in WM_MOUSEWHEEL are screen, not client coordinates
540 POINT point;
541 point.x = GET_X_LPARAM(lParam);
542 point.y = GET_Y_LPARAM(lParam);
543 WindowFromScreenCoords(hWnd, &point);
544 newEvent.button.x = static_cast<uint16_t>(point.x);
545 newEvent.button.y = static_cast<uint16_t>(point.y);
546 newEvent.button.button = GET_Y_LPARAM(wParam) > 0 ? XBMC_BUTTON_WHEELUP : XBMC_BUTTON_WHEELDOWN;
547 if (appPort)
549 appPort->OnEvent(newEvent);
550 newEvent.type = XBMC_MOUSEBUTTONUP;
551 appPort->OnEvent(newEvent);
554 return(0);
555 case WM_DPICHANGED:
556 // This message tells the program that most of its window is on a
557 // monitor with a new DPI. The wParam contains the new DPI, and the
558 // lParam contains a rect which defines the window rectangle scaled
559 // the new DPI.
561 // get the suggested size of the window on the new display with a different DPI
562 uint16_t dpi = HIWORD(wParam);
563 RECT rc = *reinterpret_cast<RECT*>(lParam);
564 CLog::LogFC(LOGDEBUG, LOGWINDOWING, "dpi changed event -> {} ({}, {}, {}, {})", dpi, rc.left,
565 rc.top, rc.right, rc.bottom);
566 DX::Windowing()->DPIChanged(dpi, rc);
567 return(0);
569 case WM_DISPLAYCHANGE:
571 CLog::LogFC(LOGDEBUG, LOGWINDOWING, "display change event");
572 if (DX::Windowing()->IsTogglingHDR() || DX::Windowing()->IsAlteringWindow())
573 return (0);
575 const auto& components = CServiceBroker::GetAppComponents();
576 const auto appPower = components.GetComponent<CApplicationPowerHandling>();
577 if (appPower->GetRenderGUI() && GET_X_LPARAM(lParam) > 0 && GET_Y_LPARAM(lParam) > 0)
579 DX::Windowing()->UpdateResolutions();
581 return(0);
583 case WM_ENTERSIZEMOVE:
585 DX::Windowing()->SetSizeMoveMode(true);
587 return(0);
588 case WM_EXITSIZEMOVE:
590 DX::Windowing()->SetSizeMoveMode(false);
591 if (g_sizeMoveMoving)
593 g_sizeMoveMoving = false;
594 newEvent.type = XBMC_VIDEOMOVE;
595 newEvent.move.x = g_sizeMoveX;
596 newEvent.move.y = g_sizeMoveY;
598 // tell the device about new position
599 DX::Windowing()->OnMove(newEvent.move.x, newEvent.move.y);
600 // tell the application about new position
601 const auto& components = CServiceBroker::GetAppComponents();
602 const auto appPower = components.GetComponent<CApplicationPowerHandling>();
603 if (appPower->GetRenderGUI() && !DX::Windowing()->IsAlteringWindow())
605 if (appPort)
606 appPort->OnEvent(newEvent);
609 if (g_sizeMoveSizing)
611 g_sizeMoveSizing = false;
612 newEvent.type = XBMC_VIDEORESIZE;
613 newEvent.resize.width = g_sizeMoveWidth;
614 newEvent.resize.height = g_sizeMoveHight;
615 newEvent.resize.scale = 1.0;
617 // tell the device about new size
618 DX::Windowing()->OnResize(newEvent.resize.width, newEvent.resize.height);
619 // tell the application about new size
620 const auto& components = CServiceBroker::GetAppComponents();
621 const auto appPower = components.GetComponent<CApplicationPowerHandling>();
622 if (appPower->GetRenderGUI() && !DX::Windowing()->IsAlteringWindow() &&
623 newEvent.resize.width > 0 && newEvent.resize.height > 0)
625 if (appPort)
626 appPort->OnEvent(newEvent);
630 return(0);
631 case WM_SIZE:
632 if (wParam == SIZE_MINIMIZED)
634 if (!DX::Windowing()->IsMinimized())
636 DX::Windowing()->SetMinimized(true);
637 const auto& components = CServiceBroker::GetAppComponents();
638 const auto appPower = components.GetComponent<CApplicationPowerHandling>();
639 if (appPower->GetRenderGUI())
641 if (appPort)
642 appPort->SetRenderGUI(false);
646 else if (DX::Windowing()->IsMinimized())
648 DX::Windowing()->SetMinimized(false);
649 const auto& components = CServiceBroker::GetAppComponents();
650 const auto appPower = components.GetComponent<CApplicationPowerHandling>();
651 if (!appPower->GetRenderGUI())
653 if (appPort)
654 appPort->SetRenderGUI(true);
657 else
659 g_sizeMoveWidth = GET_X_LPARAM(lParam);
660 g_sizeMoveHight = GET_Y_LPARAM(lParam);
661 if (DX::Windowing()->IsInSizeMoveMode())
663 // If an user is dragging the resize bars, we don't resize
664 // the buffers and don't rise XBMC_VIDEORESIZE here because
665 // as the user continuously resize the window, a lot of WM_SIZE
666 // messages are sent to the proc, and it'd be pointless (and slow)
667 // to resize for each WM_SIZE message received from dragging.
668 // So instead, we reset after the user is done resizing the
669 // window and releases the resize bars, which ends with WM_EXITSIZEMOVE.
670 g_sizeMoveSizing = true;
672 else
674 // API call such as SetWindowPos or SwapChain->SetFullscreenState
675 newEvent.type = XBMC_VIDEORESIZE;
676 newEvent.resize.width = g_sizeMoveWidth;
677 newEvent.resize.height = g_sizeMoveHight;
678 newEvent.resize.scale = 1.0;
680 CLog::LogFC(LOGDEBUG, LOGWINDOWING, "window resize event {} x {}", newEvent.resize.width,
681 newEvent.resize.height);
682 // tell device about new size
683 DX::Windowing()->OnResize(newEvent.resize.width, newEvent.resize.height);
684 // tell application about size changes
685 const auto& components = CServiceBroker::GetAppComponents();
686 const auto appPower = components.GetComponent<CApplicationPowerHandling>();
687 if (appPower->GetRenderGUI() && !DX::Windowing()->IsAlteringWindow() &&
688 newEvent.resize.width > 0 && newEvent.resize.height > 0)
690 if (appPort)
691 appPort->OnEvent(newEvent);
695 return(0);
696 case WM_MOVE:
698 g_sizeMoveX = GET_X_LPARAM(lParam);
699 g_sizeMoveY = GET_Y_LPARAM(lParam);
700 if (DX::Windowing()->IsInSizeMoveMode())
702 // the same as WM_SIZE
703 g_sizeMoveMoving = true;
705 else
707 newEvent.type = XBMC_VIDEOMOVE;
708 newEvent.move.x = g_sizeMoveX;
709 newEvent.move.y = g_sizeMoveY;
711 CLog::LogFC(LOGDEBUG, LOGWINDOWING, "window move event");
713 // tell the device about new position
714 DX::Windowing()->OnMove(newEvent.move.x, newEvent.move.y);
715 const auto& components = CServiceBroker::GetAppComponents();
716 const auto appPower = components.GetComponent<CApplicationPowerHandling>();
717 if (appPower->GetRenderGUI() && !DX::Windowing()->IsAlteringWindow())
719 if (appPort)
720 appPort->OnEvent(newEvent);
724 return(0);
725 case WM_MEDIA_CHANGE:
727 // This event detects media changes of usb, sd card and optical media.
728 // It only works if the explorer.exe process is started. Because this
729 // isn't the case for all setups we use WM_DEVICECHANGE for usb and
730 // optical media because this event is also triggered without the
731 // explorer process. Since WM_DEVICECHANGE doesn't detect sd card changes
732 // we still use this event only for sd.
733 long lEvent;
734 PIDLIST_ABSOLUTE *ppidl;
735 HANDLE hLock = SHChangeNotification_Lock(reinterpret_cast<HANDLE>(wParam), static_cast<DWORD>(lParam), &ppidl, &lEvent);
737 if (hLock)
739 wchar_t drivePath[MAX_PATH+1];
740 if (!SHGetPathFromIDList(ppidl[0], drivePath))
741 break;
743 switch(lEvent)
745 case SHCNE_DRIVEADD:
746 case SHCNE_MEDIAINSERTED:
747 if (GetDriveType(drivePath) != DRIVE_CDROM)
749 CLog::LogF(LOGDEBUG, "Drive {} Media has arrived.", FromW(drivePath));
750 CWin32StorageProvider::SetEvent();
752 break;
754 case SHCNE_DRIVEREMOVED:
755 case SHCNE_MEDIAREMOVED:
756 if (GetDriveType(drivePath) != DRIVE_CDROM)
758 CLog::LogF(LOGDEBUG, "Drive {} Media was removed.", FromW(drivePath));
759 CWin32StorageProvider::SetEvent();
761 break;
762 default:;
764 SHChangeNotification_Unlock(hLock);
766 break;
768 case WM_POWERBROADCAST:
769 if (wParam==PBT_APMSUSPEND)
771 CLog::Log(LOGDEBUG,"WM_POWERBROADCAST: PBT_APMSUSPEND event was sent");
772 CWin32PowerSyscall::SetOnSuspend();
774 else if(wParam==PBT_APMRESUMEAUTOMATIC)
776 CLog::Log(LOGDEBUG,"WM_POWERBROADCAST: PBT_APMRESUMEAUTOMATIC event was sent");
777 CWin32PowerSyscall::SetOnResume();
779 break;
780 case WM_DEVICECHANGE:
782 switch(wParam)
784 case DBT_DEVNODES_CHANGED:
785 CServiceBroker::GetPeripherals().TriggerDeviceScan(PERIPHERALS::PERIPHERAL_BUS_USB);
786 break;
787 case DBT_DEVICEARRIVAL:
788 case DBT_DEVICEREMOVECOMPLETE:
789 if (((_DEV_BROADCAST_HEADER*) lParam)->dbcd_devicetype == DBT_DEVTYP_DEVICEINTERFACE)
791 CServiceBroker::GetPeripherals().TriggerDeviceScan(PERIPHERALS::PERIPHERAL_BUS_USB);
793 // check if an usb or optical media was inserted or removed
794 if (((_DEV_BROADCAST_HEADER*) lParam)->dbcd_devicetype == DBT_DEVTYP_VOLUME)
796 PDEV_BROADCAST_VOLUME lpdbv = (PDEV_BROADCAST_VOLUME)((_DEV_BROADCAST_HEADER*) lParam);
797 // optical medium
798 if (lpdbv -> dbcv_flags & DBTF_MEDIA)
800 std::string strdrive = StringUtils::Format(
801 "{}:", CWIN32Util::FirstDriveFromMask(lpdbv->dbcv_unitmask));
802 if(wParam == DBT_DEVICEARRIVAL)
804 CLog::LogF(LOGDEBUG, "Drive {} Media has arrived.", strdrive);
805 CServiceBroker::GetJobManager()->AddJob(new CDetectDisc(strdrive, true), nullptr);
807 else
809 CLog::LogF(LOGDEBUG, "Drive {} Media was removed.", strdrive);
810 CMediaSource share;
811 share.strPath = strdrive;
812 share.strName = share.strPath;
813 CServiceBroker::GetMediaManager().RemoveAutoSource(share);
816 else
817 CWin32StorageProvider::SetEvent();
819 break;
820 default:;
822 break;
824 case WM_PAINT:
826 //some other app has painted over our window, mark everything as dirty
827 CGUIComponent* component = CServiceBroker::GetGUI();
828 if (component)
829 component->GetWindowManager().MarkDirty();
830 break;
832 case BONJOUR_EVENT:
833 CZeroconf::GetInstance()->ProcessResults();
834 break;
835 case BONJOUR_BROWSER_EVENT:
836 CZeroconfBrowser::GetInstance()->ProcessResults();
837 break;
838 case TRAY_ICON_NOTIFY:
840 switch (LOWORD(lParam))
842 case WM_LBUTTONDBLCLK:
844 DX::Windowing()->SetMinimized(false);
845 const auto& components = CServiceBroker::GetAppComponents();
846 const auto appPower = components.GetComponent<CApplicationPowerHandling>();
847 if (!appPower->GetRenderGUI())
849 if (appPort)
850 appPort->SetRenderGUI(true);
852 break;
855 break;
857 case WM_TIMER:
859 if (wParam == ID_TIMER_HDR)
861 CLog::LogFC(LOGDEBUG, LOGWINDOWING, "finish toggling HDR event");
862 DX::Windowing()->SetTogglingHDR(false);
863 KillTimer(hWnd, wParam);
865 break;
867 case WM_INITMENU:
869 HMENU hm = GetSystemMenu(hWnd, FALSE);
870 if (hm)
872 if (DX::Windowing()->IsFullScreen())
873 EnableMenuItem(hm, SC_MOVE, MF_BYCOMMAND | MF_DISABLED | MF_GRAYED);
874 else
875 EnableMenuItem(hm, SC_MOVE, MF_BYCOMMAND | MF_ENABLED);
877 break;
879 default:
880 break;
882 return(DefWindowProc(hWnd, uMsg, wParam, lParam));
885 void CWinEventsWin32::RegisterDeviceInterfaceToHwnd(GUID InterfaceClassGuid, HWND hWnd, HDEVNOTIFY *hDeviceNotify)
887 DEV_BROADCAST_DEVICEINTERFACE NotificationFilter = {};
889 NotificationFilter.dbcc_size = sizeof(DEV_BROADCAST_DEVICEINTERFACE);
890 NotificationFilter.dbcc_devicetype = DBT_DEVTYP_DEVICEINTERFACE;
891 NotificationFilter.dbcc_classguid = InterfaceClassGuid;
893 *hDeviceNotify = RegisterDeviceNotification(
894 hWnd, // events recipient
895 &NotificationFilter, // type of device
896 DEVICE_NOTIFY_WINDOW_HANDLE // type of recipient handle
900 void CWinEventsWin32::WindowFromScreenCoords(HWND hWnd, POINT *point)
902 if (!point) return;
903 RECT clientRect;
904 GetClientRect(hWnd, &clientRect);
905 POINT windowPos;
906 windowPos.x = clientRect.left;
907 windowPos.y = clientRect.top;
908 ClientToScreen(hWnd, &windowPos);
909 point->x -= windowPos.x;
910 point->y -= windowPos.y;
913 void CWinEventsWin32::OnGestureNotify(HWND hWnd, LPARAM lParam)
915 // convert to window coordinates
916 PGESTURENOTIFYSTRUCT gn = reinterpret_cast<PGESTURENOTIFYSTRUCT>(lParam);
917 POINT point = { gn->ptsLocation.x, gn->ptsLocation.y };
918 WindowFromScreenCoords(hWnd, &point);
920 // by default we only want twofingertap and pressandtap gestures
921 // the other gestures are enabled best on supported gestures
922 GESTURECONFIG gc[] = {{ GID_ZOOM, 0, GC_ZOOM},
923 { GID_ROTATE, 0, GC_ROTATE},
924 { GID_PAN, 0, GC_PAN},
925 { GID_TWOFINGERTAP, GC_TWOFINGERTAP, GC_TWOFINGERTAP },
926 { GID_PRESSANDTAP, GC_PRESSANDTAP, GC_PRESSANDTAP }};
928 // send a message to see if a control wants any
929 int gestures;
930 if ((gestures = CGenericTouchActionHandler::GetInstance().QuerySupportedGestures(static_cast<float>(point.x), static_cast<float>(point.y))) != EVENT_RESULT_UNHANDLED)
932 if (gestures & EVENT_RESULT_ZOOM)
933 gc[0].dwWant |= GC_ZOOM;
934 if (gestures & EVENT_RESULT_ROTATE)
935 gc[1].dwWant |= GC_ROTATE;
936 if (gestures & EVENT_RESULT_PAN_VERTICAL)
937 gc[2].dwWant |= GC_PAN | GC_PAN_WITH_SINGLE_FINGER_VERTICALLY | GC_PAN_WITH_GUTTER | GC_PAN_WITH_INERTIA;
938 if (gestures & EVENT_RESULT_PAN_VERTICAL_WITHOUT_INERTIA)
939 gc[2].dwWant |= GC_PAN | GC_PAN_WITH_SINGLE_FINGER_VERTICALLY;
940 if (gestures & EVENT_RESULT_PAN_HORIZONTAL)
941 gc[2].dwWant |= GC_PAN | GC_PAN_WITH_SINGLE_FINGER_HORIZONTALLY | GC_PAN_WITH_GUTTER | GC_PAN_WITH_INERTIA;
942 if (gestures & EVENT_RESULT_PAN_HORIZONTAL_WITHOUT_INERTIA)
943 gc[2].dwWant |= GC_PAN | GC_PAN_WITH_SINGLE_FINGER_HORIZONTALLY;
944 if (gestures & EVENT_RESULT_SWIPE)
946 gc[2].dwWant |= GC_PAN | GC_PAN_WITH_SINGLE_FINGER_VERTICALLY | GC_PAN_WITH_SINGLE_FINGER_HORIZONTALLY | GC_PAN_WITH_GUTTER;
948 // create a new touch swipe detector
949 m_touchSwipeDetector = new CGenericTouchSwipeDetector(&CGenericTouchActionHandler::GetInstance(), 160.0f);
952 gc[0].dwBlock = gc[0].dwWant ^ 0x01;
953 gc[1].dwBlock = gc[1].dwWant ^ 0x01;
954 gc[2].dwBlock = gc[2].dwWant ^ 0x1F;
956 SetGestureConfig(hWnd, 0, 5, gc, sizeof(GESTURECONFIG));
959 void CWinEventsWin32::OnGesture(HWND hWnd, LPARAM lParam)
961 GESTUREINFO gi = {};
962 gi.cbSize = sizeof(gi);
963 GetGestureInfo(reinterpret_cast<HGESTUREINFO>(lParam), &gi);
965 // convert to window coordinates
966 POINT point = { gi.ptsLocation.x, gi.ptsLocation.y };
967 WindowFromScreenCoords(hWnd, &point);
969 if (gi.dwID == GID_BEGIN)
970 m_touchPointer.reset();
972 // if there's a "current" touch from a previous event, copy it to "last"
973 if (m_touchPointer.current.valid())
974 m_touchPointer.last = m_touchPointer.current;
976 // set the "current" touch
977 m_touchPointer.current.x = static_cast<float>(point.x);
978 m_touchPointer.current.y = static_cast<float>(point.y);
979 m_touchPointer.current.time = time(nullptr);
981 switch (gi.dwID)
983 case GID_BEGIN:
985 // set the "down" touch
986 m_touchPointer.down = m_touchPointer.current;
987 m_originalZoomDistance = 0;
989 CGenericTouchActionHandler::GetInstance().OnTouchGestureStart(static_cast<float>(point.x), static_cast<float>(point.y));
991 break;
993 case GID_END:
994 CGenericTouchActionHandler::GetInstance().OnTouchGestureEnd(static_cast<float>(point.x), static_cast<float>(point.y), 0.0f, 0.0f, 0.0f, 0.0f);
995 break;
997 case GID_PAN:
999 if (!m_touchPointer.moving)
1000 m_touchPointer.moving = true;
1002 // calculate the velocity of the pan gesture
1003 float velocityX, velocityY;
1004 m_touchPointer.velocity(velocityX, velocityY);
1006 CGenericTouchActionHandler::GetInstance().OnTouchGesturePan(m_touchPointer.current.x, m_touchPointer.current.y,
1007 m_touchPointer.current.x - m_touchPointer.last.x, m_touchPointer.current.y - m_touchPointer.last.y,
1008 velocityX, velocityY);
1010 if (m_touchSwipeDetector != nullptr)
1012 if (gi.dwFlags & GF_BEGIN)
1014 m_touchPointer.down = m_touchPointer.current;
1015 m_touchSwipeDetector->OnTouchDown(0, m_touchPointer);
1017 else if (gi.dwFlags & GF_END)
1019 m_touchSwipeDetector->OnTouchUp(0, m_touchPointer);
1021 delete m_touchSwipeDetector;
1022 m_touchSwipeDetector = nullptr;
1024 else
1025 m_touchSwipeDetector->OnTouchMove(0, m_touchPointer);
1028 break;
1030 case GID_ROTATE:
1032 if (gi.dwFlags == GF_BEGIN)
1033 break;
1035 CGenericTouchActionHandler::GetInstance().OnRotate(static_cast<float>(point.x), static_cast<float>(point.y),
1036 -static_cast<float>(ROTATE_ANGLE_DEGREE(gi.ullArguments)));
1038 break;
1040 case GID_ZOOM:
1042 if (gi.dwFlags == GF_BEGIN)
1044 m_originalZoomDistance = static_cast<int>(LODWORD(gi.ullArguments));
1045 break;
1048 // avoid division by 0
1049 if (m_originalZoomDistance == 0)
1050 break;
1052 CGenericTouchActionHandler::GetInstance().OnZoomPinch(static_cast<float>(point.x), static_cast<float>(point.y),
1053 static_cast<float>(LODWORD(gi.ullArguments)) / static_cast<float>(m_originalZoomDistance));
1055 break;
1057 case GID_TWOFINGERTAP:
1058 CGenericTouchActionHandler::GetInstance().OnTap(static_cast<float>(point.x), static_cast<float>(point.y), 2);
1059 break;
1061 case GID_PRESSANDTAP:
1062 default:
1063 // You have encountered an unknown gesture
1064 break;
1066 CloseGestureInfoHandle(reinterpret_cast<HGESTUREINFO>(lParam));