2 * Copyright (C) 2005-2013 Team XBMC
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)
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.
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/>.
22 #ifdef HAS_SDL_WIN_EVENTS
24 #include "WinEvents.h"
25 #include "WinEventsSDL.h"
26 #include "Application.h"
27 #include "ApplicationMessenger.h"
28 #include "GUIUserMessages.h"
29 #include "settings/DisplaySettings.h"
30 #include "guilib/GUIWindowManager.h"
31 #include "guilib/Key.h"
32 #ifdef HAS_SDL_JOYSTICK
33 #include "input/SDLJoystick.h"
35 #include "input/MouseStat.h"
36 #include "WindowingFactory.h"
37 #if defined(TARGET_DARWIN)
38 #include "osx/CocoaInterface.h"
41 #if defined(TARGET_POSIX) && !defined(TARGET_DARWIN) && !defined(TARGET_ANDROID)
43 #include <X11/XKBlib.h>
44 #include "input/XBMC_keysym.h"
45 #include "utils/log.h"
48 #if defined(TARGET_POSIX) && !defined(TARGET_DARWIN)
49 // The following chunk of code is Linux specific. For keys that have
50 // with keysym.sym set to zero it checks the scan code, and sets the sym
51 // for some known scan codes. This is mostly the multimedia keys.
52 // Note that the scan code to sym mapping is different with and without
53 // the evdev driver so we need to check if evdev is loaded.
55 // m_bEvdev == true if evdev is loaded
56 static bool m_bEvdev = true;
57 // We need to initialise some local storage once. Set m_bEvdevInit to true
58 // once the initialisation has been done
59 static bool m_bEvdevInit = false;
61 // Mappings of scancodes to syms for the evdev driver
62 static uint16_t SymMappingsEvdev[][2] =
63 { { 121, XBMCK_VOLUME_MUTE } // Volume mute
64 , { 122, XBMCK_VOLUME_DOWN } // Volume down
65 , { 123, XBMCK_VOLUME_UP } // Volume up
66 , { 124, XBMCK_POWER } // Power button on PC case
67 , { 127, XBMCK_SPACE } // Pause
68 , { 135, XBMCK_MENU } // Right click
69 , { 136, XBMCK_MEDIA_STOP } // Stop
70 , { 138, 0x69 /* 'i' */} // Info
71 , { 147, 0x6d /* 'm' */} // Menu
72 , { 148, XBMCK_LAUNCH_APP2 } // Launch app 2
73 , { 150, XBMCK_SLEEP } // Sleep
74 , { 152, XBMCK_LAUNCH_APP1 } // Launch app 1
75 , { 163, XBMCK_LAUNCH_MAIL } // Launch Mail
76 , { 164, XBMCK_BROWSER_FAVORITES } // Browser favorites
77 , { 166, XBMCK_BROWSER_BACK } // Back
78 , { 167, XBMCK_BROWSER_FORWARD } // Browser forward
79 , { 171, XBMCK_MEDIA_NEXT_TRACK } // Next track
80 , { 172, XBMCK_MEDIA_PLAY_PAUSE } // Play_Pause
81 , { 173, XBMCK_MEDIA_PREV_TRACK } // Prev track
82 , { 174, XBMCK_MEDIA_STOP } // Stop
83 , { 176, 0x72 /* 'r' */} // Rewind
84 , { 179, XBMCK_LAUNCH_MEDIA_SELECT } // Launch media select
85 , { 180, XBMCK_BROWSER_HOME } // Browser home
86 , { 181, XBMCK_BROWSER_REFRESH } // Browser refresh
87 , { 214, XBMCK_ESCAPE } // Close
88 , { 215, XBMCK_MEDIA_PLAY_PAUSE } // Play_Pause
89 , { 216, 0x66 /* 'f' */} // Forward
90 //, {167, 0xb3 } // Record
93 // The non-evdev mappings need to be checked. At the moment XBMC will never
95 static uint16_t SymMappingsUbuntu[][2] =
96 { { 0xf5, XBMCK_F13 } // F13 Launch help browser
97 , { 0x87, XBMCK_F14 } // F14 undo
98 , { 0x8a, XBMCK_F15 } // F15 redo
99 , { 0x89, 0x7f } // F16 new
100 , { 0xbf, 0x80 } // F17 open
101 , { 0xaf, 0x81 } // F18 close
102 , { 0xe4, 0x82 } // F19 reply
103 , { 0x8e, 0x83 } // F20 forward
104 , { 0xda, 0x84 } // F21 send
105 //, { 0x, 0x85 } // F22 check spell (doesn't work for me with ubuntu)
106 , { 0xd5, 0x86 } // F23 save
107 , { 0xb9, 0x87 } // 0x2a?? F24 print
108 // end of special keys above F1 till F12
110 , { 234, XBMCK_BROWSER_BACK } // Browser back
111 , { 233, XBMCK_BROWSER_FORWARD } // Browser forward
112 , { 231, XBMCK_BROWSER_REFRESH } // Browser refresh
113 //, { , XBMCK_BROWSER_STOP } // Browser stop
114 , { 122, XBMCK_BROWSER_SEARCH } // Browser search
115 , { 0xe5, XBMCK_BROWSER_SEARCH } // Browser search
116 , { 230, XBMCK_BROWSER_FAVORITES } // Browser favorites
117 , { 130, XBMCK_BROWSER_HOME } // Browser home
118 , { 0xa0, XBMCK_VOLUME_MUTE } // Volume mute
119 , { 0xae, XBMCK_VOLUME_DOWN } // Volume down
120 , { 0xb0, XBMCK_VOLUME_UP } // Volume up
121 , { 0x99, XBMCK_MEDIA_NEXT_TRACK } // Next track
122 , { 0x90, XBMCK_MEDIA_PREV_TRACK } // Prev track
123 , { 0xa4, XBMCK_MEDIA_STOP } // Stop
124 , { 0xa2, XBMCK_MEDIA_PLAY_PAUSE } // Play_Pause
125 , { 0xec, XBMCK_LAUNCH_MAIL } // Launch mail
126 , { 129, XBMCK_LAUNCH_MEDIA_SELECT } // Launch media_select
127 , { 198, XBMCK_LAUNCH_APP1 } // Launch App1/PC icon
128 , { 0xa1, XBMCK_LAUNCH_APP2 } // Launch App2/Calculator
129 , { 34, 0x3b /* vkey 0xba */} // OEM 1: [ on us keyboard
130 , { 51, 0x2f /* vkey 0xbf */} // OEM 2: additional key on european keyboards between enter and ' on us keyboards
131 , { 47, 0x60 /* vkey 0xc0 */} // OEM 3: ; on us keyboards
132 , { 20, 0xdb } // OEM 4: - on us keyboards (between 0 and =)
133 , { 49, 0xdc } // OEM 5: ` on us keyboards (below ESC)
134 , { 21, 0xdd } // OEM 6: =??? on us keyboards (between - and backspace)
135 , { 48, 0xde } // OEM 7: ' on us keyboards (on right side of ;)
136 //, { 0, 0xdf } // OEM 8
137 , { 94, 0xe2 } // OEM 102: additional key on european keyboards between left shift and z on us keyboards
138 //, { 0xb2, 0x } // Ubuntu default setting for launch browser
139 //, { 0x76, 0x } // Ubuntu default setting for launch music player
140 //, { 0xcc, 0x } // Ubuntu default setting for eject
141 , { 117, 0x5d } // right click
144 // Called once. Checks whether evdev is loaded and sets m_bEvdev accordingly.
145 static void InitEvdev(void)
147 // Set m_bEvdevInit to indicate we have been initialised
150 Display* dpy = XOpenDisplay(NULL);
153 CLog::Log(LOGERROR, "CWinEventsSDL::CWinEventsSDL - XOpenDisplay failed");
160 desc = XkbGetKeyboard(dpy, XkbAllComponentsMask, XkbUseCoreKbd);
164 CLog::Log(LOGERROR, "CWinEventsSDL::CWinEventsSDL - XkbGetKeyboard failed");
168 symbols = XGetAtomName(dpy, desc->names->symbols);
171 CLog::Log(LOGDEBUG, "CWinEventsSDL::CWinEventsSDL - XKb symbols %s", symbols);
172 if(strstr(symbols, "(evdev)"))
179 XkbFreeKeyboard(desc, XkbAllComponentsMask, True);
182 CLog::Log(LOGDEBUG, "CWinEventsSDL::CWinEventsSDL - m_bEvdev = %d", m_bEvdev);
185 // Check the scan code and return the matching sym, or zero if the scan code
187 static uint16_t SymFromScancode(uint16_t scancode)
191 // We need to initialise m_bEvdev once
195 // If evdev is loaded look up the scan code in SymMappingsEvdev
198 for (i = 0; i < sizeof(SymMappingsEvdev)/4; i++)
199 if (scancode == SymMappingsEvdev[i][0])
200 return SymMappingsEvdev[i][1];
203 // If evdev is not loaded look up the scan code in SymMappingsUbuntu
206 for (i = 0; i < sizeof(SymMappingsUbuntu)/4; i++)
207 if (scancode == SymMappingsUbuntu[i][0])
208 return SymMappingsUbuntu[i][1];
211 // Scan code wasn't found, return zero
214 #endif // End of checks for keysym.sym == 0
216 bool CWinEventsSDL::MessagePump()
221 while (SDL_PollEvent(&event))
226 if (!g_application.m_bStop)
227 CApplicationMessenger::Get().Quit();
230 #ifdef HAS_SDL_JOYSTICK
231 case SDL_JOYBUTTONUP:
232 case SDL_JOYBUTTONDOWN:
233 case SDL_JOYAXISMOTION:
234 case SDL_JOYBALLMOTION:
235 case SDL_JOYHATMOTION:
236 g_Joystick.Update(event);
241 case SDL_ACTIVEEVENT:
242 //If the window was inconified or restored
243 if( event.active.state & SDL_APPACTIVE )
245 g_application.SetRenderGUI(event.active.gain != 0);
246 g_Windowing.NotifyAppActiveChange(g_application.GetRenderGUI());
248 else if (event.active.state & SDL_APPINPUTFOCUS)
250 g_application.m_AppFocused = event.active.gain != 0;
251 g_Windowing.NotifyAppFocusChange(g_application.m_AppFocused);
257 // process any platform specific shortcuts before handing off to XBMC
258 #ifdef TARGET_DARWIN_OSX
259 if (ProcessOSXShortcuts(event))
267 newEvent.type = XBMC_KEYDOWN;
268 newEvent.key.keysym.scancode = event.key.keysym.scancode;
269 newEvent.key.keysym.sym = (XBMCKey) event.key.keysym.sym;
270 newEvent.key.keysym.unicode = event.key.keysym.unicode;
271 newEvent.key.state = event.key.state;
272 newEvent.key.type = event.key.type;
273 newEvent.key.which = event.key.which;
275 // Check if the Windows keys are down because SDL doesn't flag this.
276 uint16_t mod = event.key.keysym.mod;
277 uint8_t* keystate = SDL_GetKeyState(NULL);
278 if (keystate[SDLK_LSUPER] || keystate[SDLK_RSUPER])
279 mod |= XBMCKMOD_LSUPER;
280 newEvent.key.keysym.mod = (XBMCMod) mod;
282 #if defined(TARGET_POSIX) && !defined(TARGET_DARWIN)
283 // If the keysym.sym is zero try to get it from the scan code
284 if (newEvent.key.keysym.sym == 0)
285 newEvent.key.keysym.sym = (XBMCKey) SymFromScancode(newEvent.key.keysym.scancode);
288 // don't handle any more messages in the queue until we've handled keydown,
289 // if a keyup is in the queue it will reset the keypress before it is handled.
290 ret |= g_application.OnEvent(newEvent);
297 newEvent.type = XBMC_KEYUP;
298 newEvent.key.keysym.scancode = event.key.keysym.scancode;
299 newEvent.key.keysym.sym = (XBMCKey) event.key.keysym.sym;
300 newEvent.key.keysym.mod =(XBMCMod) event.key.keysym.mod;
301 newEvent.key.keysym.unicode = event.key.keysym.unicode;
302 newEvent.key.state = event.key.state;
303 newEvent.key.type = event.key.type;
304 newEvent.key.which = event.key.which;
306 ret |= g_application.OnEvent(newEvent);
310 case SDL_MOUSEBUTTONDOWN:
313 newEvent.type = XBMC_MOUSEBUTTONDOWN;
314 newEvent.button.button = event.button.button;
315 newEvent.button.state = event.button.state;
316 newEvent.button.type = event.button.type;
317 newEvent.button.which = event.button.which;
318 newEvent.button.x = event.button.x;
319 newEvent.button.y = event.button.y;
321 ret |= g_application.OnEvent(newEvent);
325 case SDL_MOUSEBUTTONUP:
328 newEvent.type = XBMC_MOUSEBUTTONUP;
329 newEvent.button.button = event.button.button;
330 newEvent.button.state = event.button.state;
331 newEvent.button.type = event.button.type;
332 newEvent.button.which = event.button.which;
333 newEvent.button.x = event.button.x;
334 newEvent.button.y = event.button.y;
336 ret |= g_application.OnEvent(newEvent);
340 case SDL_MOUSEMOTION:
342 if (0 == (SDL_GetAppState() & SDL_APPMOUSEFOCUS))
344 g_Mouse.SetActive(false);
345 #if defined(TARGET_DARWIN_OSX)
346 // See CApplication::ProcessSlow() for a description as to why we call Cocoa_HideMouse.
347 // this is here to restore the pointer when toggling back to window mode from fullscreen.
353 newEvent.type = XBMC_MOUSEMOTION;
354 newEvent.motion.xrel = event.motion.xrel;
355 newEvent.motion.yrel = event.motion.yrel;
356 newEvent.motion.state = event.motion.state;
357 newEvent.motion.type = event.motion.type;
358 newEvent.motion.which = event.motion.which;
359 newEvent.motion.x = event.motion.x;
360 newEvent.motion.y = event.motion.y;
362 ret |= g_application.OnEvent(newEvent);
365 case SDL_VIDEORESIZE:
367 // Under linux returning from fullscreen, SDL sends an extra event to resize to the desktop
368 // resolution causing the previous window dimensions to be lost. This is needed to rectify
370 if(!g_Windowing.IsFullScreen())
372 int RES_SCREEN = g_Windowing.DesktopResolution(g_Windowing.GetCurrentScreen());
373 if((event.resize.w == CDisplaySettings::Get().GetResolutionInfo(RES_SCREEN).iWidth) &&
374 (event.resize.h == CDisplaySettings::Get().GetResolutionInfo(RES_SCREEN).iHeight))
378 newEvent.type = XBMC_VIDEORESIZE;
379 newEvent.resize.w = event.resize.w;
380 newEvent.resize.h = event.resize.h;
381 ret |= g_application.OnEvent(newEvent);
382 g_windowManager.MarkDirty();
388 newEvent.type = XBMC_USEREVENT;
389 newEvent.user.code = event.user.code;
390 ret |= g_application.OnEvent(newEvent);
393 case SDL_VIDEOEXPOSE:
394 g_windowManager.MarkDirty();
397 memset(&event, 0, sizeof(SDL_Event));
403 size_t CWinEventsSDL::GetQueueSize()
408 if (-1 == (ret = SDL_PeepEvents(&event, 0, SDL_PEEKEVENT, ~0)))
414 #ifdef TARGET_DARWIN_OSX
415 bool CWinEventsSDL::ProcessOSXShortcuts(SDL_Event& event)
417 static bool shift = false, cmd = false;
419 cmd = !!(SDL_GetModState() & (KMOD_LMETA | KMOD_RMETA ));
420 shift = !!(SDL_GetModState() & (KMOD_LSHIFT | KMOD_RSHIFT));
422 if (cmd && event.key.type == SDL_KEYDOWN)
424 switch(event.key.keysym.sym)
426 case SDLK_q: // CMD-q to quit
427 if (!g_application.m_bStop)
428 CApplicationMessenger::Get().Quit();
431 case SDLK_f: // CMD-f to toggle fullscreen
432 g_application.OnAction(CAction(ACTION_TOGGLE_FULLSCREEN));
435 case SDLK_s: // CMD-3 to take a screenshot
436 g_application.OnAction(CAction(ACTION_TAKE_SCREENSHOT));
439 case SDLK_h: // CMD-h to hide
443 case SDLK_m: // CMD-m to minimize
444 CApplicationMessenger::Get().Minimize();
447 case SDLK_v: // CMD-v to paste clipboard text
448 if (g_Windowing.IsTextInputEnabled())
450 const char *szStr = Cocoa_Paste();
453 CGUIMessage msg(GUI_MSG_INPUT_TEXT, 0, 0);
455 g_windowManager.SendMessage(msg, g_windowManager.GetFocusedWindow());
468 #elif defined(TARGET_POSIX)
470 bool CWinEventsSDL::ProcessLinuxShortcuts(SDL_Event& event)
474 alt = !!(SDL_GetModState() & (XBMCKMOD_LALT | XBMCKMOD_RALT));
476 if (alt && event.key.type == SDL_KEYDOWN)
478 switch(event.key.keysym.sym)
480 case SDLK_TAB: // ALT+TAB to minimize/hide
481 g_application.Minimize();