2 * Copyright (C) 2005-2008 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, write to
17 * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
18 * http://www.gnu.org/copyleft/gpl.html
23 #include "interfaces/Builtins.h"
24 #include "ButtonTranslator.h"
25 #include "utils/URIUtils.h"
26 #include "settings/Settings.h"
27 #include "guilib/Key.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;
53 static const ActionMapping actions[] =
55 {"left" , ACTION_MOVE_LEFT },
56 {"right" , ACTION_MOVE_RIGHT},
57 {"up" , ACTION_MOVE_UP },
58 {"down" , ACTION_MOVE_DOWN },
59 {"pageup" , ACTION_PAGE_UP },
60 {"pagedown" , ACTION_PAGE_DOWN},
61 {"select" , ACTION_SELECT_ITEM},
62 {"highlight" , ACTION_HIGHLIGHT_ITEM},
63 {"parentdir" , ACTION_NAV_BACK}, // backward compatibility
64 {"parentfolder" , ACTION_PARENT_DIR},
65 {"back" , ACTION_NAV_BACK},
66 {"previousmenu" , ACTION_PREVIOUS_MENU},
67 {"info" , ACTION_SHOW_INFO},
68 {"pause" , ACTION_PAUSE},
69 {"stop" , ACTION_STOP},
70 {"skipnext" , ACTION_NEXT_ITEM},
71 {"skipprevious" , ACTION_PREV_ITEM},
72 {"fullscreen" , ACTION_SHOW_GUI},
73 {"aspectratio" , ACTION_ASPECT_RATIO},
74 {"stepforward" , ACTION_STEP_FORWARD},
75 {"stepback" , ACTION_STEP_BACK},
76 {"bigstepforward" , ACTION_BIG_STEP_FORWARD},
77 {"bigstepback" , ACTION_BIG_STEP_BACK},
78 {"osd" , ACTION_SHOW_OSD},
79 {"showsubtitles" , ACTION_SHOW_SUBTITLES},
80 {"nextsubtitle" , ACTION_NEXT_SUBTITLE},
81 {"codecinfo" , ACTION_SHOW_CODEC},
82 {"nextpicture" , ACTION_NEXT_PICTURE},
83 {"previouspicture" , ACTION_PREV_PICTURE},
84 {"zoomout" , ACTION_ZOOM_OUT},
85 {"zoomin" , ACTION_ZOOM_IN},
86 {"playlist" , ACTION_SHOW_PLAYLIST},
87 {"queue" , ACTION_QUEUE_ITEM},
88 {"zoomnormal" , ACTION_ZOOM_LEVEL_NORMAL},
89 {"zoomlevel1" , ACTION_ZOOM_LEVEL_1},
90 {"zoomlevel2" , ACTION_ZOOM_LEVEL_2},
91 {"zoomlevel3" , ACTION_ZOOM_LEVEL_3},
92 {"zoomlevel4" , ACTION_ZOOM_LEVEL_4},
93 {"zoomlevel5" , ACTION_ZOOM_LEVEL_5},
94 {"zoomlevel6" , ACTION_ZOOM_LEVEL_6},
95 {"zoomlevel7" , ACTION_ZOOM_LEVEL_7},
96 {"zoomlevel8" , ACTION_ZOOM_LEVEL_8},
97 {"zoomlevel9" , ACTION_ZOOM_LEVEL_9},
98 {"nextcalibration" , ACTION_CALIBRATE_SWAP_ARROWS},
99 {"resetcalibration" , ACTION_CALIBRATE_RESET},
100 {"analogmove" , ACTION_ANALOG_MOVE},
101 {"rotate" , ACTION_ROTATE_PICTURE},
102 {"close" , ACTION_NAV_BACK}, // backwards compatibility
103 {"subtitledelayminus", ACTION_SUBTITLE_DELAY_MIN},
104 {"subtitledelay" , ACTION_SUBTITLE_DELAY},
105 {"subtitledelayplus" , ACTION_SUBTITLE_DELAY_PLUS},
106 {"audiodelayminus" , ACTION_AUDIO_DELAY_MIN},
107 {"audiodelay" , ACTION_AUDIO_DELAY},
108 {"audiodelayplus" , ACTION_AUDIO_DELAY_PLUS},
109 {"subtitleshiftup" , ACTION_SUBTITLE_VSHIFT_UP},
110 {"subtitleshiftdown" , ACTION_SUBTITLE_VSHIFT_DOWN},
111 {"subtitlealign" , ACTION_SUBTITLE_ALIGN},
112 {"audionextlanguage" , ACTION_AUDIO_NEXT_LANGUAGE},
113 {"verticalshiftup" , ACTION_VSHIFT_UP},
114 {"verticalshiftdown" , ACTION_VSHIFT_DOWN},
115 {"nextresolution" , ACTION_CHANGE_RESOLUTION},
116 {"audiotoggledigital", ACTION_TOGGLE_DIGITAL_ANALOG},
117 {"number0" , REMOTE_0},
118 {"number1" , REMOTE_1},
119 {"number2" , REMOTE_2},
120 {"number3" , REMOTE_3},
121 {"number4" , REMOTE_4},
122 {"number5" , REMOTE_5},
123 {"number6" , REMOTE_6},
124 {"number7" , REMOTE_7},
125 {"number8" , REMOTE_8},
126 {"number9" , REMOTE_9},
127 {"osdleft" , ACTION_OSD_SHOW_LEFT},
128 {"osdright" , ACTION_OSD_SHOW_RIGHT},
129 {"osdup" , ACTION_OSD_SHOW_UP},
130 {"osddown" , ACTION_OSD_SHOW_DOWN},
131 {"osdselect" , ACTION_OSD_SHOW_SELECT},
132 {"osdvalueplus" , ACTION_OSD_SHOW_VALUE_PLUS},
133 {"osdvalueminus" , ACTION_OSD_SHOW_VALUE_MIN},
134 {"smallstepback" , ACTION_SMALL_STEP_BACK},
135 {"fastforward" , ACTION_PLAYER_FORWARD},
136 {"rewind" , ACTION_PLAYER_REWIND},
137 {"play" , ACTION_PLAYER_PLAY},
138 {"playpause" , ACTION_PLAYER_PLAYPAUSE},
139 {"delete" , ACTION_DELETE_ITEM},
140 {"copy" , ACTION_COPY_ITEM},
141 {"move" , ACTION_MOVE_ITEM},
142 {"mplayerosd" , ACTION_SHOW_MPLAYER_OSD},
143 {"hidesubmenu" , ACTION_OSD_HIDESUBMENU},
144 {"screenshot" , ACTION_TAKE_SCREENSHOT},
145 {"rename" , ACTION_RENAME_ITEM},
146 {"togglewatched" , ACTION_TOGGLE_WATCHED},
147 {"scanitem" , ACTION_SCAN_ITEM},
148 {"reloadkeymaps" , ACTION_RELOAD_KEYMAPS},
149 {"volumeup" , ACTION_VOLUME_UP},
150 {"volumedown" , ACTION_VOLUME_DOWN},
151 {"mute" , ACTION_MUTE},
152 {"backspace" , ACTION_BACKSPACE},
153 {"scrollup" , ACTION_SCROLL_UP},
154 {"scrolldown" , ACTION_SCROLL_DOWN},
155 {"analogfastforward" , ACTION_ANALOG_FORWARD},
156 {"analogrewind" , ACTION_ANALOG_REWIND},
157 {"moveitemup" , ACTION_MOVE_ITEM_UP},
158 {"moveitemdown" , ACTION_MOVE_ITEM_DOWN},
159 {"contextmenu" , ACTION_CONTEXT_MENU},
160 {"shift" , ACTION_SHIFT},
161 {"symbols" , ACTION_SYMBOLS},
162 {"cursorleft" , ACTION_CURSOR_LEFT},
163 {"cursorright" , ACTION_CURSOR_RIGHT},
164 {"showtime" , ACTION_SHOW_OSD_TIME},
165 {"analogseekforward" , ACTION_ANALOG_SEEK_FORWARD},
166 {"analogseekback" , ACTION_ANALOG_SEEK_BACK},
167 {"showpreset" , ACTION_VIS_PRESET_SHOW},
168 {"presetlist" , ACTION_VIS_PRESET_LIST},
169 {"nextpreset" , ACTION_VIS_PRESET_NEXT},
170 {"previouspreset" , ACTION_VIS_PRESET_PREV},
171 {"lockpreset" , ACTION_VIS_PRESET_LOCK},
172 {"randompreset" , ACTION_VIS_PRESET_RANDOM},
173 {"increasevisrating" , ACTION_VIS_RATE_PRESET_PLUS},
174 {"decreasevisrating" , ACTION_VIS_RATE_PRESET_MINUS},
175 {"showvideomenu" , ACTION_SHOW_VIDEOMENU},
176 {"enter" , ACTION_ENTER},
177 {"increaserating" , ACTION_INCREASE_RATING},
178 {"decreaserating" , ACTION_DECREASE_RATING},
179 {"togglefullscreen" , ACTION_TOGGLE_FULLSCREEN},
180 {"nextscene" , ACTION_NEXT_SCENE},
181 {"previousscene" , ACTION_PREV_SCENE},
182 {"nextletter" , ACTION_NEXT_LETTER},
183 {"prevletter" , ACTION_PREV_LETTER},
184 {"jumpsms2" , ACTION_JUMP_SMS2},
185 {"jumpsms3" , ACTION_JUMP_SMS3},
186 {"jumpsms4" , ACTION_JUMP_SMS4},
187 {"jumpsms5" , ACTION_JUMP_SMS5},
188 {"jumpsms6" , ACTION_JUMP_SMS6},
189 {"jumpsms7" , ACTION_JUMP_SMS7},
190 {"jumpsms8" , ACTION_JUMP_SMS8},
191 {"jumpsms9" , ACTION_JUMP_SMS9},
192 {"filterclear" , ACTION_FILTER_CLEAR},
193 {"filtersms2" , ACTION_FILTER_SMS2},
194 {"filtersms3" , ACTION_FILTER_SMS3},
195 {"filtersms4" , ACTION_FILTER_SMS4},
196 {"filtersms5" , ACTION_FILTER_SMS5},
197 {"filtersms6" , ACTION_FILTER_SMS6},
198 {"filtersms7" , ACTION_FILTER_SMS7},
199 {"filtersms8" , ACTION_FILTER_SMS8},
200 {"filtersms9" , ACTION_FILTER_SMS9},
201 {"firstpage" , ACTION_FIRST_PAGE},
202 {"lastpage" , ACTION_LAST_PAGE},
203 {"guiprofile" , ACTION_GUIPROFILE_BEGIN},
204 {"red" , ACTION_TELETEXT_RED},
205 {"green" , ACTION_TELETEXT_GREEN},
206 {"yellow" , ACTION_TELETEXT_YELLOW},
207 {"blue" , ACTION_TELETEXT_BLUE},
208 {"increasepar" , ACTION_INCREASE_PAR},
209 {"decreasepar" , ACTION_DECREASE_PAR},
210 {"volampup" , ACTION_VOLAMP_UP},
211 {"volampdown" , ACTION_VOLAMP_DOWN},
214 {"leftclick" , ACTION_MOUSE_LEFT_CLICK},
215 {"rightclick" , ACTION_MOUSE_RIGHT_CLICK},
216 {"middleclick" , ACTION_MOUSE_MIDDLE_CLICK},
217 {"doubleclick" , ACTION_MOUSE_DOUBLE_CLICK},
218 {"wheelup" , ACTION_MOUSE_WHEEL_UP},
219 {"wheeldown" , ACTION_MOUSE_WHEEL_DOWN},
220 {"mousedrag" , ACTION_MOUSE_DRAG},
221 {"mousemove" , ACTION_MOUSE_MOVE},
224 { "noop" , ACTION_NOOP}
227 static const ActionMapping windows[] =
228 {{"home" , WINDOW_HOME},
229 {"programs" , WINDOW_PROGRAMS},
230 {"pictures" , WINDOW_PICTURES},
231 {"filemanager" , WINDOW_FILES},
232 {"files" , WINDOW_FILES}, // backward compat
233 {"settings" , WINDOW_SETTINGS_MENU},
234 {"music" , WINDOW_MUSIC},
235 {"video" , WINDOW_VIDEOS},
236 {"videos" , WINDOW_VIDEO_NAV},
237 {"systeminfo" , WINDOW_SYSTEM_INFORMATION},
238 {"testpattern" , WINDOW_TEST_PATTERN},
239 {"screencalibration" , WINDOW_SCREEN_CALIBRATION},
240 {"guicalibration" , WINDOW_SCREEN_CALIBRATION}, // backward compat
241 {"picturessettings" , WINDOW_SETTINGS_MYPICTURES},
242 {"programssettings" , WINDOW_SETTINGS_MYPROGRAMS},
243 {"weathersettings" , WINDOW_SETTINGS_MYWEATHER},
244 {"musicsettings" , WINDOW_SETTINGS_MYMUSIC},
245 {"systemsettings" , WINDOW_SETTINGS_SYSTEM},
246 {"videossettings" , WINDOW_SETTINGS_MYVIDEOS},
247 {"networksettings" , WINDOW_SETTINGS_SERVICE}, // backward compat
248 {"servicesettings" , WINDOW_SETTINGS_SERVICE},
249 {"appearancesettings" , WINDOW_SETTINGS_APPEARANCE},
250 {"scripts" , WINDOW_PROGRAMS}, // backward compat
251 {"videofiles" , WINDOW_VIDEO_FILES},
252 {"videolibrary" , WINDOW_VIDEO_NAV},
253 {"videoplaylist" , WINDOW_VIDEO_PLAYLIST},
254 {"loginscreen" , WINDOW_LOGIN_SCREEN},
255 {"profiles" , WINDOW_SETTINGS_PROFILES},
256 {"addonbrowser" , WINDOW_ADDON_BROWSER},
257 {"yesnodialog" , WINDOW_DIALOG_YES_NO},
258 {"progressdialog" , WINDOW_DIALOG_PROGRESS},
259 {"virtualkeyboard" , WINDOW_DIALOG_KEYBOARD},
260 {"volumebar" , WINDOW_DIALOG_VOLUME_BAR},
261 {"submenu" , WINDOW_DIALOG_SUB_MENU},
262 {"favourites" , WINDOW_DIALOG_FAVOURITES},
263 {"contextmenu" , WINDOW_DIALOG_CONTEXT_MENU},
264 {"infodialog" , WINDOW_DIALOG_KAI_TOAST},
265 {"numericinput" , WINDOW_DIALOG_NUMERIC},
266 {"gamepadinput" , WINDOW_DIALOG_GAMEPAD},
267 {"shutdownmenu" , WINDOW_DIALOG_BUTTON_MENU},
268 {"musicscan" , WINDOW_DIALOG_MUSIC_SCAN},
269 {"mutebug" , WINDOW_DIALOG_MUTE_BUG},
270 {"playercontrols" , WINDOW_DIALOG_PLAYER_CONTROLS},
271 {"seekbar" , WINDOW_DIALOG_SEEK_BAR},
272 {"musicosd" , WINDOW_DIALOG_MUSIC_OSD},
273 {"addonsettings" , WINDOW_DIALOG_ADDON_SETTINGS},
274 {"visualisationsettings" , WINDOW_DIALOG_ADDON_SETTINGS}, // backward compat
275 {"visualisationpresetlist" , WINDOW_DIALOG_VIS_PRESET_LIST},
276 {"osdvideosettings" , WINDOW_DIALOG_VIDEO_OSD_SETTINGS},
277 {"osdaudiosettings" , WINDOW_DIALOG_AUDIO_OSD_SETTINGS},
278 {"videobookmarks" , WINDOW_DIALOG_VIDEO_BOOKMARKS},
279 {"filebrowser" , WINDOW_DIALOG_FILE_BROWSER},
280 {"networksetup" , WINDOW_DIALOG_NETWORK_SETUP},
281 {"mediasource" , WINDOW_DIALOG_MEDIA_SOURCE},
282 {"profilesettings" , WINDOW_DIALOG_PROFILE_SETTINGS},
283 {"locksettings" , WINDOW_DIALOG_LOCK_SETTINGS},
284 {"contentsettings" , WINDOW_DIALOG_CONTENT_SETTINGS},
285 {"videoscan" , WINDOW_DIALOG_VIDEO_SCAN},
286 {"favourites" , WINDOW_DIALOG_FAVOURITES},
287 {"songinformation" , WINDOW_DIALOG_SONG_INFO},
288 {"smartplaylisteditor" , WINDOW_DIALOG_SMART_PLAYLIST_EDITOR},
289 {"smartplaylistrule" , WINDOW_DIALOG_SMART_PLAYLIST_RULE},
290 {"busydialog" , WINDOW_DIALOG_BUSY},
291 {"pictureinfo" , WINDOW_DIALOG_PICTURE_INFO},
292 {"accesspoints" , WINDOW_DIALOG_ACCESS_POINTS},
293 {"fullscreeninfo" , WINDOW_DIALOG_FULLSCREEN_INFO},
294 {"karaokeselector" , WINDOW_DIALOG_KARAOKE_SONGSELECT},
295 {"karaokelargeselector" , WINDOW_DIALOG_KARAOKE_SELECTOR},
296 {"sliderdialog" , WINDOW_DIALOG_SLIDER},
297 {"addoninformation" , WINDOW_DIALOG_ADDON_INFO},
298 {"musicplaylist" , WINDOW_MUSIC_PLAYLIST},
299 {"musicfiles" , WINDOW_MUSIC_FILES},
300 {"musiclibrary" , WINDOW_MUSIC_NAV},
301 {"musicplaylisteditor" , WINDOW_MUSIC_PLAYLIST_EDITOR},
302 {"teletext" , WINDOW_DIALOG_OSD_TELETEXT},
303 {"selectdialog" , WINDOW_DIALOG_SELECT},
304 {"musicinformation" , WINDOW_DIALOG_MUSIC_INFO},
305 {"okdialog" , WINDOW_DIALOG_OK},
306 {"movieinformation" , WINDOW_DIALOG_VIDEO_INFO},
307 {"textviewer" , WINDOW_DIALOG_TEXT_VIEWER},
308 {"fullscreenvideo" , WINDOW_FULLSCREEN_VIDEO},
309 {"visualisation" , WINDOW_VISUALISATION},
310 {"slideshow" , WINDOW_SLIDESHOW},
311 {"filestackingdialog" , WINDOW_DIALOG_FILESTACKING},
312 {"karaoke" , WINDOW_KARAOKELYRICS},
313 {"weather" , WINDOW_WEATHER},
314 {"screensaver" , WINDOW_SCREENSAVER},
315 {"videoosd" , WINDOW_DIALOG_VIDEO_OSD},
316 {"videomenu" , WINDOW_VIDEO_MENU},
317 {"videotimeseek" , WINDOW_VIDEO_TIME_SEEK},
318 {"musicoverlay" , WINDOW_DIALOG_MUSIC_OVERLAY},
319 {"videooverlay" , WINDOW_DIALOG_VIDEO_OVERLAY},
320 {"startwindow" , WINDOW_START},
321 {"startup" , WINDOW_STARTUP_ANIM},
322 {"peripherals" , WINDOW_DIALOG_PERIPHERAL_MANAGER},
323 {"peripheralsettings" , WINDOW_DIALOG_PERIPHERAL_SETTINGS}};
325 static const ActionMapping mousecommands[] =
327 { "leftclick", ACTION_MOUSE_LEFT_CLICK },
328 { "rightclick", ACTION_MOUSE_RIGHT_CLICK },
329 { "middleclick", ACTION_MOUSE_MIDDLE_CLICK },
330 { "doubleclick", ACTION_MOUSE_DOUBLE_CLICK },
331 { "wheelup", ACTION_MOUSE_WHEEL_UP },
332 { "wheeldown", ACTION_MOUSE_WHEEL_DOWN },
333 { "mousedrag", ACTION_MOUSE_DRAG },
334 { "mousemove", ACTION_MOUSE_MOVE }
338 static const ActionMapping appcommands[] =
340 { "browser_back", APPCOMMAND_BROWSER_BACKWARD },
341 { "browser_forward", APPCOMMAND_BROWSER_FORWARD },
342 { "browser_refresh", APPCOMMAND_BROWSER_REFRESH },
343 { "browser_stop", APPCOMMAND_BROWSER_STOP },
344 { "browser_search", APPCOMMAND_BROWSER_SEARCH },
345 { "browser_favorites", APPCOMMAND_BROWSER_FAVORITES },
346 { "browser_home", APPCOMMAND_BROWSER_HOME },
347 { "volume_mute", APPCOMMAND_VOLUME_MUTE },
348 { "volume_down", APPCOMMAND_VOLUME_DOWN },
349 { "volume_up", APPCOMMAND_VOLUME_UP },
350 { "next_track", APPCOMMAND_MEDIA_NEXTTRACK },
351 { "prev_track", APPCOMMAND_MEDIA_PREVIOUSTRACK },
352 { "stop", APPCOMMAND_MEDIA_STOP },
353 { "play_pause", APPCOMMAND_MEDIA_PLAY_PAUSE },
354 { "launch_mail", APPCOMMAND_LAUNCH_MAIL },
355 { "launch_media_select", APPCOMMAND_LAUNCH_MEDIA_SELECT },
356 { "launch_app1", APPCOMMAND_LAUNCH_APP1 },
357 { "launch_app2", APPCOMMAND_LAUNCH_APP2 },
358 { "play", APPCOMMAND_MEDIA_PLAY },
359 { "pause", APPCOMMAND_MEDIA_PAUSE },
360 { "fastforward", APPCOMMAND_MEDIA_FAST_FORWARD },
361 { "rewind", APPCOMMAND_MEDIA_REWIND },
362 { "channelup", APPCOMMAND_MEDIA_CHANNEL_UP },
363 { "channeldown", APPCOMMAND_MEDIA_CHANNEL_DOWN }
367 CButtonTranslator& CButtonTranslator::GetInstance()
369 static CButtonTranslator sl_instance;
373 CButtonTranslator::CButtonTranslator()
375 m_deviceList.clear();
379 CButtonTranslator::~CButtonTranslator()
381 #if defined(HAS_LIRC) || defined(HAS_IRSERVERSUITE)
382 vector<lircButtonMap*> maps;
383 for (map<CStdString,lircButtonMap*>::iterator it = lircRemotesMap.begin();
384 it != lircRemotesMap.end();++it)
385 maps.push_back(it->second);
386 sort(maps.begin(),maps.end());
387 vector<lircButtonMap*>::iterator itend = unique(maps.begin(),maps.end());
388 for (vector<lircButtonMap*>::iterator it = maps.begin(); it != itend;++it)
393 // Add the supplied device name to the list of connected devices
394 void CButtonTranslator::AddDevice(CStdString& strDevice)
396 // Only add the device if it isn't already in the list
397 std::list<CStdString>::iterator it;
398 for (it = m_deviceList.begin(); it != m_deviceList.end(); it++)
399 if (*it == strDevice)
403 m_deviceList.push_back(strDevice);
406 // New device added so reload the key mappings
410 void CButtonTranslator::RemoveDevice(CStdString& strDevice)
413 std::list<CStdString>::iterator it;
414 for (it = m_deviceList.begin(); it != m_deviceList.end(); it++)
415 if (*it == strDevice)
417 if (it == m_deviceList.end())
421 m_deviceList.remove(strDevice);
423 // Device removed so reload the key mappings
427 bool CButtonTranslator::Load(bool AlwaysLoad)
429 m_translatorMap.clear();
431 // Directories to search for keymaps. They're applied in this order,
432 // so keymaps in profile/keymaps/ override e.g. system/keymaps
433 static const char* DIRS_TO_CHECK[] = {
434 "special://xbmc/system/keymaps/",
435 "special://masterprofile/keymaps/",
436 "special://profile/keymaps/"
438 bool success = false;
440 for (unsigned int dirIndex = 0; dirIndex < sizeof(DIRS_TO_CHECK)/sizeof(DIRS_TO_CHECK[0]); ++dirIndex)
442 if (XFILE::CDirectory::Exists(DIRS_TO_CHECK[dirIndex]))
445 XFILE::CDirectory::GetDirectory(DIRS_TO_CHECK[dirIndex], files, ".xml");
446 // Sort the list for filesystem based priorities, e.g. 01-keymap.xml, 02-keymap-overrides.xml
447 files.Sort(SORT_METHOD_FILE, SortOrderAscending);
448 for(int fileIndex = 0; fileIndex<files.Size(); ++fileIndex)
450 if (!files[fileIndex]->m_bIsFolder)
451 success |= LoadKeymap(files[fileIndex]->GetPath());
454 // Load mappings for any HID devices we have connected
455 std::list<CStdString>::iterator it;
456 for (it = m_deviceList.begin(); it != m_deviceList.end(); it++)
458 CStdString devicedir = DIRS_TO_CHECK[dirIndex];
459 devicedir.append(*it);
460 devicedir.append("/");
461 if( XFILE::CDirectory::Exists(devicedir) )
464 XFILE::CDirectory::GetDirectory(devicedir, files, ".xml");
465 // Sort the list for filesystem based priorities, e.g. 01-keymap.xml, 02-keymap-overrides.xml
466 files.Sort(SORT_METHOD_FILE, SortOrderAscending);
467 for(int fileIndex = 0; fileIndex<files.Size(); ++fileIndex)
469 if (!files[fileIndex]->m_bIsFolder)
470 success |= LoadKeymap(files[fileIndex]->GetPath());
479 CLog::Log(LOGERROR, "Error loading keymaps from: %s or %s or %s", DIRS_TO_CHECK[0], DIRS_TO_CHECK[1], DIRS_TO_CHECK[2]);
483 #if defined(HAS_LIRC) || defined(HAS_IRSERVERSUITE)
485 #define REMOTEMAP "Lircmap.xml"
487 #define REMOTEMAP "IRSSmap.xml"
489 CStdString lircmapPath;
490 URIUtils::AddFileToFolder("special://xbmc/system/", REMOTEMAP, lircmapPath);
491 lircRemotesMap.clear();
492 if(CFile::Exists(lircmapPath))
493 success |= LoadLircMap(lircmapPath);
495 CLog::Log(LOGDEBUG, "CButtonTranslator::Load - no system %s found, skipping", REMOTEMAP);
497 lircmapPath = g_settings.GetUserDataItem(REMOTEMAP);
498 if(CFile::Exists(lircmapPath))
499 success |= LoadLircMap(lircmapPath);
501 CLog::Log(LOGDEBUG, "CButtonTranslator::Load - no userdata %s found, skipping", REMOTEMAP);
504 CLog::Log(LOGERROR, "CButtonTranslator::Load - unable to load remote map %s", REMOTEMAP);
505 // don't return false - it is to only indicate a fatal error (which this is not)
513 bool CButtonTranslator::LoadKeymap(const CStdString &keymapPath)
517 CLog::Log(LOGINFO, "Loading %s", keymapPath.c_str());
518 if (!xmlDoc.LoadFile(keymapPath))
520 CLog::Log(LOGERROR, "Error loading keymap: %s, Line %d\n%s", keymapPath.c_str(), xmlDoc.ErrorRow(), xmlDoc.ErrorDesc());
523 TiXmlElement* pRoot = xmlDoc.RootElement();
524 CStdString strValue = pRoot->Value();
525 if ( strValue != "keymap")
527 CLog::Log(LOGERROR, "%s Doesn't contain <keymap>", keymapPath.c_str());
530 // run through our window groups
531 TiXmlNode* pWindow = pRoot->FirstChild();
534 if (pWindow->Type() == TiXmlNode::TINYXML_ELEMENT)
536 int windowID = WINDOW_INVALID;
537 const char *szWindow = pWindow->Value();
540 if (strcmpi(szWindow, "global") == 0)
543 windowID = TranslateWindow(szWindow);
545 MapWindowActions(pWindow, windowID);
547 pWindow = pWindow->NextSibling();
553 #if defined(HAS_LIRC) || defined(HAS_IRSERVERSUITE)
554 bool CButtonTranslator::LoadLircMap(const CStdString &lircmapPath)
557 #define REMOTEMAPTAG "lircmap"
559 #define REMOTEMAPTAG "irssmap"
561 // load our xml file, and fill up our mapping tables
564 // Load the config file
565 CLog::Log(LOGINFO, "Loading %s", lircmapPath.c_str());
566 if (!xmlDoc.LoadFile(lircmapPath))
568 CLog::Log(LOGERROR, "%s, Line %d\n%s", lircmapPath.c_str(), xmlDoc.ErrorRow(), xmlDoc.ErrorDesc());
569 return false; // This is so people who don't have the file won't fail, just warn
572 TiXmlElement* pRoot = xmlDoc.RootElement();
573 CStdString strValue = pRoot->Value();
574 if (strValue != REMOTEMAPTAG)
576 CLog::Log(LOGERROR, "%sl Doesn't contain <%s>", lircmapPath.c_str(), REMOTEMAPTAG);
580 // run through our window groups
581 TiXmlNode* pRemote = pRoot->FirstChild();
584 if (pRemote->Type() == TiXmlNode::TINYXML_ELEMENT)
586 const char *szRemote = pRemote->Value();
589 TiXmlAttribute* pAttr = pRemote->ToElement()->FirstAttribute();
590 const char* szDeviceName = pAttr->Value();
591 MapRemote(pRemote, szDeviceName);
594 pRemote = pRemote->NextSibling();
600 void CButtonTranslator::MapRemote(TiXmlNode *pRemote, const char* szDevice)
602 CLog::Log(LOGINFO, "* Adding remote mapping for device '%s'", szDevice);
603 vector<string> RemoteNames;
604 map<CStdString, lircButtonMap*>::iterator it = lircRemotesMap.find(szDevice);
605 if (it == lircRemotesMap.end())
606 lircRemotesMap[szDevice] = new lircButtonMap;
607 lircButtonMap& buttons = *lircRemotesMap[szDevice];
609 TiXmlElement *pButton = pRemote->FirstChildElement();
612 if (strcmpi(pButton->Value(), "altname")==0)
613 RemoteNames.push_back(string(pButton->GetText()));
616 if (pButton->FirstChild() && pButton->FirstChild()->Value())
617 buttons[pButton->FirstChild()->Value()] = pButton->Value();
620 pButton = pButton->NextSiblingElement();
622 for (vector<string>::iterator it = RemoteNames.begin();
623 it != RemoteNames.end();++it)
625 CLog::Log(LOGINFO, "* Linking remote mapping for '%s' to '%s'", szDevice, it->c_str());
626 lircRemotesMap[*it] = &buttons;
630 int CButtonTranslator::TranslateLircRemoteString(const char* szDevice, const char *szButton)
633 map<CStdString, lircButtonMap*>::iterator it = lircRemotesMap.find(szDevice);
634 if (it == lircRemotesMap.end())
638 lircButtonMap::iterator it2 = (*it).second->find(szButton);
639 if (it2 == (*it).second->end())
642 // Convert the button to code
643 if (strnicmp((*it2).second.c_str(), "obc", 3) == 0)
644 return TranslateUniversalRemoteString((*it2).second.c_str());
646 return TranslateRemoteString((*it2).second.c_str());
650 #if defined(HAS_SDL_JOYSTICK) || defined(HAS_EVENT_SERVER)
651 void CButtonTranslator::MapJoystickActions(int windowID, TiXmlNode *pJoystick)
653 string joyname = "_xbmc_"; // default global map name
654 vector<string> joynames;
655 map<int, string> buttonMap;
656 map<int, string> axisMap;
657 map<int, string> hatMap;
659 TiXmlElement *pJoy = pJoystick->ToElement();
660 if (pJoy && pJoy->Attribute("name"))
661 joyname = pJoy->Attribute("name");
663 CLog::Log(LOGNOTICE, "No Joystick name specified, loading default map");
665 joynames.push_back(joyname);
668 TiXmlElement *pButton = pJoystick->FirstChildElement();
672 const char *szAction;
675 szType = pButton->Value();
676 szAction = pButton->GetText();
677 if (szAction == NULL)
681 if ((pButton->QueryIntAttribute("id", &id) == TIXML_SUCCESS) && id>=0 && id<=256)
683 if (strcmpi(szType, "button")==0)
685 buttonMap[id] = string(szAction);
687 else if (strcmpi(szType, "axis")==0)
690 if (pButton->QueryIntAttribute("limit", &limit) == TIXML_SUCCESS)
693 axisMap[-id] = string(szAction);
695 axisMap[id] = string(szAction);
697 axisMap[id|0xFFFF0000] = string(szAction);
700 axisMap[id] = string(szAction);
701 axisMap[-id] = string(szAction);
702 CLog::Log(LOGERROR, "Error in joystick map, invalid limit specified %d for axis %d", limit, id);
707 axisMap[id] = string(szAction);
708 axisMap[-id] = string(szAction);
711 else if (strcmpi(szType, "hat")==0)
714 if (pButton->QueryValueAttribute("position", &position) == TIXML_SUCCESS)
716 uint32_t hatID = id|0xFFF00000;
717 if (position.compare("up") == 0)
718 hatMap[(JACTIVE_HAT_UP<<16)|hatID] = string(szAction);
719 else if (position.compare("down") == 0)
720 hatMap[(JACTIVE_HAT_DOWN<<16)|hatID] = string(szAction);
721 else if (position.compare("right") == 0)
722 hatMap[(JACTIVE_HAT_RIGHT<<16)|hatID] = string(szAction);
723 else if (position.compare("left") == 0)
724 hatMap[(JACTIVE_HAT_LEFT<<16)|hatID] = string(szAction);
726 CLog::Log(LOGERROR, "Error in joystick map, invalid position specified %s for axis %d", position.c_str(), id);
730 CLog::Log(LOGERROR, "Error reading joystick map element, unknown button type: %s", szType);
732 else if (strcmpi(szType, "altname")==0)
733 joynames.push_back(string(szAction));
735 CLog::Log(LOGERROR, "Error reading joystick map element, Invalid id: %d", id);
738 CLog::Log(LOGERROR, "Error reading joystick map element, skipping");
740 pButton = pButton->NextSiblingElement();
742 vector<string>::iterator it = joynames.begin();
743 while (it!=joynames.end())
745 m_joystickButtonMap[*it][windowID] = buttonMap;
746 m_joystickAxisMap[*it][windowID] = axisMap;
747 m_joystickHatMap[*it][windowID] = hatMap;
748 // CLog::Log(LOGDEBUG, "Found Joystick map for window %d using %s", windowID, it->c_str());
753 bool CButtonTranslator::TranslateJoystickString(int window, const char* szDevice, int id, short inputType, int& action, CStdString& strAction, bool &fullrange)
757 map<string, JoystickMap>::iterator it;
758 map<string, JoystickMap> *jmap;
761 if (inputType == JACTIVE_AXIS)
762 jmap = &m_joystickAxisMap;
763 else if (inputType == JACTIVE_BUTTON)
764 jmap = &m_joystickButtonMap;
765 else if (inputType == JACTIVE_HAT)
766 jmap = &m_joystickHatMap;
769 CLog::Log(LOGERROR, "Error reading joystick input type");
773 it = jmap->find(szDevice);
777 JoystickMap wmap = it->second;
778 JoystickMap::iterator it2;
779 map<int, string> windowbmap;
780 map<int, string> globalbmap;
781 map<int, string>::iterator it3;
783 it2 = wmap.find(window);
785 // first try local window map
788 windowbmap = it2->second;
789 it3 = windowbmap.find(id);
790 if (it3 != windowbmap.end())
792 strAction = (it3->second).c_str();
795 it3 = windowbmap.find(abs(id)|0xFFFF0000);
796 if (it3 != windowbmap.end())
798 strAction = (it3->second).c_str();
803 it3 = windowbmap.find(id|0xFFF00000);
804 if (it3 != windowbmap.end())
806 strAction = (it3->second).c_str();
811 // if not found, try global map
815 if (it2 != wmap.end())
817 globalbmap = it2->second;
818 it3 = globalbmap.find(id);
819 if (it3 != globalbmap.end())
821 strAction = (it3->second).c_str();
824 it3 = globalbmap.find(abs(id)|0xFFFF0000);
825 if (it3 != globalbmap.end())
827 strAction = (it3->second).c_str();
831 it3 = globalbmap.find(id|0xFFF00000);
832 if (it3 != globalbmap.end())
834 strAction = (it3->second).c_str();
840 // translated found action
842 return TranslateActionString(strAction.c_str(), action);
848 CAction CButtonTranslator::GetAction(int window, const CKey &key, bool fallback)
850 CStdString strAction;
851 // try to get the action from the current window
852 int actionID = GetActionCode(window, key, strAction);
853 // if it's invalid, try to get it from the global map
854 if (actionID == 0 && fallback)
855 actionID = GetActionCode( -1, key, strAction);
856 // Now fill our action structure
857 CAction action(actionID, strAction, key);
861 int CButtonTranslator::GetActionCode(int window, const CKey &key, CStdString &strAction) const
863 uint32_t code = key.GetButtonCode();
865 map<int, buttonMap>::const_iterator it = m_translatorMap.find(window);
866 if (it == m_translatorMap.end())
868 buttonMap::const_iterator it2 = (*it).second.find(code);
870 while (it2 != (*it).second.end())
872 action = (*it2).second.id;
873 strAction = (*it2).second.strID;
874 it2 = (*it).second.end();
877 // Some buttoncodes changed in Hardy
878 if (action == 0 && (code & KEY_VKEY) == KEY_VKEY && (code & 0x0F00))
880 CLog::Log(LOGDEBUG, "%s: Trying Hardy keycode for %#04x", __FUNCTION__, code);
882 buttonMap::const_iterator it2 = (*it).second.find(code);
883 while (it2 != (*it).second.end())
885 action = (*it2).second.id;
886 strAction = (*it2).second.strID;
887 it2 = (*it).second.end();
894 void CButtonTranslator::MapAction(uint32_t buttonCode, const char *szAction, buttonMap &map)
896 int action = ACTION_NONE;
897 if (!TranslateActionString(szAction, action) || !buttonCode)
898 return; // no valid action, or an invalid buttoncode
899 // have a valid action, and a valid button - map it.
900 // check to see if we've already got this (button,action) pair defined
901 buttonMap::iterator it = map.find(buttonCode);
902 if (it == map.end() || (*it).second.id != action || (*it).second.strID != szAction)
904 // NOTE: This multimap is only being used as a normal map at this point (no support
905 // for multiple actions per key)
908 CButtonAction button;
910 button.strID = szAction;
911 map.insert(pair<uint32_t, CButtonAction>(buttonCode, button));
915 bool CButtonTranslator::HasDeviceType(TiXmlNode *pWindow, CStdString type)
917 return pWindow->FirstChild(type) != NULL;
920 void CButtonTranslator::MapWindowActions(TiXmlNode *pWindow, int windowID)
922 if (!pWindow || windowID == WINDOW_INVALID)
927 const char* types[] = {"gamepad", "remote", "universalremote", "keyboard", "mouse", "appcommand", NULL};
928 for (int i = 0; types[i]; ++i)
930 CStdString type(types[i]);
931 if (HasDeviceType(pWindow, type))
934 std::map<int, buttonMap>::iterator it = m_translatorMap.find(windowID);
935 if (it != m_translatorMap.end())
938 m_translatorMap.erase(it);
941 pDevice = pWindow->FirstChild(type);
943 TiXmlElement *pButton = pDevice->FirstChildElement();
947 uint32_t buttonCode=0;
948 if (type == "gamepad")
949 buttonCode = TranslateGamepadString(pButton->Value());
950 else if (type == "remote")
951 buttonCode = TranslateRemoteString(pButton->Value());
952 else if (type == "universalremote")
953 buttonCode = TranslateUniversalRemoteString(pButton->Value());
954 else if (type == "keyboard")
955 buttonCode = TranslateKeyboardButton(pButton);
956 else if (type == "mouse")
957 buttonCode = TranslateMouseCommand(pButton->Value());
958 else if (type == "appcommand")
959 buttonCode = TranslateAppCommand(pButton->Value());
961 if (buttonCode && pButton->FirstChild())
962 MapAction(buttonCode, pButton->FirstChild()->Value(), map);
963 pButton = pButton->NextSiblingElement();
966 // add our map to our table
968 m_translatorMap.insert(pair<int, buttonMap>( windowID, map));
972 #if defined(HAS_SDL_JOYSTICK) || defined(HAS_EVENT_SERVER)
973 if ((pDevice = pWindow->FirstChild("joystick")) != NULL)
975 // map joystick actions
978 MapJoystickActions(windowID, pDevice);
979 pDevice = pDevice->NextSibling("joystick");
985 bool CButtonTranslator::TranslateActionString(const char *szAction, int &action)
987 action = ACTION_NONE;
988 CStdString strAction = szAction;
990 if (CBuiltins::HasCommand(strAction))
991 action = ACTION_BUILT_IN_FUNCTION;
993 for (unsigned int index=0;index < sizeof(actions)/sizeof(actions[0]);++index)
995 if (strAction.Equals(actions[index].name))
997 action = actions[index].action;
1002 if (action == ACTION_NONE)
1004 CLog::Log(LOGERROR, "Keymapping error: no such action '%s' defined", strAction.c_str());
1011 CStdString CButtonTranslator::TranslateWindow(int windowID)
1013 for (unsigned int index = 0; index < sizeof(windows) / sizeof(windows[0]); ++index)
1015 if (windows[index].action == windowID)
1016 return windows[index].name;
1021 int CButtonTranslator::TranslateWindow(const CStdString &window)
1023 CStdString strWindow(window);
1024 if (strWindow.IsEmpty())
1025 return WINDOW_INVALID;
1026 strWindow.ToLower();
1028 if (strWindow.Mid(strWindow.GetLength() - 4) == ".xml" )
1029 strWindow = strWindow.Mid(0, strWindow.GetLength() - 4);
1031 // window12345, for custom window to be keymapped
1032 if (strWindow.length() > 6 && strWindow.Left(6).Equals("window"))
1033 strWindow = strWindow.Mid(6);
1034 if (strWindow.Left(2) == "my") // drop "my" prefix
1035 strWindow = strWindow.Mid(2);
1036 if (StringUtils::IsNaturalNumber(strWindow))
1038 // allow a full window id or a delta id
1039 int iWindow = atoi(strWindow.c_str());
1040 if (iWindow > WINDOW_INVALID)
1042 return WINDOW_HOME + iWindow;
1045 // run through the window structure
1046 for (unsigned int index = 0; index < sizeof(windows) / sizeof(windows[0]); ++index)
1048 if (strWindow.Equals(windows[index].name))
1049 return windows[index].action;
1052 CLog::Log(LOGERROR, "Window Translator: Can't find window %s", strWindow.c_str());
1053 return WINDOW_INVALID;
1056 uint32_t CButtonTranslator::TranslateGamepadString(const char *szButton)
1060 uint32_t buttonCode = 0;
1061 CStdString strButton = szButton;
1062 strButton.ToLower();
1063 if (strButton.Equals("a")) buttonCode = KEY_BUTTON_A;
1064 else if (strButton.Equals("b")) buttonCode = KEY_BUTTON_B;
1065 else if (strButton.Equals("x")) buttonCode = KEY_BUTTON_X;
1066 else if (strButton.Equals("y")) buttonCode = KEY_BUTTON_Y;
1067 else if (strButton.Equals("white")) buttonCode = KEY_BUTTON_WHITE;
1068 else if (strButton.Equals("black")) buttonCode = KEY_BUTTON_BLACK;
1069 else if (strButton.Equals("start")) buttonCode = KEY_BUTTON_START;
1070 else if (strButton.Equals("back")) buttonCode = KEY_BUTTON_BACK;
1071 else if (strButton.Equals("leftthumbbutton")) buttonCode = KEY_BUTTON_LEFT_THUMB_BUTTON;
1072 else if (strButton.Equals("rightthumbbutton")) buttonCode = KEY_BUTTON_RIGHT_THUMB_BUTTON;
1073 else if (strButton.Equals("leftthumbstick")) buttonCode = KEY_BUTTON_LEFT_THUMB_STICK;
1074 else if (strButton.Equals("leftthumbstickup")) buttonCode = KEY_BUTTON_LEFT_THUMB_STICK_UP;
1075 else if (strButton.Equals("leftthumbstickdown")) buttonCode = KEY_BUTTON_LEFT_THUMB_STICK_DOWN;
1076 else if (strButton.Equals("leftthumbstickleft")) buttonCode = KEY_BUTTON_LEFT_THUMB_STICK_LEFT;
1077 else if (strButton.Equals("leftthumbstickright")) buttonCode = KEY_BUTTON_LEFT_THUMB_STICK_RIGHT;
1078 else if (strButton.Equals("rightthumbstick")) buttonCode = KEY_BUTTON_RIGHT_THUMB_STICK;
1079 else if (strButton.Equals("rightthumbstickup")) buttonCode = KEY_BUTTON_RIGHT_THUMB_STICK_UP;
1080 else if (strButton.Equals("rightthumbstickdown")) buttonCode = KEY_BUTTON_RIGHT_THUMB_STICK_DOWN;
1081 else if (strButton.Equals("rightthumbstickleft")) buttonCode = KEY_BUTTON_RIGHT_THUMB_STICK_LEFT;
1082 else if (strButton.Equals("rightthumbstickright")) buttonCode = KEY_BUTTON_RIGHT_THUMB_STICK_RIGHT;
1083 else if (strButton.Equals("lefttrigger")) buttonCode = KEY_BUTTON_LEFT_TRIGGER;
1084 else if (strButton.Equals("righttrigger")) buttonCode = KEY_BUTTON_RIGHT_TRIGGER;
1085 else if (strButton.Equals("leftanalogtrigger")) buttonCode = KEY_BUTTON_LEFT_ANALOG_TRIGGER;
1086 else if (strButton.Equals("rightanalogtrigger")) buttonCode = KEY_BUTTON_RIGHT_ANALOG_TRIGGER;
1087 else if (strButton.Equals("dpadleft")) buttonCode = KEY_BUTTON_DPAD_LEFT;
1088 else if (strButton.Equals("dpadright")) buttonCode = KEY_BUTTON_DPAD_RIGHT;
1089 else if (strButton.Equals("dpadup")) buttonCode = KEY_BUTTON_DPAD_UP;
1090 else if (strButton.Equals("dpaddown")) buttonCode = KEY_BUTTON_DPAD_DOWN;
1091 else CLog::Log(LOGERROR, "Gamepad Translator: Can't find button %s", strButton.c_str());
1095 uint32_t CButtonTranslator::TranslateRemoteString(const char *szButton)
1099 uint32_t buttonCode = 0;
1100 CStdString strButton = szButton;
1101 strButton.ToLower();
1102 if (strButton.Equals("left")) buttonCode = XINPUT_IR_REMOTE_LEFT;
1103 else if (strButton.Equals("right")) buttonCode = XINPUT_IR_REMOTE_RIGHT;
1104 else if (strButton.Equals("up")) buttonCode = XINPUT_IR_REMOTE_UP;
1105 else if (strButton.Equals("down")) buttonCode = XINPUT_IR_REMOTE_DOWN;
1106 else if (strButton.Equals("select")) buttonCode = XINPUT_IR_REMOTE_SELECT;
1107 else if (strButton.Equals("back")) buttonCode = XINPUT_IR_REMOTE_BACK;
1108 else if (strButton.Equals("menu")) buttonCode = XINPUT_IR_REMOTE_MENU;
1109 else if (strButton.Equals("info")) buttonCode = XINPUT_IR_REMOTE_INFO;
1110 else if (strButton.Equals("display")) buttonCode = XINPUT_IR_REMOTE_DISPLAY;
1111 else if (strButton.Equals("title")) buttonCode = XINPUT_IR_REMOTE_TITLE;
1112 else if (strButton.Equals("play")) buttonCode = XINPUT_IR_REMOTE_PLAY;
1113 else if (strButton.Equals("pause")) buttonCode = XINPUT_IR_REMOTE_PAUSE;
1114 else if (strButton.Equals("reverse")) buttonCode = XINPUT_IR_REMOTE_REVERSE;
1115 else if (strButton.Equals("forward")) buttonCode = XINPUT_IR_REMOTE_FORWARD;
1116 else if (strButton.Equals("skipplus")) buttonCode = XINPUT_IR_REMOTE_SKIP_PLUS;
1117 else if (strButton.Equals("skipminus")) buttonCode = XINPUT_IR_REMOTE_SKIP_MINUS;
1118 else if (strButton.Equals("stop")) buttonCode = XINPUT_IR_REMOTE_STOP;
1119 else if (strButton.Equals("zero")) buttonCode = XINPUT_IR_REMOTE_0;
1120 else if (strButton.Equals("one")) buttonCode = XINPUT_IR_REMOTE_1;
1121 else if (strButton.Equals("two")) buttonCode = XINPUT_IR_REMOTE_2;
1122 else if (strButton.Equals("three")) buttonCode = XINPUT_IR_REMOTE_3;
1123 else if (strButton.Equals("four")) buttonCode = XINPUT_IR_REMOTE_4;
1124 else if (strButton.Equals("five")) buttonCode = XINPUT_IR_REMOTE_5;
1125 else if (strButton.Equals("six")) buttonCode = XINPUT_IR_REMOTE_6;
1126 else if (strButton.Equals("seven")) buttonCode = XINPUT_IR_REMOTE_7;
1127 else if (strButton.Equals("eight")) buttonCode = XINPUT_IR_REMOTE_8;
1128 else if (strButton.Equals("nine")) buttonCode = XINPUT_IR_REMOTE_9;
1129 // additional keys from the media center extender for xbox remote
1130 else if (strButton.Equals("power")) buttonCode = XINPUT_IR_REMOTE_POWER;
1131 else if (strButton.Equals("mytv")) buttonCode = XINPUT_IR_REMOTE_MY_TV;
1132 else if (strButton.Equals("mymusic")) buttonCode = XINPUT_IR_REMOTE_MY_MUSIC;
1133 else if (strButton.Equals("mypictures")) buttonCode = XINPUT_IR_REMOTE_MY_PICTURES;
1134 else if (strButton.Equals("myvideo")) buttonCode = XINPUT_IR_REMOTE_MY_VIDEOS;
1135 else if (strButton.Equals("record")) buttonCode = XINPUT_IR_REMOTE_RECORD;
1136 else if (strButton.Equals("start")) buttonCode = XINPUT_IR_REMOTE_START;
1137 else if (strButton.Equals("volumeplus")) buttonCode = XINPUT_IR_REMOTE_VOLUME_PLUS;
1138 else if (strButton.Equals("volumeminus")) buttonCode = XINPUT_IR_REMOTE_VOLUME_MINUS;
1139 else if (strButton.Equals("channelplus")) buttonCode = XINPUT_IR_REMOTE_CHANNEL_PLUS;
1140 else if (strButton.Equals("channelminus")) buttonCode = XINPUT_IR_REMOTE_CHANNEL_MINUS;
1141 else if (strButton.Equals("pageplus")) buttonCode = XINPUT_IR_REMOTE_CHANNEL_PLUS;
1142 else if (strButton.Equals("pageminus")) buttonCode = XINPUT_IR_REMOTE_CHANNEL_MINUS;
1143 else if (strButton.Equals("mute")) buttonCode = XINPUT_IR_REMOTE_MUTE;
1144 else if (strButton.Equals("recordedtv")) buttonCode = XINPUT_IR_REMOTE_RECORDED_TV;
1145 else if (strButton.Equals("guide")) buttonCode = XINPUT_IR_REMOTE_TITLE; // same as title
1146 else if (strButton.Equals("livetv")) buttonCode = XINPUT_IR_REMOTE_LIVE_TV;
1147 else if (strButton.Equals("star")) buttonCode = XINPUT_IR_REMOTE_STAR;
1148 else if (strButton.Equals("hash")) buttonCode = XINPUT_IR_REMOTE_HASH;
1149 else if (strButton.Equals("clear")) buttonCode = XINPUT_IR_REMOTE_CLEAR;
1150 else if (strButton.Equals("enter")) buttonCode = XINPUT_IR_REMOTE_ENTER;
1151 else if (strButton.Equals("xbox")) buttonCode = XINPUT_IR_REMOTE_DISPLAY; // same as display
1152 else if (strButton.Equals("teletext")) buttonCode = XINPUT_IR_REMOTE_TELETEXT;
1153 else if (strButton.Equals("red")) buttonCode = XINPUT_IR_REMOTE_RED;
1154 else if (strButton.Equals("green")) buttonCode = XINPUT_IR_REMOTE_GREEN;
1155 else if (strButton.Equals("yellow")) buttonCode = XINPUT_IR_REMOTE_YELLOW;
1156 else if (strButton.Equals("blue")) buttonCode = XINPUT_IR_REMOTE_BLUE;
1157 else if (strButton.Equals("subtitle")) buttonCode = XINPUT_IR_REMOTE_SUBTITLE;
1158 else if (strButton.Equals("language")) buttonCode = XINPUT_IR_REMOTE_LANGUAGE;
1159 else CLog::Log(LOGERROR, "Remote Translator: Can't find button %s", strButton.c_str());
1163 uint32_t CButtonTranslator::TranslateUniversalRemoteString(const char *szButton)
1165 if (!szButton || strlen(szButton) < 4 || strnicmp(szButton, "obc", 3))
1167 const char *szCode = szButton + 3;
1168 // Button Code is 255 - OBC (Original Button Code) of the button
1169 uint32_t buttonCode = 255 - atol(szCode);
1170 if (buttonCode > 255)
1175 uint32_t CButtonTranslator::TranslateKeyboardString(const char *szButton)
1177 uint32_t buttonCode = 0;
1178 XBMCKEYTABLE keytable;
1180 // Look up the key name
1181 if (KeyTableLookupName(szButton, &keytable))
1183 buttonCode = keytable.vkey;
1186 // The lookup failed i.e. the key name wasn't found
1189 CLog::Log(LOGERROR, "Keyboard Translator: Can't find button %s", szButton);
1192 buttonCode |= KEY_VKEY;
1197 uint32_t CButtonTranslator::TranslateKeyboardButton(TiXmlElement *pButton)
1199 uint32_t button_id = 0;
1200 const char *szButton = pButton->Value();
1204 CStdString strKey = szButton;
1205 if (strKey.Equals("key"))
1208 if (pButton->QueryIntAttribute("id", &id) == TIXML_SUCCESS)
1209 button_id = (uint32_t)id;
1211 CLog::Log(LOGERROR, "Keyboard Translator: `key' button has no id");
1214 button_id = TranslateKeyboardString(szButton);
1216 // Process the ctrl/shift/alt modifiers
1218 if (pButton->QueryValueAttribute("mod", &strMod) == TIXML_SUCCESS)
1222 CStdStringArray modArray;
1223 StringUtils::SplitString(strMod, ",", modArray);
1224 for (unsigned int i = 0; i < modArray.size(); i++)
1226 CStdString& substr = modArray[i];
1229 if (substr == "ctrl" || substr == "control")
1230 button_id |= CKey::MODIFIER_CTRL;
1231 else if (substr == "shift")
1232 button_id |= CKey::MODIFIER_SHIFT;
1233 else if (substr == "alt")
1234 button_id |= CKey::MODIFIER_ALT;
1235 else if (substr == "super" || substr == "win")
1236 button_id |= CKey::MODIFIER_SUPER;
1238 CLog::Log(LOGERROR, "Keyboard Translator: Unknown key modifier %s in %s", substr.c_str(), strMod.c_str());
1245 uint32_t CButtonTranslator::TranslateAppCommand(const char *szButton)
1248 CStdString strAppCommand = szButton;
1249 strAppCommand.ToLower();
1251 for (int i = 0; i < sizeof(appcommands)/sizeof(appcommands[0]); i++)
1252 if (strAppCommand.Equals(appcommands[i].name))
1253 return appcommands[i].action | KEY_APPCOMMAND;
1255 CLog::Log(LOGERROR, "%s: Can't find appcommand %s", __FUNCTION__, szButton);
1261 uint32_t CButtonTranslator::TranslateMouseCommand(const char *szButton)
1263 CStdString strMouseCommand = szButton;
1264 strMouseCommand.ToLower();
1266 for (unsigned int i = 0; i < sizeof(mousecommands)/sizeof(mousecommands[0]); i++)
1267 if (strMouseCommand.Equals(mousecommands[i].name))
1268 return mousecommands[i].action | KEY_MOUSE;
1270 CLog::Log(LOGERROR, "%s: Can't find mouse command %s", __FUNCTION__, szButton);
1275 void CButtonTranslator::Clear()
1277 m_translatorMap.clear();
1278 #if defined(HAS_LIRC) || defined(HAS_IRSERVERSUITE)
1279 lircRemotesMap.clear();
1282 #if defined(HAS_SDL_JOYSTICK) || defined(HAS_EVENT_SERVER)
1283 m_joystickButtonMap.clear();
1284 m_joystickAxisMap.clear();
1285 m_joystickHatMap.clear();