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 #include "interfaces/Builtins.h"
23 #include "ButtonTranslator.h"
24 #include "profiles/ProfilesManager.h"
25 #include "utils/URIUtils.h"
26 #include "guilib/Key.h"
27 #include "guilib/WindowIDs.h"
28 #include "input/XBMC_keysym.h"
29 #include "input/XBMC_keytable.h"
30 #include "filesystem/File.h"
31 #include "filesystem/Directory.h"
33 #include "utils/StringUtils.h"
34 #include "utils/log.h"
35 #include "utils/XBMCTinyXML.h"
36 #include "XBIRRemote.h"
38 #if defined(TARGET_WINDOWS)
39 #include "input/windows/WINJoystick.h"
40 #elif defined(HAS_SDL_JOYSTICK) || defined(HAS_EVENT_SERVER)
41 #include "SDLJoystick.h"
45 using namespace XFILE;
59 static const ActionMapping actions[] =
61 {"left" , ACTION_MOVE_LEFT },
62 {"right" , ACTION_MOVE_RIGHT},
63 {"up" , ACTION_MOVE_UP },
64 {"down" , ACTION_MOVE_DOWN },
65 {"pageup" , ACTION_PAGE_UP },
66 {"pagedown" , ACTION_PAGE_DOWN},
67 {"select" , ACTION_SELECT_ITEM},
68 {"highlight" , ACTION_HIGHLIGHT_ITEM},
69 {"parentdir" , ACTION_NAV_BACK}, // backward compatibility
70 {"parentfolder" , ACTION_PARENT_DIR},
71 {"back" , ACTION_NAV_BACK},
72 {"previousmenu" , ACTION_PREVIOUS_MENU},
73 {"info" , ACTION_SHOW_INFO},
74 {"pause" , ACTION_PAUSE},
75 {"stop" , ACTION_STOP},
76 {"skipnext" , ACTION_NEXT_ITEM},
77 {"skipprevious" , ACTION_PREV_ITEM},
78 {"fullscreen" , ACTION_SHOW_GUI},
79 {"aspectratio" , ACTION_ASPECT_RATIO},
80 {"stepforward" , ACTION_STEP_FORWARD},
81 {"stepback" , ACTION_STEP_BACK},
82 {"bigstepforward" , ACTION_BIG_STEP_FORWARD},
83 {"bigstepback" , ACTION_BIG_STEP_BACK},
84 {"osd" , ACTION_SHOW_OSD},
85 {"showsubtitles" , ACTION_SHOW_SUBTITLES},
86 {"nextsubtitle" , ACTION_NEXT_SUBTITLE},
87 {"codecinfo" , ACTION_SHOW_CODEC},
88 {"nextpicture" , ACTION_NEXT_PICTURE},
89 {"previouspicture" , ACTION_PREV_PICTURE},
90 {"zoomout" , ACTION_ZOOM_OUT},
91 {"zoomin" , ACTION_ZOOM_IN},
92 {"playlist" , ACTION_SHOW_PLAYLIST},
93 {"queue" , ACTION_QUEUE_ITEM},
94 {"zoomnormal" , ACTION_ZOOM_LEVEL_NORMAL},
95 {"zoomlevel1" , ACTION_ZOOM_LEVEL_1},
96 {"zoomlevel2" , ACTION_ZOOM_LEVEL_2},
97 {"zoomlevel3" , ACTION_ZOOM_LEVEL_3},
98 {"zoomlevel4" , ACTION_ZOOM_LEVEL_4},
99 {"zoomlevel5" , ACTION_ZOOM_LEVEL_5},
100 {"zoomlevel6" , ACTION_ZOOM_LEVEL_6},
101 {"zoomlevel7" , ACTION_ZOOM_LEVEL_7},
102 {"zoomlevel8" , ACTION_ZOOM_LEVEL_8},
103 {"zoomlevel9" , ACTION_ZOOM_LEVEL_9},
104 {"nextcalibration" , ACTION_CALIBRATE_SWAP_ARROWS},
105 {"resetcalibration" , ACTION_CALIBRATE_RESET},
106 {"analogmove" , ACTION_ANALOG_MOVE},
107 {"rotate" , ACTION_ROTATE_PICTURE_CW},
108 {"rotateccw" , ACTION_ROTATE_PICTURE_CCW},
109 {"close" , ACTION_NAV_BACK}, // backwards compatibility
110 {"subtitledelayminus", ACTION_SUBTITLE_DELAY_MIN},
111 {"subtitledelay" , ACTION_SUBTITLE_DELAY},
112 {"subtitledelayplus" , ACTION_SUBTITLE_DELAY_PLUS},
113 {"audiodelayminus" , ACTION_AUDIO_DELAY_MIN},
114 {"audiodelay" , ACTION_AUDIO_DELAY},
115 {"audiodelayplus" , ACTION_AUDIO_DELAY_PLUS},
116 {"subtitleshiftup" , ACTION_SUBTITLE_VSHIFT_UP},
117 {"subtitleshiftdown" , ACTION_SUBTITLE_VSHIFT_DOWN},
118 {"subtitlealign" , ACTION_SUBTITLE_ALIGN},
119 {"audionextlanguage" , ACTION_AUDIO_NEXT_LANGUAGE},
120 {"verticalshiftup" , ACTION_VSHIFT_UP},
121 {"verticalshiftdown" , ACTION_VSHIFT_DOWN},
122 {"nextresolution" , ACTION_CHANGE_RESOLUTION},
123 {"audiotoggledigital", ACTION_TOGGLE_DIGITAL_ANALOG},
124 {"number0" , REMOTE_0},
125 {"number1" , REMOTE_1},
126 {"number2" , REMOTE_2},
127 {"number3" , REMOTE_3},
128 {"number4" , REMOTE_4},
129 {"number5" , REMOTE_5},
130 {"number6" , REMOTE_6},
131 {"number7" , REMOTE_7},
132 {"number8" , REMOTE_8},
133 {"number9" , REMOTE_9},
134 {"osdleft" , ACTION_OSD_SHOW_LEFT},
135 {"osdright" , ACTION_OSD_SHOW_RIGHT},
136 {"osdup" , ACTION_OSD_SHOW_UP},
137 {"osddown" , ACTION_OSD_SHOW_DOWN},
138 {"osdselect" , ACTION_OSD_SHOW_SELECT},
139 {"osdvalueplus" , ACTION_OSD_SHOW_VALUE_PLUS},
140 {"osdvalueminus" , ACTION_OSD_SHOW_VALUE_MIN},
141 {"smallstepback" , ACTION_SMALL_STEP_BACK},
142 {"fastforward" , ACTION_PLAYER_FORWARD},
143 {"rewind" , ACTION_PLAYER_REWIND},
144 {"play" , ACTION_PLAYER_PLAY},
145 {"playpause" , ACTION_PLAYER_PLAYPAUSE},
146 {"switchplayer" , ACTION_SWITCH_PLAYER},
147 {"delete" , ACTION_DELETE_ITEM},
148 {"copy" , ACTION_COPY_ITEM},
149 {"move" , ACTION_MOVE_ITEM},
150 {"mplayerosd" , ACTION_SHOW_MPLAYER_OSD},
151 {"hidesubmenu" , ACTION_OSD_HIDESUBMENU},
152 {"screenshot" , ACTION_TAKE_SCREENSHOT},
153 {"rename" , ACTION_RENAME_ITEM},
154 {"togglewatched" , ACTION_TOGGLE_WATCHED},
155 {"scanitem" , ACTION_SCAN_ITEM},
156 {"reloadkeymaps" , ACTION_RELOAD_KEYMAPS},
157 {"volumeup" , ACTION_VOLUME_UP},
158 {"volumedown" , ACTION_VOLUME_DOWN},
159 {"mute" , ACTION_MUTE},
160 {"backspace" , ACTION_BACKSPACE},
161 {"scrollup" , ACTION_SCROLL_UP},
162 {"scrolldown" , ACTION_SCROLL_DOWN},
163 {"analogfastforward" , ACTION_ANALOG_FORWARD},
164 {"analogrewind" , ACTION_ANALOG_REWIND},
165 {"moveitemup" , ACTION_MOVE_ITEM_UP},
166 {"moveitemdown" , ACTION_MOVE_ITEM_DOWN},
167 {"contextmenu" , ACTION_CONTEXT_MENU},
168 {"shift" , ACTION_SHIFT},
169 {"symbols" , ACTION_SYMBOLS},
170 {"cursorleft" , ACTION_CURSOR_LEFT},
171 {"cursorright" , ACTION_CURSOR_RIGHT},
172 {"showtime" , ACTION_SHOW_OSD_TIME},
173 {"analogseekforward" , ACTION_ANALOG_SEEK_FORWARD},
174 {"analogseekback" , ACTION_ANALOG_SEEK_BACK},
175 {"showpreset" , ACTION_VIS_PRESET_SHOW},
176 {"presetlist" , ACTION_VIS_PRESET_LIST},
177 {"nextpreset" , ACTION_VIS_PRESET_NEXT},
178 {"previouspreset" , ACTION_VIS_PRESET_PREV},
179 {"lockpreset" , ACTION_VIS_PRESET_LOCK},
180 {"randompreset" , ACTION_VIS_PRESET_RANDOM},
181 {"increasevisrating" , ACTION_VIS_RATE_PRESET_PLUS},
182 {"decreasevisrating" , ACTION_VIS_RATE_PRESET_MINUS},
183 {"showvideomenu" , ACTION_SHOW_VIDEOMENU},
184 {"enter" , ACTION_ENTER},
185 {"increaserating" , ACTION_INCREASE_RATING},
186 {"decreaserating" , ACTION_DECREASE_RATING},
187 {"togglefullscreen" , ACTION_TOGGLE_FULLSCREEN},
188 {"nextscene" , ACTION_NEXT_SCENE},
189 {"previousscene" , ACTION_PREV_SCENE},
190 {"nextletter" , ACTION_NEXT_LETTER},
191 {"prevletter" , ACTION_PREV_LETTER},
192 {"jumpsms2" , ACTION_JUMP_SMS2},
193 {"jumpsms3" , ACTION_JUMP_SMS3},
194 {"jumpsms4" , ACTION_JUMP_SMS4},
195 {"jumpsms5" , ACTION_JUMP_SMS5},
196 {"jumpsms6" , ACTION_JUMP_SMS6},
197 {"jumpsms7" , ACTION_JUMP_SMS7},
198 {"jumpsms8" , ACTION_JUMP_SMS8},
199 {"jumpsms9" , ACTION_JUMP_SMS9},
200 {"filter" , ACTION_FILTER},
201 {"filterclear" , ACTION_FILTER_CLEAR},
202 {"filtersms2" , ACTION_FILTER_SMS2},
203 {"filtersms3" , ACTION_FILTER_SMS3},
204 {"filtersms4" , ACTION_FILTER_SMS4},
205 {"filtersms5" , ACTION_FILTER_SMS5},
206 {"filtersms6" , ACTION_FILTER_SMS6},
207 {"filtersms7" , ACTION_FILTER_SMS7},
208 {"filtersms8" , ACTION_FILTER_SMS8},
209 {"filtersms9" , ACTION_FILTER_SMS9},
210 {"firstpage" , ACTION_FIRST_PAGE},
211 {"lastpage" , ACTION_LAST_PAGE},
212 {"guiprofile" , ACTION_GUIPROFILE_BEGIN},
213 {"red" , ACTION_TELETEXT_RED},
214 {"green" , ACTION_TELETEXT_GREEN},
215 {"yellow" , ACTION_TELETEXT_YELLOW},
216 {"blue" , ACTION_TELETEXT_BLUE},
217 {"increasepar" , ACTION_INCREASE_PAR},
218 {"decreasepar" , ACTION_DECREASE_PAR},
219 {"volampup" , ACTION_VOLAMP_UP},
220 {"volampdown" , ACTION_VOLAMP_DOWN},
223 {"channelup" , ACTION_CHANNEL_UP},
224 {"channeldown" , ACTION_CHANNEL_DOWN},
225 {"previouschannelgroup" , ACTION_PREVIOUS_CHANNELGROUP},
226 {"nextchannelgroup" , ACTION_NEXT_CHANNELGROUP},
227 {"playpvr" , ACTION_PVR_PLAY},
228 {"playpvrtv" , ACTION_PVR_PLAY_TV},
229 {"playpvrradio" , ACTION_PVR_PLAY_RADIO},
232 {"leftclick" , ACTION_MOUSE_LEFT_CLICK},
233 {"rightclick" , ACTION_MOUSE_RIGHT_CLICK},
234 {"middleclick" , ACTION_MOUSE_MIDDLE_CLICK},
235 {"doubleclick" , ACTION_MOUSE_DOUBLE_CLICK},
236 {"wheelup" , ACTION_MOUSE_WHEEL_UP},
237 {"wheeldown" , ACTION_MOUSE_WHEEL_DOWN},
238 {"mousedrag" , ACTION_MOUSE_DRAG},
239 {"mousemove" , ACTION_MOUSE_MOVE},
242 {"tap" , ACTION_TOUCH_TAP},
243 {"longpress" , ACTION_TOUCH_LONGPRESS},
244 {"pangesture" , ACTION_GESTURE_PAN},
245 {"zoomgesture" , ACTION_GESTURE_ZOOM},
246 {"rotategesture" , ACTION_GESTURE_ROTATE},
247 {"swipeleft" , ACTION_GESTURE_SWIPE_LEFT},
248 {"swiperight" , ACTION_GESTURE_SWIPE_RIGHT},
249 {"swipeup" , ACTION_GESTURE_SWIPE_UP},
250 {"swipedown" , ACTION_GESTURE_SWIPE_DOWN},
253 { "noop" , ACTION_NOOP}
256 static const ActionMapping windows[] =
257 {{"home" , WINDOW_HOME},
258 {"programs" , WINDOW_PROGRAMS},
259 {"pictures" , WINDOW_PICTURES},
260 {"filemanager" , WINDOW_FILES},
261 {"files" , WINDOW_FILES}, // backward compat
262 {"settings" , WINDOW_SETTINGS_MENU},
263 {"music" , WINDOW_MUSIC},
264 {"video" , WINDOW_VIDEOS},
265 {"videos" , WINDOW_VIDEO_NAV},
266 {"tv" , WINDOW_PVR}, // backward compat
267 {"pvr" , WINDOW_PVR},
268 {"pvrguideinfo" , WINDOW_DIALOG_PVR_GUIDE_INFO},
269 {"pvrrecordinginfo" , WINDOW_DIALOG_PVR_RECORDING_INFO},
270 {"pvrtimersetting" , WINDOW_DIALOG_PVR_TIMER_SETTING},
271 {"pvrgroupmanager" , WINDOW_DIALOG_PVR_GROUP_MANAGER},
272 {"pvrchannelmanager" , WINDOW_DIALOG_PVR_CHANNEL_MANAGER},
273 {"pvrguidesearch" , WINDOW_DIALOG_PVR_GUIDE_SEARCH},
274 {"pvrchannelscan" , WINDOW_DIALOG_PVR_CHANNEL_SCAN},
275 {"pvrupdateprogress" , WINDOW_DIALOG_PVR_UPDATE_PROGRESS},
276 {"pvrosdchannels" , WINDOW_DIALOG_PVR_OSD_CHANNELS},
277 {"pvrosdguide" , WINDOW_DIALOG_PVR_OSD_GUIDE},
278 {"pvrosddirector" , WINDOW_DIALOG_PVR_OSD_DIRECTOR},
279 {"pvrosdcutter" , WINDOW_DIALOG_PVR_OSD_CUTTER},
280 {"pvrosdteletext" , WINDOW_DIALOG_OSD_TELETEXT},
281 {"systeminfo" , WINDOW_SYSTEM_INFORMATION},
282 {"testpattern" , WINDOW_TEST_PATTERN},
283 {"screencalibration" , WINDOW_SCREEN_CALIBRATION},
284 {"guicalibration" , WINDOW_SCREEN_CALIBRATION}, // backward compat
285 {"picturessettings" , WINDOW_SETTINGS_MYPICTURES},
286 {"programssettings" , WINDOW_SETTINGS_MYPROGRAMS},
287 {"weathersettings" , WINDOW_SETTINGS_MYWEATHER},
288 {"musicsettings" , WINDOW_SETTINGS_MYMUSIC},
289 {"systemsettings" , WINDOW_SETTINGS_SYSTEM},
290 {"videossettings" , WINDOW_SETTINGS_MYVIDEOS},
291 {"networksettings" , WINDOW_SETTINGS_SERVICE}, // backward compat
292 {"servicesettings" , WINDOW_SETTINGS_SERVICE},
293 {"appearancesettings" , WINDOW_SETTINGS_APPEARANCE},
294 {"pvrsettings" , WINDOW_SETTINGS_MYPVR},
295 {"tvsettings" , WINDOW_SETTINGS_MYPVR}, // backward compat
296 {"scripts" , WINDOW_PROGRAMS}, // backward compat
297 {"videofiles" , WINDOW_VIDEO_FILES},
298 {"videolibrary" , WINDOW_VIDEO_NAV},
299 {"videoplaylist" , WINDOW_VIDEO_PLAYLIST},
300 {"loginscreen" , WINDOW_LOGIN_SCREEN},
301 {"profiles" , WINDOW_SETTINGS_PROFILES},
302 {"skinsettings" , WINDOW_SKIN_SETTINGS},
303 {"addonbrowser" , WINDOW_ADDON_BROWSER},
304 {"yesnodialog" , WINDOW_DIALOG_YES_NO},
305 {"progressdialog" , WINDOW_DIALOG_PROGRESS},
306 {"virtualkeyboard" , WINDOW_DIALOG_KEYBOARD},
307 {"volumebar" , WINDOW_DIALOG_VOLUME_BAR},
308 {"submenu" , WINDOW_DIALOG_SUB_MENU},
309 {"favourites" , WINDOW_DIALOG_FAVOURITES},
310 {"contextmenu" , WINDOW_DIALOG_CONTEXT_MENU},
311 {"infodialog" , WINDOW_DIALOG_KAI_TOAST},
312 {"numericinput" , WINDOW_DIALOG_NUMERIC},
313 {"gamepadinput" , WINDOW_DIALOG_GAMEPAD},
314 {"shutdownmenu" , WINDOW_DIALOG_BUTTON_MENU},
315 {"mutebug" , WINDOW_DIALOG_MUTE_BUG},
316 {"playercontrols" , WINDOW_DIALOG_PLAYER_CONTROLS},
317 {"seekbar" , WINDOW_DIALOG_SEEK_BAR},
318 {"musicosd" , WINDOW_DIALOG_MUSIC_OSD},
319 {"addonsettings" , WINDOW_DIALOG_ADDON_SETTINGS},
320 {"visualisationsettings" , WINDOW_DIALOG_ADDON_SETTINGS}, // backward compat
321 {"visualisationpresetlist" , WINDOW_DIALOG_VIS_PRESET_LIST},
322 {"osdvideosettings" , WINDOW_DIALOG_VIDEO_OSD_SETTINGS},
323 {"osdaudiosettings" , WINDOW_DIALOG_AUDIO_OSD_SETTINGS},
324 {"videobookmarks" , WINDOW_DIALOG_VIDEO_BOOKMARKS},
325 {"filebrowser" , WINDOW_DIALOG_FILE_BROWSER},
326 {"networksetup" , WINDOW_DIALOG_NETWORK_SETUP},
327 {"mediasource" , WINDOW_DIALOG_MEDIA_SOURCE},
328 {"profilesettings" , WINDOW_DIALOG_PROFILE_SETTINGS},
329 {"locksettings" , WINDOW_DIALOG_LOCK_SETTINGS},
330 {"contentsettings" , WINDOW_DIALOG_CONTENT_SETTINGS},
331 {"songinformation" , WINDOW_DIALOG_SONG_INFO},
332 {"smartplaylisteditor" , WINDOW_DIALOG_SMART_PLAYLIST_EDITOR},
333 {"smartplaylistrule" , WINDOW_DIALOG_SMART_PLAYLIST_RULE},
334 {"busydialog" , WINDOW_DIALOG_BUSY},
335 {"pictureinfo" , WINDOW_DIALOG_PICTURE_INFO},
336 {"accesspoints" , WINDOW_DIALOG_ACCESS_POINTS},
337 {"fullscreeninfo" , WINDOW_DIALOG_FULLSCREEN_INFO},
338 {"karaokeselector" , WINDOW_DIALOG_KARAOKE_SONGSELECT},
339 {"karaokelargeselector" , WINDOW_DIALOG_KARAOKE_SELECTOR},
340 {"sliderdialog" , WINDOW_DIALOG_SLIDER},
341 {"addoninformation" , WINDOW_DIALOG_ADDON_INFO},
342 {"musicplaylist" , WINDOW_MUSIC_PLAYLIST},
343 {"musicfiles" , WINDOW_MUSIC_FILES},
344 {"musiclibrary" , WINDOW_MUSIC_NAV},
345 {"musicplaylisteditor" , WINDOW_MUSIC_PLAYLIST_EDITOR},
346 {"teletext" , WINDOW_DIALOG_OSD_TELETEXT},
347 {"selectdialog" , WINDOW_DIALOG_SELECT},
348 {"musicinformation" , WINDOW_DIALOG_MUSIC_INFO},
349 {"okdialog" , WINDOW_DIALOG_OK},
350 {"movieinformation" , WINDOW_DIALOG_VIDEO_INFO},
351 {"textviewer" , WINDOW_DIALOG_TEXT_VIEWER},
352 {"fullscreenvideo" , WINDOW_FULLSCREEN_VIDEO},
353 {"fullscreenlivetv" , WINDOW_FULLSCREEN_LIVETV}, // virtual window/keymap section for PVR specific bindings in fullscreen playback (which internally uses WINDOW_FULLSCREEN_VIDEO)
354 {"visualisation" , WINDOW_VISUALISATION},
355 {"slideshow" , WINDOW_SLIDESHOW},
356 {"filestackingdialog" , WINDOW_DIALOG_FILESTACKING},
357 {"karaoke" , WINDOW_KARAOKELYRICS},
358 {"weather" , WINDOW_WEATHER},
359 {"screensaver" , WINDOW_SCREENSAVER},
360 {"videoosd" , WINDOW_DIALOG_VIDEO_OSD},
361 {"videomenu" , WINDOW_VIDEO_MENU},
362 {"videotimeseek" , WINDOW_VIDEO_TIME_SEEK},
363 {"musicoverlay" , WINDOW_DIALOG_MUSIC_OVERLAY},
364 {"videooverlay" , WINDOW_DIALOG_VIDEO_OVERLAY},
365 {"startwindow" , WINDOW_START},
366 {"startup" , WINDOW_STARTUP_ANIM},
367 {"peripherals" , WINDOW_DIALOG_PERIPHERAL_MANAGER},
368 {"peripheralsettings" , WINDOW_DIALOG_PERIPHERAL_SETTINGS},
369 {"extendedprogressdialog" , WINDOW_DIALOG_EXT_PROGRESS},
370 {"mediafilter" , WINDOW_DIALOG_MEDIA_FILTER},
371 {"addon" , WINDOW_ADDON_START}};
373 static const ActionMapping mousecommands[] =
375 { "leftclick", ACTION_MOUSE_LEFT_CLICK },
376 { "rightclick", ACTION_MOUSE_RIGHT_CLICK },
377 { "middleclick", ACTION_MOUSE_MIDDLE_CLICK },
378 { "doubleclick", ACTION_MOUSE_DOUBLE_CLICK },
379 { "wheelup", ACTION_MOUSE_WHEEL_UP },
380 { "wheeldown", ACTION_MOUSE_WHEEL_DOWN },
381 { "mousedrag", ACTION_MOUSE_DRAG },
382 { "mousemove", ACTION_MOUSE_MOVE }
385 static const ActionMapping touchcommands[] =
387 { "tap", ACTION_TOUCH_TAP },
388 { "longpress", ACTION_TOUCH_LONGPRESS },
389 { "pan", ACTION_GESTURE_PAN },
390 { "zoom", ACTION_GESTURE_ZOOM },
391 { "rotate", ACTION_GESTURE_ROTATE },
392 { "swipeleft", ACTION_GESTURE_SWIPE_LEFT },
393 { "swiperight", ACTION_GESTURE_SWIPE_RIGHT },
394 { "swipeup", ACTION_GESTURE_SWIPE_UP },
395 { "swipedown", ACTION_GESTURE_SWIPE_DOWN }
398 static const WindowMapping fallbackWindows[] =
400 { WINDOW_FULLSCREEN_LIVETV, WINDOW_FULLSCREEN_VIDEO },
401 { WINDOW_DIALOG_FULLSCREEN_INFO, WINDOW_FULLSCREEN_VIDEO }
405 static const ActionMapping appcommands[] =
407 { "browser_back", APPCOMMAND_BROWSER_BACKWARD },
408 { "browser_forward", APPCOMMAND_BROWSER_FORWARD },
409 { "browser_refresh", APPCOMMAND_BROWSER_REFRESH },
410 { "browser_stop", APPCOMMAND_BROWSER_STOP },
411 { "browser_search", APPCOMMAND_BROWSER_SEARCH },
412 { "browser_favorites", APPCOMMAND_BROWSER_FAVORITES },
413 { "browser_home", APPCOMMAND_BROWSER_HOME },
414 { "volume_mute", APPCOMMAND_VOLUME_MUTE },
415 { "volume_down", APPCOMMAND_VOLUME_DOWN },
416 { "volume_up", APPCOMMAND_VOLUME_UP },
417 { "next_track", APPCOMMAND_MEDIA_NEXTTRACK },
418 { "prev_track", APPCOMMAND_MEDIA_PREVIOUSTRACK },
419 { "stop", APPCOMMAND_MEDIA_STOP },
420 { "play_pause", APPCOMMAND_MEDIA_PLAY_PAUSE },
421 { "launch_mail", APPCOMMAND_LAUNCH_MAIL },
422 { "launch_media_select", APPCOMMAND_LAUNCH_MEDIA_SELECT },
423 { "launch_app1", APPCOMMAND_LAUNCH_APP1 },
424 { "launch_app2", APPCOMMAND_LAUNCH_APP2 },
425 { "play", APPCOMMAND_MEDIA_PLAY },
426 { "pause", APPCOMMAND_MEDIA_PAUSE },
427 { "fastforward", APPCOMMAND_MEDIA_FAST_FORWARD },
428 { "rewind", APPCOMMAND_MEDIA_REWIND },
429 { "channelup", APPCOMMAND_MEDIA_CHANNEL_UP },
430 { "channeldown", APPCOMMAND_MEDIA_CHANNEL_DOWN }
434 CButtonTranslator& CButtonTranslator::GetInstance()
436 static CButtonTranslator sl_instance;
440 CButtonTranslator::CButtonTranslator()
442 m_deviceList.clear();
446 #if defined(HAS_LIRC) || defined(HAS_IRSERVERSUITE)
447 void CButtonTranslator::ClearLircButtonMapEntries()
449 vector<lircButtonMap*> maps;
450 for (map<CStdString,lircButtonMap*>::iterator it = lircRemotesMap.begin();
451 it != lircRemotesMap.end();++it)
452 maps.push_back(it->second);
453 sort(maps.begin(),maps.end());
454 vector<lircButtonMap*>::iterator itend = unique(maps.begin(),maps.end());
455 for (vector<lircButtonMap*>::iterator it = maps.begin(); it != itend;++it)
460 CButtonTranslator::~CButtonTranslator()
462 #if defined(HAS_LIRC) || defined(HAS_IRSERVERSUITE)
463 ClearLircButtonMapEntries();
467 // Add the supplied device name to the list of connected devices
468 void CButtonTranslator::AddDevice(CStdString& strDevice)
470 // Only add the device if it isn't already in the list
471 std::list<CStdString>::iterator it;
472 for (it = m_deviceList.begin(); it != m_deviceList.end(); it++)
473 if (*it == strDevice)
477 m_deviceList.push_back(strDevice);
480 // New device added so reload the key mappings
484 void CButtonTranslator::RemoveDevice(CStdString& strDevice)
487 std::list<CStdString>::iterator it;
488 for (it = m_deviceList.begin(); it != m_deviceList.end(); it++)
489 if (*it == strDevice)
491 if (it == m_deviceList.end())
495 m_deviceList.remove(strDevice);
497 // Device removed so reload the key mappings
501 bool CButtonTranslator::Load(bool AlwaysLoad)
503 m_translatorMap.clear();
505 // Directories to search for keymaps. They're applied in this order,
506 // so keymaps in profile/keymaps/ override e.g. system/keymaps
507 static const char* DIRS_TO_CHECK[] = {
508 "special://xbmc/system/keymaps/",
509 "special://masterprofile/keymaps/",
510 "special://profile/keymaps/"
512 bool success = false;
514 for (unsigned int dirIndex = 0; dirIndex < sizeof(DIRS_TO_CHECK)/sizeof(DIRS_TO_CHECK[0]); ++dirIndex)
516 if (XFILE::CDirectory::Exists(DIRS_TO_CHECK[dirIndex]))
519 XFILE::CDirectory::GetDirectory(DIRS_TO_CHECK[dirIndex], files, ".xml");
520 // Sort the list for filesystem based priorities, e.g. 01-keymap.xml, 02-keymap-overrides.xml
521 files.Sort(SORT_METHOD_FILE, SortOrderAscending);
522 for(int fileIndex = 0; fileIndex<files.Size(); ++fileIndex)
524 if (!files[fileIndex]->m_bIsFolder)
525 success |= LoadKeymap(files[fileIndex]->GetPath());
528 // Load mappings for any HID devices we have connected
529 std::list<CStdString>::iterator it;
530 for (it = m_deviceList.begin(); it != m_deviceList.end(); it++)
532 CStdString devicedir = DIRS_TO_CHECK[dirIndex];
533 devicedir.append(*it);
534 devicedir.append("/");
535 if( XFILE::CDirectory::Exists(devicedir) )
538 XFILE::CDirectory::GetDirectory(devicedir, files, ".xml");
539 // Sort the list for filesystem based priorities, e.g. 01-keymap.xml, 02-keymap-overrides.xml
540 files.Sort(SORT_METHOD_FILE, SortOrderAscending);
541 for(int fileIndex = 0; fileIndex<files.Size(); ++fileIndex)
543 if (!files[fileIndex]->m_bIsFolder)
544 success |= LoadKeymap(files[fileIndex]->GetPath());
553 CLog::Log(LOGERROR, "Error loading keymaps from: %s or %s or %s", DIRS_TO_CHECK[0], DIRS_TO_CHECK[1], DIRS_TO_CHECK[2]);
557 #if defined(HAS_LIRC) || defined(HAS_IRSERVERSUITE)
559 #define REMOTEMAP "Lircmap.xml"
561 #define REMOTEMAP "IRSSmap.xml"
563 CStdString lircmapPath;
564 URIUtils::AddFileToFolder("special://xbmc/system/", REMOTEMAP, lircmapPath);
565 lircRemotesMap.clear();
566 if(CFile::Exists(lircmapPath))
567 success |= LoadLircMap(lircmapPath);
569 CLog::Log(LOGDEBUG, "CButtonTranslator::Load - no system %s found, skipping", REMOTEMAP);
571 lircmapPath = CProfilesManager::Get().GetUserDataItem(REMOTEMAP);
572 if(CFile::Exists(lircmapPath))
573 success |= LoadLircMap(lircmapPath);
575 CLog::Log(LOGDEBUG, "CButtonTranslator::Load - no userdata %s found, skipping", REMOTEMAP);
578 CLog::Log(LOGERROR, "CButtonTranslator::Load - unable to load remote map %s", REMOTEMAP);
579 // don't return false - it is to only indicate a fatal error (which this is not)
587 bool CButtonTranslator::LoadKeymap(const CStdString &keymapPath)
591 CLog::Log(LOGINFO, "Loading %s", keymapPath.c_str());
592 if (!xmlDoc.LoadFile(keymapPath))
594 CLog::Log(LOGERROR, "Error loading keymap: %s, Line %d\n%s", keymapPath.c_str(), xmlDoc.ErrorRow(), xmlDoc.ErrorDesc());
597 TiXmlElement* pRoot = xmlDoc.RootElement();
600 CLog::Log(LOGERROR, "Error getting keymap root: %s", keymapPath.c_str());
603 CStdString strValue = pRoot->Value();
604 if ( strValue != "keymap")
606 CLog::Log(LOGERROR, "%s Doesn't contain <keymap>", keymapPath.c_str());
609 // run through our window groups
610 TiXmlNode* pWindow = pRoot->FirstChild();
613 if (pWindow->Type() == TiXmlNode::TINYXML_ELEMENT)
615 int windowID = WINDOW_INVALID;
616 const char *szWindow = pWindow->Value();
619 if (strcmpi(szWindow, "global") == 0)
622 windowID = TranslateWindow(szWindow);
624 MapWindowActions(pWindow, windowID);
626 pWindow = pWindow->NextSibling();
632 #if defined(HAS_LIRC) || defined(HAS_IRSERVERSUITE)
633 bool CButtonTranslator::LoadLircMap(const CStdString &lircmapPath)
636 #define REMOTEMAPTAG "lircmap"
638 #define REMOTEMAPTAG "irssmap"
640 // load our xml file, and fill up our mapping tables
643 // Load the config file
644 CLog::Log(LOGINFO, "Loading %s", lircmapPath.c_str());
645 if (!xmlDoc.LoadFile(lircmapPath))
647 CLog::Log(LOGERROR, "%s, Line %d\n%s", lircmapPath.c_str(), xmlDoc.ErrorRow(), xmlDoc.ErrorDesc());
648 return false; // This is so people who don't have the file won't fail, just warn
651 TiXmlElement* pRoot = xmlDoc.RootElement();
652 CStdString strValue = pRoot->Value();
653 if (strValue != REMOTEMAPTAG)
655 CLog::Log(LOGERROR, "%sl Doesn't contain <%s>", lircmapPath.c_str(), REMOTEMAPTAG);
659 // run through our window groups
660 TiXmlNode* pRemote = pRoot->FirstChild();
663 if (pRemote->Type() == TiXmlNode::TINYXML_ELEMENT)
665 const char *szRemote = pRemote->Value();
668 TiXmlAttribute* pAttr = pRemote->ToElement()->FirstAttribute();
669 const char* szDeviceName = pAttr->Value();
670 MapRemote(pRemote, szDeviceName);
673 pRemote = pRemote->NextSibling();
679 void CButtonTranslator::MapRemote(TiXmlNode *pRemote, const char* szDevice)
681 CLog::Log(LOGINFO, "* Adding remote mapping for device '%s'", szDevice);
682 vector<string> RemoteNames;
683 map<CStdString, lircButtonMap*>::iterator it = lircRemotesMap.find(szDevice);
684 if (it == lircRemotesMap.end())
685 lircRemotesMap[szDevice] = new lircButtonMap;
686 lircButtonMap& buttons = *lircRemotesMap[szDevice];
688 TiXmlElement *pButton = pRemote->FirstChildElement();
691 if (strcmpi(pButton->Value(), "altname")==0)
692 RemoteNames.push_back(string(pButton->GetText()));
695 if (pButton->FirstChild() && pButton->FirstChild()->Value())
696 buttons[pButton->FirstChild()->Value()] = pButton->Value();
699 pButton = pButton->NextSiblingElement();
701 for (vector<string>::iterator it = RemoteNames.begin();
702 it != RemoteNames.end();++it)
704 CLog::Log(LOGINFO, "* Linking remote mapping for '%s' to '%s'", szDevice, it->c_str());
705 lircRemotesMap[*it] = &buttons;
709 int CButtonTranslator::TranslateLircRemoteString(const char* szDevice, const char *szButton)
712 map<CStdString, lircButtonMap*>::iterator it = lircRemotesMap.find(szDevice);
713 if (it == lircRemotesMap.end())
717 lircButtonMap::iterator it2 = (*it).second->find(szButton);
718 if (it2 == (*it).second->end())
721 // Convert the button to code
722 if (strnicmp((*it2).second.c_str(), "obc", 3) == 0)
723 return TranslateUniversalRemoteString((*it2).second.c_str());
725 return TranslateRemoteString((*it2).second.c_str());
729 #if defined(HAS_SDL_JOYSTICK) || defined(HAS_EVENT_SERVER)
730 void CButtonTranslator::MapJoystickActions(int windowID, TiXmlNode *pJoystick)
732 string joyname = "_xbmc_"; // default global map name
733 vector<string> joynames;
734 map<int, string> buttonMap;
735 map<int, string> axisMap;
736 map<int, string> hatMap;
738 TiXmlElement *pJoy = pJoystick->ToElement();
739 if (pJoy && pJoy->Attribute("name"))
740 joyname = pJoy->Attribute("name");
742 CLog::Log(LOGNOTICE, "No Joystick name specified, loading default map");
744 joynames.push_back(joyname);
747 TiXmlElement *pButton = pJoystick->FirstChildElement();
751 const char *szAction;
754 szType = pButton->Value();
755 szAction = pButton->GetText();
756 if (szAction == NULL)
760 if ((pButton->QueryIntAttribute("id", &id) == TIXML_SUCCESS) && id>=0 && id<=256)
762 if (strcmpi(szType, "button")==0)
764 buttonMap[id] = string(szAction);
766 else if (strcmpi(szType, "axis")==0)
769 if (pButton->QueryIntAttribute("limit", &limit) == TIXML_SUCCESS)
772 axisMap[-id] = string(szAction);
774 axisMap[id] = string(szAction);
776 axisMap[id|0xFFFF0000] = string(szAction);
779 axisMap[id] = string(szAction);
780 axisMap[-id] = string(szAction);
781 CLog::Log(LOGERROR, "Error in joystick map, invalid limit specified %d for axis %d", limit, id);
786 axisMap[id] = string(szAction);
787 axisMap[-id] = string(szAction);
790 else if (strcmpi(szType, "hat")==0)
793 if (pButton->QueryValueAttribute("position", &position) == TIXML_SUCCESS)
795 uint32_t hatID = id|0xFFF00000;
796 if (position.compare("up") == 0)
797 hatMap[(JACTIVE_HAT_UP<<16)|hatID] = string(szAction);
798 else if (position.compare("down") == 0)
799 hatMap[(JACTIVE_HAT_DOWN<<16)|hatID] = string(szAction);
800 else if (position.compare("right") == 0)
801 hatMap[(JACTIVE_HAT_RIGHT<<16)|hatID] = string(szAction);
802 else if (position.compare("left") == 0)
803 hatMap[(JACTIVE_HAT_LEFT<<16)|hatID] = string(szAction);
805 CLog::Log(LOGERROR, "Error in joystick map, invalid position specified %s for axis %d", position.c_str(), id);
809 CLog::Log(LOGERROR, "Error reading joystick map element, unknown button type: %s", szType);
811 else if (strcmpi(szType, "altname")==0)
812 joynames.push_back(string(szAction));
814 CLog::Log(LOGERROR, "Error reading joystick map element, Invalid id: %d", id);
817 CLog::Log(LOGERROR, "Error reading joystick map element, skipping");
819 pButton = pButton->NextSiblingElement();
821 vector<string>::iterator it = joynames.begin();
822 while (it!=joynames.end())
824 m_joystickButtonMap[*it][windowID] = buttonMap;
825 m_joystickAxisMap[*it][windowID] = axisMap;
826 m_joystickHatMap[*it][windowID] = hatMap;
827 // CLog::Log(LOGDEBUG, "Found Joystick map for window %d using %s", windowID, it->c_str());
832 bool CButtonTranslator::TranslateJoystickString(int window, const char* szDevice, int id, short inputType, int& action, CStdString& strAction, bool &fullrange)
836 // resolve the correct JoystickMap
837 map<string, JoystickMap> *jmap;
838 if (inputType == JACTIVE_AXIS)
839 jmap = &m_joystickAxisMap;
840 else if (inputType == JACTIVE_BUTTON)
841 jmap = &m_joystickButtonMap;
842 else if (inputType == JACTIVE_HAT)
843 jmap = &m_joystickHatMap;
846 CLog::Log(LOGERROR, "Error reading joystick input type '%i'", (int) inputType);
850 map<string, JoystickMap>::iterator it = jmap->find(szDevice);
854 JoystickMap wmap = it->second;
856 // try to get the action from the current window
857 action = GetActionCode(window, id, wmap, strAction, fullrange);
859 // if it's invalid, try to get it from a fallback window or the global map
862 int fallbackWindow = GetFallbackWindow(window);
863 if (fallbackWindow > -1)
864 action = GetActionCode(fallbackWindow, id, wmap, strAction, fullrange);
865 // still no valid action? use global map
867 action = GetActionCode(-1, id, wmap, strAction, fullrange);
873 bool CButtonTranslator::TranslateTouchAction(int touchAction, int touchPointers, int &window, int &action)
876 if (touchPointers <= 0)
879 touchAction += touchPointers - 1;
880 touchAction |= KEY_TOUCH;
882 action = GetTouchActionCode(window, touchAction);
885 window = WINDOW_INVALID;
886 action = GetTouchActionCode(-1, touchAction);
892 int CButtonTranslator::GetActionCode(int window, int action)
894 map<int, buttonMap>::const_iterator it = m_translatorMap.find(window);
895 if (it == m_translatorMap.end())
898 buttonMap::const_iterator it2 = it->second.find(action);
899 if (it2 == it->second.end())
902 return it2->second.id;
906 * Translates a joystick input to an action code
908 int CButtonTranslator::GetActionCode(int window, int id, const JoystickMap &wmap, CStdString &strAction, bool &fullrange) const
913 JoystickMap::const_iterator it = wmap.find(window);
914 if (it != wmap.end())
916 const map<int, string> &windowbmap = it->second;
917 map<int, string>::const_iterator it2 = windowbmap.find(id);
918 if (it2 != windowbmap.end())
920 strAction = (it2->second).c_str();
924 it2 = windowbmap.find(abs(id)|0xFFFF0000);
925 if (it2 != windowbmap.end())
927 strAction = (it2->second).c_str();
933 it2 = windowbmap.find(id|0xFFF00000);
934 if (it2 != windowbmap.end())
936 strAction = (it2->second).c_str();
942 TranslateActionString(strAction.c_str(), action);
947 void CButtonTranslator::GetActions(std::vector<std::string> &actionList)
949 unsigned int size = sizeof(actions) / sizeof(ActionMapping);
951 actionList.reserve(size);
952 for (unsigned int index = 0; index < size; index++)
953 actionList.push_back(actions[index].name);
956 void CButtonTranslator::GetWindows(std::vector<std::string> &windowList)
958 unsigned int size = sizeof(windows) / sizeof(ActionMapping);
960 windowList.reserve(size);
961 for (unsigned int index = 0; index < size; index++)
962 windowList.push_back(windows[index].name);
965 int CButtonTranslator::GetFallbackWindow(int windowID)
967 for (unsigned int index = 0; index < sizeof(fallbackWindows) / sizeof(fallbackWindows[0]); ++index)
969 if (fallbackWindows[index].origin == windowID)
970 return fallbackWindows[index].target;
972 // for addon windows use WINDOW_ADDON_START
973 // because id is dynamic
974 if (windowID >= WINDOW_ADDON_START && windowID <= WINDOW_ADDON_END)
975 return WINDOW_ADDON_START;
980 CAction CButtonTranslator::GetAction(int window, const CKey &key, bool fallback)
982 CStdString strAction;
983 // try to get the action from the current window
984 int actionID = GetActionCode(window, key, strAction);
985 // if it's invalid, try to get it from the global map
986 if (actionID == 0 && fallback)
988 int fallbackWindow = GetFallbackWindow(window);
989 if (fallbackWindow > -1)
990 actionID = GetActionCode(fallbackWindow, key, strAction);
991 // still no valid action? use global map
993 actionID = GetActionCode( -1, key, strAction);
995 // Now fill our action structure
996 CAction action(actionID, strAction, key);
1000 int CButtonTranslator::GetActionCode(int window, const CKey &key, CStdString &strAction) const
1002 uint32_t code = key.GetButtonCode();
1004 map<int, buttonMap>::const_iterator it = m_translatorMap.find(window);
1005 if (it == m_translatorMap.end())
1007 buttonMap::const_iterator it2 = (*it).second.find(code);
1009 while (it2 != (*it).second.end())
1011 action = (*it2).second.id;
1012 strAction = (*it2).second.strID;
1013 it2 = (*it).second.end();
1016 // Some buttoncodes changed in Hardy
1017 if (action == 0 && (code & KEY_VKEY) == KEY_VKEY && (code & 0x0F00))
1019 CLog::Log(LOGDEBUG, "%s: Trying Hardy keycode for %#04x", __FUNCTION__, code);
1021 buttonMap::const_iterator it2 = (*it).second.find(code);
1022 while (it2 != (*it).second.end())
1024 action = (*it2).second.id;
1025 strAction = (*it2).second.strID;
1026 it2 = (*it).second.end();
1033 void CButtonTranslator::MapAction(uint32_t buttonCode, const char *szAction, buttonMap &map)
1035 int action = ACTION_NONE;
1036 if (!TranslateActionString(szAction, action) || !buttonCode)
1037 return; // no valid action, or an invalid buttoncode
1039 // have a valid action, and a valid button - map it.
1040 // check to see if we've already got this (button,action) pair defined
1041 buttonMap::iterator it = map.find(buttonCode);
1042 if (it == map.end() || (*it).second.id != action || (*it).second.strID != szAction)
1044 // NOTE: This multimap is only being used as a normal map at this point (no support
1045 // for multiple actions per key)
1046 if (it != map.end())
1048 CButtonAction button;
1050 button.strID = szAction;
1051 map.insert(pair<uint32_t, CButtonAction>(buttonCode, button));
1055 bool CButtonTranslator::HasDeviceType(TiXmlNode *pWindow, CStdString type)
1057 return pWindow->FirstChild(type) != NULL;
1060 void CButtonTranslator::MapWindowActions(TiXmlNode *pWindow, int windowID)
1062 if (!pWindow || windowID == WINDOW_INVALID)
1067 const char* types[] = {"gamepad", "remote", "universalremote", "keyboard", "mouse", "appcommand", NULL};
1068 for (int i = 0; types[i]; ++i)
1070 CStdString type(types[i]);
1071 if (HasDeviceType(pWindow, type))
1074 std::map<int, buttonMap>::iterator it = m_translatorMap.find(windowID);
1075 if (it != m_translatorMap.end())
1078 m_translatorMap.erase(it);
1081 pDevice = pWindow->FirstChild(type);
1083 TiXmlElement *pButton = pDevice->FirstChildElement();
1087 uint32_t buttonCode=0;
1088 if (type == "gamepad")
1089 buttonCode = TranslateGamepadString(pButton->Value());
1090 else if (type == "remote")
1091 buttonCode = TranslateRemoteString(pButton->Value());
1092 else if (type == "universalremote")
1093 buttonCode = TranslateUniversalRemoteString(pButton->Value());
1094 else if (type == "keyboard")
1095 buttonCode = TranslateKeyboardButton(pButton);
1096 else if (type == "mouse")
1097 buttonCode = TranslateMouseCommand(pButton->Value());
1098 else if (type == "appcommand")
1099 buttonCode = TranslateAppCommand(pButton->Value());
1101 if (buttonCode && pButton->FirstChild())
1102 MapAction(buttonCode, pButton->FirstChild()->Value(), map);
1103 pButton = pButton->NextSiblingElement();
1106 // add our map to our table
1108 m_translatorMap.insert(pair<int, buttonMap>( windowID, map));
1112 #if defined(HAS_SDL_JOYSTICK) || defined(HAS_EVENT_SERVER)
1113 if ((pDevice = pWindow->FirstChild("joystick")) != NULL)
1115 // map joystick actions
1118 MapJoystickActions(windowID, pDevice);
1119 pDevice = pDevice->NextSibling("joystick");
1124 if ((pDevice = pWindow->FirstChild("touch")) != NULL)
1126 // map touch actions
1129 MapTouchActions(windowID, pDevice);
1130 pDevice = pDevice->NextSibling("touch");
1135 bool CButtonTranslator::TranslateActionString(const char *szAction, int &action)
1137 action = ACTION_NONE;
1138 CStdString strAction = szAction;
1139 strAction.ToLower();
1140 if (CBuiltins::HasCommand(strAction))
1141 action = ACTION_BUILT_IN_FUNCTION;
1143 for (unsigned int index=0;index < sizeof(actions)/sizeof(actions[0]);++index)
1145 if (strAction.Equals(actions[index].name))
1147 action = actions[index].action;
1152 if (action == ACTION_NONE)
1154 CLog::Log(LOGERROR, "Keymapping error: no such action '%s' defined", strAction.c_str());
1161 CStdString CButtonTranslator::TranslateWindow(int windowID)
1163 for (unsigned int index = 0; index < sizeof(windows) / sizeof(windows[0]); ++index)
1165 if (windows[index].action == windowID)
1166 return windows[index].name;
1171 int CButtonTranslator::TranslateWindow(const CStdString &window)
1173 CStdString strWindow(window);
1174 if (strWindow.IsEmpty())
1175 return WINDOW_INVALID;
1176 strWindow.ToLower();
1178 if (strWindow.Mid(strWindow.GetLength() - 4) == ".xml" )
1179 strWindow = strWindow.Mid(0, strWindow.GetLength() - 4);
1181 // window12345, for custom window to be keymapped
1182 if (strWindow.length() > 6 && strWindow.Left(6).Equals("window"))
1183 strWindow = strWindow.Mid(6);
1184 if (strWindow.Left(2) == "my") // drop "my" prefix
1185 strWindow = strWindow.Mid(2);
1186 if (StringUtils::IsNaturalNumber(strWindow))
1188 // allow a full window id or a delta id
1189 int iWindow = atoi(strWindow.c_str());
1190 if (iWindow > WINDOW_INVALID)
1192 return WINDOW_HOME + iWindow;
1195 // run through the window structure
1196 for (unsigned int index = 0; index < sizeof(windows) / sizeof(windows[0]); ++index)
1198 if (strWindow.Equals(windows[index].name))
1199 return windows[index].action;
1202 CLog::Log(LOGERROR, "Window Translator: Can't find window %s", strWindow.c_str());
1203 return WINDOW_INVALID;
1206 uint32_t CButtonTranslator::TranslateGamepadString(const char *szButton)
1210 uint32_t buttonCode = 0;
1211 CStdString strButton = szButton;
1212 strButton.ToLower();
1213 if (strButton.Equals("a")) buttonCode = KEY_BUTTON_A;
1214 else if (strButton.Equals("b")) buttonCode = KEY_BUTTON_B;
1215 else if (strButton.Equals("x")) buttonCode = KEY_BUTTON_X;
1216 else if (strButton.Equals("y")) buttonCode = KEY_BUTTON_Y;
1217 else if (strButton.Equals("white")) buttonCode = KEY_BUTTON_WHITE;
1218 else if (strButton.Equals("black")) buttonCode = KEY_BUTTON_BLACK;
1219 else if (strButton.Equals("start")) buttonCode = KEY_BUTTON_START;
1220 else if (strButton.Equals("back")) buttonCode = KEY_BUTTON_BACK;
1221 else if (strButton.Equals("leftthumbbutton")) buttonCode = KEY_BUTTON_LEFT_THUMB_BUTTON;
1222 else if (strButton.Equals("rightthumbbutton")) buttonCode = KEY_BUTTON_RIGHT_THUMB_BUTTON;
1223 else if (strButton.Equals("leftthumbstick")) buttonCode = KEY_BUTTON_LEFT_THUMB_STICK;
1224 else if (strButton.Equals("leftthumbstickup")) buttonCode = KEY_BUTTON_LEFT_THUMB_STICK_UP;
1225 else if (strButton.Equals("leftthumbstickdown")) buttonCode = KEY_BUTTON_LEFT_THUMB_STICK_DOWN;
1226 else if (strButton.Equals("leftthumbstickleft")) buttonCode = KEY_BUTTON_LEFT_THUMB_STICK_LEFT;
1227 else if (strButton.Equals("leftthumbstickright")) buttonCode = KEY_BUTTON_LEFT_THUMB_STICK_RIGHT;
1228 else if (strButton.Equals("rightthumbstick")) buttonCode = KEY_BUTTON_RIGHT_THUMB_STICK;
1229 else if (strButton.Equals("rightthumbstickup")) buttonCode = KEY_BUTTON_RIGHT_THUMB_STICK_UP;
1230 else if (strButton.Equals("rightthumbstickdown")) buttonCode = KEY_BUTTON_RIGHT_THUMB_STICK_DOWN;
1231 else if (strButton.Equals("rightthumbstickleft")) buttonCode = KEY_BUTTON_RIGHT_THUMB_STICK_LEFT;
1232 else if (strButton.Equals("rightthumbstickright")) buttonCode = KEY_BUTTON_RIGHT_THUMB_STICK_RIGHT;
1233 else if (strButton.Equals("lefttrigger")) buttonCode = KEY_BUTTON_LEFT_TRIGGER;
1234 else if (strButton.Equals("righttrigger")) buttonCode = KEY_BUTTON_RIGHT_TRIGGER;
1235 else if (strButton.Equals("leftanalogtrigger")) buttonCode = KEY_BUTTON_LEFT_ANALOG_TRIGGER;
1236 else if (strButton.Equals("rightanalogtrigger")) buttonCode = KEY_BUTTON_RIGHT_ANALOG_TRIGGER;
1237 else if (strButton.Equals("dpadleft")) buttonCode = KEY_BUTTON_DPAD_LEFT;
1238 else if (strButton.Equals("dpadright")) buttonCode = KEY_BUTTON_DPAD_RIGHT;
1239 else if (strButton.Equals("dpadup")) buttonCode = KEY_BUTTON_DPAD_UP;
1240 else if (strButton.Equals("dpaddown")) buttonCode = KEY_BUTTON_DPAD_DOWN;
1241 else CLog::Log(LOGERROR, "Gamepad Translator: Can't find button %s", strButton.c_str());
1245 uint32_t CButtonTranslator::TranslateRemoteString(const char *szButton)
1249 uint32_t buttonCode = 0;
1250 CStdString strButton = szButton;
1251 strButton.ToLower();
1252 if (strButton.Equals("left")) buttonCode = XINPUT_IR_REMOTE_LEFT;
1253 else if (strButton.Equals("right")) buttonCode = XINPUT_IR_REMOTE_RIGHT;
1254 else if (strButton.Equals("up")) buttonCode = XINPUT_IR_REMOTE_UP;
1255 else if (strButton.Equals("down")) buttonCode = XINPUT_IR_REMOTE_DOWN;
1256 else if (strButton.Equals("select")) buttonCode = XINPUT_IR_REMOTE_SELECT;
1257 else if (strButton.Equals("back")) buttonCode = XINPUT_IR_REMOTE_BACK;
1258 else if (strButton.Equals("menu")) buttonCode = XINPUT_IR_REMOTE_MENU;
1259 else if (strButton.Equals("info")) buttonCode = XINPUT_IR_REMOTE_INFO;
1260 else if (strButton.Equals("display")) buttonCode = XINPUT_IR_REMOTE_DISPLAY;
1261 else if (strButton.Equals("title")) buttonCode = XINPUT_IR_REMOTE_TITLE;
1262 else if (strButton.Equals("play")) buttonCode = XINPUT_IR_REMOTE_PLAY;
1263 else if (strButton.Equals("pause")) buttonCode = XINPUT_IR_REMOTE_PAUSE;
1264 else if (strButton.Equals("reverse")) buttonCode = XINPUT_IR_REMOTE_REVERSE;
1265 else if (strButton.Equals("forward")) buttonCode = XINPUT_IR_REMOTE_FORWARD;
1266 else if (strButton.Equals("skipplus")) buttonCode = XINPUT_IR_REMOTE_SKIP_PLUS;
1267 else if (strButton.Equals("skipminus")) buttonCode = XINPUT_IR_REMOTE_SKIP_MINUS;
1268 else if (strButton.Equals("stop")) buttonCode = XINPUT_IR_REMOTE_STOP;
1269 else if (strButton.Equals("zero")) buttonCode = XINPUT_IR_REMOTE_0;
1270 else if (strButton.Equals("one")) buttonCode = XINPUT_IR_REMOTE_1;
1271 else if (strButton.Equals("two")) buttonCode = XINPUT_IR_REMOTE_2;
1272 else if (strButton.Equals("three")) buttonCode = XINPUT_IR_REMOTE_3;
1273 else if (strButton.Equals("four")) buttonCode = XINPUT_IR_REMOTE_4;
1274 else if (strButton.Equals("five")) buttonCode = XINPUT_IR_REMOTE_5;
1275 else if (strButton.Equals("six")) buttonCode = XINPUT_IR_REMOTE_6;
1276 else if (strButton.Equals("seven")) buttonCode = XINPUT_IR_REMOTE_7;
1277 else if (strButton.Equals("eight")) buttonCode = XINPUT_IR_REMOTE_8;
1278 else if (strButton.Equals("nine")) buttonCode = XINPUT_IR_REMOTE_9;
1279 // additional keys from the media center extender for xbox remote
1280 else if (strButton.Equals("power")) buttonCode = XINPUT_IR_REMOTE_POWER;
1281 else if (strButton.Equals("mytv")) buttonCode = XINPUT_IR_REMOTE_MY_TV;
1282 else if (strButton.Equals("mymusic")) buttonCode = XINPUT_IR_REMOTE_MY_MUSIC;
1283 else if (strButton.Equals("mypictures")) buttonCode = XINPUT_IR_REMOTE_MY_PICTURES;
1284 else if (strButton.Equals("myvideo")) buttonCode = XINPUT_IR_REMOTE_MY_VIDEOS;
1285 else if (strButton.Equals("record")) buttonCode = XINPUT_IR_REMOTE_RECORD;
1286 else if (strButton.Equals("start")) buttonCode = XINPUT_IR_REMOTE_START;
1287 else if (strButton.Equals("volumeplus")) buttonCode = XINPUT_IR_REMOTE_VOLUME_PLUS;
1288 else if (strButton.Equals("volumeminus")) buttonCode = XINPUT_IR_REMOTE_VOLUME_MINUS;
1289 else if (strButton.Equals("channelplus")) buttonCode = XINPUT_IR_REMOTE_CHANNEL_PLUS;
1290 else if (strButton.Equals("channelminus")) buttonCode = XINPUT_IR_REMOTE_CHANNEL_MINUS;
1291 else if (strButton.Equals("pageplus")) buttonCode = XINPUT_IR_REMOTE_CHANNEL_PLUS;
1292 else if (strButton.Equals("pageminus")) buttonCode = XINPUT_IR_REMOTE_CHANNEL_MINUS;
1293 else if (strButton.Equals("mute")) buttonCode = XINPUT_IR_REMOTE_MUTE;
1294 else if (strButton.Equals("recordedtv")) buttonCode = XINPUT_IR_REMOTE_RECORDED_TV;
1295 else if (strButton.Equals("guide")) buttonCode = XINPUT_IR_REMOTE_GUIDE;
1296 else if (strButton.Equals("livetv")) buttonCode = XINPUT_IR_REMOTE_LIVE_TV;
1297 else if (strButton.Equals("liveradio")) buttonCode = XINPUT_IR_REMOTE_LIVE_RADIO;
1298 else if (strButton.Equals("epgsearch")) buttonCode = XINPUT_IR_REMOTE_EPG_SEARCH;
1299 else if (strButton.Equals("star")) buttonCode = XINPUT_IR_REMOTE_STAR;
1300 else if (strButton.Equals("hash")) buttonCode = XINPUT_IR_REMOTE_HASH;
1301 else if (strButton.Equals("clear")) buttonCode = XINPUT_IR_REMOTE_CLEAR;
1302 else if (strButton.Equals("enter")) buttonCode = XINPUT_IR_REMOTE_ENTER;
1303 else if (strButton.Equals("xbox")) buttonCode = XINPUT_IR_REMOTE_DISPLAY; // same as display
1304 else if (strButton.Equals("playlist")) buttonCode = XINPUT_IR_REMOTE_PLAYLIST;
1305 else if (strButton.Equals("guide")) buttonCode = XINPUT_IR_REMOTE_GUIDE;
1306 else if (strButton.Equals("teletext")) buttonCode = XINPUT_IR_REMOTE_TELETEXT;
1307 else if (strButton.Equals("red")) buttonCode = XINPUT_IR_REMOTE_RED;
1308 else if (strButton.Equals("green")) buttonCode = XINPUT_IR_REMOTE_GREEN;
1309 else if (strButton.Equals("yellow")) buttonCode = XINPUT_IR_REMOTE_YELLOW;
1310 else if (strButton.Equals("blue")) buttonCode = XINPUT_IR_REMOTE_BLUE;
1311 else if (strButton.Equals("subtitle")) buttonCode = XINPUT_IR_REMOTE_SUBTITLE;
1312 else if (strButton.Equals("language")) buttonCode = XINPUT_IR_REMOTE_LANGUAGE;
1313 else CLog::Log(LOGERROR, "Remote Translator: Can't find button %s", strButton.c_str());
1317 uint32_t CButtonTranslator::TranslateUniversalRemoteString(const char *szButton)
1319 if (!szButton || strlen(szButton) < 4 || strnicmp(szButton, "obc", 3))
1321 const char *szCode = szButton + 3;
1322 // Button Code is 255 - OBC (Original Button Code) of the button
1323 uint32_t buttonCode = 255 - atol(szCode);
1324 if (buttonCode > 255)
1329 uint32_t CButtonTranslator::TranslateKeyboardString(const char *szButton)
1331 uint32_t buttonCode = 0;
1332 XBMCKEYTABLE keytable;
1334 // Look up the key name
1335 if (KeyTableLookupName(szButton, &keytable))
1337 buttonCode = keytable.vkey;
1340 // The lookup failed i.e. the key name wasn't found
1343 CLog::Log(LOGERROR, "Keyboard Translator: Can't find button %s", szButton);
1346 buttonCode |= KEY_VKEY;
1351 uint32_t CButtonTranslator::TranslateKeyboardButton(TiXmlElement *pButton)
1353 uint32_t button_id = 0;
1354 const char *szButton = pButton->Value();
1358 CStdString strKey = szButton;
1359 if (strKey.Equals("key"))
1362 if (pButton->QueryValueAttribute("id", &strID) == TIXML_SUCCESS)
1364 const char *str = strID.c_str();
1366 long int id = strtol(str, &endptr, 0);
1367 if (endptr - str != (int)strlen(str) || id <= 0 || id > 0x00FFFFFF)
1368 CLog::Log(LOGDEBUG, "%s - invalid key id %s", __FUNCTION__, strID.c_str());
1370 button_id = (uint32_t) id;
1373 CLog::Log(LOGERROR, "Keyboard Translator: `key' button has no id");
1376 button_id = TranslateKeyboardString(szButton);
1378 // Process the ctrl/shift/alt modifiers
1380 if (pButton->QueryValueAttribute("mod", &strMod) == TIXML_SUCCESS)
1384 CStdStringArray modArray;
1385 StringUtils::SplitString(strMod, ",", modArray);
1386 for (unsigned int i = 0; i < modArray.size(); i++)
1388 CStdString& substr = modArray[i];
1391 if (substr == "ctrl" || substr == "control")
1392 button_id |= CKey::MODIFIER_CTRL;
1393 else if (substr == "shift")
1394 button_id |= CKey::MODIFIER_SHIFT;
1395 else if (substr == "alt")
1396 button_id |= CKey::MODIFIER_ALT;
1397 else if (substr == "super" || substr == "win")
1398 button_id |= CKey::MODIFIER_SUPER;
1400 CLog::Log(LOGERROR, "Keyboard Translator: Unknown key modifier %s in %s", substr.c_str(), strMod.c_str());
1407 uint32_t CButtonTranslator::TranslateAppCommand(const char *szButton)
1410 CStdString strAppCommand = szButton;
1411 strAppCommand.ToLower();
1413 for (int i = 0; i < sizeof(appcommands)/sizeof(appcommands[0]); i++)
1414 if (strAppCommand.Equals(appcommands[i].name))
1415 return appcommands[i].action | KEY_APPCOMMAND;
1417 CLog::Log(LOGERROR, "%s: Can't find appcommand %s", __FUNCTION__, szButton);
1423 uint32_t CButtonTranslator::TranslateMouseCommand(const char *szButton)
1425 CStdString strMouseCommand = szButton;
1426 strMouseCommand.ToLower();
1428 for (unsigned int i = 0; i < sizeof(mousecommands)/sizeof(mousecommands[0]); i++)
1429 if (strMouseCommand.Equals(mousecommands[i].name))
1430 return mousecommands[i].action | KEY_MOUSE;
1432 CLog::Log(LOGERROR, "%s: Can't find mouse command %s", __FUNCTION__, szButton);
1437 void CButtonTranslator::Clear()
1439 m_translatorMap.clear();
1440 #if defined(HAS_LIRC) || defined(HAS_IRSERVERSUITE)
1441 ClearLircButtonMapEntries();
1442 lircRemotesMap.clear();
1445 #if defined(HAS_SDL_JOYSTICK) || defined(HAS_EVENT_SERVER)
1446 m_joystickButtonMap.clear();
1447 m_joystickAxisMap.clear();
1448 m_joystickHatMap.clear();
1454 uint32_t CButtonTranslator::TranslateTouchCommand(TiXmlElement *pButton, CButtonAction &action)
1456 const char *szButton = pButton->Value();
1457 if (szButton == NULL || pButton->FirstChild() == NULL)
1460 const char *szAction = pButton->FirstChild()->Value();
1461 if (szAction == NULL)
1464 CStdString strTouchCommand = szButton;
1465 strTouchCommand.ToLower();
1468 if (pButton->QueryStringAttribute("direction", &strTmp) == TIXML_SUCCESS)
1469 strTouchCommand += strTmp;
1471 uint32_t actionId = ACTION_NONE;
1472 for (unsigned int i = 0; i < sizeof(touchcommands)/sizeof(touchcommands[0]); i++)
1474 if (strTouchCommand.Equals(touchcommands[i].name))
1476 actionId = touchcommands[i].action;
1481 if (actionId <= ACTION_NONE)
1483 CLog::Log(LOGERROR, "%s: Can't find touch command %s", __FUNCTION__, szButton);
1488 if (pButton->QueryStringAttribute("pointers", &strTmp) == TIXML_SUCCESS)
1490 int pointers = (int)strtol(strTmp.c_str(), NULL, 0);
1492 actionId += pointers - 1;
1495 action.strID = szAction;
1496 if (!TranslateActionString(szAction, action.id) || action.id <= ACTION_NONE)
1499 return actionId | KEY_TOUCH;
1502 void CButtonTranslator::MapTouchActions(int windowID, TiXmlNode *pTouch)
1508 // check if there already is a touch map for the window ID
1509 std::map<int, buttonMap>::iterator it = m_touchMap.find(windowID);
1510 if (it != m_touchMap.end())
1512 // get the existing touch map and remove it from the window mapping
1513 // as it will be inserted later on
1515 m_touchMap.erase(it);
1518 uint32_t actionId = 0;
1519 TiXmlElement *pTouchElem = pTouch->ToElement();
1520 if (pTouchElem == NULL)
1523 TiXmlElement *pButton = pTouchElem->FirstChildElement();
1524 while (pButton != NULL)
1526 CButtonAction action;
1527 actionId = TranslateTouchCommand(pButton, action);
1530 // check if there already is a mapping for the parsed action
1531 // and remove it if necessary
1532 buttonMap::iterator actionIt = map.find(actionId);
1533 if (actionIt != map.end())
1534 map.erase(actionIt);
1536 map.insert(std::make_pair(actionId, action));
1539 pButton = pButton->NextSiblingElement();
1542 // add the modified touch map with the window ID
1544 m_touchMap.insert(std::pair<int, buttonMap>(windowID, map));
1547 int CButtonTranslator::GetTouchActionCode(int window, int action)
1549 std::map<int, buttonMap>::const_iterator windowIt = m_touchMap.find(window);
1550 if (windowIt == m_touchMap.end())
1553 buttonMap::const_iterator touchIt = windowIt->second.find(action);
1554 if (touchIt == windowIt->second.end())
1557 return touchIt->second.id;