VTB: release CVBuffer after it actually has been rendered
[xbmc.git] / xbmc / input / ButtonTranslator.cpp
blob84a02eb08f6dd96825383b29609dd194a8a16b8f
1 /*
2 * Copyright (C) 2005-2015 Team XBMC
3 * http://kodi.tv
5 * This Program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2, or (at your option)
8 * any later version.
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 Kodi; see the file COPYING. If not, see
17 * <http://www.gnu.org/licenses/>.
21 #include "ButtonTranslator.h"
23 #include <algorithm>
24 #include <utility>
26 #include "FileItem.h"
27 #include "filesystem/Directory.h"
28 #include "filesystem/File.h"
29 #include "guilib/WindowIDs.h"
30 #include "input/Key.h"
31 #include "input/MouseStat.h"
32 #include "input/XBMC_keytable.h"
33 #include "interfaces/builtins/Builtins.h"
34 #include "profiles/ProfilesManager.h"
35 #include "system.h"
36 #include "Util.h"
37 #include "utils/log.h"
38 #include "utils/RegExp.h"
39 #include "utils/StringUtils.h"
40 #include "utils/URIUtils.h"
41 #include "utils/XBMCTinyXML.h"
42 #include "XBIRRemote.h"
44 using namespace XFILE;
46 typedef struct
48 const char* name;
49 int action;
50 } ActionMapping;
52 typedef struct
54 int origin;
55 int target;
56 } WindowMapping;
58 static const ActionMapping actions[] =
60 { "left" , ACTION_MOVE_LEFT },
61 { "right" , ACTION_MOVE_RIGHT },
62 { "up" , ACTION_MOVE_UP },
63 { "down" , ACTION_MOVE_DOWN },
64 { "pageup" , ACTION_PAGE_UP },
65 { "pagedown" , ACTION_PAGE_DOWN },
66 { "select" , ACTION_SELECT_ITEM },
67 { "highlight" , ACTION_HIGHLIGHT_ITEM },
68 { "parentdir" , ACTION_NAV_BACK }, // backward compatibility
69 { "parentfolder" , ACTION_PARENT_DIR },
70 { "back" , ACTION_NAV_BACK },
71 { "menu" , ACTION_MENU},
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 { "chapterorbigstepforward" , ACTION_CHAPTER_OR_BIG_STEP_FORWARD },
85 { "chapterorbigstepback" , ACTION_CHAPTER_OR_BIG_STEP_BACK },
86 { "osd" , ACTION_SHOW_OSD },
87 { "showsubtitles" , ACTION_SHOW_SUBTITLES },
88 { "nextsubtitle" , ACTION_NEXT_SUBTITLE },
89 { "cyclesubtitle" , ACTION_CYCLE_SUBTITLE },
90 { "playerdebug" , ACTION_PLAYER_DEBUG },
91 { "playerprocessinfo" , ACTION_PLAYER_PROCESS_INFO },
92 { "nextpicture" , ACTION_NEXT_PICTURE },
93 { "previouspicture" , ACTION_PREV_PICTURE },
94 { "zoomout" , ACTION_ZOOM_OUT },
95 { "zoomin" , ACTION_ZOOM_IN },
96 { "playlist" , ACTION_SHOW_PLAYLIST },
97 { "queue" , ACTION_QUEUE_ITEM },
98 { "zoomnormal" , ACTION_ZOOM_LEVEL_NORMAL },
99 { "zoomlevel1" , ACTION_ZOOM_LEVEL_1 },
100 { "zoomlevel2" , ACTION_ZOOM_LEVEL_2 },
101 { "zoomlevel3" , ACTION_ZOOM_LEVEL_3 },
102 { "zoomlevel4" , ACTION_ZOOM_LEVEL_4 },
103 { "zoomlevel5" , ACTION_ZOOM_LEVEL_5 },
104 { "zoomlevel6" , ACTION_ZOOM_LEVEL_6 },
105 { "zoomlevel7" , ACTION_ZOOM_LEVEL_7 },
106 { "zoomlevel8" , ACTION_ZOOM_LEVEL_8 },
107 { "zoomlevel9" , ACTION_ZOOM_LEVEL_9 },
108 { "nextcalibration" , ACTION_CALIBRATE_SWAP_ARROWS },
109 { "resetcalibration" , ACTION_CALIBRATE_RESET },
110 { "analogmove" , ACTION_ANALOG_MOVE },
111 { "analogmovex" , ACTION_ANALOG_MOVE_X },
112 { "analogmovey" , ACTION_ANALOG_MOVE_Y },
113 { "rotate" , ACTION_ROTATE_PICTURE_CW },
114 { "rotateccw" , ACTION_ROTATE_PICTURE_CCW },
115 { "close" , ACTION_NAV_BACK }, // backwards compatibility
116 { "subtitledelayminus" , ACTION_SUBTITLE_DELAY_MIN },
117 { "subtitledelay" , ACTION_SUBTITLE_DELAY },
118 { "subtitledelayplus" , ACTION_SUBTITLE_DELAY_PLUS },
119 { "audiodelayminus" , ACTION_AUDIO_DELAY_MIN },
120 { "audiodelay" , ACTION_AUDIO_DELAY },
121 { "audiodelayplus" , ACTION_AUDIO_DELAY_PLUS },
122 { "subtitleshiftup" , ACTION_SUBTITLE_VSHIFT_UP },
123 { "subtitleshiftdown" , ACTION_SUBTITLE_VSHIFT_DOWN },
124 { "subtitlealign" , ACTION_SUBTITLE_ALIGN },
125 { "audionextlanguage" , ACTION_AUDIO_NEXT_LANGUAGE },
126 { "verticalshiftup" , ACTION_VSHIFT_UP },
127 { "verticalshiftdown" , ACTION_VSHIFT_DOWN },
128 { "nextresolution" , ACTION_CHANGE_RESOLUTION },
129 { "audiotoggledigital" , ACTION_TOGGLE_DIGITAL_ANALOG },
130 { "number0" , REMOTE_0 },
131 { "number1" , REMOTE_1 },
132 { "number2" , REMOTE_2 },
133 { "number3" , REMOTE_3 },
134 { "number4" , REMOTE_4 },
135 { "number5" , REMOTE_5 },
136 { "number6" , REMOTE_6 },
137 { "number7" , REMOTE_7 },
138 { "number8" , REMOTE_8 },
139 { "number9" , REMOTE_9 },
140 { "smallstepback" , ACTION_SMALL_STEP_BACK },
141 { "fastforward" , ACTION_PLAYER_FORWARD },
142 { "rewind" , ACTION_PLAYER_REWIND },
143 { "play" , ACTION_PLAYER_PLAY },
144 { "playpause" , ACTION_PLAYER_PLAYPAUSE },
145 { "switchplayer" , ACTION_SWITCH_PLAYER },
146 { "delete" , ACTION_DELETE_ITEM },
147 { "copy" , ACTION_COPY_ITEM },
148 { "move" , ACTION_MOVE_ITEM },
149 { "screenshot" , ACTION_TAKE_SCREENSHOT },
150 { "rename" , ACTION_RENAME_ITEM },
151 { "togglewatched" , ACTION_TOGGLE_WATCHED },
152 { "scanitem" , ACTION_SCAN_ITEM },
153 { "reloadkeymaps" , ACTION_RELOAD_KEYMAPS },
154 { "volumeup" , ACTION_VOLUME_UP },
155 { "volumedown" , ACTION_VOLUME_DOWN },
156 { "mute" , ACTION_MUTE },
157 { "backspace" , ACTION_BACKSPACE },
158 { "scrollup" , ACTION_SCROLL_UP },
159 { "scrolldown" , ACTION_SCROLL_DOWN },
160 { "analogfastforward" , ACTION_ANALOG_FORWARD },
161 { "analogrewind" , ACTION_ANALOG_REWIND },
162 { "moveitemup" , ACTION_MOVE_ITEM_UP },
163 { "moveitemdown" , ACTION_MOVE_ITEM_DOWN },
164 { "contextmenu" , ACTION_CONTEXT_MENU },
165 { "shift" , ACTION_SHIFT },
166 { "symbols" , ACTION_SYMBOLS },
167 { "cursorleft" , ACTION_CURSOR_LEFT },
168 { "cursorright" , ACTION_CURSOR_RIGHT },
169 { "showtime" , ACTION_SHOW_OSD_TIME },
170 { "analogseekforward" , ACTION_ANALOG_SEEK_FORWARD },
171 { "analogseekback" , ACTION_ANALOG_SEEK_BACK },
172 { "showpreset" , ACTION_VIS_PRESET_SHOW },
173 { "nextpreset" , ACTION_VIS_PRESET_NEXT },
174 { "previouspreset" , ACTION_VIS_PRESET_PREV },
175 { "lockpreset" , ACTION_VIS_PRESET_LOCK },
176 { "randompreset" , ACTION_VIS_PRESET_RANDOM },
177 { "increasevisrating" , ACTION_VIS_RATE_PRESET_PLUS },
178 { "decreasevisrating" , ACTION_VIS_RATE_PRESET_MINUS },
179 { "showvideomenu" , ACTION_SHOW_VIDEOMENU },
180 { "enter" , ACTION_ENTER },
181 { "increaserating" , ACTION_INCREASE_RATING },
182 { "decreaserating" , ACTION_DECREASE_RATING },
183 { "setrating" , ACTION_SET_RATING },
184 { "togglefullscreen" , ACTION_TOGGLE_FULLSCREEN },
185 { "nextscene" , ACTION_NEXT_SCENE },
186 { "previousscene" , ACTION_PREV_SCENE },
187 { "nextletter" , ACTION_NEXT_LETTER },
188 { "prevletter" , ACTION_PREV_LETTER },
189 { "jumpsms2" , ACTION_JUMP_SMS2 },
190 { "jumpsms3" , ACTION_JUMP_SMS3 },
191 { "jumpsms4" , ACTION_JUMP_SMS4 },
192 { "jumpsms5" , ACTION_JUMP_SMS5 },
193 { "jumpsms6" , ACTION_JUMP_SMS6 },
194 { "jumpsms7" , ACTION_JUMP_SMS7 },
195 { "jumpsms8" , ACTION_JUMP_SMS8 },
196 { "jumpsms9" , ACTION_JUMP_SMS9 },
197 { "filter" , ACTION_FILTER },
198 { "filterclear" , ACTION_FILTER_CLEAR },
199 { "filtersms2" , ACTION_FILTER_SMS2 },
200 { "filtersms3" , ACTION_FILTER_SMS3 },
201 { "filtersms4" , ACTION_FILTER_SMS4 },
202 { "filtersms5" , ACTION_FILTER_SMS5 },
203 { "filtersms6" , ACTION_FILTER_SMS6 },
204 { "filtersms7" , ACTION_FILTER_SMS7 },
205 { "filtersms8" , ACTION_FILTER_SMS8 },
206 { "filtersms9" , ACTION_FILTER_SMS9 },
207 { "firstpage" , ACTION_FIRST_PAGE },
208 { "lastpage" , ACTION_LAST_PAGE },
209 { "guiprofile" , ACTION_GUIPROFILE_BEGIN },
210 { "red" , ACTION_TELETEXT_RED },
211 { "green" , ACTION_TELETEXT_GREEN },
212 { "yellow" , ACTION_TELETEXT_YELLOW },
213 { "blue" , ACTION_TELETEXT_BLUE },
214 { "increasepar" , ACTION_INCREASE_PAR },
215 { "decreasepar" , ACTION_DECREASE_PAR },
216 { "volampup" , ACTION_VOLAMP_UP },
217 { "volampdown" , ACTION_VOLAMP_DOWN },
218 { "volumeamplification" , ACTION_VOLAMP },
219 { "createbookmark" , ACTION_CREATE_BOOKMARK },
220 { "createepisodebookmark" , ACTION_CREATE_EPISODE_BOOKMARK },
221 { "settingsreset" , ACTION_SETTINGS_RESET },
222 { "settingslevelchange" , ACTION_SETTINGS_LEVEL_CHANGE },
224 // 3D movie playback/GUI
225 { "stereomode" , ACTION_STEREOMODE_SELECT }, // cycle 3D modes, for now an alias for next
226 { "nextstereomode" , ACTION_STEREOMODE_NEXT },
227 { "previousstereomode" , ACTION_STEREOMODE_PREVIOUS },
228 { "togglestereomode" , ACTION_STEREOMODE_TOGGLE },
229 { "stereomodetomono" , ACTION_STEREOMODE_TOMONO },
231 // PVR actions
232 { "channelup" , ACTION_CHANNEL_UP },
233 { "channeldown" , ACTION_CHANNEL_DOWN },
234 { "previouschannelgroup" , ACTION_PREVIOUS_CHANNELGROUP },
235 { "nextchannelgroup" , ACTION_NEXT_CHANNELGROUP },
236 { "playpvr" , ACTION_PVR_PLAY },
237 { "playpvrtv" , ACTION_PVR_PLAY_TV },
238 { "playpvrradio" , ACTION_PVR_PLAY_RADIO },
239 { "record" , ACTION_RECORD },
240 { "togglecommskip" , ACTION_TOGGLE_COMMSKIP },
241 { "showtimerrule" , ACTION_PVR_SHOW_TIMER_RULE },
243 // Mouse actions
244 { "leftclick" , ACTION_MOUSE_LEFT_CLICK },
245 { "rightclick" , ACTION_MOUSE_RIGHT_CLICK },
246 { "middleclick" , ACTION_MOUSE_MIDDLE_CLICK },
247 { "doubleclick" , ACTION_MOUSE_DOUBLE_CLICK },
248 { "longclick" , ACTION_MOUSE_LONG_CLICK },
249 { "wheelup" , ACTION_MOUSE_WHEEL_UP },
250 { "wheeldown" , ACTION_MOUSE_WHEEL_DOWN },
251 { "mousedrag" , ACTION_MOUSE_DRAG },
252 { "mousemove" , ACTION_MOUSE_MOVE },
254 // Touch
255 { "tap" , ACTION_TOUCH_TAP },
256 { "longpress" , ACTION_TOUCH_LONGPRESS },
257 { "pangesture" , ACTION_GESTURE_PAN },
258 { "zoomgesture" , ACTION_GESTURE_ZOOM },
259 { "rotategesture" , ACTION_GESTURE_ROTATE },
260 { "swipeleft" , ACTION_GESTURE_SWIPE_LEFT },
261 { "swiperight" , ACTION_GESTURE_SWIPE_RIGHT },
262 { "swipeup" , ACTION_GESTURE_SWIPE_UP },
263 { "swipedown" , ACTION_GESTURE_SWIPE_DOWN },
265 // Do nothing / error action
266 { "error" , ACTION_ERROR },
267 { "noop" , ACTION_NOOP }
270 bool CButtonTranslator::IsAnalog(int actionID)
272 switch (actionID)
274 case ACTION_ANALOG_SEEK_FORWARD:
275 case ACTION_ANALOG_SEEK_BACK:
276 case ACTION_SCROLL_UP:
277 case ACTION_SCROLL_DOWN:
278 case ACTION_ANALOG_FORWARD:
279 case ACTION_ANALOG_REWIND:
280 case ACTION_ANALOG_MOVE:
281 case ACTION_ANALOG_MOVE_X:
282 case ACTION_ANALOG_MOVE_Y:
283 case ACTION_CURSOR_LEFT:
284 case ACTION_CURSOR_RIGHT:
285 case ACTION_VOLUME_UP:
286 case ACTION_VOLUME_DOWN:
287 case ACTION_ZOOM_IN:
288 case ACTION_ZOOM_OUT:
289 return true;
290 default:
291 return false;
295 static const ActionMapping windows[] =
297 { "home" , WINDOW_HOME },
298 { "programs" , WINDOW_PROGRAMS },
299 { "pictures" , WINDOW_PICTURES },
300 { "filemanager" , WINDOW_FILES },
301 { "settings" , WINDOW_SETTINGS_MENU },
302 { "music" , WINDOW_MUSIC_NAV },
303 { "videos" , WINDOW_VIDEO_NAV },
304 { "tvchannels" , WINDOW_TV_CHANNELS },
305 { "tvrecordings" , WINDOW_TV_RECORDINGS },
306 { "tvguide" , WINDOW_TV_GUIDE },
307 { "tvtimers" , WINDOW_TV_TIMERS },
308 { "tvsearch" , WINDOW_TV_SEARCH },
309 { "radiochannels" , WINDOW_RADIO_CHANNELS },
310 { "radiorecordings" , WINDOW_RADIO_RECORDINGS },
311 { "radioguide" , WINDOW_RADIO_GUIDE },
312 { "radiotimers" , WINDOW_RADIO_TIMERS },
313 { "radiosearch" , WINDOW_RADIO_SEARCH },
314 { "gamecontrollers" , WINDOW_DIALOG_GAME_CONTROLLERS },
315 { "pvrguideinfo" , WINDOW_DIALOG_PVR_GUIDE_INFO },
316 { "pvrrecordinginfo" , WINDOW_DIALOG_PVR_RECORDING_INFO },
317 { "pvrradiordsinfo" , WINDOW_DIALOG_PVR_RADIO_RDS_INFO },
318 { "pvrtimersetting" , WINDOW_DIALOG_PVR_TIMER_SETTING },
319 { "pvrgroupmanager" , WINDOW_DIALOG_PVR_GROUP_MANAGER },
320 { "pvrchannelmanager" , WINDOW_DIALOG_PVR_CHANNEL_MANAGER },
321 { "pvrguidesearch" , WINDOW_DIALOG_PVR_GUIDE_SEARCH },
322 { "pvrchannelscan" , WINDOW_DIALOG_PVR_CHANNEL_SCAN },
323 { "pvrupdateprogress" , WINDOW_DIALOG_PVR_UPDATE_PROGRESS },
324 { "pvrosdchannels" , WINDOW_DIALOG_PVR_OSD_CHANNELS },
325 { "pvrosdguide" , WINDOW_DIALOG_PVR_OSD_GUIDE },
326 { "pvrosdteletext" , WINDOW_DIALOG_OSD_TELETEXT },
327 { "systeminfo" , WINDOW_SYSTEM_INFORMATION },
328 { "testpattern" , WINDOW_TEST_PATTERN },
329 { "screencalibration" , WINDOW_SCREEN_CALIBRATION },
330 { "systemsettings" , WINDOW_SETTINGS_SYSTEM },
331 { "servicesettings" , WINDOW_SETTINGS_SERVICE },
332 { "pvrsettings" , WINDOW_SETTINGS_MYPVR },
333 { "playersettings" , WINDOW_SETTINGS_PLAYER },
334 { "mediasettings" , WINDOW_SETTINGS_MEDIA },
335 { "interfacesettings" , WINDOW_SETTINGS_INTERFACE },
336 { "appearancesettings" , WINDOW_SETTINGS_INTERFACE }, // backward compatibility to v16
337 { "videoplaylist" , WINDOW_VIDEO_PLAYLIST },
338 { "loginscreen" , WINDOW_LOGIN_SCREEN },
339 { "profiles" , WINDOW_SETTINGS_PROFILES },
340 { "skinsettings" , WINDOW_SKIN_SETTINGS },
341 { "addonbrowser" , WINDOW_ADDON_BROWSER },
342 { "yesnodialog" , WINDOW_DIALOG_YES_NO },
343 { "progressdialog" , WINDOW_DIALOG_PROGRESS },
344 { "virtualkeyboard" , WINDOW_DIALOG_KEYBOARD },
345 { "volumebar" , WINDOW_DIALOG_VOLUME_BAR },
346 { "submenu" , WINDOW_DIALOG_SUB_MENU },
347 { "favourites" , WINDOW_DIALOG_FAVOURITES },
348 { "contextmenu" , WINDOW_DIALOG_CONTEXT_MENU },
349 { "notification" , WINDOW_DIALOG_KAI_TOAST },
350 { "numericinput" , WINDOW_DIALOG_NUMERIC },
351 { "gamepadinput" , WINDOW_DIALOG_GAMEPAD },
352 { "shutdownmenu" , WINDOW_DIALOG_BUTTON_MENU },
353 { "playercontrols" , WINDOW_DIALOG_PLAYER_CONTROLS },
354 { "playerprocessinfo" , WINDOW_DIALOG_PLAYER_PROCESS_INFO },
355 { "seekbar" , WINDOW_DIALOG_SEEK_BAR },
356 { "musicosd" , WINDOW_DIALOG_MUSIC_OSD },
357 { "addonsettings" , WINDOW_DIALOG_ADDON_SETTINGS },
358 { "visualisationpresetlist" , WINDOW_DIALOG_VIS_PRESET_LIST },
359 { "osdcmssettings" , WINDOW_DIALOG_CMS_OSD_SETTINGS },
360 { "osdvideosettings" , WINDOW_DIALOG_VIDEO_OSD_SETTINGS },
361 { "osdaudiosettings" , WINDOW_DIALOG_AUDIO_OSD_SETTINGS },
362 { "audiodspmanager" , WINDOW_DIALOG_AUDIO_DSP_MANAGER },
363 { "osdaudiodspsettings" , WINDOW_DIALOG_AUDIO_DSP_OSD_SETTINGS },
364 { "videobookmarks" , WINDOW_DIALOG_VIDEO_BOOKMARKS },
365 { "filebrowser" , WINDOW_DIALOG_FILE_BROWSER },
366 { "networksetup" , WINDOW_DIALOG_NETWORK_SETUP },
367 { "mediasource" , WINDOW_DIALOG_MEDIA_SOURCE },
368 { "profilesettings" , WINDOW_DIALOG_PROFILE_SETTINGS },
369 { "locksettings" , WINDOW_DIALOG_LOCK_SETTINGS },
370 { "contentsettings" , WINDOW_DIALOG_CONTENT_SETTINGS },
371 { "songinformation" , WINDOW_DIALOG_SONG_INFO },
372 { "smartplaylisteditor" , WINDOW_DIALOG_SMART_PLAYLIST_EDITOR },
373 { "smartplaylistrule" , WINDOW_DIALOG_SMART_PLAYLIST_RULE },
374 { "busydialog" , WINDOW_DIALOG_BUSY },
375 { "pictureinfo" , WINDOW_DIALOG_PICTURE_INFO },
376 { "accesspoints" , WINDOW_DIALOG_ACCESS_POINTS },
377 { "fullscreeninfo" , WINDOW_DIALOG_FULLSCREEN_INFO },
378 { "sliderdialog" , WINDOW_DIALOG_SLIDER },
379 { "addoninformation" , WINDOW_DIALOG_ADDON_INFO },
380 { "subtitlesearch" , WINDOW_DIALOG_SUBTITLES },
381 { "musicplaylist" , WINDOW_MUSIC_PLAYLIST },
382 { "musicplaylisteditor" , WINDOW_MUSIC_PLAYLIST_EDITOR },
383 { "teletext" , WINDOW_DIALOG_OSD_TELETEXT },
384 { "selectdialog" , WINDOW_DIALOG_SELECT },
385 { "musicinformation" , WINDOW_DIALOG_MUSIC_INFO },
386 { "okdialog" , WINDOW_DIALOG_OK },
387 { "movieinformation" , WINDOW_DIALOG_VIDEO_INFO },
388 { "textviewer" , WINDOW_DIALOG_TEXT_VIEWER },
389 { "fullscreenvideo" , WINDOW_FULLSCREEN_VIDEO },
390 { "fullscreenlivetv" , WINDOW_FULLSCREEN_LIVETV }, // virtual window/keymap section for PVR specific bindings in fullscreen playback (which internally uses WINDOW_FULLSCREEN_VIDEO)
391 { "fullscreenradio" , WINDOW_FULLSCREEN_RADIO }, // virtual window for fullscreen radio, uses WINDOW_VISUALISATION as fallback
392 { "visualisation" , WINDOW_VISUALISATION },
393 { "slideshow" , WINDOW_SLIDESHOW },
394 { "weather" , WINDOW_WEATHER },
395 { "screensaver" , WINDOW_SCREENSAVER },
396 { "videoosd" , WINDOW_DIALOG_VIDEO_OSD },
397 { "videomenu" , WINDOW_VIDEO_MENU },
398 { "videotimeseek" , WINDOW_VIDEO_TIME_SEEK },
399 { "startwindow" , WINDOW_START },
400 { "startup" , WINDOW_STARTUP_ANIM },
401 { "peripheralsettings" , WINDOW_DIALOG_PERIPHERAL_SETTINGS },
402 { "extendedprogressdialog" , WINDOW_DIALOG_EXT_PROGRESS },
403 { "mediafilter" , WINDOW_DIALOG_MEDIA_FILTER },
404 { "addon" , WINDOW_ADDON_START },
405 { "eventlog" , WINDOW_EVENT_LOG},
406 { "tvtimerrules" , WINDOW_TV_TIMER_RULES},
407 { "radiotimerrules" , WINDOW_RADIO_TIMER_RULES}
410 static const ActionMapping mousekeys[] =
412 { "click" , KEY_MOUSE_CLICK },
413 { "leftclick" , KEY_MOUSE_CLICK },
414 { "rightclick" , KEY_MOUSE_RIGHTCLICK },
415 { "middleclick" , KEY_MOUSE_MIDDLECLICK },
416 { "doubleclick" , KEY_MOUSE_DOUBLE_CLICK },
417 { "longclick" , KEY_MOUSE_LONG_CLICK },
418 { "wheelup" , KEY_MOUSE_WHEEL_UP },
419 { "wheeldown" , KEY_MOUSE_WHEEL_DOWN },
420 { "mousemove" , KEY_MOUSE_MOVE },
421 { "mousedrag" , KEY_MOUSE_DRAG },
422 { "mousedragstart" , KEY_MOUSE_DRAG_START },
423 { "mousedragend" , KEY_MOUSE_DRAG_END },
424 { "mouserdrag" , KEY_MOUSE_RDRAG },
425 { "mouserdragstart" , KEY_MOUSE_RDRAG_START },
426 { "mouserdragend" , KEY_MOUSE_RDRAG_END }
429 static const ActionMapping touchcommands[] =
431 { "tap" , ACTION_TOUCH_TAP },
432 { "longpress" , ACTION_TOUCH_LONGPRESS },
433 { "pan" , ACTION_GESTURE_PAN },
434 { "zoom" , ACTION_GESTURE_ZOOM },
435 { "rotate" , ACTION_GESTURE_ROTATE },
436 { "swipeleft" , ACTION_GESTURE_SWIPE_LEFT },
437 { "swiperight" , ACTION_GESTURE_SWIPE_RIGHT },
438 { "swipeup" , ACTION_GESTURE_SWIPE_UP },
439 { "swipedown" , ACTION_GESTURE_SWIPE_DOWN }
442 static const WindowMapping fallbackWindows[] =
444 { WINDOW_FULLSCREEN_LIVETV , WINDOW_FULLSCREEN_VIDEO },
445 { WINDOW_FULLSCREEN_RADIO , WINDOW_VISUALISATION }
448 #ifdef TARGET_WINDOWS
449 static const ActionMapping appcommands[] =
451 { "browser_back" , APPCOMMAND_BROWSER_BACKWARD },
452 { "browser_forward" , APPCOMMAND_BROWSER_FORWARD },
453 { "browser_refresh" , APPCOMMAND_BROWSER_REFRESH },
454 { "browser_stop" , APPCOMMAND_BROWSER_STOP },
455 { "browser_search" , APPCOMMAND_BROWSER_SEARCH },
456 { "browser_favorites" , APPCOMMAND_BROWSER_FAVORITES },
457 { "browser_home" , APPCOMMAND_BROWSER_HOME },
458 { "volume_mute" , APPCOMMAND_VOLUME_MUTE },
459 { "volume_down" , APPCOMMAND_VOLUME_DOWN },
460 { "volume_up" , APPCOMMAND_VOLUME_UP },
461 { "next_track" , APPCOMMAND_MEDIA_NEXTTRACK },
462 { "prev_track" , APPCOMMAND_MEDIA_PREVIOUSTRACK },
463 { "stop" , APPCOMMAND_MEDIA_STOP },
464 { "play_pause" , APPCOMMAND_MEDIA_PLAY_PAUSE },
465 { "launch_mail" , APPCOMMAND_LAUNCH_MAIL },
466 { "launch_media_select" , APPCOMMAND_LAUNCH_MEDIA_SELECT },
467 { "launch_app1" , APPCOMMAND_LAUNCH_APP1 },
468 { "launch_app2" , APPCOMMAND_LAUNCH_APP2 },
469 { "play" , APPCOMMAND_MEDIA_PLAY },
470 { "pause" , APPCOMMAND_MEDIA_PAUSE },
471 { "fastforward" , APPCOMMAND_MEDIA_FAST_FORWARD },
472 { "rewind" , APPCOMMAND_MEDIA_REWIND },
473 { "channelup" , APPCOMMAND_MEDIA_CHANNEL_UP },
474 { "channeldown" , APPCOMMAND_MEDIA_CHANNEL_DOWN }
476 #endif
478 CButtonTranslator& CButtonTranslator::GetInstance()
480 static CButtonTranslator sl_instance;
481 return sl_instance;
484 CButtonTranslator::CButtonTranslator()
486 m_deviceList.clear();
487 m_Loaded = false;
490 #if defined(HAS_LIRC) || defined(HAS_IRSERVERSUITE)
491 void CButtonTranslator::ClearLircButtonMapEntries()
493 std::vector<lircButtonMap*> maps;
494 for (std::map<std::string,lircButtonMap*>::iterator it = lircRemotesMap.begin();
495 it != lircRemotesMap.end();++it)
496 maps.push_back(it->second);
497 sort(maps.begin(),maps.end());
498 std::vector<lircButtonMap*>::iterator itend = unique(maps.begin(),maps.end());
499 for (std::vector<lircButtonMap*>::iterator it = maps.begin(); it != itend;++it)
500 delete *it;
502 #endif
504 CButtonTranslator::~CButtonTranslator()
506 #if defined(HAS_LIRC) || defined(HAS_IRSERVERSUITE)
507 ClearLircButtonMapEntries();
508 #endif
511 // Add the supplied device name to the list of connected devices
512 void CButtonTranslator::AddDevice(std::string& strDevice)
514 // Only add the device if it isn't already in the list
515 std::list<std::string>::iterator it;
516 for (it = m_deviceList.begin(); it != m_deviceList.end(); ++it)
517 if (*it == strDevice)
518 return;
520 // Add the device
521 m_deviceList.push_back(strDevice);
522 m_deviceList.sort();
524 // New device added so reload the key mappings
525 Load();
528 void CButtonTranslator::RemoveDevice(std::string& strDevice)
530 // Find the device
531 std::list<std::string>::iterator it;
532 for (it = m_deviceList.begin(); it != m_deviceList.end(); ++it)
533 if (*it == strDevice)
534 break;
535 if (it == m_deviceList.end())
536 return;
538 // Remove the device
539 m_deviceList.remove(strDevice);
541 // Device removed so reload the key mappings
542 Load();
545 bool CButtonTranslator::Load(bool AlwaysLoad)
547 m_translatorMap.clear();
549 // Directories to search for keymaps. They're applied in this order,
550 // so keymaps in profile/keymaps/ override e.g. system/keymaps
551 static const char* DIRS_TO_CHECK[] = {
552 "special://xbmc/system/keymaps/",
553 "special://masterprofile/keymaps/",
554 "special://profile/keymaps/"
556 bool success = false;
558 for (unsigned int dirIndex = 0; dirIndex < ARRAY_SIZE(DIRS_TO_CHECK); ++dirIndex)
560 if (XFILE::CDirectory::Exists(DIRS_TO_CHECK[dirIndex]))
562 CFileItemList files;
563 XFILE::CDirectory::GetDirectory(DIRS_TO_CHECK[dirIndex], files, ".xml");
564 // Sort the list for filesystem based priorities, e.g. 01-keymap.xml, 02-keymap-overrides.xml
565 files.Sort(SortByFile, SortOrderAscending);
566 for(int fileIndex = 0; fileIndex<files.Size(); ++fileIndex)
568 if (!files[fileIndex]->m_bIsFolder)
569 success |= LoadKeymap(files[fileIndex]->GetPath());
572 // Load mappings for any HID devices we have connected
573 std::list<std::string>::iterator it;
574 for (it = m_deviceList.begin(); it != m_deviceList.end(); ++it)
576 std::string devicedir = DIRS_TO_CHECK[dirIndex];
577 devicedir.append(*it);
578 devicedir.append("/");
579 if( XFILE::CDirectory::Exists(devicedir) )
581 CFileItemList files;
582 XFILE::CDirectory::GetDirectory(devicedir, files, ".xml");
583 // Sort the list for filesystem based priorities, e.g. 01-keymap.xml, 02-keymap-overrides.xml
584 files.Sort(SortByFile, SortOrderAscending);
585 for(int fileIndex = 0; fileIndex<files.Size(); ++fileIndex)
587 if (!files[fileIndex]->m_bIsFolder)
588 success |= LoadKeymap(files[fileIndex]->GetPath());
595 if (!success)
597 CLog::Log(LOGERROR, "Error loading keymaps from: %s or %s or %s", DIRS_TO_CHECK[0], DIRS_TO_CHECK[1], DIRS_TO_CHECK[2]);
598 return false;
601 #if defined(HAS_LIRC) || defined(HAS_IRSERVERSUITE)
602 #ifdef TARGET_POSIX
603 #define REMOTEMAP "Lircmap.xml"
604 #else
605 #define REMOTEMAP "IRSSmap.xml"
606 #endif
607 std::string lircmapPath = URIUtils::AddFileToFolder("special://xbmc/system/", REMOTEMAP);
608 lircRemotesMap.clear();
609 if(CFile::Exists(lircmapPath))
610 success |= LoadLircMap(lircmapPath);
611 else
612 CLog::Log(LOGDEBUG, "CButtonTranslator::Load - no system %s found, skipping", REMOTEMAP);
614 lircmapPath = CProfilesManager::GetInstance().GetUserDataItem(REMOTEMAP);
615 if(CFile::Exists(lircmapPath))
616 success |= LoadLircMap(lircmapPath);
617 else
618 CLog::Log(LOGDEBUG, "CButtonTranslator::Load - no userdata %s found, skipping", REMOTEMAP);
620 if (!success)
621 CLog::Log(LOGERROR, "CButtonTranslator::Load - unable to load remote map %s", REMOTEMAP);
622 // don't return false - it is to only indicate a fatal error (which this is not)
623 #endif
625 // Done!
626 m_Loaded = true;
627 return true;
630 bool CButtonTranslator::LoadKeymap(const std::string &keymapPath)
632 CXBMCTinyXML xmlDoc;
634 CLog::Log(LOGINFO, "Loading %s", keymapPath.c_str());
635 if (!xmlDoc.LoadFile(keymapPath))
637 CLog::Log(LOGERROR, "Error loading keymap: %s, Line %d\n%s", keymapPath.c_str(), xmlDoc.ErrorRow(), xmlDoc.ErrorDesc());
638 return false;
640 TiXmlElement* pRoot = xmlDoc.RootElement();
641 if (!pRoot)
643 CLog::Log(LOGERROR, "Error getting keymap root: %s", keymapPath.c_str());
644 return false;
646 std::string strValue = pRoot->Value();
647 if ( strValue != "keymap")
649 CLog::Log(LOGERROR, "%s Doesn't contain <keymap>", keymapPath.c_str());
650 return false;
652 // run through our window groups
653 TiXmlNode* pWindow = pRoot->FirstChild();
654 while (pWindow)
656 if (pWindow->Type() == TiXmlNode::TINYXML_ELEMENT)
658 int windowID = WINDOW_INVALID;
659 const char *szWindow = pWindow->Value();
660 if (szWindow)
662 if (strcmpi(szWindow, "global") == 0)
663 windowID = -1;
664 else
665 windowID = TranslateWindow(szWindow);
667 MapWindowActions(pWindow, windowID);
669 pWindow = pWindow->NextSibling();
672 return true;
675 bool CButtonTranslator::LoadLircMap(const std::string &lircmapPath)
677 #ifdef TARGET_POSIX
678 #define REMOTEMAPTAG "lircmap"
679 #else
680 #define REMOTEMAPTAG "irssmap"
681 #endif
682 // load our xml file, and fill up our mapping tables
683 CXBMCTinyXML xmlDoc;
685 // Load the config file
686 CLog::Log(LOGINFO, "Loading %s", lircmapPath.c_str());
687 if (!xmlDoc.LoadFile(lircmapPath))
689 CLog::Log(LOGERROR, "%s, Line %d\n%s", lircmapPath.c_str(), xmlDoc.ErrorRow(), xmlDoc.ErrorDesc());
690 return false; // This is so people who don't have the file won't fail, just warn
693 TiXmlElement* pRoot = xmlDoc.RootElement();
694 std::string strValue = pRoot->Value();
695 if (strValue != REMOTEMAPTAG)
697 CLog::Log(LOGERROR, "%sl Doesn't contain <%s>", lircmapPath.c_str(), REMOTEMAPTAG);
698 return false;
701 // run through our window groups
702 TiXmlNode* pRemote = pRoot->FirstChild();
703 while (pRemote)
705 if (pRemote->Type() == TiXmlNode::TINYXML_ELEMENT)
707 const char *szRemote = pRemote->Value();
708 if (szRemote)
710 TiXmlAttribute* pAttr = pRemote->ToElement()->FirstAttribute();
711 if (pAttr)
712 MapRemote(pRemote, pAttr->Value());
715 pRemote = pRemote->NextSibling();
718 return true;
721 void CButtonTranslator::MapRemote(TiXmlNode *pRemote, const char* szDevice)
723 CLog::Log(LOGINFO, "* Adding remote mapping for device '%s'", szDevice);
724 std::vector<std::string> RemoteNames;
725 std::map<std::string, lircButtonMap*>::iterator it = lircRemotesMap.find(szDevice);
726 if (it == lircRemotesMap.end())
727 lircRemotesMap[szDevice] = new lircButtonMap;
728 lircButtonMap& buttons = *lircRemotesMap[szDevice];
730 TiXmlElement *pButton = pRemote->FirstChildElement();
731 while (pButton)
733 if (!pButton->NoChildren())
735 if (pButton->ValueStr() == "altname")
736 RemoteNames.push_back(pButton->FirstChild()->ValueStr());
737 else
738 buttons[pButton->FirstChild()->ValueStr()] = pButton->ValueStr();
740 pButton = pButton->NextSiblingElement();
742 for (std::vector<std::string>::iterator it = RemoteNames.begin();
743 it != RemoteNames.end();++it)
745 CLog::Log(LOGINFO, "* Linking remote mapping for '%s' to '%s'", szDevice, it->c_str());
746 lircRemotesMap[*it] = &buttons;
750 int CButtonTranslator::TranslateLircRemoteString(const char* szDevice, const char *szButton)
752 // Find the device
753 std::map<std::string, lircButtonMap*>::iterator it = lircRemotesMap.find(szDevice);
754 if (it == lircRemotesMap.end())
755 return 0;
757 // Find the button
758 lircButtonMap::iterator it2 = (*it).second->find(szButton);
759 if (it2 == (*it).second->end())
760 return 0;
762 // Convert the button to code
763 if (strnicmp((*it2).second.c_str(), "obc", 3) == 0)
764 return TranslateUniversalRemoteString((*it2).second.c_str());
766 return TranslateRemoteString((*it2).second.c_str());
769 int CButtonTranslator::GetCustomControllerActionCode(int windowId, int buttonId, const CustomControllerWindowMap *windowMap, std::string& strAction) const
771 int action = 0;
773 auto it = windowMap->find(windowId);
774 if (it != windowMap->end())
776 const CustomControllerButtonMap &buttonMap = it->second;
777 auto it2 = buttonMap.find(buttonId);
778 if (it2 != buttonMap.end())
780 strAction = it2->second;
781 TranslateActionString(strAction.c_str(), action);
785 return action;
788 bool CButtonTranslator::TranslateCustomControllerString(int windowId, const std::string& controllerName, int buttonId, int& action, std::string& strAction)
790 // resolve the correct custom controller
791 auto it = m_customControllersMap.find(controllerName);
792 if (it == m_customControllersMap.end())
794 return false;
797 const CustomControllerWindowMap *wmap = &it->second;
799 // try to get the action from the current window
800 action = GetCustomControllerActionCode(windowId, buttonId, wmap, strAction);
802 // if it's invalid, try to get it from a fallback window or the global map
803 if (action == 0)
805 int fallbackWindow = GetFallbackWindow(windowId);
806 if (fallbackWindow > -1)
807 action = GetCustomControllerActionCode(fallbackWindow, buttonId, wmap, strAction);
808 // still no valid action? use global map
809 if (action == 0)
810 action = GetCustomControllerActionCode(-1, buttonId, wmap, strAction);
813 return (action > 0);
817 bool CButtonTranslator::TranslateTouchAction(int window, int touchAction, int touchPointers, int &action, std::string &actionString)
819 action = 0;
820 if (touchPointers <= 0)
821 touchPointers = 1;
823 touchAction += touchPointers - 1;
824 touchAction |= KEY_TOUCH;
826 action = GetTouchActionCode(window, touchAction, actionString);
827 if (action <= 0)
829 int fallbackWindow = GetFallbackWindow(window);
830 if (fallbackWindow > -1)
831 action = GetTouchActionCode(fallbackWindow, touchAction, actionString);
832 if (action <= 0)
833 action = GetTouchActionCode(-1, touchAction, actionString);
836 return action > 0;
839 int CButtonTranslator::GetActionCode(int window, int action)
841 std::map<int, buttonMap>::const_iterator it = m_translatorMap.find(window);
842 if (it == m_translatorMap.end())
843 return 0;
845 buttonMap::const_iterator it2 = it->second.find(action);
846 if (it2 == it->second.end())
847 return 0;
849 return it2->second.id;
852 void CButtonTranslator::GetActions(std::vector<std::string> &actionList)
854 unsigned int size = sizeof(actions) / sizeof(ActionMapping);
855 actionList.clear();
856 actionList.reserve(size);
857 for (unsigned int index = 0; index < size; index++)
858 actionList.push_back(actions[index].name);
861 void CButtonTranslator::GetWindows(std::vector<std::string> &windowList)
863 unsigned int size = sizeof(windows) / sizeof(ActionMapping);
864 windowList.clear();
865 windowList.reserve(size);
866 for (unsigned int index = 0; index < size; index++)
867 windowList.push_back(windows[index].name);
870 int CButtonTranslator::GetFallbackWindow(int windowID)
872 for (unsigned int index = 0; index < ARRAY_SIZE(fallbackWindows); ++index)
874 if (fallbackWindows[index].origin == windowID)
875 return fallbackWindows[index].target;
877 // for addon windows use WINDOW_ADDON_START because id is dynamic
878 if (windowID > WINDOW_ADDON_START && windowID <= WINDOW_ADDON_END)
879 return WINDOW_ADDON_START;
881 return -1;
884 CAction CButtonTranslator::GetAction(int window, const CKey &key, bool fallback)
886 std::string strAction;
887 // try to get the action from the current window
888 int actionID = GetActionCode(window, key, strAction);
889 // if it's invalid, try to get it from the global map
890 if (actionID == 0 && fallback)
892 int fallbackWindow = GetFallbackWindow(window);
893 if (fallbackWindow > -1)
894 actionID = GetActionCode(fallbackWindow, key, strAction);
895 // still no valid action? use global map
896 if (actionID == 0)
897 actionID = GetActionCode( -1, key, strAction);
899 // Now fill our action structure
900 CAction action(actionID, strAction, key);
901 return action;
904 CAction CButtonTranslator::GetGlobalAction(const CKey &key)
906 return GetAction(-1, key, true);
909 bool CButtonTranslator::HasLonpressMapping(int window, const CKey &key)
911 std::map<int, buttonMap>::const_iterator it = m_translatorMap.find(window);
912 if (it != m_translatorMap.end())
914 uint32_t code = key.GetButtonCode();
915 code |= CKey::MODIFIER_LONG;
916 buttonMap::const_iterator it2 = (*it).second.find(code);
918 if (it2 != (*it).second.end())
919 return true;
921 #ifdef TARGET_POSIX
922 // Some buttoncodes changed in Hardy
923 if ((code & KEY_VKEY) == KEY_VKEY && (code & 0x0F00))
925 code &= ~0x0F00;
926 it2 = (*it).second.find(code);
927 if (it2 != (*it).second.end())
928 return true;
930 #endif
933 // no key mapping found for the current window do the fallback handling
934 if (window > -1)
936 // first check if we have a fallback for the window
937 int fallbackWindow = GetFallbackWindow(window);
938 if (fallbackWindow > -1 && HasLonpressMapping(fallbackWindow, key))
939 return true;
941 // fallback to default section
942 return HasLonpressMapping(-1, key);
945 return false;
948 int CButtonTranslator::GetActionCode(int window, const CKey &key, std::string &strAction) const
950 uint32_t code = key.GetButtonCode();
952 std::map<int, buttonMap>::const_iterator it = m_translatorMap.find(window);
953 if (it == m_translatorMap.end())
954 return 0;
955 buttonMap::const_iterator it2 = (*it).second.find(code);
956 int action = 0;
957 if (it2 == (*it).second.end() && code & CKey::MODIFIER_LONG) // If long action not found, try short one
959 code &= ~CKey::MODIFIER_LONG;
960 it2 = (*it).second.find(code);
962 if (it2 != (*it).second.end())
964 action = (*it2).second.id;
965 strAction = (*it2).second.strID;
967 #ifdef TARGET_POSIX
968 // Some buttoncodes changed in Hardy
969 if (action == 0 && (code & KEY_VKEY) == KEY_VKEY && (code & 0x0F00))
971 CLog::Log(LOGDEBUG, "%s: Trying Hardy keycode for %#04x", __FUNCTION__, code);
972 code &= ~0x0F00;
973 it2 = (*it).second.find(code);
974 if (it2 != (*it).second.end())
976 action = (*it2).second.id;
977 strAction = (*it2).second.strID;
980 #endif
981 return action;
984 void CButtonTranslator::MapAction(uint32_t buttonCode, const char *szAction, buttonMap &map)
986 int action = ACTION_NONE;
987 if (!TranslateActionString(szAction, action) || !buttonCode)
988 return; // no valid action, or an invalid buttoncode
990 // have a valid action, and a valid button - map it.
991 // check to see if we've already got this (button,action) pair defined
992 buttonMap::iterator it = map.find(buttonCode);
993 if (it == map.end() || (*it).second.id != action || (*it).second.strID != szAction)
995 // NOTE: This multimap is only being used as a normal map at this point (no support
996 // for multiple actions per key)
997 if (it != map.end())
998 map.erase(it);
999 CButtonAction button;
1000 button.id = action;
1001 button.strID = szAction;
1002 map.insert(std::pair<uint32_t, CButtonAction>(buttonCode, button));
1006 void CButtonTranslator::MapCustomControllerActions(int windowID, TiXmlNode *pCustomController)
1008 CustomControllerButtonMap buttonMap;
1009 std::string controllerName;
1011 TiXmlElement *pController = pCustomController->ToElement();
1012 if (pController)
1014 // transform loose name to new family, including altnames
1015 if(pController->Attribute("name"))
1017 controllerName = pController->Attribute("name");
1019 else
1021 CLog::Log(LOGERROR, "Missing attribute \"name\" for tag \"customcontroller\"");
1022 return;
1026 // parse map
1027 TiXmlElement *pButton = pCustomController->FirstChildElement();
1028 int id = 0;
1029 while (pButton)
1031 std::string action;
1032 if (!pButton->NoChildren())
1033 action = pButton->FirstChild()->ValueStr();
1035 if ((pButton->QueryIntAttribute("id", &id) == TIXML_SUCCESS) && id >= 0)
1037 buttonMap[id] = action;
1039 else
1040 CLog::Log(LOGERROR, "Error reading customController map element, Invalid id: %d", id);
1042 pButton = pButton->NextSiblingElement();
1045 // add/overwrite button with mapped actions
1046 for (auto button : buttonMap)
1047 m_customControllersMap[controllerName][windowID][button.first] = button.second;
1050 bool CButtonTranslator::HasDeviceType(TiXmlNode *pWindow, std::string type)
1052 return pWindow->FirstChild(type) != NULL;
1055 void CButtonTranslator::MapWindowActions(TiXmlNode *pWindow, int windowID)
1057 if (!pWindow || windowID == WINDOW_INVALID)
1058 return;
1060 TiXmlNode* pDevice;
1062 const char* types[] = {"gamepad", "remote", "universalremote", "keyboard", "mouse", "appcommand", "joystick", NULL};
1063 for (int i = 0; types[i]; ++i)
1065 std::string type(types[i]);
1066 if (HasDeviceType(pWindow, type))
1068 buttonMap map;
1069 std::map<int, buttonMap>::iterator it = m_translatorMap.find(windowID);
1070 if (it != m_translatorMap.end())
1072 map = it->second;
1073 m_translatorMap.erase(it);
1076 pDevice = pWindow->FirstChild(type);
1078 TiXmlElement *pButton = pDevice->FirstChildElement();
1080 while (pButton)
1082 uint32_t buttonCode=0;
1083 if (type == "gamepad")
1084 buttonCode = TranslateGamepadString(pButton->Value());
1085 else if (type == "remote")
1086 buttonCode = TranslateRemoteString(pButton->Value());
1087 else if (type == "universalremote")
1088 buttonCode = TranslateUniversalRemoteString(pButton->Value());
1089 else if (type == "keyboard")
1090 buttonCode = TranslateKeyboardButton(pButton);
1091 else if (type == "mouse")
1092 buttonCode = TranslateMouseCommand(pButton);
1093 else if (type == "appcommand")
1094 buttonCode = TranslateAppCommand(pButton->Value());
1095 else if (type == "joystick")
1096 buttonCode = TranslateJoystickString(pButton->Value());
1098 if (buttonCode)
1100 if (pButton->FirstChild() && pButton->FirstChild()->Value()[0])
1101 MapAction(buttonCode, pButton->FirstChild()->Value(), map);
1102 else
1104 buttonMap::iterator it = map.find(buttonCode);
1105 while (it != map.end())
1107 map.erase(it);
1108 it = map.find(buttonCode);
1112 pButton = pButton->NextSiblingElement();
1115 // add our map to our table
1116 if (!map.empty())
1117 m_translatorMap.insert(std::pair<int, buttonMap>( windowID, map));
1121 if ((pDevice = pWindow->FirstChild("touch")) != NULL)
1123 // map touch actions
1124 while (pDevice)
1126 MapTouchActions(windowID, pDevice);
1127 pDevice = pDevice->NextSibling("touch");
1131 if ((pDevice = pWindow->FirstChild("customcontroller")) != NULL)
1133 // map custom controller actions
1134 while (pDevice)
1136 MapCustomControllerActions(windowID, pDevice);
1137 pDevice = pDevice->NextSibling("customcontroller");
1143 bool CButtonTranslator::TranslateActionString(const char *szAction, int &action)
1145 action = ACTION_NONE;
1146 std::string strAction = szAction;
1147 StringUtils::ToLower(strAction);
1148 if (CBuiltins::GetInstance().HasCommand(strAction))
1149 action = ACTION_BUILT_IN_FUNCTION;
1151 for (unsigned int index=0;index < ARRAY_SIZE(actions);++index)
1153 if (strAction == actions[index].name)
1155 action = actions[index].action;
1156 break;
1160 if (action == ACTION_NONE)
1162 CLog::Log(LOGERROR, "Keymapping error: no such action '%s' defined", strAction.c_str());
1163 return false;
1166 return true;
1169 std::string CButtonTranslator::TranslateWindow(int windowID)
1171 for (unsigned int index = 0; index < ARRAY_SIZE(windows); ++index)
1173 if (windows[index].action == windowID)
1174 return windows[index].name;
1176 return "";
1179 int CButtonTranslator::TranslateWindow(const std::string &window)
1181 std::string strWindow(window);
1182 if (strWindow.empty())
1183 return WINDOW_INVALID;
1184 StringUtils::ToLower(strWindow);
1185 // eliminate .xml
1186 if (StringUtils::EndsWith(strWindow, ".xml"))
1187 strWindow = strWindow.substr(0, strWindow.size() - 4);
1189 // window12345, for custom window to be keymapped
1190 if (strWindow.length() > 6 && StringUtils::StartsWithNoCase(strWindow, "window"))
1191 strWindow = strWindow.substr(6);
1192 if (StringUtils::StartsWithNoCase(strWindow, "my")) // drop "my" prefix
1193 strWindow = strWindow.substr(2);
1194 if (StringUtils::IsNaturalNumber(strWindow))
1196 // allow a full window id or a delta id
1197 int iWindow = atoi(strWindow.c_str());
1198 if (iWindow > WINDOW_INVALID)
1199 return iWindow;
1200 return WINDOW_HOME + iWindow;
1203 // run through the window structure
1204 for (unsigned int index = 0; index < ARRAY_SIZE(windows); ++index)
1206 if (strWindow == windows[index].name)
1207 return windows[index].action;
1210 CLog::Log(LOGERROR, "Window Translator: Can't find window %s", strWindow.c_str());
1211 return WINDOW_INVALID;
1214 uint32_t CButtonTranslator::TranslateGamepadString(const char *szButton)
1216 if (!szButton)
1217 return 0;
1218 uint32_t buttonCode = 0;
1219 std::string strButton = szButton;
1220 StringUtils::ToLower(strButton);
1221 if (strButton == "a") buttonCode = KEY_BUTTON_A;
1222 else if (strButton == "b") buttonCode = KEY_BUTTON_B;
1223 else if (strButton == "x") buttonCode = KEY_BUTTON_X;
1224 else if (strButton == "y") buttonCode = KEY_BUTTON_Y;
1225 else if (strButton == "white") buttonCode = KEY_BUTTON_WHITE;
1226 else if (strButton == "black") buttonCode = KEY_BUTTON_BLACK;
1227 else if (strButton == "start") buttonCode = KEY_BUTTON_START;
1228 else if (strButton == "back") buttonCode = KEY_BUTTON_BACK;
1229 else if (strButton == "leftthumbbutton") buttonCode = KEY_BUTTON_LEFT_THUMB_BUTTON;
1230 else if (strButton == "rightthumbbutton") buttonCode = KEY_BUTTON_RIGHT_THUMB_BUTTON;
1231 else if (strButton == "leftthumbstick") buttonCode = KEY_BUTTON_LEFT_THUMB_STICK;
1232 else if (strButton == "leftthumbstickup") buttonCode = KEY_BUTTON_LEFT_THUMB_STICK_UP;
1233 else if (strButton == "leftthumbstickdown") buttonCode = KEY_BUTTON_LEFT_THUMB_STICK_DOWN;
1234 else if (strButton == "leftthumbstickleft") buttonCode = KEY_BUTTON_LEFT_THUMB_STICK_LEFT;
1235 else if (strButton == "leftthumbstickright") buttonCode = KEY_BUTTON_LEFT_THUMB_STICK_RIGHT;
1236 else if (strButton == "rightthumbstick") buttonCode = KEY_BUTTON_RIGHT_THUMB_STICK;
1237 else if (strButton == "rightthumbstickup") buttonCode = KEY_BUTTON_RIGHT_THUMB_STICK_UP;
1238 else if (strButton == "rightthumbstickdown") buttonCode = KEY_BUTTON_RIGHT_THUMB_STICK_DOWN;
1239 else if (strButton == "rightthumbstickleft") buttonCode = KEY_BUTTON_RIGHT_THUMB_STICK_LEFT;
1240 else if (strButton == "rightthumbstickright") buttonCode = KEY_BUTTON_RIGHT_THUMB_STICK_RIGHT;
1241 else if (strButton == "lefttrigger") buttonCode = KEY_BUTTON_LEFT_TRIGGER;
1242 else if (strButton == "righttrigger") buttonCode = KEY_BUTTON_RIGHT_TRIGGER;
1243 else if (strButton == "leftanalogtrigger") buttonCode = KEY_BUTTON_LEFT_ANALOG_TRIGGER;
1244 else if (strButton == "rightanalogtrigger") buttonCode = KEY_BUTTON_RIGHT_ANALOG_TRIGGER;
1245 else if (strButton == "dpadleft") buttonCode = KEY_BUTTON_DPAD_LEFT;
1246 else if (strButton == "dpadright") buttonCode = KEY_BUTTON_DPAD_RIGHT;
1247 else if (strButton == "dpadup") buttonCode = KEY_BUTTON_DPAD_UP;
1248 else if (strButton == "dpaddown") buttonCode = KEY_BUTTON_DPAD_DOWN;
1249 else CLog::Log(LOGERROR, "Gamepad Translator: Can't find button %s", strButton.c_str());
1250 return buttonCode;
1253 uint32_t CButtonTranslator::TranslateRemoteString(const char *szButton)
1255 if (!szButton)
1256 return 0;
1257 uint32_t buttonCode = 0;
1258 std::string strButton = szButton;
1259 StringUtils::ToLower(strButton);
1260 if (strButton == "left") buttonCode = XINPUT_IR_REMOTE_LEFT;
1261 else if (strButton == "right") buttonCode = XINPUT_IR_REMOTE_RIGHT;
1262 else if (strButton == "up") buttonCode = XINPUT_IR_REMOTE_UP;
1263 else if (strButton == "down") buttonCode = XINPUT_IR_REMOTE_DOWN;
1264 else if (strButton == "select") buttonCode = XINPUT_IR_REMOTE_SELECT;
1265 else if (strButton == "back") buttonCode = XINPUT_IR_REMOTE_BACK;
1266 else if (strButton == "menu") buttonCode = XINPUT_IR_REMOTE_MENU;
1267 else if (strButton == "info") buttonCode = XINPUT_IR_REMOTE_INFO;
1268 else if (strButton == "display") buttonCode = XINPUT_IR_REMOTE_DISPLAY;
1269 else if (strButton == "title") buttonCode = XINPUT_IR_REMOTE_TITLE;
1270 else if (strButton == "play") buttonCode = XINPUT_IR_REMOTE_PLAY;
1271 else if (strButton == "pause") buttonCode = XINPUT_IR_REMOTE_PAUSE;
1272 else if (strButton == "reverse") buttonCode = XINPUT_IR_REMOTE_REVERSE;
1273 else if (strButton == "forward") buttonCode = XINPUT_IR_REMOTE_FORWARD;
1274 else if (strButton == "skipplus") buttonCode = XINPUT_IR_REMOTE_SKIP_PLUS;
1275 else if (strButton == "skipminus") buttonCode = XINPUT_IR_REMOTE_SKIP_MINUS;
1276 else if (strButton == "stop") buttonCode = XINPUT_IR_REMOTE_STOP;
1277 else if (strButton == "zero") buttonCode = XINPUT_IR_REMOTE_0;
1278 else if (strButton == "one") buttonCode = XINPUT_IR_REMOTE_1;
1279 else if (strButton == "two") buttonCode = XINPUT_IR_REMOTE_2;
1280 else if (strButton == "three") buttonCode = XINPUT_IR_REMOTE_3;
1281 else if (strButton == "four") buttonCode = XINPUT_IR_REMOTE_4;
1282 else if (strButton == "five") buttonCode = XINPUT_IR_REMOTE_5;
1283 else if (strButton == "six") buttonCode = XINPUT_IR_REMOTE_6;
1284 else if (strButton == "seven") buttonCode = XINPUT_IR_REMOTE_7;
1285 else if (strButton == "eight") buttonCode = XINPUT_IR_REMOTE_8;
1286 else if (strButton == "nine") buttonCode = XINPUT_IR_REMOTE_9;
1287 // additional keys from the media center extender for xbox remote
1288 else if (strButton == "power") buttonCode = XINPUT_IR_REMOTE_POWER;
1289 else if (strButton == "mytv") buttonCode = XINPUT_IR_REMOTE_MY_TV;
1290 else if (strButton == "mymusic") buttonCode = XINPUT_IR_REMOTE_MY_MUSIC;
1291 else if (strButton == "mypictures") buttonCode = XINPUT_IR_REMOTE_MY_PICTURES;
1292 else if (strButton == "myvideo") buttonCode = XINPUT_IR_REMOTE_MY_VIDEOS;
1293 else if (strButton == "record") buttonCode = XINPUT_IR_REMOTE_RECORD;
1294 else if (strButton == "start") buttonCode = XINPUT_IR_REMOTE_START;
1295 else if (strButton == "volumeplus") buttonCode = XINPUT_IR_REMOTE_VOLUME_PLUS;
1296 else if (strButton == "volumeminus") buttonCode = XINPUT_IR_REMOTE_VOLUME_MINUS;
1297 else if (strButton == "channelplus") buttonCode = XINPUT_IR_REMOTE_CHANNEL_PLUS;
1298 else if (strButton == "channelminus") buttonCode = XINPUT_IR_REMOTE_CHANNEL_MINUS;
1299 else if (strButton == "pageplus") buttonCode = XINPUT_IR_REMOTE_CHANNEL_PLUS;
1300 else if (strButton == "pageminus") buttonCode = XINPUT_IR_REMOTE_CHANNEL_MINUS;
1301 else if (strButton == "mute") buttonCode = XINPUT_IR_REMOTE_MUTE;
1302 else if (strButton == "recordedtv") buttonCode = XINPUT_IR_REMOTE_RECORDED_TV;
1303 else if (strButton == "guide") buttonCode = XINPUT_IR_REMOTE_GUIDE;
1304 else if (strButton == "livetv") buttonCode = XINPUT_IR_REMOTE_LIVE_TV;
1305 else if (strButton == "liveradio") buttonCode = XINPUT_IR_REMOTE_LIVE_RADIO;
1306 else if (strButton == "epgsearch") buttonCode = XINPUT_IR_REMOTE_EPG_SEARCH;
1307 else if (strButton == "star") buttonCode = XINPUT_IR_REMOTE_STAR;
1308 else if (strButton == "hash") buttonCode = XINPUT_IR_REMOTE_HASH;
1309 else if (strButton == "clear") buttonCode = XINPUT_IR_REMOTE_CLEAR;
1310 else if (strButton == "enter") buttonCode = XINPUT_IR_REMOTE_ENTER;
1311 else if (strButton == "xbox") buttonCode = XINPUT_IR_REMOTE_DISPLAY; // same as display
1312 else if (strButton == "playlist") buttonCode = XINPUT_IR_REMOTE_PLAYLIST;
1313 else if (strButton == "teletext") buttonCode = XINPUT_IR_REMOTE_TELETEXT;
1314 else if (strButton == "red") buttonCode = XINPUT_IR_REMOTE_RED;
1315 else if (strButton == "green") buttonCode = XINPUT_IR_REMOTE_GREEN;
1316 else if (strButton == "yellow") buttonCode = XINPUT_IR_REMOTE_YELLOW;
1317 else if (strButton == "blue") buttonCode = XINPUT_IR_REMOTE_BLUE;
1318 else if (strButton == "subtitle") buttonCode = XINPUT_IR_REMOTE_SUBTITLE;
1319 else if (strButton == "language") buttonCode = XINPUT_IR_REMOTE_LANGUAGE;
1320 else if (strButton == "eject") buttonCode = XINPUT_IR_REMOTE_EJECT;
1321 else if (strButton == "contentsmenu") buttonCode = XINPUT_IR_REMOTE_CONTENTS_MENU;
1322 else if (strButton == "rootmenu") buttonCode = XINPUT_IR_REMOTE_ROOT_MENU;
1323 else if (strButton == "topmenu") buttonCode = XINPUT_IR_REMOTE_TOP_MENU;
1324 else if (strButton == "dvdmenu") buttonCode = XINPUT_IR_REMOTE_DVD_MENU;
1325 else if (strButton == "print") buttonCode = XINPUT_IR_REMOTE_PRINT;
1326 else CLog::Log(LOGERROR, "Remote Translator: Can't find button %s", strButton.c_str());
1327 return buttonCode;
1330 uint32_t CButtonTranslator::TranslateUniversalRemoteString(const char *szButton)
1332 if (!szButton || strlen(szButton) < 4 || strnicmp(szButton, "obc", 3))
1333 return 0;
1334 const char *szCode = szButton + 3;
1335 // Button Code is 255 - OBC (Original Button Code) of the button
1336 uint32_t buttonCode = 255 - atol(szCode);
1337 if (buttonCode > 255)
1338 buttonCode = 0;
1339 return buttonCode;
1342 uint32_t CButtonTranslator::TranslateKeyboardString(const char *szButton)
1344 uint32_t buttonCode = 0;
1345 XBMCKEYTABLE keytable;
1347 // Look up the key name
1348 if (KeyTableLookupName(szButton, &keytable))
1350 buttonCode = keytable.vkey;
1353 // The lookup failed i.e. the key name wasn't found
1354 else
1356 CLog::Log(LOGERROR, "Keyboard Translator: Can't find button %s", szButton);
1359 buttonCode |= KEY_VKEY;
1361 return buttonCode;
1364 uint32_t CButtonTranslator::TranslateKeyboardButton(TiXmlElement *pButton)
1366 uint32_t button_id = 0;
1367 const char *szButton = pButton->Value();
1369 if (!szButton)
1370 return 0;
1371 const std::string strKey = szButton;
1372 if (strKey == "key")
1374 std::string strID;
1375 if (pButton->QueryValueAttribute("id", &strID) == TIXML_SUCCESS)
1377 const char *str = strID.c_str();
1378 char *endptr;
1379 long int id = strtol(str, &endptr, 0);
1380 if (endptr - str != (int)strlen(str) || id <= 0 || id > 0x00FFFFFF)
1381 CLog::Log(LOGDEBUG, "%s - invalid key id %s", __FUNCTION__, strID.c_str());
1382 else
1383 button_id = (uint32_t) id;
1385 else
1386 CLog::Log(LOGERROR, "Keyboard Translator: `key' button has no id");
1388 else
1389 button_id = TranslateKeyboardString(szButton);
1391 // Process the ctrl/shift/alt modifiers
1392 std::string strMod;
1393 if (pButton->QueryValueAttribute("mod", &strMod) == TIXML_SUCCESS)
1395 StringUtils::ToLower(strMod);
1397 std::vector<std::string> modArray = StringUtils::Split(strMod, ",");
1398 for (std::vector<std::string>::const_iterator i = modArray.begin(); i != modArray.end(); ++i)
1400 std::string substr = *i;
1401 StringUtils::Trim(substr);
1403 if (substr == "ctrl" || substr == "control")
1404 button_id |= CKey::MODIFIER_CTRL;
1405 else if (substr == "shift")
1406 button_id |= CKey::MODIFIER_SHIFT;
1407 else if (substr == "alt")
1408 button_id |= CKey::MODIFIER_ALT;
1409 else if (substr == "super" || substr == "win")
1410 button_id |= CKey::MODIFIER_SUPER;
1411 else if (substr == "meta" || substr == "cmd")
1412 button_id |= CKey::MODIFIER_META;
1413 else if (substr == "longpress")
1414 button_id |= CKey::MODIFIER_LONG;
1415 else
1416 CLog::Log(LOGERROR, "Keyboard Translator: Unknown key modifier %s in %s", substr.c_str(), strMod.c_str());
1420 return button_id;
1423 uint32_t CButtonTranslator::TranslateAppCommand(const char *szButton)
1425 #ifdef TARGET_WINDOWS
1426 std::string strAppCommand = szButton;
1427 StringUtils::ToLower(strAppCommand);
1429 for (int i = 0; i < ARRAY_SIZE(appcommands); i++)
1430 if (strAppCommand == appcommands[i].name)
1431 return appcommands[i].action | KEY_APPCOMMAND;
1433 CLog::Log(LOGERROR, "%s: Can't find appcommand %s", __FUNCTION__, szButton);
1434 #endif
1436 return 0;
1439 uint32_t CButtonTranslator::TranslateMouseCommand(TiXmlElement *pButton)
1441 uint32_t buttonId = 0;
1443 if (pButton)
1445 std::string szKey = pButton->ValueStr();
1446 if (!szKey.empty())
1448 StringUtils::ToLower(szKey);
1449 for (unsigned int i = 0; i < ARRAY_SIZE(mousekeys); i++)
1451 if (szKey == mousekeys[i].name)
1453 buttonId = mousekeys[i].action;
1454 break;
1457 if (!buttonId)
1459 CLog::Log(LOGERROR, "Unknown mouse action (%s), skipping", pButton->Value());
1461 else
1463 int id = 0;
1464 if ((pButton->QueryIntAttribute("id", &id) == TIXML_SUCCESS) && id>=0 && id<MOUSE_MAX_BUTTON)
1466 buttonId += id;
1472 return buttonId;
1475 void CButtonTranslator::Clear()
1477 m_translatorMap.clear();
1478 #if defined(HAS_LIRC) || defined(HAS_IRSERVERSUITE)
1479 ClearLircButtonMapEntries();
1480 lircRemotesMap.clear();
1481 #endif
1483 m_customControllersMap.clear();
1485 m_Loaded = false;
1488 uint32_t CButtonTranslator::TranslateTouchCommand(TiXmlElement *pButton, CButtonAction &action)
1490 const char *szButton = pButton->Value();
1491 if (szButton == NULL || pButton->FirstChild() == NULL)
1492 return ACTION_NONE;
1494 const char *szAction = pButton->FirstChild()->Value();
1495 if (szAction == NULL)
1496 return ACTION_NONE;
1498 std::string strTouchCommand = szButton;
1499 StringUtils::ToLower(strTouchCommand);
1501 const char *attrVal = pButton->Attribute("direction");
1502 if (attrVal != NULL)
1503 strTouchCommand += attrVal;
1505 uint32_t actionId = ACTION_NONE;
1506 for (unsigned int i = 0; i < ARRAY_SIZE(touchcommands); i++)
1508 if (strTouchCommand == touchcommands[i].name)
1510 actionId = touchcommands[i].action;
1511 break;
1515 if (actionId <= ACTION_NONE)
1517 CLog::Log(LOGERROR, "%s: Can't find touch command %s", __FUNCTION__, szButton);
1518 return ACTION_NONE;
1521 attrVal = pButton->Attribute("pointers");
1522 if (attrVal != NULL)
1524 int pointers = (int)strtol(attrVal, NULL, 0);
1525 if (pointers >= 1)
1526 actionId += pointers - 1;
1529 action.strID = szAction;
1530 if (!TranslateActionString(szAction, action.id) || action.id <= ACTION_NONE)
1531 return ACTION_NONE;
1533 return actionId | KEY_TOUCH;
1536 void CButtonTranslator::MapTouchActions(int windowID, TiXmlNode *pTouch)
1538 if (pTouch == NULL)
1539 return;
1541 buttonMap map;
1542 // check if there already is a touch map for the window ID
1543 std::map<int, buttonMap>::iterator it = m_touchMap.find(windowID);
1544 if (it != m_touchMap.end())
1546 // get the existing touch map and remove it from the window mapping
1547 // as it will be inserted later on
1548 map = it->second;
1549 m_touchMap.erase(it);
1552 uint32_t actionId = 0;
1553 TiXmlElement *pTouchElem = pTouch->ToElement();
1554 if (pTouchElem == NULL)
1555 return;
1557 TiXmlElement *pButton = pTouchElem->FirstChildElement();
1558 while (pButton != NULL)
1560 CButtonAction action;
1561 actionId = TranslateTouchCommand(pButton, action);
1562 if (actionId > 0)
1564 // check if there already is a mapping for the parsed action
1565 // and remove it if necessary
1566 buttonMap::iterator actionIt = map.find(actionId);
1567 if (actionIt != map.end())
1568 map.erase(actionIt);
1570 map.insert(std::make_pair(actionId, action));
1573 pButton = pButton->NextSiblingElement();
1576 // add the modified touch map with the window ID
1577 if (!map.empty())
1578 m_touchMap.insert(std::pair<int, buttonMap>(windowID, map));
1581 int CButtonTranslator::GetTouchActionCode(int window, int action, std::string &actionString)
1583 std::map<int, buttonMap>::const_iterator windowIt = m_touchMap.find(window);
1584 if (windowIt == m_touchMap.end())
1585 return ACTION_NONE;
1587 buttonMap::const_iterator touchIt = windowIt->second.find(action);
1588 if (touchIt == windowIt->second.end())
1589 return ACTION_NONE;
1591 actionString = touchIt->second.strID;
1592 return touchIt->second.id;
1595 uint32_t CButtonTranslator::TranslateJoystickString(const char *szButton)
1597 if (!szButton)
1598 return 0;
1599 uint32_t buttonCode = 0;
1600 std::string strButton = szButton;
1601 StringUtils::ToLower(strButton);
1603 if (strButton == "a") buttonCode = KEY_JOYSTICK_BUTTON_A;
1604 else if (strButton == "b") buttonCode = KEY_JOYSTICK_BUTTON_B;
1605 else if (strButton == "x") buttonCode = KEY_JOYSTICK_BUTTON_X;
1606 else if (strButton == "y") buttonCode = KEY_JOYSTICK_BUTTON_Y;
1607 else if (strButton == "start") buttonCode = KEY_JOYSTICK_BUTTON_START;
1608 else if (strButton == "back") buttonCode = KEY_JOYSTICK_BUTTON_BACK;
1609 else if (strButton == "left") buttonCode = KEY_JOYSTICK_BUTTON_DPAD_LEFT;
1610 else if (strButton == "right") buttonCode = KEY_JOYSTICK_BUTTON_DPAD_RIGHT;
1611 else if (strButton == "up") buttonCode = KEY_JOYSTICK_BUTTON_DPAD_UP;
1612 else if (strButton == "down") buttonCode = KEY_JOYSTICK_BUTTON_DPAD_DOWN;
1613 else if (strButton == "leftthumb") buttonCode = KEY_JOYSTICK_BUTTON_LEFT_STICK_BUTTON;
1614 else if (strButton == "rightthumb") buttonCode = KEY_JOYSTICK_BUTTON_RIGHT_STICK_BUTTON;
1615 else if (strButton == "leftstickup") buttonCode = KEY_JOYSTICK_BUTTON_LEFT_THUMB_STICK_UP;
1616 else if (strButton == "leftstickdown") buttonCode = KEY_JOYSTICK_BUTTON_LEFT_THUMB_STICK_DOWN;
1617 else if (strButton == "leftstickleft") buttonCode = KEY_JOYSTICK_BUTTON_LEFT_THUMB_STICK_LEFT;
1618 else if (strButton == "leftstickright") buttonCode = KEY_JOYSTICK_BUTTON_LEFT_THUMB_STICK_RIGHT;
1619 else if (strButton == "rightstickup") buttonCode = KEY_JOYSTICK_BUTTON_RIGHT_THUMB_STICK_UP;
1620 else if (strButton == "rightstickdown") buttonCode = KEY_JOYSTICK_BUTTON_RIGHT_THUMB_STICK_DOWN;
1621 else if (strButton == "rightstickleft") buttonCode = KEY_JOYSTICK_BUTTON_RIGHT_THUMB_STICK_LEFT;
1622 else if (strButton == "rightstickright") buttonCode = KEY_JOYSTICK_BUTTON_RIGHT_THUMB_STICK_RIGHT;
1623 else if (strButton == "lefttrigger") buttonCode = KEY_JOYSTICK_BUTTON_LEFT_TRIGGER;
1624 else if (strButton == "righttrigger") buttonCode = KEY_JOYSTICK_BUTTON_RIGHT_TRIGGER;
1625 else if (strButton == "leftbumper") buttonCode = KEY_JOYSTICK_BUTTON_LEFT_SHOULDER;
1626 else if (strButton == "rightbumper") buttonCode = KEY_JOYSTICK_BUTTON_RIGHT_SHOULDER;
1627 else if (strButton == "guide") buttonCode = KEY_JOYSTICK_BUTTON_GUIDE;
1628 else CLog::Log(LOGERROR, "Joystick Translator: Can't find button %s", strButton.c_str());
1630 return buttonCode;