Merge pull request #3297 from t-nelson/pivos_video_busy_dialog_delay_as
[vuplus_xbmc] / xbmc / windowing / windows / WinEventsWin32.cpp
1 /*
2 *      Copyright (C) 2005-2013 Team XBMC
3  *      http://xbmc.org
4  *
5  *  This Program is free software; you can redistribute it and/or modify
6  *  it under the terms of the GNU General Public License as published by
7  *  the Free Software Foundation; either version 2, or (at your option)
8  *  any later version.
9  *
10  *  This Program is distributed in the hope that it will be useful,
11  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
12  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13  *  GNU General Public License for more details.
14  *
15  *  You should have received a copy of the GNU General Public License
16  *  along with XBMC; see the file COPYING.  If not, see
17  *  <http://www.gnu.org/licenses/>.
18  *
19  */
20
21 #ifndef _USE_MATH_DEFINES
22 #define _USE_MATH_DEFINES
23 #endif
24 #include <math.h>
25
26 #include "utils/log.h"
27 #include "Windowsx.h"
28 #include "WinEventsWin32.h"
29 #include "WIN32Util.h"
30 #include "storage/windows/Win32StorageProvider.h"
31 #include "Application.h"
32 #include "input/XBMC_vkeys.h"
33 #include "input/MouseStat.h"
34 #include "input/touch/generic/GenericTouchActionHandler.h"
35 #include "input/touch/generic/GenericTouchSwipeDetector.h"
36 #include "input/windows/WINJoystick.h"
37 #include "storage/MediaManager.h"
38 #include "windowing/WindowingFactory.h"
39 #include <dbt.h>
40 #include "guilib/LocalizeStrings.h"
41 #include "input/KeyboardStat.h"
42 #include "guilib/GUIWindowManager.h"
43 #include "guilib/GUIControl.h"       // for EVENT_RESULT
44 #include "powermanagement/windows/Win32PowerSyscall.h"
45 #include "Shlobj.h"
46 #include "settings/AdvancedSettings.h"
47 #include "settings/Settings.h"
48 #include "peripherals/Peripherals.h"
49 #include "utils/JobManager.h"
50 #include "network/Zeroconf.h"
51 #include "network/ZeroconfBrowser.h"
52 #include "GUIUserMessages.h"
53 #include "utils/CharsetConverter.h"
54
55 #ifdef TARGET_WINDOWS
56
57 using namespace PERIPHERALS;
58
59 HWND g_hWnd = NULL;
60
61 #ifndef LODWORD
62 #define LODWORD(longval) ((DWORD)((DWORDLONG)(longval)))
63 #endif
64
65 #define ROTATE_ANGLE_DEGREE(arg) GID_ROTATE_ANGLE_FROM_ARGUMENT(LODWORD(arg)) * 180 / M_PI
66
67 #define XBMC_arraysize(array)   (sizeof(array)/sizeof(array[0]))
68
69 /* Masks for processing the windows KEYDOWN and KEYUP messages */
70 #define REPEATED_KEYMASK  (1<<30)
71 #define EXTENDED_KEYMASK  (1<<24)
72 #define EXTKEYPAD(keypad) ((scancode & 0x100)?(mvke):(keypad))
73
74 static XBMCKey VK_keymap[XBMCK_LAST];
75
76 static GUID USB_HID_GUID = { 0x4D1E55B2, 0xF16F, 0x11CF, { 0x88, 0xCB, 0x00, 0x11, 0x11, 0x00, 0x00, 0x30 } };
77
78 uint32_t g_uQueryCancelAutoPlay = 0;
79
80 int XBMC_TranslateUNICODE = 1;
81
82 PHANDLE_EVENT_FUNC CWinEventsWin32::m_pEventFunc = NULL;
83 int CWinEventsWin32::m_originalZoomDistance = 0;
84 Pointer CWinEventsWin32::m_touchPointer;
85 CGenericTouchSwipeDetector* CWinEventsWin32::m_touchSwipeDetector = NULL;
86
87 // register to receive SD card events (insert/remove)
88 // seen at http://www.codeproject.com/Messages/2897423/Re-No-message-triggered-on-SD-card-insertion-remov.aspx
89 #define WM_MEDIA_CHANGE (WM_USER + 666)
90 SHChangeNotifyEntry shcne;
91
92 void DIB_InitOSKeymap()
93 {
94   char current_layout[KL_NAMELENGTH];
95
96   GetKeyboardLayoutName(current_layout);
97
98   LoadKeyboardLayout(current_layout, KLF_ACTIVATE);
99
100   /* Map the VK keysyms */
101   for (int i = 0; i < XBMC_arraysize(VK_keymap); ++i)
102     VK_keymap[i] = XBMCK_UNKNOWN;
103
104   VK_keymap[VK_BACK] = XBMCK_BACKSPACE;
105   VK_keymap[VK_TAB] = XBMCK_TAB;
106   VK_keymap[VK_CLEAR] = XBMCK_CLEAR;
107   VK_keymap[VK_RETURN] = XBMCK_RETURN;
108   VK_keymap[VK_PAUSE] = XBMCK_PAUSE;
109   VK_keymap[VK_ESCAPE] = XBMCK_ESCAPE;
110   VK_keymap[VK_SPACE] = XBMCK_SPACE;
111   VK_keymap[VK_APOSTROPHE] = XBMCK_QUOTE;
112   VK_keymap[VK_COMMA] = XBMCK_COMMA;
113   VK_keymap[VK_MINUS] = XBMCK_MINUS;
114   VK_keymap[VK_PERIOD] = XBMCK_PERIOD;
115   VK_keymap[VK_SLASH] = XBMCK_SLASH;
116   VK_keymap[VK_0] = XBMCK_0;
117   VK_keymap[VK_1] = XBMCK_1;
118   VK_keymap[VK_2] = XBMCK_2;
119   VK_keymap[VK_3] = XBMCK_3;
120   VK_keymap[VK_4] = XBMCK_4;
121   VK_keymap[VK_5] = XBMCK_5;
122   VK_keymap[VK_6] = XBMCK_6;
123   VK_keymap[VK_7] = XBMCK_7;
124   VK_keymap[VK_8] = XBMCK_8;
125   VK_keymap[VK_9] = XBMCK_9;
126   VK_keymap[VK_SEMICOLON] = XBMCK_SEMICOLON;
127   VK_keymap[VK_EQUALS] = XBMCK_EQUALS;
128   VK_keymap[VK_LBRACKET] = XBMCK_LEFTBRACKET;
129   VK_keymap[VK_BACKSLASH] = XBMCK_BACKSLASH;
130   VK_keymap[VK_OEM_102] = XBMCK_BACKSLASH;
131   VK_keymap[VK_RBRACKET] = XBMCK_RIGHTBRACKET;
132   VK_keymap[VK_GRAVE] = XBMCK_BACKQUOTE;
133   VK_keymap[VK_BACKTICK] = XBMCK_BACKQUOTE;
134   VK_keymap[VK_A] = XBMCK_a;
135   VK_keymap[VK_B] = XBMCK_b;
136   VK_keymap[VK_C] = XBMCK_c;
137   VK_keymap[VK_D] = XBMCK_d;
138   VK_keymap[VK_E] = XBMCK_e;
139   VK_keymap[VK_F] = XBMCK_f;
140   VK_keymap[VK_G] = XBMCK_g;
141   VK_keymap[VK_H] = XBMCK_h;
142   VK_keymap[VK_I] = XBMCK_i;
143   VK_keymap[VK_J] = XBMCK_j;
144   VK_keymap[VK_K] = XBMCK_k;
145   VK_keymap[VK_L] = XBMCK_l;
146   VK_keymap[VK_M] = XBMCK_m;
147   VK_keymap[VK_N] = XBMCK_n;
148   VK_keymap[VK_O] = XBMCK_o;
149   VK_keymap[VK_P] = XBMCK_p;
150   VK_keymap[VK_Q] = XBMCK_q;
151   VK_keymap[VK_R] = XBMCK_r;
152   VK_keymap[VK_S] = XBMCK_s;
153   VK_keymap[VK_T] = XBMCK_t;
154   VK_keymap[VK_U] = XBMCK_u;
155   VK_keymap[VK_V] = XBMCK_v;
156   VK_keymap[VK_W] = XBMCK_w;
157   VK_keymap[VK_X] = XBMCK_x;
158   VK_keymap[VK_Y] = XBMCK_y;
159   VK_keymap[VK_Z] = XBMCK_z;
160   VK_keymap[VK_DELETE] = XBMCK_DELETE;
161
162   VK_keymap[VK_NUMPAD0] = XBMCK_KP0;
163   VK_keymap[VK_NUMPAD1] = XBMCK_KP1;
164   VK_keymap[VK_NUMPAD2] = XBMCK_KP2;
165   VK_keymap[VK_NUMPAD3] = XBMCK_KP3;
166   VK_keymap[VK_NUMPAD4] = XBMCK_KP4;
167   VK_keymap[VK_NUMPAD5] = XBMCK_KP5;
168   VK_keymap[VK_NUMPAD6] = XBMCK_KP6;
169   VK_keymap[VK_NUMPAD7] = XBMCK_KP7;
170   VK_keymap[VK_NUMPAD8] = XBMCK_KP8;
171   VK_keymap[VK_NUMPAD9] = XBMCK_KP9;
172   VK_keymap[VK_DECIMAL] = XBMCK_KP_PERIOD;
173   VK_keymap[VK_DIVIDE] = XBMCK_KP_DIVIDE;
174   VK_keymap[VK_MULTIPLY] = XBMCK_KP_MULTIPLY;
175   VK_keymap[VK_SUBTRACT] = XBMCK_KP_MINUS;
176   VK_keymap[VK_ADD] = XBMCK_KP_PLUS;
177
178   VK_keymap[VK_UP] = XBMCK_UP;
179   VK_keymap[VK_DOWN] = XBMCK_DOWN;
180   VK_keymap[VK_RIGHT] = XBMCK_RIGHT;
181   VK_keymap[VK_LEFT] = XBMCK_LEFT;
182   VK_keymap[VK_INSERT] = XBMCK_INSERT;
183   VK_keymap[VK_HOME] = XBMCK_HOME;
184   VK_keymap[VK_END] = XBMCK_END;
185   VK_keymap[VK_PRIOR] = XBMCK_PAGEUP;
186   VK_keymap[VK_NEXT] = XBMCK_PAGEDOWN;
187
188   VK_keymap[VK_F1] = XBMCK_F1;
189   VK_keymap[VK_F2] = XBMCK_F2;
190   VK_keymap[VK_F3] = XBMCK_F3;
191   VK_keymap[VK_F4] = XBMCK_F4;
192   VK_keymap[VK_F5] = XBMCK_F5;
193   VK_keymap[VK_F6] = XBMCK_F6;
194   VK_keymap[VK_F7] = XBMCK_F7;
195   VK_keymap[VK_F8] = XBMCK_F8;
196   VK_keymap[VK_F9] = XBMCK_F9;
197   VK_keymap[VK_F10] = XBMCK_F10;
198   VK_keymap[VK_F11] = XBMCK_F11;
199   VK_keymap[VK_F12] = XBMCK_F12;
200   VK_keymap[VK_F13] = XBMCK_F13;
201   VK_keymap[VK_F14] = XBMCK_F14;
202   VK_keymap[VK_F15] = XBMCK_F15;
203
204   VK_keymap[VK_NUMLOCK] = XBMCK_NUMLOCK;
205   VK_keymap[VK_CAPITAL] = XBMCK_CAPSLOCK;
206   VK_keymap[VK_SCROLL] = XBMCK_SCROLLOCK;
207   VK_keymap[VK_RSHIFT] = XBMCK_RSHIFT;
208   VK_keymap[VK_LSHIFT] = XBMCK_LSHIFT;
209   VK_keymap[VK_RCONTROL] = XBMCK_RCTRL;
210   VK_keymap[VK_LCONTROL] = XBMCK_LCTRL;
211   VK_keymap[VK_RMENU] = XBMCK_RALT;
212   VK_keymap[VK_LMENU] = XBMCK_LALT;
213   VK_keymap[VK_RWIN] = XBMCK_RSUPER;
214   VK_keymap[VK_LWIN] = XBMCK_LSUPER;
215
216   VK_keymap[VK_HELP] = XBMCK_HELP;
217 #ifdef VK_PRINT
218   VK_keymap[VK_PRINT] = XBMCK_PRINT;
219 #endif
220   VK_keymap[VK_SNAPSHOT] = XBMCK_PRINT;
221   VK_keymap[VK_CANCEL] = XBMCK_BREAK;
222   VK_keymap[VK_APPS] = XBMCK_MENU;
223
224   // Only include the multimedia keys if they have been enabled in the
225   // advanced settings
226   if (g_advancedSettings.m_enableMultimediaKeys)
227   {
228     VK_keymap[VK_BROWSER_BACK]        = XBMCK_BROWSER_BACK;
229     VK_keymap[VK_BROWSER_FORWARD]     = XBMCK_BROWSER_FORWARD;
230     VK_keymap[VK_BROWSER_REFRESH]     = XBMCK_BROWSER_REFRESH;
231     VK_keymap[VK_BROWSER_STOP]        = XBMCK_BROWSER_STOP;
232     VK_keymap[VK_BROWSER_SEARCH]      = XBMCK_BROWSER_SEARCH;
233     VK_keymap[VK_BROWSER_FAVORITES]   = XBMCK_BROWSER_FAVORITES;
234     VK_keymap[VK_BROWSER_HOME]        = XBMCK_BROWSER_HOME;
235     VK_keymap[VK_VOLUME_MUTE]         = XBMCK_VOLUME_MUTE;
236     VK_keymap[VK_VOLUME_DOWN]         = XBMCK_VOLUME_DOWN;
237     VK_keymap[VK_VOLUME_UP]           = XBMCK_VOLUME_UP;
238     VK_keymap[VK_MEDIA_NEXT_TRACK]    = XBMCK_MEDIA_NEXT_TRACK;
239     VK_keymap[VK_MEDIA_PREV_TRACK]    = XBMCK_MEDIA_PREV_TRACK;
240     VK_keymap[VK_MEDIA_STOP]          = XBMCK_MEDIA_STOP;
241     VK_keymap[VK_MEDIA_PLAY_PAUSE]    = XBMCK_MEDIA_PLAY_PAUSE;
242     VK_keymap[VK_LAUNCH_MAIL]         = XBMCK_LAUNCH_MAIL;
243     VK_keymap[VK_LAUNCH_MEDIA_SELECT] = XBMCK_LAUNCH_MEDIA_SELECT;
244     VK_keymap[VK_LAUNCH_APP1]         = XBMCK_LAUNCH_APP1;
245     VK_keymap[VK_LAUNCH_APP2]         = XBMCK_LAUNCH_APP2;
246   }
247 }
248
249 static int XBMC_MapVirtualKey(int scancode, int vkey)
250 {
251   int mvke = MapVirtualKeyEx(scancode & 0xFF, 1, NULL);
252
253   switch(vkey)
254   { /* These are always correct */
255     case VK_DIVIDE:
256     case VK_MULTIPLY:
257     case VK_SUBTRACT:
258     case VK_ADD:
259     case VK_LWIN:
260     case VK_RWIN:
261     case VK_APPS:
262     /* These are already handled */
263     case VK_LCONTROL:
264     case VK_RCONTROL:
265     case VK_LSHIFT:
266     case VK_RSHIFT:
267     case VK_LMENU:
268     case VK_RMENU:
269     case VK_SNAPSHOT:
270     case VK_PAUSE:
271     /* Multimedia keys are already handled */
272     case VK_BROWSER_BACK:
273     case VK_BROWSER_FORWARD:
274     case VK_BROWSER_REFRESH:
275     case VK_BROWSER_STOP:
276     case VK_BROWSER_SEARCH:
277     case VK_BROWSER_FAVORITES:
278     case VK_BROWSER_HOME:
279     case VK_VOLUME_MUTE:
280     case VK_VOLUME_DOWN:
281     case VK_VOLUME_UP:
282     case VK_MEDIA_NEXT_TRACK:
283     case VK_MEDIA_PREV_TRACK:
284     case VK_MEDIA_STOP:
285     case VK_MEDIA_PLAY_PAUSE:
286     case VK_LAUNCH_MAIL:
287     case VK_LAUNCH_MEDIA_SELECT:
288     case VK_LAUNCH_APP1:
289     case VK_LAUNCH_APP2:
290       return vkey;
291   }
292   switch(mvke)
293   { /* Distinguish between keypad and extended keys */
294     case VK_INSERT: return EXTKEYPAD(VK_NUMPAD0);
295     case VK_DELETE: return EXTKEYPAD(VK_DECIMAL);
296     case VK_END:    return EXTKEYPAD(VK_NUMPAD1);
297     case VK_DOWN:   return EXTKEYPAD(VK_NUMPAD2);
298     case VK_NEXT:   return EXTKEYPAD(VK_NUMPAD3);
299     case VK_LEFT:   return EXTKEYPAD(VK_NUMPAD4);
300     case VK_CLEAR:  return EXTKEYPAD(VK_NUMPAD5);
301     case VK_RIGHT:  return EXTKEYPAD(VK_NUMPAD6);
302     case VK_HOME:   return EXTKEYPAD(VK_NUMPAD7);
303     case VK_UP:     return EXTKEYPAD(VK_NUMPAD8);
304     case VK_PRIOR:  return EXTKEYPAD(VK_NUMPAD9);
305   }
306   return mvke ? mvke : vkey;
307 }
308
309 static XBMC_keysym *TranslateKey(WPARAM vkey, UINT scancode, XBMC_keysym *keysym, int pressed)
310 { uint16_t mod;
311   uint8_t keystate[256];
312
313   /* Set the keysym information */
314   keysym->scancode = (unsigned char) scancode;
315   keysym->unicode = 0;
316
317   if ((vkey == VK_RETURN) && (scancode & 0x100))
318   {
319     /* No VK_ code for the keypad enter key */
320     keysym->sym = XBMCK_KP_ENTER;
321   }
322   else
323   {
324     keysym->sym = VK_keymap[XBMC_MapVirtualKey(scancode, vkey)];
325   }
326
327   // Attempt to convert the keypress to a UNICODE character
328   GetKeyboardState(keystate);
329
330   if ( pressed && XBMC_TranslateUNICODE )
331   { uint16_t  wchars[2];
332
333     /* Numlock isn't taken into account in ToUnicode,
334     * so we handle it as a special case here */
335     if ((keystate[VK_NUMLOCK] & 1) && vkey >= VK_NUMPAD0 && vkey <= VK_NUMPAD9)
336     {
337       keysym->unicode = vkey - VK_NUMPAD0 + '0';
338     }
339     else if (ToUnicode((UINT)vkey, scancode, keystate, (LPWSTR)wchars, sizeof(wchars)/sizeof(wchars[0]), 0) > 0)
340     {
341       keysym->unicode = wchars[0];
342     }
343   }
344
345   // Set the modifier bitmap
346
347   mod = (uint16_t) XBMCKMOD_NONE;
348
349   // If left control and right alt are down this usually means that
350   // AltGr is down
351   if ((keystate[VK_LCONTROL] & 0x80) && (keystate[VK_RMENU] & 0x80))
352   {
353     mod |= XBMCKMOD_MODE;
354   }
355   else
356   {
357     if (keystate[VK_LCONTROL] & 0x80) mod |= XBMCKMOD_LCTRL;
358     if (keystate[VK_RMENU]    & 0x80) mod |= XBMCKMOD_RALT;
359   }
360
361   // Check the remaining modifiers
362   if (keystate[VK_LSHIFT]   & 0x80) mod |= XBMCKMOD_LSHIFT;
363   if (keystate[VK_RSHIFT]   & 0x80) mod |= XBMCKMOD_RSHIFT;
364   if (keystate[VK_RCONTROL] & 0x80) mod |= XBMCKMOD_RCTRL;
365   if (keystate[VK_LMENU]    & 0x80) mod |= XBMCKMOD_LALT;
366   if (keystate[VK_LWIN]     & 0x80) mod |= XBMCKMOD_LSUPER;
367   if (keystate[VK_RWIN]     & 0x80) mod |= XBMCKMOD_LSUPER;
368   keysym->mod = (XBMCMod) mod;
369
370   // Return the updated keysym
371   return(keysym);
372 }
373
374 void CWinEventsWin32::MessagePush(XBMC_Event *newEvent)
375 {
376   // m_pEventFunc should be set because MessagePush is only executed by
377   // methods called from WndProc()
378   if (m_pEventFunc == NULL)
379     return;
380
381   m_pEventFunc(*newEvent);
382 }
383
384 bool CWinEventsWin32::MessagePump()
385 {
386   MSG  msg;
387   while( PeekMessage( &msg, NULL, 0U, 0U, PM_REMOVE ) )
388   {
389     TranslateMessage( &msg );
390     DispatchMessage( &msg );
391   }
392   return true;
393 }
394
395 size_t CWinEventsWin32::GetQueueSize()
396 {
397   MSG  msg;
398   return PeekMessage( &msg, NULL, 0U, 0U, PM_NOREMOVE );
399 }
400
401 LRESULT CALLBACK CWinEventsWin32::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
402 {
403   XBMC_Event newEvent;
404   ZeroMemory(&newEvent, sizeof(newEvent));
405   static HDEVNOTIFY hDeviceNotify;
406
407   if (uMsg == WM_CREATE)
408   {
409     g_hWnd = hWnd;
410     SetWindowLongPtr(hWnd, GWLP_USERDATA, (LONG)(((LPCREATESTRUCT)lParam)->lpCreateParams));
411     DIB_InitOSKeymap();
412     g_uQueryCancelAutoPlay = RegisterWindowMessage(TEXT("QueryCancelAutoPlay"));
413     shcne.pidl = NULL;
414     shcne.fRecursive = TRUE;
415     long fEvents = SHCNE_DRIVEADD | SHCNE_DRIVEREMOVED | SHCNE_MEDIAREMOVED | SHCNE_MEDIAINSERTED;
416     SHChangeNotifyRegister(hWnd, SHCNRF_ShellLevel | SHCNRF_NewDelivery, fEvents, WM_MEDIA_CHANGE, 1, &shcne);
417     RegisterDeviceInterfaceToHwnd(USB_HID_GUID, hWnd, &hDeviceNotify);
418     return 0;
419   }
420
421   if (uMsg == WM_DESTROY)
422     g_hWnd = NULL;
423
424   m_pEventFunc = (PHANDLE_EVENT_FUNC)GetWindowLongPtr(hWnd, GWLP_USERDATA);
425   if (!m_pEventFunc)
426     return DefWindowProc(hWnd, uMsg, wParam, lParam);
427
428   if(g_uQueryCancelAutoPlay != 0 && uMsg == g_uQueryCancelAutoPlay)
429     return S_FALSE;
430
431   switch (uMsg)
432   {
433     case WM_CLOSE:
434     case WM_QUIT:
435     case WM_DESTROY:
436       if (hDeviceNotify)
437         UnregisterDeviceNotification(hDeviceNotify);
438       newEvent.type = XBMC_QUIT;
439       m_pEventFunc(newEvent);
440       break;
441     case WM_SHOWWINDOW:
442       {
443         bool active = g_application.GetRenderGUI();
444         g_application.SetRenderGUI(wParam != 0);
445         if (g_application.GetRenderGUI() != active)
446           g_Windowing.NotifyAppActiveChange(g_application.GetRenderGUI());
447         CLog::Log(LOGDEBUG, __FUNCTION__"Window is %s", g_application.GetRenderGUI() ? "shown" : "hidden");
448       }
449       break;
450     case WM_ACTIVATE:
451       {
452         if( WA_INACTIVE != wParam )
453           g_Joystick.Reinitialize();
454
455         bool active = g_application.GetRenderGUI();
456         if (HIWORD(wParam))
457         {
458           g_application.SetRenderGUI(false);
459         }
460         else
461         {
462           WINDOWPLACEMENT lpwndpl;
463           lpwndpl.length = sizeof(lpwndpl);
464           if (LOWORD(wParam) != WA_INACTIVE)
465           {
466             if (GetWindowPlacement(hWnd, &lpwndpl))
467               g_application.SetRenderGUI(lpwndpl.showCmd != SW_HIDE);
468           }
469           else
470           {
471             g_application.SetRenderGUI(g_Windowing.WindowedMode());
472           }
473         }
474         if (g_application.GetRenderGUI() != active)
475           g_Windowing.NotifyAppActiveChange(g_application.GetRenderGUI());
476         CLog::Log(LOGDEBUG, __FUNCTION__"Window is %s", g_application.GetRenderGUI() ? "active" : "inactive");
477       }
478       break;
479     case WM_SETFOCUS:
480     case WM_KILLFOCUS:
481       g_application.m_AppFocused = uMsg == WM_SETFOCUS;
482       g_Windowing.NotifyAppFocusChange(g_application.m_AppFocused);
483       if (uMsg == WM_KILLFOCUS)
484       {
485         CStdString procfile;
486         if (CWIN32Util::GetFocussedProcess(procfile))
487           CLog::Log(LOGDEBUG, __FUNCTION__": Focus switched to process %s", procfile.c_str());
488       }
489       break;
490     /* needs to be reviewed after frodo. we reset the system idle timer
491        and the display timer directly now (see m_screenSaverTimer).
492     case WM_SYSCOMMAND:
493       switch( wParam&0xFFF0 )
494       {
495         case SC_MONITORPOWER:
496           if (g_application.m_pPlayer->IsPlaying() || g_application.m_pPlayer->IsPausedPlayback())
497             return 0;
498           else if(CSettings::Get().GetInt("powermanagement.displaysoff") == 0)
499             return 0;
500           break;
501         case SC_SCREENSAVE:
502           return 0;
503       }
504       break;*/
505     case WM_SYSKEYDOWN:
506       switch (wParam)
507       {
508         case VK_F4: //alt-f4, default event quit.
509           return(DefWindowProc(hWnd, uMsg, wParam, lParam));
510         case VK_RETURN: //alt-return
511           if ((lParam & REPEATED_KEYMASK) == 0)
512             g_graphicsContext.ToggleFullScreenRoot();
513           return 0;
514       }
515       //deliberate fallthrough
516     case WM_KEYDOWN:
517     {
518       switch (wParam)
519       {
520         case VK_CONTROL:
521           if ( lParam & EXTENDED_KEYMASK )
522             wParam = VK_RCONTROL;
523           else
524             wParam = VK_LCONTROL;
525           break;
526         case VK_SHIFT:
527           /* EXTENDED trick doesn't work here */
528           if (GetKeyState(VK_LSHIFT) & 0x8000)
529             wParam = VK_LSHIFT;
530           else if (GetKeyState(VK_RSHIFT) & 0x8000)
531             wParam = VK_RSHIFT;
532           break;
533         case VK_MENU:
534           if ( lParam & EXTENDED_KEYMASK )
535             wParam = VK_RMENU;
536           else
537             wParam = VK_LMENU;
538           break;
539       }
540       XBMC_keysym keysym;
541       TranslateKey(wParam, HIWORD(lParam), &keysym, 1);
542
543       newEvent.type = XBMC_KEYDOWN;
544       newEvent.key.keysym = keysym;
545       m_pEventFunc(newEvent);
546     }
547     return(0);
548
549     case WM_SYSKEYUP:
550     case WM_KEYUP:
551       {
552       switch (wParam)
553       {
554         case VK_CONTROL:
555           if ( lParam&EXTENDED_KEYMASK )
556             wParam = VK_RCONTROL;
557           else
558             wParam = VK_LCONTROL;
559           break;
560         case VK_SHIFT:
561           {
562             uint32_t scanCodeL = MapVirtualKey(VK_LSHIFT, MAPVK_VK_TO_VSC);
563             uint32_t scanCodeR = MapVirtualKey(VK_RSHIFT, MAPVK_VK_TO_VSC);
564             uint32_t keyCode = (uint32_t)((lParam & 0xFF0000) >> 16);
565             if (keyCode == scanCodeL)
566               wParam = VK_LSHIFT;
567             else if (keyCode == scanCodeR)
568               wParam = VK_RSHIFT;
569           }
570           break;
571         case VK_MENU:
572           if ( lParam&EXTENDED_KEYMASK )
573             wParam = VK_RMENU;
574           else
575             wParam = VK_LMENU;
576           break;
577       }
578       XBMC_keysym keysym;
579       TranslateKey(wParam, HIWORD(lParam), &keysym, 1);
580
581       if (wParam == VK_SNAPSHOT)
582         newEvent.type = XBMC_KEYDOWN;
583       else
584         newEvent.type = XBMC_KEYUP;
585       newEvent.key.keysym = keysym;
586       m_pEventFunc(newEvent);
587     }
588     return(0);
589     case WM_APPCOMMAND: // MULTIMEDIA keys are mapped to APPCOMMANDS
590     {
591       CLog::Log(LOGDEBUG, "WinEventsWin32.cpp: APPCOMMAND %d", GET_APPCOMMAND_LPARAM(lParam));
592       newEvent.appcommand.type = XBMC_APPCOMMAND;
593       newEvent.appcommand.action = GET_APPCOMMAND_LPARAM(lParam);
594       if (m_pEventFunc(newEvent))
595         return TRUE;
596       else
597         return DefWindowProc(hWnd, uMsg, wParam, lParam);
598     }
599     case WM_GESTURENOTIFY:
600     {
601       OnGestureNotify(hWnd, lParam);
602       return DefWindowProc(hWnd, WM_GESTURENOTIFY, wParam, lParam);
603     }
604     case WM_GESTURE:
605     {
606       OnGesture(hWnd, lParam);
607       return 0;
608     }
609     case WM_SYSCHAR:
610       if (wParam == VK_RETURN) //stop system beep on alt-return
611         return 0;
612       break;
613     case WM_SETCURSOR:
614       if (HTCLIENT != LOWORD(lParam))
615         g_Windowing.ShowOSMouse(true);
616       break;
617     case WM_MOUSEMOVE:
618       newEvent.type = XBMC_MOUSEMOTION;
619       newEvent.motion.x = GET_X_LPARAM(lParam);
620       newEvent.motion.y = GET_Y_LPARAM(lParam);
621       newEvent.motion.state = 0;
622       m_pEventFunc(newEvent);
623       return(0);
624     case WM_LBUTTONDOWN:
625     case WM_MBUTTONDOWN:
626     case WM_RBUTTONDOWN:
627       newEvent.type = XBMC_MOUSEBUTTONDOWN;
628       newEvent.button.state = XBMC_PRESSED;
629       newEvent.button.x = GET_X_LPARAM(lParam);
630       newEvent.button.y = GET_Y_LPARAM(lParam);
631       newEvent.button.button = 0;
632       if (uMsg == WM_LBUTTONDOWN) newEvent.button.button = XBMC_BUTTON_LEFT;
633       else if (uMsg == WM_MBUTTONDOWN) newEvent.button.button = XBMC_BUTTON_MIDDLE;
634       else if (uMsg == WM_RBUTTONDOWN) newEvent.button.button = XBMC_BUTTON_RIGHT;
635       m_pEventFunc(newEvent);
636       return(0);
637     case WM_LBUTTONUP:
638     case WM_MBUTTONUP:
639     case WM_RBUTTONUP:
640       newEvent.type = XBMC_MOUSEBUTTONUP;
641       newEvent.button.state = XBMC_RELEASED;
642       newEvent.button.x = GET_X_LPARAM(lParam);
643       newEvent.button.y = GET_Y_LPARAM(lParam);
644       newEvent.button.button = 0;
645       if (uMsg == WM_LBUTTONUP) newEvent.button.button = XBMC_BUTTON_LEFT;
646       else if (uMsg == WM_MBUTTONUP) newEvent.button.button = XBMC_BUTTON_MIDDLE;
647       else if (uMsg == WM_RBUTTONUP) newEvent.button.button = XBMC_BUTTON_RIGHT;
648       m_pEventFunc(newEvent);
649       return(0);
650     case WM_MOUSEWHEEL:
651       {
652         // SDL, which our events system is based off, sends a MOUSEBUTTONDOWN message
653         // followed by a MOUSEBUTTONUP message.  As this is a momentary event, we just
654         // react on the MOUSEBUTTONUP message, resetting the state after processing.
655         newEvent.type = XBMC_MOUSEBUTTONDOWN;
656         newEvent.button.state = XBMC_PRESSED;
657         // the coordinates in WM_MOUSEWHEEL are screen, not client coordinates
658         POINT point;
659         point.x = GET_X_LPARAM(lParam);
660         point.y = GET_Y_LPARAM(lParam);
661         WindowFromScreenCoords(hWnd, &point);
662         newEvent.button.x = (uint16_t)point.x;
663         newEvent.button.y = (uint16_t)point.y;
664         newEvent.button.button = GET_Y_LPARAM(wParam) > 0 ? XBMC_BUTTON_WHEELUP : XBMC_BUTTON_WHEELDOWN;
665         m_pEventFunc(newEvent);
666         newEvent.type = XBMC_MOUSEBUTTONUP;
667         newEvent.button.state = XBMC_RELEASED;
668         m_pEventFunc(newEvent);
669       }
670       return(0);
671     case WM_SIZE:
672       newEvent.type = XBMC_VIDEORESIZE;
673       newEvent.resize.w = GET_X_LPARAM(lParam);
674       newEvent.resize.h = GET_Y_LPARAM(lParam);
675
676       CLog::Log(LOGDEBUG, __FUNCTION__": window resize event");
677
678       if (!g_Windowing.IsAlteringWindow() && newEvent.resize.w > 0 && newEvent.resize.h > 0)
679         m_pEventFunc(newEvent);
680
681       return(0);
682     case WM_MOVE:
683       newEvent.type = XBMC_VIDEOMOVE;
684       newEvent.move.x = GET_X_LPARAM(lParam);
685       newEvent.move.y = GET_Y_LPARAM(lParam);
686
687       CLog::Log(LOGDEBUG, __FUNCTION__": window move event");
688
689       if (!g_Windowing.IsAlteringWindow())
690         m_pEventFunc(newEvent);
691
692       return(0);
693     case WM_MEDIA_CHANGE:
694       {
695         // This event detects media changes of usb, sd card and optical media.
696         // It only works if the explorer.exe process is started. Because this
697         // isn't the case for all setups we use WM_DEVICECHANGE for usb and 
698         // optical media because this event is also triggered without the 
699         // explorer process. Since WM_DEVICECHANGE doesn't detect sd card changes
700         // we still use this event only for sd.
701         long lEvent;
702         PIDLIST_ABSOLUTE *ppidl;
703         HANDLE hLock = SHChangeNotification_Lock((HANDLE)wParam, (DWORD)lParam, &ppidl, &lEvent);
704
705         if (hLock)
706         {
707           char drivePath[MAX_PATH+1];
708           if (!SHGetPathFromIDList(ppidl[0], drivePath))
709             break;
710
711           switch(lEvent)
712           {
713             case SHCNE_DRIVEADD:
714             case SHCNE_MEDIAINSERTED:
715               if (GetDriveType(drivePath) != DRIVE_CDROM)
716               {
717                 CLog::Log(LOGDEBUG, __FUNCTION__": Drive %s Media has arrived.", drivePath);
718                 CWin32StorageProvider::SetEvent();
719               }
720               break;
721
722             case SHCNE_DRIVEREMOVED:
723             case SHCNE_MEDIAREMOVED:
724               if (GetDriveType(drivePath) != DRIVE_CDROM)
725               {
726                 CLog::Log(LOGDEBUG, __FUNCTION__": Drive %s Media was removed.", drivePath);
727                 CWin32StorageProvider::SetEvent();
728               }
729               break;
730           }
731           SHChangeNotification_Unlock(hLock);
732         }
733         break;
734       }
735     case WM_POWERBROADCAST:
736       if (wParam==PBT_APMSUSPEND)
737       {
738         CLog::Log(LOGDEBUG,"WM_POWERBROADCAST: PBT_APMSUSPEND event was sent");
739         CWin32PowerSyscall::SetOnSuspend();
740       }
741       else if(wParam==PBT_APMRESUMEAUTOMATIC)
742       {
743         CLog::Log(LOGDEBUG,"WM_POWERBROADCAST: PBT_APMRESUMEAUTOMATIC event was sent");
744         CWin32PowerSyscall::SetOnResume();
745       }
746       break;
747     case WM_DEVICECHANGE:
748       {
749         switch(wParam)
750         {
751           case DBT_DEVNODES_CHANGED:
752             g_peripherals.TriggerDeviceScan(PERIPHERAL_BUS_USB);
753             break;
754           case DBT_DEVICEARRIVAL:
755           case DBT_DEVICEREMOVECOMPLETE:
756             if (((_DEV_BROADCAST_HEADER*) lParam)->dbcd_devicetype == DBT_DEVTYP_DEVICEINTERFACE)
757             {
758               g_peripherals.TriggerDeviceScan(PERIPHERAL_BUS_USB);
759               g_Joystick.Reinitialize();
760             }
761             // check if an usb or optical media was inserted or removed
762             if (((_DEV_BROADCAST_HEADER*) lParam)->dbcd_devicetype == DBT_DEVTYP_VOLUME)
763             {
764               PDEV_BROADCAST_VOLUME lpdbv = (PDEV_BROADCAST_VOLUME)((_DEV_BROADCAST_HEADER*) lParam);
765               // optical medium
766               if (lpdbv -> dbcv_flags & DBTF_MEDIA)
767               {
768                 CStdString strdrive;
769                 strdrive.Format("%c:\\", CWIN32Util::FirstDriveFromMask(lpdbv ->dbcv_unitmask));
770                 if(wParam == DBT_DEVICEARRIVAL)
771                 {
772                   CLog::Log(LOGDEBUG, __FUNCTION__": Drive %s Media has arrived.", strdrive.c_str());
773                   CJobManager::GetInstance().AddJob(new CDetectDisc(strdrive, true), NULL);
774                 }
775                 else
776                 {
777                   CLog::Log(LOGDEBUG, __FUNCTION__": Drive %s Media was removed.", strdrive.c_str());
778                   CMediaSource share;
779                   share.strPath = strdrive;
780                   share.strName = share.strPath;
781                   g_mediaManager.RemoveAutoSource(share);
782                 }
783               }
784               else
785                 CWin32StorageProvider::SetEvent();
786             }
787         }
788         break;
789       }
790     case WM_PAINT:
791       //some other app has painted over our window, mark everything as dirty
792       g_windowManager.MarkDirty();
793       break;
794     case BONJOUR_EVENT:
795       CZeroconf::GetInstance()->ProcessResults();
796       break;
797     case BONJOUR_BROWSER_EVENT:
798       CZeroconfBrowser::GetInstance()->ProcessResults();
799       break;
800   }
801   return(DefWindowProc(hWnd, uMsg, wParam, lParam));
802 }
803
804 void CWinEventsWin32::RegisterDeviceInterfaceToHwnd(GUID InterfaceClassGuid, HWND hWnd, HDEVNOTIFY *hDeviceNotify)
805 {
806   DEV_BROADCAST_DEVICEINTERFACE NotificationFilter;
807
808   ZeroMemory( &NotificationFilter, sizeof(NotificationFilter) );
809   NotificationFilter.dbcc_size = sizeof(DEV_BROADCAST_DEVICEINTERFACE);
810   NotificationFilter.dbcc_devicetype = DBT_DEVTYP_DEVICEINTERFACE;
811   NotificationFilter.dbcc_classguid = InterfaceClassGuid;
812
813   *hDeviceNotify = RegisterDeviceNotification(
814       hWnd,                       // events recipient
815       &NotificationFilter,        // type of device
816       DEVICE_NOTIFY_WINDOW_HANDLE // type of recipient handle
817       );
818 }
819
820 void CWinEventsWin32::WindowFromScreenCoords(HWND hWnd, POINT *point)
821 {
822   if (!point) return;
823   RECT clientRect;
824   GetClientRect(hWnd, &clientRect);
825   POINT windowPos;
826   windowPos.x = clientRect.left;
827   windowPos.y = clientRect.top;
828   ClientToScreen(hWnd, &windowPos);
829   point->x -= windowPos.x;
830   point->y -= windowPos.y;
831 }
832
833 void CWinEventsWin32::OnGestureNotify(HWND hWnd, LPARAM lParam)
834 {
835   // convert to window coordinates
836   PGESTURENOTIFYSTRUCT gn = (PGESTURENOTIFYSTRUCT)(lParam);
837   POINT point = { gn->ptsLocation.x, gn->ptsLocation.y };
838   WindowFromScreenCoords(hWnd, &point);
839
840   // by default we only want twofingertap and pressandtap gestures
841   // the other gestures are enabled best on supported gestures
842   GESTURECONFIG gc[] = {{ GID_ZOOM, 0, GC_ZOOM},
843                         { GID_ROTATE, 0, GC_ROTATE},
844                         { GID_PAN, 0, GC_PAN},
845                         { GID_TWOFINGERTAP, GC_TWOFINGERTAP, GC_TWOFINGERTAP },
846                         { GID_PRESSANDTAP, GC_PRESSANDTAP, GC_PRESSANDTAP }};
847
848   // send a message to see if a control wants any
849   int gestures = 0;
850   if ((gestures = CGenericTouchActionHandler::Get().QuerySupportedGestures((float)point.x, (float)point.y)) != EVENT_RESULT_UNHANDLED)
851   {
852     if (gestures & EVENT_RESULT_ZOOM)
853       gc[0].dwWant |= GC_ZOOM;
854     if (gestures & EVENT_RESULT_ROTATE)
855       gc[1].dwWant |= GC_ROTATE;
856     if (gestures & EVENT_RESULT_PAN_VERTICAL)
857       gc[2].dwWant |= GC_PAN | GC_PAN_WITH_SINGLE_FINGER_VERTICALLY | GC_PAN_WITH_GUTTER | GC_PAN_WITH_INERTIA;
858     if (gestures & EVENT_RESULT_PAN_VERTICAL_WITHOUT_INERTIA)
859       gc[2].dwWant |= GC_PAN | GC_PAN_WITH_SINGLE_FINGER_VERTICALLY;
860     if (gestures & EVENT_RESULT_PAN_HORIZONTAL)
861       gc[2].dwWant |= GC_PAN | GC_PAN_WITH_SINGLE_FINGER_HORIZONTALLY | GC_PAN_WITH_GUTTER | GC_PAN_WITH_INERTIA;
862     if (gestures & EVENT_RESULT_PAN_HORIZONTAL_WITHOUT_INERTIA)
863       gc[2].dwWant |= GC_PAN | GC_PAN_WITH_SINGLE_FINGER_HORIZONTALLY;
864     if (gestures & EVENT_RESULT_SWIPE)
865     {
866       gc[2].dwWant |= GC_PAN | GC_PAN_WITH_SINGLE_FINGER_VERTICALLY | GC_PAN_WITH_SINGLE_FINGER_HORIZONTALLY | GC_PAN_WITH_GUTTER;
867
868       // create a new touch swipe detector
869       m_touchSwipeDetector = new CGenericTouchSwipeDetector(&CGenericTouchActionHandler::Get(), 160.0f);
870     }
871
872     gc[0].dwBlock = gc[0].dwWant ^ 0x01;
873     gc[1].dwBlock = gc[1].dwWant ^ 0x01;
874     gc[2].dwBlock = gc[2].dwWant ^ 0x1F;
875   }
876   if (g_Windowing.PtrSetGestureConfig)
877     g_Windowing.PtrSetGestureConfig(hWnd, 0, 5, gc, sizeof(GESTURECONFIG));
878 }
879
880 void CWinEventsWin32::OnGesture(HWND hWnd, LPARAM lParam)
881 {
882   if (!g_Windowing.PtrGetGestureInfo)
883     return;
884
885   GESTUREINFO gi = {0};
886   gi.cbSize = sizeof(gi);
887   g_Windowing.PtrGetGestureInfo((HGESTUREINFO)lParam, &gi);
888
889   // convert to window coordinates
890   POINT point = { gi.ptsLocation.x, gi.ptsLocation.y };
891   WindowFromScreenCoords(hWnd, &point);
892
893   if (gi.dwID == GID_BEGIN)
894     m_touchPointer.reset();
895
896   // if there's a "current" touch from a previous event, copy it to "last"
897   if (m_touchPointer.current.valid())
898     m_touchPointer.last = m_touchPointer.current;
899
900   // set the "current" touch
901   m_touchPointer.current.x = (float)point.x;
902   m_touchPointer.current.y = (float)point.y;
903   m_touchPointer.current.time = time(NULL);
904
905   switch (gi.dwID)
906   {
907   case GID_BEGIN:
908     {
909       // set the "down" touch
910       m_touchPointer.down = m_touchPointer.current;
911       m_originalZoomDistance = 0;
912
913       CGenericTouchActionHandler::Get().OnTouchGestureStart((float)point.x, (float)point.y);
914     }
915     break;
916
917   case GID_END:
918     CGenericTouchActionHandler::Get().OnTouchGestureEnd((float)point.x, (float)point.y, 0.0f, 0.0f, 0.0f, 0.0f);
919     break;
920
921   case GID_PAN:
922     {
923       if (!m_touchPointer.moving)
924         m_touchPointer.moving = true;
925
926       // calculate the velocity of the pan gesture
927       float velocityX, velocityY;
928       m_touchPointer.velocity(velocityX, velocityY);
929
930       CGenericTouchActionHandler::Get().OnTouchGesturePan(m_touchPointer.current.x, m_touchPointer.current.y,
931                                                           m_touchPointer.current.x - m_touchPointer.last.x, m_touchPointer.current.y - m_touchPointer.last.y,
932                                                           velocityX, velocityY);
933
934       if (m_touchSwipeDetector != NULL)
935       {
936         if (gi.dwFlags & GF_BEGIN)
937         {
938           m_touchPointer.down = m_touchPointer.current;
939           m_touchSwipeDetector->OnTouchDown(0, m_touchPointer);
940         }
941         else if (gi.dwFlags & GF_END)
942         {
943           m_touchSwipeDetector->OnTouchUp(0, m_touchPointer);
944
945           delete m_touchSwipeDetector;
946           m_touchSwipeDetector = NULL;
947         }
948         else
949           m_touchSwipeDetector->OnTouchMove(0, m_touchPointer);
950       }
951     }
952     break;
953
954   case GID_ROTATE:
955     {
956       if (gi.dwFlags == GF_BEGIN)
957         break;
958
959       CGenericTouchActionHandler::Get().OnRotate((float)point.x, (float)point.y,
960                                                  -(float)ROTATE_ANGLE_DEGREE(gi.ullArguments));
961     }
962     break;
963
964   case GID_ZOOM:
965     {
966       if (gi.dwFlags == GF_BEGIN)
967       {
968         m_originalZoomDistance = (int)LODWORD(gi.ullArguments);
969         break;
970       }
971
972       // avoid division by 0
973       if (m_originalZoomDistance == 0)
974         break;
975
976       CGenericTouchActionHandler::Get().OnZoomPinch((float)point.x, (float)point.y,
977                                                     (float)LODWORD(gi.ullArguments) / (float)m_originalZoomDistance);
978     }
979     break;
980
981   case GID_TWOFINGERTAP:
982     CGenericTouchActionHandler::Get().OnTap((float)point.x, (float)point.y, 2);
983     break;
984
985   case GID_PRESSANDTAP:
986   default:
987     // You have encountered an unknown gesture
988     break;
989   }
990   if(g_Windowing.PtrCloseGestureInfoHandle)
991     g_Windowing.PtrCloseGestureInfoHandle((HGESTUREINFO)lParam);
992 }
993
994 #endif