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