[videodb] remove unused seasons table from episode_view
[xbmc.git] / xbmc / windowing / windows / WinEventsWin32.cpp
blobf419c6d58beecd417d6c463631ad261867dfc83f
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<unsigned char>(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.w = g_sizeMoveWidth;
614 newEvent.resize.h = g_sizeMoveHight;
616 // tell the device about new size
617 DX::Windowing()->OnResize(newEvent.resize.w, newEvent.resize.h);
618 // tell the application about new size
619 const auto& components = CServiceBroker::GetAppComponents();
620 const auto appPower = components.GetComponent<CApplicationPowerHandling>();
621 if (appPower->GetRenderGUI() && !DX::Windowing()->IsAlteringWindow() &&
622 newEvent.resize.w > 0 && newEvent.resize.h > 0)
624 if (appPort)
625 appPort->OnEvent(newEvent);
629 return(0);
630 case WM_SIZE:
631 if (wParam == SIZE_MINIMIZED)
633 if (!DX::Windowing()->IsMinimized())
635 DX::Windowing()->SetMinimized(true);
636 const auto& components = CServiceBroker::GetAppComponents();
637 const auto appPower = components.GetComponent<CApplicationPowerHandling>();
638 if (appPower->GetRenderGUI())
640 if (appPort)
641 appPort->SetRenderGUI(false);
645 else if (DX::Windowing()->IsMinimized())
647 DX::Windowing()->SetMinimized(false);
648 const auto& components = CServiceBroker::GetAppComponents();
649 const auto appPower = components.GetComponent<CApplicationPowerHandling>();
650 if (!appPower->GetRenderGUI())
652 if (appPort)
653 appPort->SetRenderGUI(true);
656 else
658 g_sizeMoveWidth = GET_X_LPARAM(lParam);
659 g_sizeMoveHight = GET_Y_LPARAM(lParam);
660 if (DX::Windowing()->IsInSizeMoveMode())
662 // If an user is dragging the resize bars, we don't resize
663 // the buffers and don't rise XBMC_VIDEORESIZE here because
664 // as the user continuously resize the window, a lot of WM_SIZE
665 // messages are sent to the proc, and it'd be pointless (and slow)
666 // to resize for each WM_SIZE message received from dragging.
667 // So instead, we reset after the user is done resizing the
668 // window and releases the resize bars, which ends with WM_EXITSIZEMOVE.
669 g_sizeMoveSizing = true;
671 else
673 // API call such as SetWindowPos or SwapChain->SetFullscreenState
674 newEvent.type = XBMC_VIDEORESIZE;
675 newEvent.resize.w = g_sizeMoveWidth;
676 newEvent.resize.h = g_sizeMoveHight;
678 CLog::LogFC(LOGDEBUG, LOGWINDOWING, "window resize event {} x {}", newEvent.resize.w,
679 newEvent.resize.h);
680 // tell device about new size
681 DX::Windowing()->OnResize(newEvent.resize.w, newEvent.resize.h);
682 // tell application about size changes
683 const auto& components = CServiceBroker::GetAppComponents();
684 const auto appPower = components.GetComponent<CApplicationPowerHandling>();
685 if (appPower->GetRenderGUI() && !DX::Windowing()->IsAlteringWindow() &&
686 newEvent.resize.w > 0 && newEvent.resize.h > 0)
688 if (appPort)
689 appPort->OnEvent(newEvent);
693 return(0);
694 case WM_MOVE:
696 g_sizeMoveX = GET_X_LPARAM(lParam);
697 g_sizeMoveY = GET_Y_LPARAM(lParam);
698 if (DX::Windowing()->IsInSizeMoveMode())
700 // the same as WM_SIZE
701 g_sizeMoveMoving = true;
703 else
705 newEvent.type = XBMC_VIDEOMOVE;
706 newEvent.move.x = g_sizeMoveX;
707 newEvent.move.y = g_sizeMoveY;
709 CLog::LogFC(LOGDEBUG, LOGWINDOWING, "window move event");
711 // tell the device about new position
712 DX::Windowing()->OnMove(newEvent.move.x, newEvent.move.y);
713 const auto& components = CServiceBroker::GetAppComponents();
714 const auto appPower = components.GetComponent<CApplicationPowerHandling>();
715 if (appPower->GetRenderGUI() && !DX::Windowing()->IsAlteringWindow())
717 if (appPort)
718 appPort->OnEvent(newEvent);
722 return(0);
723 case WM_MEDIA_CHANGE:
725 // This event detects media changes of usb, sd card and optical media.
726 // It only works if the explorer.exe process is started. Because this
727 // isn't the case for all setups we use WM_DEVICECHANGE for usb and
728 // optical media because this event is also triggered without the
729 // explorer process. Since WM_DEVICECHANGE doesn't detect sd card changes
730 // we still use this event only for sd.
731 long lEvent;
732 PIDLIST_ABSOLUTE *ppidl;
733 HANDLE hLock = SHChangeNotification_Lock(reinterpret_cast<HANDLE>(wParam), static_cast<DWORD>(lParam), &ppidl, &lEvent);
735 if (hLock)
737 wchar_t drivePath[MAX_PATH+1];
738 if (!SHGetPathFromIDList(ppidl[0], drivePath))
739 break;
741 switch(lEvent)
743 case SHCNE_DRIVEADD:
744 case SHCNE_MEDIAINSERTED:
745 if (GetDriveType(drivePath) != DRIVE_CDROM)
747 CLog::LogF(LOGDEBUG, "Drive {} Media has arrived.", FromW(drivePath));
748 CWin32StorageProvider::SetEvent();
750 break;
752 case SHCNE_DRIVEREMOVED:
753 case SHCNE_MEDIAREMOVED:
754 if (GetDriveType(drivePath) != DRIVE_CDROM)
756 CLog::LogF(LOGDEBUG, "Drive {} Media was removed.", FromW(drivePath));
757 CWin32StorageProvider::SetEvent();
759 break;
760 default:;
762 SHChangeNotification_Unlock(hLock);
764 break;
766 case WM_POWERBROADCAST:
767 if (wParam==PBT_APMSUSPEND)
769 CLog::Log(LOGDEBUG,"WM_POWERBROADCAST: PBT_APMSUSPEND event was sent");
770 CWin32PowerSyscall::SetOnSuspend();
772 else if(wParam==PBT_APMRESUMEAUTOMATIC)
774 CLog::Log(LOGDEBUG,"WM_POWERBROADCAST: PBT_APMRESUMEAUTOMATIC event was sent");
775 CWin32PowerSyscall::SetOnResume();
777 break;
778 case WM_DEVICECHANGE:
780 switch(wParam)
782 case DBT_DEVNODES_CHANGED:
783 CServiceBroker::GetPeripherals().TriggerDeviceScan(PERIPHERALS::PERIPHERAL_BUS_USB);
784 break;
785 case DBT_DEVICEARRIVAL:
786 case DBT_DEVICEREMOVECOMPLETE:
787 if (((_DEV_BROADCAST_HEADER*) lParam)->dbcd_devicetype == DBT_DEVTYP_DEVICEINTERFACE)
789 CServiceBroker::GetPeripherals().TriggerDeviceScan(PERIPHERALS::PERIPHERAL_BUS_USB);
791 // check if an usb or optical media was inserted or removed
792 if (((_DEV_BROADCAST_HEADER*) lParam)->dbcd_devicetype == DBT_DEVTYP_VOLUME)
794 PDEV_BROADCAST_VOLUME lpdbv = (PDEV_BROADCAST_VOLUME)((_DEV_BROADCAST_HEADER*) lParam);
795 // optical medium
796 if (lpdbv -> dbcv_flags & DBTF_MEDIA)
798 std::string strdrive = StringUtils::Format(
799 "{}:", CWIN32Util::FirstDriveFromMask(lpdbv->dbcv_unitmask));
800 if(wParam == DBT_DEVICEARRIVAL)
802 CLog::LogF(LOGDEBUG, "Drive {} Media has arrived.", strdrive);
803 CServiceBroker::GetJobManager()->AddJob(new CDetectDisc(strdrive, true), nullptr);
805 else
807 CLog::LogF(LOGDEBUG, "Drive {} Media was removed.", strdrive);
808 CMediaSource share;
809 share.strPath = strdrive;
810 share.strName = share.strPath;
811 CServiceBroker::GetMediaManager().RemoveAutoSource(share);
814 else
815 CWin32StorageProvider::SetEvent();
817 break;
818 default:;
820 break;
822 case WM_PAINT:
824 //some other app has painted over our window, mark everything as dirty
825 CGUIComponent* component = CServiceBroker::GetGUI();
826 if (component)
827 component->GetWindowManager().MarkDirty();
828 break;
830 case BONJOUR_EVENT:
831 CZeroconf::GetInstance()->ProcessResults();
832 break;
833 case BONJOUR_BROWSER_EVENT:
834 CZeroconfBrowser::GetInstance()->ProcessResults();
835 break;
836 case TRAY_ICON_NOTIFY:
838 switch (LOWORD(lParam))
840 case WM_LBUTTONDBLCLK:
842 DX::Windowing()->SetMinimized(false);
843 const auto& components = CServiceBroker::GetAppComponents();
844 const auto appPower = components.GetComponent<CApplicationPowerHandling>();
845 if (!appPower->GetRenderGUI())
847 if (appPort)
848 appPort->SetRenderGUI(true);
850 break;
853 break;
855 case WM_TIMER:
857 if (wParam == ID_TIMER_HDR)
859 CLog::LogFC(LOGDEBUG, LOGWINDOWING, "finish toggling HDR event");
860 DX::Windowing()->SetTogglingHDR(false);
861 KillTimer(hWnd, wParam);
863 break;
865 case WM_INITMENU:
867 HMENU hm = GetSystemMenu(hWnd, FALSE);
868 if (hm)
870 if (DX::Windowing()->IsFullScreen())
871 EnableMenuItem(hm, SC_MOVE, MF_BYCOMMAND | MF_DISABLED | MF_GRAYED);
872 else
873 EnableMenuItem(hm, SC_MOVE, MF_BYCOMMAND | MF_ENABLED);
875 break;
877 default:
878 break;
880 return(DefWindowProc(hWnd, uMsg, wParam, lParam));
883 void CWinEventsWin32::RegisterDeviceInterfaceToHwnd(GUID InterfaceClassGuid, HWND hWnd, HDEVNOTIFY *hDeviceNotify)
885 DEV_BROADCAST_DEVICEINTERFACE NotificationFilter = {};
887 NotificationFilter.dbcc_size = sizeof(DEV_BROADCAST_DEVICEINTERFACE);
888 NotificationFilter.dbcc_devicetype = DBT_DEVTYP_DEVICEINTERFACE;
889 NotificationFilter.dbcc_classguid = InterfaceClassGuid;
891 *hDeviceNotify = RegisterDeviceNotification(
892 hWnd, // events recipient
893 &NotificationFilter, // type of device
894 DEVICE_NOTIFY_WINDOW_HANDLE // type of recipient handle
898 void CWinEventsWin32::WindowFromScreenCoords(HWND hWnd, POINT *point)
900 if (!point) return;
901 RECT clientRect;
902 GetClientRect(hWnd, &clientRect);
903 POINT windowPos;
904 windowPos.x = clientRect.left;
905 windowPos.y = clientRect.top;
906 ClientToScreen(hWnd, &windowPos);
907 point->x -= windowPos.x;
908 point->y -= windowPos.y;
911 void CWinEventsWin32::OnGestureNotify(HWND hWnd, LPARAM lParam)
913 // convert to window coordinates
914 PGESTURENOTIFYSTRUCT gn = reinterpret_cast<PGESTURENOTIFYSTRUCT>(lParam);
915 POINT point = { gn->ptsLocation.x, gn->ptsLocation.y };
916 WindowFromScreenCoords(hWnd, &point);
918 // by default we only want twofingertap and pressandtap gestures
919 // the other gestures are enabled best on supported gestures
920 GESTURECONFIG gc[] = {{ GID_ZOOM, 0, GC_ZOOM},
921 { GID_ROTATE, 0, GC_ROTATE},
922 { GID_PAN, 0, GC_PAN},
923 { GID_TWOFINGERTAP, GC_TWOFINGERTAP, GC_TWOFINGERTAP },
924 { GID_PRESSANDTAP, GC_PRESSANDTAP, GC_PRESSANDTAP }};
926 // send a message to see if a control wants any
927 int gestures;
928 if ((gestures = CGenericTouchActionHandler::GetInstance().QuerySupportedGestures(static_cast<float>(point.x), static_cast<float>(point.y))) != EVENT_RESULT_UNHANDLED)
930 if (gestures & EVENT_RESULT_ZOOM)
931 gc[0].dwWant |= GC_ZOOM;
932 if (gestures & EVENT_RESULT_ROTATE)
933 gc[1].dwWant |= GC_ROTATE;
934 if (gestures & EVENT_RESULT_PAN_VERTICAL)
935 gc[2].dwWant |= GC_PAN | GC_PAN_WITH_SINGLE_FINGER_VERTICALLY | GC_PAN_WITH_GUTTER | GC_PAN_WITH_INERTIA;
936 if (gestures & EVENT_RESULT_PAN_VERTICAL_WITHOUT_INERTIA)
937 gc[2].dwWant |= GC_PAN | GC_PAN_WITH_SINGLE_FINGER_VERTICALLY;
938 if (gestures & EVENT_RESULT_PAN_HORIZONTAL)
939 gc[2].dwWant |= GC_PAN | GC_PAN_WITH_SINGLE_FINGER_HORIZONTALLY | GC_PAN_WITH_GUTTER | GC_PAN_WITH_INERTIA;
940 if (gestures & EVENT_RESULT_PAN_HORIZONTAL_WITHOUT_INERTIA)
941 gc[2].dwWant |= GC_PAN | GC_PAN_WITH_SINGLE_FINGER_HORIZONTALLY;
942 if (gestures & EVENT_RESULT_SWIPE)
944 gc[2].dwWant |= GC_PAN | GC_PAN_WITH_SINGLE_FINGER_VERTICALLY | GC_PAN_WITH_SINGLE_FINGER_HORIZONTALLY | GC_PAN_WITH_GUTTER;
946 // create a new touch swipe detector
947 m_touchSwipeDetector = new CGenericTouchSwipeDetector(&CGenericTouchActionHandler::GetInstance(), 160.0f);
950 gc[0].dwBlock = gc[0].dwWant ^ 0x01;
951 gc[1].dwBlock = gc[1].dwWant ^ 0x01;
952 gc[2].dwBlock = gc[2].dwWant ^ 0x1F;
954 SetGestureConfig(hWnd, 0, 5, gc, sizeof(GESTURECONFIG));
957 void CWinEventsWin32::OnGesture(HWND hWnd, LPARAM lParam)
959 GESTUREINFO gi = {};
960 gi.cbSize = sizeof(gi);
961 GetGestureInfo(reinterpret_cast<HGESTUREINFO>(lParam), &gi);
963 // convert to window coordinates
964 POINT point = { gi.ptsLocation.x, gi.ptsLocation.y };
965 WindowFromScreenCoords(hWnd, &point);
967 if (gi.dwID == GID_BEGIN)
968 m_touchPointer.reset();
970 // if there's a "current" touch from a previous event, copy it to "last"
971 if (m_touchPointer.current.valid())
972 m_touchPointer.last = m_touchPointer.current;
974 // set the "current" touch
975 m_touchPointer.current.x = static_cast<float>(point.x);
976 m_touchPointer.current.y = static_cast<float>(point.y);
977 m_touchPointer.current.time = time(nullptr);
979 switch (gi.dwID)
981 case GID_BEGIN:
983 // set the "down" touch
984 m_touchPointer.down = m_touchPointer.current;
985 m_originalZoomDistance = 0;
987 CGenericTouchActionHandler::GetInstance().OnTouchGestureStart(static_cast<float>(point.x), static_cast<float>(point.y));
989 break;
991 case GID_END:
992 CGenericTouchActionHandler::GetInstance().OnTouchGestureEnd(static_cast<float>(point.x), static_cast<float>(point.y), 0.0f, 0.0f, 0.0f, 0.0f);
993 break;
995 case GID_PAN:
997 if (!m_touchPointer.moving)
998 m_touchPointer.moving = true;
1000 // calculate the velocity of the pan gesture
1001 float velocityX, velocityY;
1002 m_touchPointer.velocity(velocityX, velocityY);
1004 CGenericTouchActionHandler::GetInstance().OnTouchGesturePan(m_touchPointer.current.x, m_touchPointer.current.y,
1005 m_touchPointer.current.x - m_touchPointer.last.x, m_touchPointer.current.y - m_touchPointer.last.y,
1006 velocityX, velocityY);
1008 if (m_touchSwipeDetector != nullptr)
1010 if (gi.dwFlags & GF_BEGIN)
1012 m_touchPointer.down = m_touchPointer.current;
1013 m_touchSwipeDetector->OnTouchDown(0, m_touchPointer);
1015 else if (gi.dwFlags & GF_END)
1017 m_touchSwipeDetector->OnTouchUp(0, m_touchPointer);
1019 delete m_touchSwipeDetector;
1020 m_touchSwipeDetector = nullptr;
1022 else
1023 m_touchSwipeDetector->OnTouchMove(0, m_touchPointer);
1026 break;
1028 case GID_ROTATE:
1030 if (gi.dwFlags == GF_BEGIN)
1031 break;
1033 CGenericTouchActionHandler::GetInstance().OnRotate(static_cast<float>(point.x), static_cast<float>(point.y),
1034 -static_cast<float>(ROTATE_ANGLE_DEGREE(gi.ullArguments)));
1036 break;
1038 case GID_ZOOM:
1040 if (gi.dwFlags == GF_BEGIN)
1042 m_originalZoomDistance = static_cast<int>(LODWORD(gi.ullArguments));
1043 break;
1046 // avoid division by 0
1047 if (m_originalZoomDistance == 0)
1048 break;
1050 CGenericTouchActionHandler::GetInstance().OnZoomPinch(static_cast<float>(point.x), static_cast<float>(point.y),
1051 static_cast<float>(LODWORD(gi.ullArguments)) / static_cast<float>(m_originalZoomDistance));
1053 break;
1055 case GID_TWOFINGERTAP:
1056 CGenericTouchActionHandler::GetInstance().OnTap(static_cast<float>(point.x), static_cast<float>(point.y), 2);
1057 break;
1059 case GID_PRESSANDTAP:
1060 default:
1061 // You have encountered an unknown gesture
1062 break;
1064 CloseGestureInfoHandle(reinterpret_cast<HGESTUREINFO>(lParam));