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.
9 #ifndef _USE_MATH_DEFINES
10 #define _USE_MATH_DEFINES
12 #include "WinEventsWin32.h"
14 #include "ServiceBroker.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"
52 HWND g_hWnd
= nullptr;
55 #define LODWORD(longval) ((DWORD)((DWORDLONG)(longval)))
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);
92 { /* These are always correct */
100 /* These are already handled */
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
:
120 case VK_MEDIA_NEXT_TRACK
:
121 case VK_MEDIA_PREV_TRACK
:
123 case VK_MEDIA_PLAY_PAUSE
:
125 case VK_LAUNCH_MEDIA_SELECT
:
128 return static_cast<int>(vkey
);
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
);
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
);
160 if ((vkey
== VK_RETURN
) && (scancode
& 0x100))
162 /* No VK_ code for the keypad enter key */
163 keysym
->sym
= XBMCK_KP_ENTER
;
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());
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()),
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
201 if ((keystate
[VK_LCONTROL
] & 0x80) && (keystate
[VK_RMENU
] & 0x80))
203 mod
|= XBMCKMOD_MODE
;
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
225 bool CWinEventsWin32::MessagePump()
228 while( PeekMessage( &msg
, nullptr, 0U, 0U, PM_REMOVE
) )
230 TranslateMessage( &msg
);
231 DispatchMessage( &msg
);
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
)
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
);
260 if (uMsg
== WM_DESTROY
)
263 if(g_uQueryCancelAutoPlay
!= 0 && uMsg
== g_uQueryCancelAutoPlay
)
266 std::shared_ptr
<CAppInboundProtocol
> appPort
= CServiceBroker::GetAppPort();
275 if (UnregisterDeviceNotification(hDeviceNotify
))
276 hDeviceNotify
= nullptr;
278 CLog::LogF(LOGINFO
, "UnregisterDeviceNotification failed ({})", GetLastError());
280 newEvent
.type
= XBMC_QUIT
;
282 appPort
->OnEvent(newEvent
);
286 const auto& components
= CServiceBroker::GetAppComponents();
287 const auto appPower
= components
.GetComponent
<CApplicationPowerHandling
>();
288 bool active
= appPower
->GetRenderGUI();
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");
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();
307 appPort
->SetRenderGUI(false);
311 WINDOWPLACEMENT lpwndpl
;
312 lpwndpl
.length
= sizeof(lpwndpl
);
313 if (LOWORD(wParam
) != WA_INACTIVE
)
315 if (GetWindowPlacement(hWnd
, &lpwndpl
))
318 appPort
->SetRenderGUI(lpwndpl
.showCmd
!= SW_HIDE
);
326 if (appPower
->GetRenderGUI() != active
)
327 DX::Windowing()->NotifyAppActiveChange(appPower
->GetRenderGUI());
328 CLog::LogFC(LOGDEBUG
, LOGWINDOWING
, "window is {}",
329 appPower
->GetRenderGUI() ? "active" : "inactive");
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
);
346 /* needs to be reviewed after frodo. we reset the system idle timer
347 and the display timer directly now (see m_screenSaverTimer).
349 switch( wParam&0xFFF0 )
351 case SC_MONITORPOWER:
352 if (g_application.GetAppPlayer().IsPlaying() || g_application.GetAppPlayer().IsPausedPlayback())
354 else if(CServiceBroker::GetSettingsComponent()->GetSettings()->GetInt(CSettings::SETTING_POWERMANAGEMENT_DISPLAYSOFF) == 0)
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
);
378 if ( lParam
& EXTENDED_KEYMASK
)
379 wParam
= VK_RCONTROL
;
381 wParam
= VK_LCONTROL
;
384 /* EXTENDED trick doesn't work here */
385 if (GetKeyState(VK_LSHIFT
) & 0x8000)
387 else if (GetKeyState(VK_RSHIFT
) & 0x8000)
391 if ( lParam
& EXTENDED_KEYMASK
)
399 TranslateKey(wParam
, HIWORD(lParam
), &keysym
, 1);
401 newEvent
.type
= XBMC_KEYDOWN
;
402 newEvent
.key
.keysym
= keysym
;
404 appPort
->OnEvent(newEvent
);
414 if ( lParam
&EXTENDED_KEYMASK
)
415 wParam
= VK_RCONTROL
;
417 wParam
= VK_LCONTROL
;
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
)
426 else if (keyCode
== scanCodeR
)
431 if ( lParam
&EXTENDED_KEYMASK
)
439 TranslateKey(wParam
, HIWORD(lParam
), &keysym
, 1);
441 if (wParam
== VK_SNAPSHOT
)
442 newEvent
.type
= XBMC_KEYDOWN
;
444 newEvent
.type
= XBMC_KEYUP
;
445 newEvent
.key
.keysym
= keysym
;
447 appPort
->OnEvent(newEvent
);
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
463 if (appPower
->WakeUpScreenSaverAndDPMS())
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
);
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
);
489 OnGesture(hWnd
, lParam
);
493 if (wParam
== VK_RETURN
) //stop system beep on alt-return
497 if (HTCLIENT
!= LOWORD(lParam
))
498 DX::Windowing()->ShowOSMouse(true);
501 newEvent
.type
= XBMC_MOUSEMOTION
;
502 newEvent
.motion
.x
= GET_X_LPARAM(lParam
);
503 newEvent
.motion
.y
= GET_Y_LPARAM(lParam
);
505 appPort
->OnEvent(newEvent
);
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
;
518 appPort
->OnEvent(newEvent
);
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
;
531 appPort
->OnEvent(newEvent
);
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
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
;
549 appPort
->OnEvent(newEvent
);
550 newEvent
.type
= XBMC_MOUSEBUTTONUP
;
551 appPort
->OnEvent(newEvent
);
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
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
);
569 case WM_DISPLAYCHANGE
:
571 CLog::LogFC(LOGDEBUG
, LOGWINDOWING
, "display change event");
572 if (DX::Windowing()->IsTogglingHDR() || DX::Windowing()->IsAlteringWindow())
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();
583 case WM_ENTERSIZEMOVE
:
585 DX::Windowing()->SetSizeMoveMode(true);
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())
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)
625 appPort
->OnEvent(newEvent
);
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())
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())
653 appPort
->SetRenderGUI(true);
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;
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
,
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)
689 appPort
->OnEvent(newEvent
);
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;
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())
718 appPort
->OnEvent(newEvent
);
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.
732 PIDLIST_ABSOLUTE
*ppidl
;
733 HANDLE hLock
= SHChangeNotification_Lock(reinterpret_cast<HANDLE
>(wParam
), static_cast<DWORD
>(lParam
), &ppidl
, &lEvent
);
737 wchar_t drivePath
[MAX_PATH
+1];
738 if (!SHGetPathFromIDList(ppidl
[0], drivePath
))
744 case SHCNE_MEDIAINSERTED
:
745 if (GetDriveType(drivePath
) != DRIVE_CDROM
)
747 CLog::LogF(LOGDEBUG
, "Drive {} Media has arrived.", FromW(drivePath
));
748 CWin32StorageProvider::SetEvent();
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();
762 SHChangeNotification_Unlock(hLock
);
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();
778 case WM_DEVICECHANGE
:
782 case DBT_DEVNODES_CHANGED
:
783 CServiceBroker::GetPeripherals().TriggerDeviceScan(PERIPHERALS::PERIPHERAL_BUS_USB
);
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
);
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);
807 CLog::LogF(LOGDEBUG
, "Drive {} Media was removed.", strdrive
);
809 share
.strPath
= strdrive
;
810 share
.strName
= share
.strPath
;
811 CServiceBroker::GetMediaManager().RemoveAutoSource(share
);
815 CWin32StorageProvider::SetEvent();
824 //some other app has painted over our window, mark everything as dirty
825 CGUIComponent
* component
= CServiceBroker::GetGUI();
827 component
->GetWindowManager().MarkDirty();
831 CZeroconf::GetInstance()->ProcessResults();
833 case BONJOUR_BROWSER_EVENT
:
834 CZeroconfBrowser::GetInstance()->ProcessResults();
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())
848 appPort
->SetRenderGUI(true);
857 if (wParam
== ID_TIMER_HDR
)
859 CLog::LogFC(LOGDEBUG
, LOGWINDOWING
, "finish toggling HDR event");
860 DX::Windowing()->SetTogglingHDR(false);
861 KillTimer(hWnd
, wParam
);
867 HMENU hm
= GetSystemMenu(hWnd
, FALSE
);
870 if (DX::Windowing()->IsFullScreen())
871 EnableMenuItem(hm
, SC_MOVE
, MF_BYCOMMAND
| MF_DISABLED
| MF_GRAYED
);
873 EnableMenuItem(hm
, SC_MOVE
, MF_BYCOMMAND
| MF_ENABLED
);
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
)
902 GetClientRect(hWnd
, &clientRect
);
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
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
)
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);
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
));
992 CGenericTouchActionHandler::GetInstance().OnTouchGestureEnd(static_cast<float>(point
.x
), static_cast<float>(point
.y
), 0.0f
, 0.0f
, 0.0f
, 0.0f
);
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;
1023 m_touchSwipeDetector
->OnTouchMove(0, m_touchPointer
);
1030 if (gi
.dwFlags
== GF_BEGIN
)
1033 CGenericTouchActionHandler::GetInstance().OnRotate(static_cast<float>(point
.x
), static_cast<float>(point
.y
),
1034 -static_cast<float>(ROTATE_ANGLE_DEGREE(gi
.ullArguments
)));
1040 if (gi
.dwFlags
== GF_BEGIN
)
1042 m_originalZoomDistance
= static_cast<int>(LODWORD(gi
.ullArguments
));
1046 // avoid division by 0
1047 if (m_originalZoomDistance
== 0)
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
));
1055 case GID_TWOFINGERTAP
:
1056 CGenericTouchActionHandler::GetInstance().OnTap(static_cast<float>(point
.x
), static_cast<float>(point
.y
), 2);
1059 case GID_PRESSANDTAP
:
1061 // You have encountered an unknown gesture
1064 CloseGestureInfoHandle(reinterpret_cast<HGESTUREINFO
>(lParam
));