Merge pull request #4594 from FernetMenta/paplayer
[xbmc.git] / xbmc / input / ButtonTranslator.cpp
blob4407d8d1fca873e81dadf8644e7ccdaa7b4404b7
1 /*
2 * Copyright (C) 2005-2013 Team XBMC
3 * http://xbmc.org
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 XBMC; see the file COPYING. If not, see
17 * <http://www.gnu.org/licenses/>.
21 #include "system.h"
22 #include "interfaces/Builtins.h"
23 #include "ButtonTranslator.h"
24 #include "profiles/ProfilesManager.h"
25 #include "utils/URIUtils.h"
26 #include "guilib/Key.h"
27 #include "guilib/WindowIDs.h"
28 #include "input/XBMC_keysym.h"
29 #include "input/XBMC_keytable.h"
30 #include "filesystem/File.h"
31 #include "filesystem/Directory.h"
32 #include "FileItem.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"
42 #endif
44 #define JOYSTICK_DEFAULT_MAP "_xbmc_"
46 using namespace std;
47 using namespace XFILE;
49 typedef struct
51 const char* name;
52 int action;
53 } ActionMapping;
55 typedef struct
57 int origin;
58 int target;
59 } WindowMapping;
61 static const ActionMapping actions[] =
63 {"left" , ACTION_MOVE_LEFT },
64 {"right" , ACTION_MOVE_RIGHT},
65 {"up" , ACTION_MOVE_UP },
66 {"down" , ACTION_MOVE_DOWN },
67 {"pageup" , ACTION_PAGE_UP },
68 {"pagedown" , ACTION_PAGE_DOWN},
69 {"select" , ACTION_SELECT_ITEM},
70 {"highlight" , ACTION_HIGHLIGHT_ITEM},
71 {"parentdir" , ACTION_NAV_BACK}, // backward compatibility
72 {"parentfolder" , ACTION_PARENT_DIR},
73 {"back" , ACTION_NAV_BACK},
74 {"previousmenu" , ACTION_PREVIOUS_MENU},
75 {"info" , ACTION_SHOW_INFO},
76 {"pause" , ACTION_PAUSE},
77 {"stop" , ACTION_STOP},
78 {"skipnext" , ACTION_NEXT_ITEM},
79 {"skipprevious" , ACTION_PREV_ITEM},
80 {"fullscreen" , ACTION_SHOW_GUI},
81 {"aspectratio" , ACTION_ASPECT_RATIO},
82 {"stepforward" , ACTION_STEP_FORWARD},
83 {"stepback" , ACTION_STEP_BACK},
84 {"bigstepforward" , ACTION_BIG_STEP_FORWARD},
85 {"bigstepback" , ACTION_BIG_STEP_BACK},
86 {"chapterorbigstepforward", ACTION_CHAPTER_OR_BIG_STEP_FORWARD},
87 {"chapterorbigstepback" , ACTION_CHAPTER_OR_BIG_STEP_BACK},
88 {"osd" , ACTION_SHOW_OSD},
89 {"showsubtitles" , ACTION_SHOW_SUBTITLES},
90 {"nextsubtitle" , ACTION_NEXT_SUBTITLE},
91 {"codecinfo" , ACTION_SHOW_CODEC},
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 {"rotate" , ACTION_ROTATE_PICTURE_CW},
112 {"rotateccw" , ACTION_ROTATE_PICTURE_CCW},
113 {"close" , ACTION_NAV_BACK}, // backwards compatibility
114 {"subtitledelayminus", ACTION_SUBTITLE_DELAY_MIN},
115 {"subtitledelay" , ACTION_SUBTITLE_DELAY},
116 {"subtitledelayplus" , ACTION_SUBTITLE_DELAY_PLUS},
117 {"audiodelayminus" , ACTION_AUDIO_DELAY_MIN},
118 {"audiodelay" , ACTION_AUDIO_DELAY},
119 {"audiodelayplus" , ACTION_AUDIO_DELAY_PLUS},
120 {"subtitleshiftup" , ACTION_SUBTITLE_VSHIFT_UP},
121 {"subtitleshiftdown" , ACTION_SUBTITLE_VSHIFT_DOWN},
122 {"subtitlealign" , ACTION_SUBTITLE_ALIGN},
123 {"audionextlanguage" , ACTION_AUDIO_NEXT_LANGUAGE},
124 {"verticalshiftup" , ACTION_VSHIFT_UP},
125 {"verticalshiftdown" , ACTION_VSHIFT_DOWN},
126 {"nextresolution" , ACTION_CHANGE_RESOLUTION},
127 {"audiotoggledigital", ACTION_TOGGLE_DIGITAL_ANALOG},
128 {"number0" , REMOTE_0},
129 {"number1" , REMOTE_1},
130 {"number2" , REMOTE_2},
131 {"number3" , REMOTE_3},
132 {"number4" , REMOTE_4},
133 {"number5" , REMOTE_5},
134 {"number6" , REMOTE_6},
135 {"number7" , REMOTE_7},
136 {"number8" , REMOTE_8},
137 {"number9" , REMOTE_9},
138 {"osdleft" , ACTION_OSD_SHOW_LEFT},
139 {"osdright" , ACTION_OSD_SHOW_RIGHT},
140 {"osdup" , ACTION_OSD_SHOW_UP},
141 {"osddown" , ACTION_OSD_SHOW_DOWN},
142 {"osdselect" , ACTION_OSD_SHOW_SELECT},
143 {"osdvalueplus" , ACTION_OSD_SHOW_VALUE_PLUS},
144 {"osdvalueminus" , ACTION_OSD_SHOW_VALUE_MIN},
145 {"smallstepback" , ACTION_SMALL_STEP_BACK},
146 {"fastforward" , ACTION_PLAYER_FORWARD},
147 {"rewind" , ACTION_PLAYER_REWIND},
148 {"play" , ACTION_PLAYER_PLAY},
149 {"playpause" , ACTION_PLAYER_PLAYPAUSE},
150 {"switchplayer" , ACTION_SWITCH_PLAYER},
151 {"delete" , ACTION_DELETE_ITEM},
152 {"copy" , ACTION_COPY_ITEM},
153 {"move" , ACTION_MOVE_ITEM},
154 {"mplayerosd" , ACTION_SHOW_MPLAYER_OSD},
155 {"hidesubmenu" , ACTION_OSD_HIDESUBMENU},
156 {"screenshot" , ACTION_TAKE_SCREENSHOT},
157 {"rename" , ACTION_RENAME_ITEM},
158 {"togglewatched" , ACTION_TOGGLE_WATCHED},
159 {"scanitem" , ACTION_SCAN_ITEM},
160 {"reloadkeymaps" , ACTION_RELOAD_KEYMAPS},
161 {"volumeup" , ACTION_VOLUME_UP},
162 {"volumedown" , ACTION_VOLUME_DOWN},
163 {"mute" , ACTION_MUTE},
164 {"backspace" , ACTION_BACKSPACE},
165 {"scrollup" , ACTION_SCROLL_UP},
166 {"scrolldown" , ACTION_SCROLL_DOWN},
167 {"analogfastforward" , ACTION_ANALOG_FORWARD},
168 {"analogrewind" , ACTION_ANALOG_REWIND},
169 {"moveitemup" , ACTION_MOVE_ITEM_UP},
170 {"moveitemdown" , ACTION_MOVE_ITEM_DOWN},
171 {"contextmenu" , ACTION_CONTEXT_MENU},
172 {"shift" , ACTION_SHIFT},
173 {"symbols" , ACTION_SYMBOLS},
174 {"cursorleft" , ACTION_CURSOR_LEFT},
175 {"cursorright" , ACTION_CURSOR_RIGHT},
176 {"showtime" , ACTION_SHOW_OSD_TIME},
177 {"analogseekforward" , ACTION_ANALOG_SEEK_FORWARD},
178 {"analogseekback" , ACTION_ANALOG_SEEK_BACK},
179 {"showpreset" , ACTION_VIS_PRESET_SHOW},
180 {"presetlist" , ACTION_VIS_PRESET_LIST},
181 {"nextpreset" , ACTION_VIS_PRESET_NEXT},
182 {"previouspreset" , ACTION_VIS_PRESET_PREV},
183 {"lockpreset" , ACTION_VIS_PRESET_LOCK},
184 {"randompreset" , ACTION_VIS_PRESET_RANDOM},
185 {"increasevisrating" , ACTION_VIS_RATE_PRESET_PLUS},
186 {"decreasevisrating" , ACTION_VIS_RATE_PRESET_MINUS},
187 {"showvideomenu" , ACTION_SHOW_VIDEOMENU},
188 {"enter" , ACTION_ENTER},
189 {"increaserating" , ACTION_INCREASE_RATING},
190 {"decreaserating" , ACTION_DECREASE_RATING},
191 {"togglefullscreen" , ACTION_TOGGLE_FULLSCREEN},
192 {"nextscene" , ACTION_NEXT_SCENE},
193 {"previousscene" , ACTION_PREV_SCENE},
194 {"nextletter" , ACTION_NEXT_LETTER},
195 {"prevletter" , ACTION_PREV_LETTER},
196 {"jumpsms2" , ACTION_JUMP_SMS2},
197 {"jumpsms3" , ACTION_JUMP_SMS3},
198 {"jumpsms4" , ACTION_JUMP_SMS4},
199 {"jumpsms5" , ACTION_JUMP_SMS5},
200 {"jumpsms6" , ACTION_JUMP_SMS6},
201 {"jumpsms7" , ACTION_JUMP_SMS7},
202 {"jumpsms8" , ACTION_JUMP_SMS8},
203 {"jumpsms9" , ACTION_JUMP_SMS9},
204 {"filter" , ACTION_FILTER},
205 {"filterclear" , ACTION_FILTER_CLEAR},
206 {"filtersms2" , ACTION_FILTER_SMS2},
207 {"filtersms3" , ACTION_FILTER_SMS3},
208 {"filtersms4" , ACTION_FILTER_SMS4},
209 {"filtersms5" , ACTION_FILTER_SMS5},
210 {"filtersms6" , ACTION_FILTER_SMS6},
211 {"filtersms7" , ACTION_FILTER_SMS7},
212 {"filtersms8" , ACTION_FILTER_SMS8},
213 {"filtersms9" , ACTION_FILTER_SMS9},
214 {"firstpage" , ACTION_FIRST_PAGE},
215 {"lastpage" , ACTION_LAST_PAGE},
216 {"guiprofile" , ACTION_GUIPROFILE_BEGIN},
217 {"red" , ACTION_TELETEXT_RED},
218 {"green" , ACTION_TELETEXT_GREEN},
219 {"yellow" , ACTION_TELETEXT_YELLOW},
220 {"blue" , ACTION_TELETEXT_BLUE},
221 {"increasepar" , ACTION_INCREASE_PAR},
222 {"decreasepar" , ACTION_DECREASE_PAR},
223 {"volampup" , ACTION_VOLAMP_UP},
224 {"volampdown" , ACTION_VOLAMP_DOWN},
225 {"createbookmark" , ACTION_CREATE_BOOKMARK},
226 {"createepisodebookmark" , ACTION_CREATE_EPISODE_BOOKMARK},
227 {"settingsreset" , ACTION_SETTINGS_RESET},
228 {"settingslevelchange", ACTION_SETTINGS_LEVEL_CHANGE},
230 // 3D movie playback/GUI
231 {"stereomode" , ACTION_STEREOMODE_SELECT}, // cycle 3D modes, for now an alias for next
232 {"nextstereomode" , ACTION_STEREOMODE_NEXT},
233 {"previousstereomode" , ACTION_STEREOMODE_PREVIOUS},
234 {"togglestereomode" , ACTION_STEREOMODE_TOGGLE},
235 {"stereomodetomono" , ACTION_STEREOMODE_TOMONO},
237 // PVR actions
238 {"channelup" , ACTION_CHANNEL_UP},
239 {"channeldown" , ACTION_CHANNEL_DOWN},
240 {"previouschannelgroup" , ACTION_PREVIOUS_CHANNELGROUP},
241 {"nextchannelgroup" , ACTION_NEXT_CHANNELGROUP},
242 {"playpvr" , ACTION_PVR_PLAY},
243 {"playpvrtv" , ACTION_PVR_PLAY_TV},
244 {"playpvrradio" , ACTION_PVR_PLAY_RADIO},
245 {"record" , ACTION_RECORD},
247 // Mouse actions
248 {"leftclick" , ACTION_MOUSE_LEFT_CLICK},
249 {"rightclick" , ACTION_MOUSE_RIGHT_CLICK},
250 {"middleclick" , ACTION_MOUSE_MIDDLE_CLICK},
251 {"doubleclick" , ACTION_MOUSE_DOUBLE_CLICK},
252 {"wheelup" , ACTION_MOUSE_WHEEL_UP},
253 {"wheeldown" , ACTION_MOUSE_WHEEL_DOWN},
254 {"mousedrag" , ACTION_MOUSE_DRAG},
255 {"mousemove" , ACTION_MOUSE_MOVE},
257 // Touch
258 {"tap" , ACTION_TOUCH_TAP},
259 {"longpress" , ACTION_TOUCH_LONGPRESS},
260 {"pangesture" , ACTION_GESTURE_PAN},
261 {"zoomgesture" , ACTION_GESTURE_ZOOM},
262 {"rotategesture" , ACTION_GESTURE_ROTATE},
263 {"swipeleft" , ACTION_GESTURE_SWIPE_LEFT},
264 {"swiperight" , ACTION_GESTURE_SWIPE_RIGHT},
265 {"swipeup" , ACTION_GESTURE_SWIPE_UP},
266 {"swipedown" , ACTION_GESTURE_SWIPE_DOWN},
268 // Do nothing action
269 { "noop" , ACTION_NOOP}
272 static const ActionMapping windows[] =
273 {{"home" , WINDOW_HOME},
274 {"programs" , WINDOW_PROGRAMS},
275 {"pictures" , WINDOW_PICTURES},
276 {"filemanager" , WINDOW_FILES},
277 {"files" , WINDOW_FILES}, // backward compat
278 {"settings" , WINDOW_SETTINGS_MENU},
279 {"music" , WINDOW_MUSIC},
280 {"video" , WINDOW_VIDEOS},
281 {"videos" , WINDOW_VIDEO_NAV},
282 {"tv" , WINDOW_PVR}, // backward compat
283 {"pvr" , WINDOW_PVR},
284 {"pvrguideinfo" , WINDOW_DIALOG_PVR_GUIDE_INFO},
285 {"pvrrecordinginfo" , WINDOW_DIALOG_PVR_RECORDING_INFO},
286 {"pvrtimersetting" , WINDOW_DIALOG_PVR_TIMER_SETTING},
287 {"pvrgroupmanager" , WINDOW_DIALOG_PVR_GROUP_MANAGER},
288 {"pvrchannelmanager" , WINDOW_DIALOG_PVR_CHANNEL_MANAGER},
289 {"pvrguidesearch" , WINDOW_DIALOG_PVR_GUIDE_SEARCH},
290 {"pvrchannelscan" , WINDOW_DIALOG_PVR_CHANNEL_SCAN},
291 {"pvrupdateprogress" , WINDOW_DIALOG_PVR_UPDATE_PROGRESS},
292 {"pvrosdchannels" , WINDOW_DIALOG_PVR_OSD_CHANNELS},
293 {"pvrosdguide" , WINDOW_DIALOG_PVR_OSD_GUIDE},
294 {"pvrosddirector" , WINDOW_DIALOG_PVR_OSD_DIRECTOR},
295 {"pvrosdcutter" , WINDOW_DIALOG_PVR_OSD_CUTTER},
296 {"pvrosdteletext" , WINDOW_DIALOG_OSD_TELETEXT},
297 {"systeminfo" , WINDOW_SYSTEM_INFORMATION},
298 {"testpattern" , WINDOW_TEST_PATTERN},
299 {"screencalibration" , WINDOW_SCREEN_CALIBRATION},
300 {"guicalibration" , WINDOW_SCREEN_CALIBRATION}, // backward compat
301 {"picturessettings" , WINDOW_SETTINGS_MYPICTURES},
302 {"programssettings" , WINDOW_SETTINGS_MYPROGRAMS},
303 {"weathersettings" , WINDOW_SETTINGS_MYWEATHER},
304 {"musicsettings" , WINDOW_SETTINGS_MYMUSIC},
305 {"systemsettings" , WINDOW_SETTINGS_SYSTEM},
306 {"videossettings" , WINDOW_SETTINGS_MYVIDEOS},
307 {"networksettings" , WINDOW_SETTINGS_SERVICE}, // backward compat
308 {"servicesettings" , WINDOW_SETTINGS_SERVICE},
309 {"appearancesettings" , WINDOW_SETTINGS_APPEARANCE},
310 {"pvrsettings" , WINDOW_SETTINGS_MYPVR},
311 {"tvsettings" , WINDOW_SETTINGS_MYPVR}, // backward compat
312 {"scripts" , WINDOW_PROGRAMS}, // backward compat
313 {"videofiles" , WINDOW_VIDEO_FILES},
314 {"videolibrary" , WINDOW_VIDEO_NAV},
315 {"videoplaylist" , WINDOW_VIDEO_PLAYLIST},
316 {"loginscreen" , WINDOW_LOGIN_SCREEN},
317 {"profiles" , WINDOW_SETTINGS_PROFILES},
318 {"skinsettings" , WINDOW_SKIN_SETTINGS},
319 {"addonbrowser" , WINDOW_ADDON_BROWSER},
320 {"yesnodialog" , WINDOW_DIALOG_YES_NO},
321 {"progressdialog" , WINDOW_DIALOG_PROGRESS},
322 {"virtualkeyboard" , WINDOW_DIALOG_KEYBOARD},
323 {"volumebar" , WINDOW_DIALOG_VOLUME_BAR},
324 {"submenu" , WINDOW_DIALOG_SUB_MENU},
325 {"favourites" , WINDOW_DIALOG_FAVOURITES},
326 {"contextmenu" , WINDOW_DIALOG_CONTEXT_MENU},
327 {"infodialog" , WINDOW_DIALOG_KAI_TOAST},
328 {"numericinput" , WINDOW_DIALOG_NUMERIC},
329 {"gamepadinput" , WINDOW_DIALOG_GAMEPAD},
330 {"shutdownmenu" , WINDOW_DIALOG_BUTTON_MENU},
331 {"mutebug" , WINDOW_DIALOG_MUTE_BUG},
332 {"playercontrols" , WINDOW_DIALOG_PLAYER_CONTROLS},
333 {"seekbar" , WINDOW_DIALOG_SEEK_BAR},
334 {"musicosd" , WINDOW_DIALOG_MUSIC_OSD},
335 {"addonsettings" , WINDOW_DIALOG_ADDON_SETTINGS},
336 {"visualisationsettings" , WINDOW_DIALOG_ADDON_SETTINGS}, // backward compat
337 {"visualisationpresetlist" , WINDOW_DIALOG_VIS_PRESET_LIST},
338 {"osdvideosettings" , WINDOW_DIALOG_VIDEO_OSD_SETTINGS},
339 {"osdaudiosettings" , WINDOW_DIALOG_AUDIO_OSD_SETTINGS},
340 {"videobookmarks" , WINDOW_DIALOG_VIDEO_BOOKMARKS},
341 {"filebrowser" , WINDOW_DIALOG_FILE_BROWSER},
342 {"networksetup" , WINDOW_DIALOG_NETWORK_SETUP},
343 {"mediasource" , WINDOW_DIALOG_MEDIA_SOURCE},
344 {"profilesettings" , WINDOW_DIALOG_PROFILE_SETTINGS},
345 {"locksettings" , WINDOW_DIALOG_LOCK_SETTINGS},
346 {"contentsettings" , WINDOW_DIALOG_CONTENT_SETTINGS},
347 {"songinformation" , WINDOW_DIALOG_SONG_INFO},
348 {"smartplaylisteditor" , WINDOW_DIALOG_SMART_PLAYLIST_EDITOR},
349 {"smartplaylistrule" , WINDOW_DIALOG_SMART_PLAYLIST_RULE},
350 {"busydialog" , WINDOW_DIALOG_BUSY},
351 {"pictureinfo" , WINDOW_DIALOG_PICTURE_INFO},
352 {"accesspoints" , WINDOW_DIALOG_ACCESS_POINTS},
353 {"fullscreeninfo" , WINDOW_DIALOG_FULLSCREEN_INFO},
354 {"karaokeselector" , WINDOW_DIALOG_KARAOKE_SONGSELECT},
355 {"karaokelargeselector" , WINDOW_DIALOG_KARAOKE_SELECTOR},
356 {"sliderdialog" , WINDOW_DIALOG_SLIDER},
357 {"addoninformation" , WINDOW_DIALOG_ADDON_INFO},
358 {"subtitlesearch" , WINDOW_DIALOG_SUBTITLES},
359 {"musicplaylist" , WINDOW_MUSIC_PLAYLIST},
360 {"musicfiles" , WINDOW_MUSIC_FILES},
361 {"musiclibrary" , WINDOW_MUSIC_NAV},
362 {"musicplaylisteditor" , WINDOW_MUSIC_PLAYLIST_EDITOR},
363 {"teletext" , WINDOW_DIALOG_OSD_TELETEXT},
364 {"selectdialog" , WINDOW_DIALOG_SELECT},
365 {"musicinformation" , WINDOW_DIALOG_MUSIC_INFO},
366 {"okdialog" , WINDOW_DIALOG_OK},
367 {"movieinformation" , WINDOW_DIALOG_VIDEO_INFO},
368 {"textviewer" , WINDOW_DIALOG_TEXT_VIEWER},
369 {"fullscreenvideo" , WINDOW_FULLSCREEN_VIDEO},
370 {"fullscreenlivetv" , WINDOW_FULLSCREEN_LIVETV}, // virtual window/keymap section for PVR specific bindings in fullscreen playback (which internally uses WINDOW_FULLSCREEN_VIDEO)
371 {"visualisation" , WINDOW_VISUALISATION},
372 {"slideshow" , WINDOW_SLIDESHOW},
373 {"filestackingdialog" , WINDOW_DIALOG_FILESTACKING},
374 {"karaoke" , WINDOW_KARAOKELYRICS},
375 {"weather" , WINDOW_WEATHER},
376 {"screensaver" , WINDOW_SCREENSAVER},
377 {"videoosd" , WINDOW_DIALOG_VIDEO_OSD},
378 {"videomenu" , WINDOW_VIDEO_MENU},
379 {"videotimeseek" , WINDOW_VIDEO_TIME_SEEK},
380 {"musicoverlay" , WINDOW_DIALOG_MUSIC_OVERLAY},
381 {"videooverlay" , WINDOW_DIALOG_VIDEO_OVERLAY},
382 {"startwindow" , WINDOW_START},
383 {"startup" , WINDOW_STARTUP_ANIM},
384 {"peripherals" , WINDOW_DIALOG_PERIPHERAL_MANAGER},
385 {"peripheralsettings" , WINDOW_DIALOG_PERIPHERAL_SETTINGS},
386 {"extendedprogressdialog" , WINDOW_DIALOG_EXT_PROGRESS},
387 {"mediafilter" , WINDOW_DIALOG_MEDIA_FILTER},
388 {"addon" , WINDOW_ADDON_START}};
390 static const ActionMapping mousecommands[] =
392 { "leftclick", ACTION_MOUSE_LEFT_CLICK },
393 { "rightclick", ACTION_MOUSE_RIGHT_CLICK },
394 { "middleclick", ACTION_MOUSE_MIDDLE_CLICK },
395 { "doubleclick", ACTION_MOUSE_DOUBLE_CLICK },
396 { "wheelup", ACTION_MOUSE_WHEEL_UP },
397 { "wheeldown", ACTION_MOUSE_WHEEL_DOWN },
398 { "mousedrag", ACTION_MOUSE_DRAG },
399 { "mousemove", ACTION_MOUSE_MOVE }
402 static const ActionMapping touchcommands[] =
404 { "tap", ACTION_TOUCH_TAP },
405 { "longpress", ACTION_TOUCH_LONGPRESS },
406 { "pan", ACTION_GESTURE_PAN },
407 { "zoom", ACTION_GESTURE_ZOOM },
408 { "rotate", ACTION_GESTURE_ROTATE },
409 { "swipeleft", ACTION_GESTURE_SWIPE_LEFT },
410 { "swiperight", ACTION_GESTURE_SWIPE_RIGHT },
411 { "swipeup", ACTION_GESTURE_SWIPE_UP },
412 { "swipedown", ACTION_GESTURE_SWIPE_DOWN }
415 static const WindowMapping fallbackWindows[] =
417 { WINDOW_FULLSCREEN_LIVETV, WINDOW_FULLSCREEN_VIDEO },
418 { WINDOW_DIALOG_FULLSCREEN_INFO, WINDOW_FULLSCREEN_VIDEO }
421 #ifdef TARGET_WINDOWS
422 static const ActionMapping appcommands[] =
424 { "browser_back", APPCOMMAND_BROWSER_BACKWARD },
425 { "browser_forward", APPCOMMAND_BROWSER_FORWARD },
426 { "browser_refresh", APPCOMMAND_BROWSER_REFRESH },
427 { "browser_stop", APPCOMMAND_BROWSER_STOP },
428 { "browser_search", APPCOMMAND_BROWSER_SEARCH },
429 { "browser_favorites", APPCOMMAND_BROWSER_FAVORITES },
430 { "browser_home", APPCOMMAND_BROWSER_HOME },
431 { "volume_mute", APPCOMMAND_VOLUME_MUTE },
432 { "volume_down", APPCOMMAND_VOLUME_DOWN },
433 { "volume_up", APPCOMMAND_VOLUME_UP },
434 { "next_track", APPCOMMAND_MEDIA_NEXTTRACK },
435 { "prev_track", APPCOMMAND_MEDIA_PREVIOUSTRACK },
436 { "stop", APPCOMMAND_MEDIA_STOP },
437 { "play_pause", APPCOMMAND_MEDIA_PLAY_PAUSE },
438 { "launch_mail", APPCOMMAND_LAUNCH_MAIL },
439 { "launch_media_select", APPCOMMAND_LAUNCH_MEDIA_SELECT },
440 { "launch_app1", APPCOMMAND_LAUNCH_APP1 },
441 { "launch_app2", APPCOMMAND_LAUNCH_APP2 },
442 { "play", APPCOMMAND_MEDIA_PLAY },
443 { "pause", APPCOMMAND_MEDIA_PAUSE },
444 { "fastforward", APPCOMMAND_MEDIA_FAST_FORWARD },
445 { "rewind", APPCOMMAND_MEDIA_REWIND },
446 { "channelup", APPCOMMAND_MEDIA_CHANNEL_UP },
447 { "channeldown", APPCOMMAND_MEDIA_CHANNEL_DOWN }
449 #endif
451 CButtonTranslator& CButtonTranslator::GetInstance()
453 static CButtonTranslator sl_instance;
454 return sl_instance;
457 CButtonTranslator::CButtonTranslator()
459 m_deviceList.clear();
460 m_Loaded = false;
463 #if defined(HAS_LIRC) || defined(HAS_IRSERVERSUITE)
464 void CButtonTranslator::ClearLircButtonMapEntries()
466 vector<lircButtonMap*> maps;
467 for (map<CStdString,lircButtonMap*>::iterator it = lircRemotesMap.begin();
468 it != lircRemotesMap.end();++it)
469 maps.push_back(it->second);
470 sort(maps.begin(),maps.end());
471 vector<lircButtonMap*>::iterator itend = unique(maps.begin(),maps.end());
472 for (vector<lircButtonMap*>::iterator it = maps.begin(); it != itend;++it)
473 delete *it;
475 #endif
477 CButtonTranslator::~CButtonTranslator()
479 #if defined(HAS_LIRC) || defined(HAS_IRSERVERSUITE)
480 ClearLircButtonMapEntries();
481 #endif
484 // Add the supplied device name to the list of connected devices
485 void CButtonTranslator::AddDevice(CStdString& strDevice)
487 // Only add the device if it isn't already in the list
488 std::list<CStdString>::iterator it;
489 for (it = m_deviceList.begin(); it != m_deviceList.end(); it++)
490 if (*it == strDevice)
491 return;
493 // Add the device
494 m_deviceList.push_back(strDevice);
495 m_deviceList.sort();
497 // New device added so reload the key mappings
498 Load();
501 void CButtonTranslator::RemoveDevice(CStdString& strDevice)
503 // Find the device
504 std::list<CStdString>::iterator it;
505 for (it = m_deviceList.begin(); it != m_deviceList.end(); it++)
506 if (*it == strDevice)
507 break;
508 if (it == m_deviceList.end())
509 return;
511 // Remove the device
512 m_deviceList.remove(strDevice);
514 // Device removed so reload the key mappings
515 Load();
518 bool CButtonTranslator::Load(bool AlwaysLoad)
520 m_translatorMap.clear();
522 // Directories to search for keymaps. They're applied in this order,
523 // so keymaps in profile/keymaps/ override e.g. system/keymaps
524 static const char* DIRS_TO_CHECK[] = {
525 "special://xbmc/system/keymaps/",
526 "special://masterprofile/keymaps/",
527 "special://profile/keymaps/"
529 bool success = false;
531 for (unsigned int dirIndex = 0; dirIndex < sizeof(DIRS_TO_CHECK)/sizeof(DIRS_TO_CHECK[0]); ++dirIndex)
533 if (XFILE::CDirectory::Exists(DIRS_TO_CHECK[dirIndex]))
535 CFileItemList files;
536 XFILE::CDirectory::GetDirectory(DIRS_TO_CHECK[dirIndex], files, ".xml");
537 // Sort the list for filesystem based priorities, e.g. 01-keymap.xml, 02-keymap-overrides.xml
538 files.Sort(SortByFile, SortOrderAscending);
539 for(int fileIndex = 0; fileIndex<files.Size(); ++fileIndex)
541 if (!files[fileIndex]->m_bIsFolder)
542 success |= LoadKeymap(files[fileIndex]->GetPath());
545 // Load mappings for any HID devices we have connected
546 std::list<CStdString>::iterator it;
547 for (it = m_deviceList.begin(); it != m_deviceList.end(); it++)
549 CStdString devicedir = DIRS_TO_CHECK[dirIndex];
550 devicedir.append(*it);
551 devicedir.append("/");
552 if( XFILE::CDirectory::Exists(devicedir) )
554 CFileItemList files;
555 XFILE::CDirectory::GetDirectory(devicedir, files, ".xml");
556 // Sort the list for filesystem based priorities, e.g. 01-keymap.xml, 02-keymap-overrides.xml
557 files.Sort(SortByFile, SortOrderAscending);
558 for(int fileIndex = 0; fileIndex<files.Size(); ++fileIndex)
560 if (!files[fileIndex]->m_bIsFolder)
561 success |= LoadKeymap(files[fileIndex]->GetPath());
568 if (!success)
570 CLog::Log(LOGERROR, "Error loading keymaps from: %s or %s or %s", DIRS_TO_CHECK[0], DIRS_TO_CHECK[1], DIRS_TO_CHECK[2]);
571 return false;
574 #if defined(HAS_LIRC) || defined(HAS_IRSERVERSUITE)
575 #ifdef TARGET_POSIX
576 #define REMOTEMAP "Lircmap.xml"
577 #else
578 #define REMOTEMAP "IRSSmap.xml"
579 #endif
580 CStdString lircmapPath = URIUtils::AddFileToFolder("special://xbmc/system/", REMOTEMAP);
581 lircRemotesMap.clear();
582 if(CFile::Exists(lircmapPath))
583 success |= LoadLircMap(lircmapPath);
584 else
585 CLog::Log(LOGDEBUG, "CButtonTranslator::Load - no system %s found, skipping", REMOTEMAP);
587 lircmapPath = CProfilesManager::Get().GetUserDataItem(REMOTEMAP);
588 if(CFile::Exists(lircmapPath))
589 success |= LoadLircMap(lircmapPath);
590 else
591 CLog::Log(LOGDEBUG, "CButtonTranslator::Load - no userdata %s found, skipping", REMOTEMAP);
593 if (!success)
594 CLog::Log(LOGERROR, "CButtonTranslator::Load - unable to load remote map %s", REMOTEMAP);
595 // don't return false - it is to only indicate a fatal error (which this is not)
596 #endif
598 // Done!
599 m_Loaded = true;
600 return true;
603 bool CButtonTranslator::LoadKeymap(const CStdString &keymapPath)
605 CXBMCTinyXML xmlDoc;
607 CLog::Log(LOGINFO, "Loading %s", keymapPath.c_str());
608 if (!xmlDoc.LoadFile(keymapPath))
610 CLog::Log(LOGERROR, "Error loading keymap: %s, Line %d\n%s", keymapPath.c_str(), xmlDoc.ErrorRow(), xmlDoc.ErrorDesc());
611 return false;
613 TiXmlElement* pRoot = xmlDoc.RootElement();
614 if (!pRoot)
616 CLog::Log(LOGERROR, "Error getting keymap root: %s", keymapPath.c_str());
617 return false;
619 CStdString strValue = pRoot->Value();
620 if ( strValue != "keymap")
622 CLog::Log(LOGERROR, "%s Doesn't contain <keymap>", keymapPath.c_str());
623 return false;
625 // run through our window groups
626 TiXmlNode* pWindow = pRoot->FirstChild();
627 while (pWindow)
629 if (pWindow->Type() == TiXmlNode::TINYXML_ELEMENT)
631 int windowID = WINDOW_INVALID;
632 const char *szWindow = pWindow->Value();
633 if (szWindow)
635 if (strcmpi(szWindow, "global") == 0)
636 windowID = -1;
637 else
638 windowID = TranslateWindow(szWindow);
640 MapWindowActions(pWindow, windowID);
642 pWindow = pWindow->NextSibling();
645 return true;
648 #if defined(HAS_LIRC) || defined(HAS_IRSERVERSUITE)
649 bool CButtonTranslator::LoadLircMap(const CStdString &lircmapPath)
651 #ifdef TARGET_POSIX
652 #define REMOTEMAPTAG "lircmap"
653 #else
654 #define REMOTEMAPTAG "irssmap"
655 #endif
656 // load our xml file, and fill up our mapping tables
657 CXBMCTinyXML xmlDoc;
659 // Load the config file
660 CLog::Log(LOGINFO, "Loading %s", lircmapPath.c_str());
661 if (!xmlDoc.LoadFile(lircmapPath))
663 CLog::Log(LOGERROR, "%s, Line %d\n%s", lircmapPath.c_str(), xmlDoc.ErrorRow(), xmlDoc.ErrorDesc());
664 return false; // This is so people who don't have the file won't fail, just warn
667 TiXmlElement* pRoot = xmlDoc.RootElement();
668 CStdString strValue = pRoot->Value();
669 if (strValue != REMOTEMAPTAG)
671 CLog::Log(LOGERROR, "%sl Doesn't contain <%s>", lircmapPath.c_str(), REMOTEMAPTAG);
672 return false;
675 // run through our window groups
676 TiXmlNode* pRemote = pRoot->FirstChild();
677 while (pRemote)
679 if (pRemote->Type() == TiXmlNode::TINYXML_ELEMENT)
681 const char *szRemote = pRemote->Value();
682 if (szRemote)
684 TiXmlAttribute* pAttr = pRemote->ToElement()->FirstAttribute();
685 const char* szDeviceName = pAttr->Value();
686 MapRemote(pRemote, szDeviceName);
689 pRemote = pRemote->NextSibling();
692 return true;
695 void CButtonTranslator::MapRemote(TiXmlNode *pRemote, const char* szDevice)
697 CLog::Log(LOGINFO, "* Adding remote mapping for device '%s'", szDevice);
698 vector<string> RemoteNames;
699 map<CStdString, lircButtonMap*>::iterator it = lircRemotesMap.find(szDevice);
700 if (it == lircRemotesMap.end())
701 lircRemotesMap[szDevice] = new lircButtonMap;
702 lircButtonMap& buttons = *lircRemotesMap[szDevice];
704 TiXmlElement *pButton = pRemote->FirstChildElement();
705 while (pButton)
707 if (strcmpi(pButton->Value(), "altname")==0)
708 RemoteNames.push_back(string(pButton->GetText()));
709 else
711 if (pButton->FirstChild() && pButton->FirstChild()->Value())
712 buttons[pButton->FirstChild()->Value()] = pButton->Value();
715 pButton = pButton->NextSiblingElement();
717 for (vector<string>::iterator it = RemoteNames.begin();
718 it != RemoteNames.end();++it)
720 CLog::Log(LOGINFO, "* Linking remote mapping for '%s' to '%s'", szDevice, it->c_str());
721 lircRemotesMap[*it] = &buttons;
725 int CButtonTranslator::TranslateLircRemoteString(const char* szDevice, const char *szButton)
727 // Find the device
728 map<CStdString, lircButtonMap*>::iterator it = lircRemotesMap.find(szDevice);
729 if (it == lircRemotesMap.end())
730 return 0;
732 // Find the button
733 lircButtonMap::iterator it2 = (*it).second->find(szButton);
734 if (it2 == (*it).second->end())
735 return 0;
737 // Convert the button to code
738 if (strnicmp((*it2).second.c_str(), "obc", 3) == 0)
739 return TranslateUniversalRemoteString((*it2).second.c_str());
741 return TranslateRemoteString((*it2).second.c_str());
743 #endif
745 #if defined(HAS_SDL_JOYSTICK) || defined(HAS_EVENT_SERVER)
746 void CButtonTranslator::MapJoystickActions(int windowID, TiXmlNode *pJoystick)
748 string joyname = JOYSTICK_DEFAULT_MAP; // default global map name
749 vector<string> joynames;
750 map<int, string> buttonMap;
751 map<int, string> axisMap;
752 map<int, string> hatMap;
754 TiXmlElement *pJoy = pJoystick->ToElement();
755 if (pJoy && pJoy->Attribute("name"))
756 joyname = pJoy->Attribute("name");
757 else
758 CLog::Log(LOGNOTICE, "No Joystick name specified, loading default map");
760 joynames.push_back(joyname);
762 // parse map
763 TiXmlElement *pButton = pJoystick->FirstChildElement();
764 int id = 0;
765 //char* szId;
766 const char* szType;
767 const char *szAction;
768 while (pButton)
770 szType = pButton->Value();
771 szAction = pButton->GetText();
772 if (szAction == NULL)
773 szAction = "";
774 if (szType)
776 if ((pButton->QueryIntAttribute("id", &id) == TIXML_SUCCESS) && id>=0 && id<=256)
778 if (strcmpi(szType, "button")==0)
780 buttonMap[id] = string(szAction);
782 else if (strcmpi(szType, "axis")==0)
784 int limit = 0;
785 if (pButton->QueryIntAttribute("limit", &limit) == TIXML_SUCCESS)
787 if (limit==-1)
788 axisMap[-id] = string(szAction);
789 else if (limit==1)
790 axisMap[id] = string(szAction);
791 else if (limit==0)
792 axisMap[id|0xFFFF0000] = string(szAction);
793 else
795 axisMap[id] = string(szAction);
796 axisMap[-id] = string(szAction);
797 CLog::Log(LOGERROR, "Error in joystick map, invalid limit specified %d for axis %d", limit, id);
800 else
802 axisMap[id] = string(szAction);
803 axisMap[-id] = string(szAction);
806 else if (strcmpi(szType, "hat")==0)
808 string position;
809 if (pButton->QueryValueAttribute("position", &position) == TIXML_SUCCESS)
811 uint32_t hatID = id|0xFFF00000;
812 if (position.compare("up") == 0)
813 hatMap[(JACTIVE_HAT_UP<<16)|hatID] = string(szAction);
814 else if (position.compare("down") == 0)
815 hatMap[(JACTIVE_HAT_DOWN<<16)|hatID] = string(szAction);
816 else if (position.compare("right") == 0)
817 hatMap[(JACTIVE_HAT_RIGHT<<16)|hatID] = string(szAction);
818 else if (position.compare("left") == 0)
819 hatMap[(JACTIVE_HAT_LEFT<<16)|hatID] = string(szAction);
820 else
821 CLog::Log(LOGERROR, "Error in joystick map, invalid position specified %s for axis %d", position.c_str(), id);
824 else
825 CLog::Log(LOGERROR, "Error reading joystick map element, unknown button type: %s", szType);
827 else if (strcmpi(szType, "altname")==0)
828 joynames.push_back(string(szAction));
829 else
830 CLog::Log(LOGERROR, "Error reading joystick map element, Invalid id: %d", id);
832 else
833 CLog::Log(LOGERROR, "Error reading joystick map element, skipping");
835 pButton = pButton->NextSiblingElement();
837 vector<string>::iterator it = joynames.begin();
838 while (it!=joynames.end())
840 m_joystickButtonMap[*it][windowID] = buttonMap;
841 m_joystickAxisMap[*it][windowID] = axisMap;
842 m_joystickHatMap[*it][windowID] = hatMap;
843 // CLog::Log(LOGDEBUG, "Found Joystick map for window %d using %s", windowID, it->c_str());
844 it++;
848 bool CButtonTranslator::TranslateJoystickString(int window, const char* szDevice, int id, short inputType, int& action, CStdString& strAction, bool &fullrange)
850 fullrange = false;
852 // resolve the correct JoystickMap
853 map<string, JoystickMap> *jmap;
854 if (inputType == JACTIVE_AXIS)
855 jmap = &m_joystickAxisMap;
856 else if (inputType == JACTIVE_BUTTON)
857 jmap = &m_joystickButtonMap;
858 else if (inputType == JACTIVE_HAT)
859 jmap = &m_joystickHatMap;
860 else
862 CLog::Log(LOGERROR, "Error reading joystick input type '%i'", (int) inputType);
863 return false;
866 map<string, JoystickMap>::iterator it = jmap->find(szDevice);
867 if (it==jmap->end())
869 it = jmap->find(JOYSTICK_DEFAULT_MAP); // default global map name
870 if (it==jmap->end())
871 return false;
874 JoystickMap wmap = it->second;
876 // try to get the action from the current window
877 action = GetActionCode(window, id, wmap, strAction, fullrange);
879 // if it's invalid, try to get it from a fallback window or the global map
880 if (action == 0)
882 int fallbackWindow = GetFallbackWindow(window);
883 if (fallbackWindow > -1)
884 action = GetActionCode(fallbackWindow, id, wmap, strAction, fullrange);
885 // still no valid action? use global map
886 if (action == 0)
887 action = GetActionCode(-1, id, wmap, strAction, fullrange);
890 return (action > 0);
893 bool CButtonTranslator::TranslateTouchAction(int window, int touchAction, int touchPointers, int &action)
895 action = 0;
896 if (touchPointers <= 0)
897 touchPointers = 1;
899 touchAction += touchPointers - 1;
900 touchAction |= KEY_TOUCH;
902 action = GetTouchActionCode(window, touchAction);
903 if (action <= 0)
904 action = GetTouchActionCode(-1, touchAction);
906 return action > 0;
909 int CButtonTranslator::GetActionCode(int window, int action)
911 map<int, buttonMap>::const_iterator it = m_translatorMap.find(window);
912 if (it == m_translatorMap.end())
913 return 0;
915 buttonMap::const_iterator it2 = it->second.find(action);
916 if (it2 == it->second.end())
917 return 0;
919 return it2->second.id;
923 * Translates a joystick input to an action code
925 int CButtonTranslator::GetActionCode(int window, int id, const JoystickMap &wmap, CStdString &strAction, bool &fullrange) const
927 int action = 0;
928 bool found = false;
930 JoystickMap::const_iterator it = wmap.find(window);
931 if (it != wmap.end())
933 const map<int, string> &windowbmap = it->second;
934 map<int, string>::const_iterator it2 = windowbmap.find(id);
935 if (it2 != windowbmap.end())
937 strAction = (it2->second).c_str();
938 found = true;
941 it2 = windowbmap.find(abs(id)|0xFFFF0000);
942 if (it2 != windowbmap.end())
944 strAction = (it2->second).c_str();
945 found = true;
946 fullrange = true;
949 // Hats joystick
950 it2 = windowbmap.find(id|0xFFF00000);
951 if (it2 != windowbmap.end())
953 strAction = (it2->second).c_str();
954 found = true;
958 if (found)
959 TranslateActionString(strAction.c_str(), action);
960 return action;
962 #endif
964 void CButtonTranslator::GetActions(std::vector<std::string> &actionList)
966 unsigned int size = sizeof(actions) / sizeof(ActionMapping);
967 actionList.clear();
968 actionList.reserve(size);
969 for (unsigned int index = 0; index < size; index++)
970 actionList.push_back(actions[index].name);
973 void CButtonTranslator::GetWindows(std::vector<std::string> &windowList)
975 unsigned int size = sizeof(windows) / sizeof(ActionMapping);
976 windowList.clear();
977 windowList.reserve(size);
978 for (unsigned int index = 0; index < size; index++)
979 windowList.push_back(windows[index].name);
982 int CButtonTranslator::GetFallbackWindow(int windowID)
984 for (unsigned int index = 0; index < sizeof(fallbackWindows) / sizeof(fallbackWindows[0]); ++index)
986 if (fallbackWindows[index].origin == windowID)
987 return fallbackWindows[index].target;
989 // for addon windows use WINDOW_ADDON_START
990 // because id is dynamic
991 if (windowID >= WINDOW_ADDON_START && windowID <= WINDOW_ADDON_END)
992 return WINDOW_ADDON_START;
994 return -1;
997 CAction CButtonTranslator::GetAction(int window, const CKey &key, bool fallback)
999 CStdString strAction;
1000 // try to get the action from the current window
1001 int actionID = GetActionCode(window, key, strAction);
1002 // if it's invalid, try to get it from the global map
1003 if (actionID == 0 && fallback)
1005 int fallbackWindow = GetFallbackWindow(window);
1006 if (fallbackWindow > -1)
1007 actionID = GetActionCode(fallbackWindow, key, strAction);
1008 // still no valid action? use global map
1009 if (actionID == 0)
1010 actionID = GetActionCode( -1, key, strAction);
1012 // Now fill our action structure
1013 CAction action(actionID, strAction, key);
1014 return action;
1017 int CButtonTranslator::GetActionCode(int window, const CKey &key, CStdString &strAction) const
1019 uint32_t code = key.GetButtonCode();
1021 map<int, buttonMap>::const_iterator it = m_translatorMap.find(window);
1022 if (it == m_translatorMap.end())
1023 return 0;
1024 buttonMap::const_iterator it2 = (*it).second.find(code);
1025 int action = 0;
1026 while (it2 != (*it).second.end())
1028 action = (*it2).second.id;
1029 strAction = (*it2).second.strID;
1030 it2 = (*it).second.end();
1032 #ifdef TARGET_POSIX
1033 // Some buttoncodes changed in Hardy
1034 if (action == 0 && (code & KEY_VKEY) == KEY_VKEY && (code & 0x0F00))
1036 CLog::Log(LOGDEBUG, "%s: Trying Hardy keycode for %#04x", __FUNCTION__, code);
1037 code &= ~0x0F00;
1038 buttonMap::const_iterator it2 = (*it).second.find(code);
1039 while (it2 != (*it).second.end())
1041 action = (*it2).second.id;
1042 strAction = (*it2).second.strID;
1043 it2 = (*it).second.end();
1046 #endif
1047 return action;
1050 void CButtonTranslator::MapAction(uint32_t buttonCode, const char *szAction, buttonMap &map)
1052 int action = ACTION_NONE;
1053 if (!TranslateActionString(szAction, action) || !buttonCode)
1054 return; // no valid action, or an invalid buttoncode
1056 // have a valid action, and a valid button - map it.
1057 // check to see if we've already got this (button,action) pair defined
1058 buttonMap::iterator it = map.find(buttonCode);
1059 if (it == map.end() || (*it).second.id != action || (*it).second.strID != szAction)
1061 // NOTE: This multimap is only being used as a normal map at this point (no support
1062 // for multiple actions per key)
1063 if (it != map.end())
1064 map.erase(it);
1065 CButtonAction button;
1066 button.id = action;
1067 button.strID = szAction;
1068 map.insert(pair<uint32_t, CButtonAction>(buttonCode, button));
1072 bool CButtonTranslator::HasDeviceType(TiXmlNode *pWindow, CStdString type)
1074 return pWindow->FirstChild(type) != NULL;
1077 void CButtonTranslator::MapWindowActions(TiXmlNode *pWindow, int windowID)
1079 if (!pWindow || windowID == WINDOW_INVALID)
1080 return;
1082 TiXmlNode* pDevice;
1084 const char* types[] = {"gamepad", "remote", "universalremote", "keyboard", "mouse", "appcommand", NULL};
1085 for (int i = 0; types[i]; ++i)
1087 CStdString type(types[i]);
1088 if (HasDeviceType(pWindow, type))
1090 buttonMap map;
1091 std::map<int, buttonMap>::iterator it = m_translatorMap.find(windowID);
1092 if (it != m_translatorMap.end())
1094 map = it->second;
1095 m_translatorMap.erase(it);
1098 pDevice = pWindow->FirstChild(type);
1100 TiXmlElement *pButton = pDevice->FirstChildElement();
1102 while (pButton)
1104 uint32_t buttonCode=0;
1105 if (type == "gamepad")
1106 buttonCode = TranslateGamepadString(pButton->Value());
1107 else if (type == "remote")
1108 buttonCode = TranslateRemoteString(pButton->Value());
1109 else if (type == "universalremote")
1110 buttonCode = TranslateUniversalRemoteString(pButton->Value());
1111 else if (type == "keyboard")
1112 buttonCode = TranslateKeyboardButton(pButton);
1113 else if (type == "mouse")
1114 buttonCode = TranslateMouseCommand(pButton->Value());
1115 else if (type == "appcommand")
1116 buttonCode = TranslateAppCommand(pButton->Value());
1118 if (buttonCode && pButton->FirstChild())
1119 MapAction(buttonCode, pButton->FirstChild()->Value(), map);
1120 pButton = pButton->NextSiblingElement();
1123 // add our map to our table
1124 if (!map.empty())
1125 m_translatorMap.insert(pair<int, buttonMap>( windowID, map));
1129 #if defined(HAS_SDL_JOYSTICK) || defined(HAS_EVENT_SERVER)
1130 if ((pDevice = pWindow->FirstChild("joystick")) != NULL)
1132 // map joystick actions
1133 while (pDevice)
1135 MapJoystickActions(windowID, pDevice);
1136 pDevice = pDevice->NextSibling("joystick");
1139 #endif
1141 if ((pDevice = pWindow->FirstChild("touch")) != NULL)
1143 // map touch actions
1144 while (pDevice)
1146 MapTouchActions(windowID, pDevice);
1147 pDevice = pDevice->NextSibling("touch");
1152 bool CButtonTranslator::TranslateActionString(const char *szAction, int &action)
1154 action = ACTION_NONE;
1155 CStdString strAction = szAction;
1156 StringUtils::ToLower(strAction);
1157 if (CBuiltins::HasCommand(strAction))
1158 action = ACTION_BUILT_IN_FUNCTION;
1160 for (unsigned int index=0;index < sizeof(actions)/sizeof(actions[0]);++index)
1162 if (strAction.Equals(actions[index].name))
1164 action = actions[index].action;
1165 break;
1169 if (action == ACTION_NONE)
1171 CLog::Log(LOGERROR, "Keymapping error: no such action '%s' defined", strAction.c_str());
1172 return false;
1175 return true;
1178 CStdString CButtonTranslator::TranslateWindow(int windowID)
1180 for (unsigned int index = 0; index < sizeof(windows) / sizeof(windows[0]); ++index)
1182 if (windows[index].action == windowID)
1183 return windows[index].name;
1185 return "";
1188 int CButtonTranslator::TranslateWindow(const CStdString &window)
1190 CStdString strWindow(window);
1191 if (strWindow.empty())
1192 return WINDOW_INVALID;
1193 StringUtils::ToLower(strWindow);
1194 // eliminate .xml
1195 if (StringUtils::EndsWith(strWindow, ".xml"))
1196 strWindow = strWindow.substr(0, strWindow.size() - 4);
1198 // window12345, for custom window to be keymapped
1199 if (strWindow.length() > 6 && StringUtils::StartsWithNoCase(strWindow, "window"))
1200 strWindow = strWindow.substr(6);
1201 if (StringUtils::StartsWithNoCase(strWindow, "my")) // drop "my" prefix
1202 strWindow = strWindow.substr(2);
1203 if (StringUtils::IsNaturalNumber(strWindow))
1205 // allow a full window id or a delta id
1206 int iWindow = atoi(strWindow.c_str());
1207 if (iWindow > WINDOW_INVALID)
1208 return iWindow;
1209 return WINDOW_HOME + iWindow;
1212 // run through the window structure
1213 for (unsigned int index = 0; index < sizeof(windows) / sizeof(windows[0]); ++index)
1215 if (strWindow.Equals(windows[index].name))
1216 return windows[index].action;
1219 CLog::Log(LOGERROR, "Window Translator: Can't find window %s", strWindow.c_str());
1220 return WINDOW_INVALID;
1223 uint32_t CButtonTranslator::TranslateGamepadString(const char *szButton)
1225 if (!szButton)
1226 return 0;
1227 uint32_t buttonCode = 0;
1228 CStdString strButton = szButton;
1229 StringUtils::ToLower(strButton);
1230 if (strButton.Equals("a")) buttonCode = KEY_BUTTON_A;
1231 else if (strButton.Equals("b")) buttonCode = KEY_BUTTON_B;
1232 else if (strButton.Equals("x")) buttonCode = KEY_BUTTON_X;
1233 else if (strButton.Equals("y")) buttonCode = KEY_BUTTON_Y;
1234 else if (strButton.Equals("white")) buttonCode = KEY_BUTTON_WHITE;
1235 else if (strButton.Equals("black")) buttonCode = KEY_BUTTON_BLACK;
1236 else if (strButton.Equals("start")) buttonCode = KEY_BUTTON_START;
1237 else if (strButton.Equals("back")) buttonCode = KEY_BUTTON_BACK;
1238 else if (strButton.Equals("leftthumbbutton")) buttonCode = KEY_BUTTON_LEFT_THUMB_BUTTON;
1239 else if (strButton.Equals("rightthumbbutton")) buttonCode = KEY_BUTTON_RIGHT_THUMB_BUTTON;
1240 else if (strButton.Equals("leftthumbstick")) buttonCode = KEY_BUTTON_LEFT_THUMB_STICK;
1241 else if (strButton.Equals("leftthumbstickup")) buttonCode = KEY_BUTTON_LEFT_THUMB_STICK_UP;
1242 else if (strButton.Equals("leftthumbstickdown")) buttonCode = KEY_BUTTON_LEFT_THUMB_STICK_DOWN;
1243 else if (strButton.Equals("leftthumbstickleft")) buttonCode = KEY_BUTTON_LEFT_THUMB_STICK_LEFT;
1244 else if (strButton.Equals("leftthumbstickright")) buttonCode = KEY_BUTTON_LEFT_THUMB_STICK_RIGHT;
1245 else if (strButton.Equals("rightthumbstick")) buttonCode = KEY_BUTTON_RIGHT_THUMB_STICK;
1246 else if (strButton.Equals("rightthumbstickup")) buttonCode = KEY_BUTTON_RIGHT_THUMB_STICK_UP;
1247 else if (strButton.Equals("rightthumbstickdown")) buttonCode = KEY_BUTTON_RIGHT_THUMB_STICK_DOWN;
1248 else if (strButton.Equals("rightthumbstickleft")) buttonCode = KEY_BUTTON_RIGHT_THUMB_STICK_LEFT;
1249 else if (strButton.Equals("rightthumbstickright")) buttonCode = KEY_BUTTON_RIGHT_THUMB_STICK_RIGHT;
1250 else if (strButton.Equals("lefttrigger")) buttonCode = KEY_BUTTON_LEFT_TRIGGER;
1251 else if (strButton.Equals("righttrigger")) buttonCode = KEY_BUTTON_RIGHT_TRIGGER;
1252 else if (strButton.Equals("leftanalogtrigger")) buttonCode = KEY_BUTTON_LEFT_ANALOG_TRIGGER;
1253 else if (strButton.Equals("rightanalogtrigger")) buttonCode = KEY_BUTTON_RIGHT_ANALOG_TRIGGER;
1254 else if (strButton.Equals("dpadleft")) buttonCode = KEY_BUTTON_DPAD_LEFT;
1255 else if (strButton.Equals("dpadright")) buttonCode = KEY_BUTTON_DPAD_RIGHT;
1256 else if (strButton.Equals("dpadup")) buttonCode = KEY_BUTTON_DPAD_UP;
1257 else if (strButton.Equals("dpaddown")) buttonCode = KEY_BUTTON_DPAD_DOWN;
1258 else CLog::Log(LOGERROR, "Gamepad Translator: Can't find button %s", strButton.c_str());
1259 return buttonCode;
1262 uint32_t CButtonTranslator::TranslateRemoteString(const char *szButton)
1264 if (!szButton)
1265 return 0;
1266 uint32_t buttonCode = 0;
1267 CStdString strButton = szButton;
1268 StringUtils::ToLower(strButton);
1269 if (strButton.Equals("left")) buttonCode = XINPUT_IR_REMOTE_LEFT;
1270 else if (strButton.Equals("right")) buttonCode = XINPUT_IR_REMOTE_RIGHT;
1271 else if (strButton.Equals("up")) buttonCode = XINPUT_IR_REMOTE_UP;
1272 else if (strButton.Equals("down")) buttonCode = XINPUT_IR_REMOTE_DOWN;
1273 else if (strButton.Equals("select")) buttonCode = XINPUT_IR_REMOTE_SELECT;
1274 else if (strButton.Equals("back")) buttonCode = XINPUT_IR_REMOTE_BACK;
1275 else if (strButton.Equals("menu")) buttonCode = XINPUT_IR_REMOTE_MENU;
1276 else if (strButton.Equals("info")) buttonCode = XINPUT_IR_REMOTE_INFO;
1277 else if (strButton.Equals("display")) buttonCode = XINPUT_IR_REMOTE_DISPLAY;
1278 else if (strButton.Equals("title")) buttonCode = XINPUT_IR_REMOTE_TITLE;
1279 else if (strButton.Equals("play")) buttonCode = XINPUT_IR_REMOTE_PLAY;
1280 else if (strButton.Equals("pause")) buttonCode = XINPUT_IR_REMOTE_PAUSE;
1281 else if (strButton.Equals("reverse")) buttonCode = XINPUT_IR_REMOTE_REVERSE;
1282 else if (strButton.Equals("forward")) buttonCode = XINPUT_IR_REMOTE_FORWARD;
1283 else if (strButton.Equals("skipplus")) buttonCode = XINPUT_IR_REMOTE_SKIP_PLUS;
1284 else if (strButton.Equals("skipminus")) buttonCode = XINPUT_IR_REMOTE_SKIP_MINUS;
1285 else if (strButton.Equals("stop")) buttonCode = XINPUT_IR_REMOTE_STOP;
1286 else if (strButton.Equals("zero")) buttonCode = XINPUT_IR_REMOTE_0;
1287 else if (strButton.Equals("one")) buttonCode = XINPUT_IR_REMOTE_1;
1288 else if (strButton.Equals("two")) buttonCode = XINPUT_IR_REMOTE_2;
1289 else if (strButton.Equals("three")) buttonCode = XINPUT_IR_REMOTE_3;
1290 else if (strButton.Equals("four")) buttonCode = XINPUT_IR_REMOTE_4;
1291 else if (strButton.Equals("five")) buttonCode = XINPUT_IR_REMOTE_5;
1292 else if (strButton.Equals("six")) buttonCode = XINPUT_IR_REMOTE_6;
1293 else if (strButton.Equals("seven")) buttonCode = XINPUT_IR_REMOTE_7;
1294 else if (strButton.Equals("eight")) buttonCode = XINPUT_IR_REMOTE_8;
1295 else if (strButton.Equals("nine")) buttonCode = XINPUT_IR_REMOTE_9;
1296 // additional keys from the media center extender for xbox remote
1297 else if (strButton.Equals("power")) buttonCode = XINPUT_IR_REMOTE_POWER;
1298 else if (strButton.Equals("mytv")) buttonCode = XINPUT_IR_REMOTE_MY_TV;
1299 else if (strButton.Equals("mymusic")) buttonCode = XINPUT_IR_REMOTE_MY_MUSIC;
1300 else if (strButton.Equals("mypictures")) buttonCode = XINPUT_IR_REMOTE_MY_PICTURES;
1301 else if (strButton.Equals("myvideo")) buttonCode = XINPUT_IR_REMOTE_MY_VIDEOS;
1302 else if (strButton.Equals("record")) buttonCode = XINPUT_IR_REMOTE_RECORD;
1303 else if (strButton.Equals("start")) buttonCode = XINPUT_IR_REMOTE_START;
1304 else if (strButton.Equals("volumeplus")) buttonCode = XINPUT_IR_REMOTE_VOLUME_PLUS;
1305 else if (strButton.Equals("volumeminus")) buttonCode = XINPUT_IR_REMOTE_VOLUME_MINUS;
1306 else if (strButton.Equals("channelplus")) buttonCode = XINPUT_IR_REMOTE_CHANNEL_PLUS;
1307 else if (strButton.Equals("channelminus")) buttonCode = XINPUT_IR_REMOTE_CHANNEL_MINUS;
1308 else if (strButton.Equals("pageplus")) buttonCode = XINPUT_IR_REMOTE_CHANNEL_PLUS;
1309 else if (strButton.Equals("pageminus")) buttonCode = XINPUT_IR_REMOTE_CHANNEL_MINUS;
1310 else if (strButton.Equals("mute")) buttonCode = XINPUT_IR_REMOTE_MUTE;
1311 else if (strButton.Equals("recordedtv")) buttonCode = XINPUT_IR_REMOTE_RECORDED_TV;
1312 else if (strButton.Equals("guide")) buttonCode = XINPUT_IR_REMOTE_GUIDE;
1313 else if (strButton.Equals("livetv")) buttonCode = XINPUT_IR_REMOTE_LIVE_TV;
1314 else if (strButton.Equals("liveradio")) buttonCode = XINPUT_IR_REMOTE_LIVE_RADIO;
1315 else if (strButton.Equals("epgsearch")) buttonCode = XINPUT_IR_REMOTE_EPG_SEARCH;
1316 else if (strButton.Equals("star")) buttonCode = XINPUT_IR_REMOTE_STAR;
1317 else if (strButton.Equals("hash")) buttonCode = XINPUT_IR_REMOTE_HASH;
1318 else if (strButton.Equals("clear")) buttonCode = XINPUT_IR_REMOTE_CLEAR;
1319 else if (strButton.Equals("enter")) buttonCode = XINPUT_IR_REMOTE_ENTER;
1320 else if (strButton.Equals("xbox")) buttonCode = XINPUT_IR_REMOTE_DISPLAY; // same as display
1321 else if (strButton.Equals("playlist")) buttonCode = XINPUT_IR_REMOTE_PLAYLIST;
1322 else if (strButton.Equals("guide")) buttonCode = XINPUT_IR_REMOTE_GUIDE;
1323 else if (strButton.Equals("teletext")) buttonCode = XINPUT_IR_REMOTE_TELETEXT;
1324 else if (strButton.Equals("red")) buttonCode = XINPUT_IR_REMOTE_RED;
1325 else if (strButton.Equals("green")) buttonCode = XINPUT_IR_REMOTE_GREEN;
1326 else if (strButton.Equals("yellow")) buttonCode = XINPUT_IR_REMOTE_YELLOW;
1327 else if (strButton.Equals("blue")) buttonCode = XINPUT_IR_REMOTE_BLUE;
1328 else if (strButton.Equals("subtitle")) buttonCode = XINPUT_IR_REMOTE_SUBTITLE;
1329 else if (strButton.Equals("language")) buttonCode = XINPUT_IR_REMOTE_LANGUAGE;
1330 else CLog::Log(LOGERROR, "Remote Translator: Can't find button %s", strButton.c_str());
1331 return buttonCode;
1334 uint32_t CButtonTranslator::TranslateUniversalRemoteString(const char *szButton)
1336 if (!szButton || strlen(szButton) < 4 || strnicmp(szButton, "obc", 3))
1337 return 0;
1338 const char *szCode = szButton + 3;
1339 // Button Code is 255 - OBC (Original Button Code) of the button
1340 uint32_t buttonCode = 255 - atol(szCode);
1341 if (buttonCode > 255)
1342 buttonCode = 0;
1343 return buttonCode;
1346 uint32_t CButtonTranslator::TranslateKeyboardString(const char *szButton)
1348 uint32_t buttonCode = 0;
1349 XBMCKEYTABLE keytable;
1351 // Look up the key name
1352 if (KeyTableLookupName(szButton, &keytable))
1354 buttonCode = keytable.vkey;
1357 // The lookup failed i.e. the key name wasn't found
1358 else
1360 CLog::Log(LOGERROR, "Keyboard Translator: Can't find button %s", szButton);
1363 buttonCode |= KEY_VKEY;
1365 return buttonCode;
1368 uint32_t CButtonTranslator::TranslateKeyboardButton(TiXmlElement *pButton)
1370 uint32_t button_id = 0;
1371 const char *szButton = pButton->Value();
1373 if (!szButton)
1374 return 0;
1375 CStdString strKey = szButton;
1376 if (strKey.Equals("key"))
1378 std::string strID;
1379 if (pButton->QueryValueAttribute("id", &strID) == TIXML_SUCCESS)
1381 const char *str = strID.c_str();
1382 char *endptr;
1383 long int id = strtol(str, &endptr, 0);
1384 if (endptr - str != (int)strlen(str) || id <= 0 || id > 0x00FFFFFF)
1385 CLog::Log(LOGDEBUG, "%s - invalid key id %s", __FUNCTION__, strID.c_str());
1386 else
1387 button_id = (uint32_t) id;
1389 else
1390 CLog::Log(LOGERROR, "Keyboard Translator: `key' button has no id");
1392 else
1393 button_id = TranslateKeyboardString(szButton);
1395 // Process the ctrl/shift/alt modifiers
1396 CStdString strMod;
1397 if (pButton->QueryValueAttribute("mod", &strMod) == TIXML_SUCCESS)
1399 StringUtils::ToLower(strMod);
1401 CStdStringArray modArray;
1402 StringUtils::SplitString(strMod, ",", modArray);
1403 for (unsigned int i = 0; i < modArray.size(); i++)
1405 CStdString& substr = modArray[i];
1406 StringUtils::Trim(substr);
1408 if (substr == "ctrl" || substr == "control")
1409 button_id |= CKey::MODIFIER_CTRL;
1410 else if (substr == "shift")
1411 button_id |= CKey::MODIFIER_SHIFT;
1412 else if (substr == "alt")
1413 button_id |= CKey::MODIFIER_ALT;
1414 else if (substr == "super" || substr == "win")
1415 button_id |= CKey::MODIFIER_SUPER;
1416 else if (substr == "meta" || substr == "cmd")
1417 button_id |= CKey::MODIFIER_META;
1418 else
1419 CLog::Log(LOGERROR, "Keyboard Translator: Unknown key modifier %s in %s", substr.c_str(), strMod.c_str());
1423 return button_id;
1426 uint32_t CButtonTranslator::TranslateAppCommand(const char *szButton)
1428 #ifdef TARGET_WINDOWS
1429 CStdString strAppCommand = szButton;
1430 StringUtils::ToLower(strAppCommand);
1432 for (int i = 0; i < sizeof(appcommands)/sizeof(appcommands[0]); i++)
1433 if (strAppCommand.Equals(appcommands[i].name))
1434 return appcommands[i].action | KEY_APPCOMMAND;
1436 CLog::Log(LOGERROR, "%s: Can't find appcommand %s", __FUNCTION__, szButton);
1437 #endif
1439 return 0;
1442 uint32_t CButtonTranslator::TranslateMouseCommand(const char *szButton)
1444 CStdString strMouseCommand = szButton;
1445 StringUtils::ToLower(strMouseCommand);
1447 for (unsigned int i = 0; i < sizeof(mousecommands)/sizeof(mousecommands[0]); i++)
1448 if (strMouseCommand.Equals(mousecommands[i].name))
1449 return mousecommands[i].action | KEY_MOUSE;
1451 CLog::Log(LOGERROR, "%s: Can't find mouse command %s", __FUNCTION__, szButton);
1453 return 0;
1456 void CButtonTranslator::Clear()
1458 m_translatorMap.clear();
1459 #if defined(HAS_LIRC) || defined(HAS_IRSERVERSUITE)
1460 ClearLircButtonMapEntries();
1461 lircRemotesMap.clear();
1462 #endif
1464 #if defined(HAS_SDL_JOYSTICK) || defined(HAS_EVENT_SERVER)
1465 m_joystickButtonMap.clear();
1466 m_joystickAxisMap.clear();
1467 m_joystickHatMap.clear();
1468 #endif
1470 m_Loaded = false;
1473 uint32_t CButtonTranslator::TranslateTouchCommand(TiXmlElement *pButton, CButtonAction &action)
1475 const char *szButton = pButton->Value();
1476 if (szButton == NULL || pButton->FirstChild() == NULL)
1477 return ACTION_NONE;
1479 const char *szAction = pButton->FirstChild()->Value();
1480 if (szAction == NULL)
1481 return ACTION_NONE;
1483 CStdString strTouchCommand = szButton;
1484 StringUtils::ToLower(strTouchCommand);
1486 const char *attrVal = pButton->Attribute("direction");
1487 if (attrVal != NULL)
1488 strTouchCommand += attrVal;
1490 uint32_t actionId = ACTION_NONE;
1491 for (unsigned int i = 0; i < sizeof(touchcommands)/sizeof(touchcommands[0]); i++)
1493 if (strTouchCommand.Equals(touchcommands[i].name))
1495 actionId = touchcommands[i].action;
1496 break;
1500 if (actionId <= ACTION_NONE)
1502 CLog::Log(LOGERROR, "%s: Can't find touch command %s", __FUNCTION__, szButton);
1503 return ACTION_NONE;
1506 attrVal = pButton->Attribute("pointers");
1507 if (attrVal != NULL)
1509 int pointers = (int)strtol(attrVal, NULL, 0);
1510 if (pointers >= 1)
1511 actionId += pointers - 1;
1514 action.strID = szAction;
1515 if (!TranslateActionString(szAction, action.id) || action.id <= ACTION_NONE)
1516 return ACTION_NONE;
1518 return actionId | KEY_TOUCH;
1521 void CButtonTranslator::MapTouchActions(int windowID, TiXmlNode *pTouch)
1523 if (pTouch == NULL)
1524 return;
1526 buttonMap map;
1527 // check if there already is a touch map for the window ID
1528 std::map<int, buttonMap>::iterator it = m_touchMap.find(windowID);
1529 if (it != m_touchMap.end())
1531 // get the existing touch map and remove it from the window mapping
1532 // as it will be inserted later on
1533 map = it->second;
1534 m_touchMap.erase(it);
1537 uint32_t actionId = 0;
1538 TiXmlElement *pTouchElem = pTouch->ToElement();
1539 if (pTouchElem == NULL)
1540 return;
1542 TiXmlElement *pButton = pTouchElem->FirstChildElement();
1543 while (pButton != NULL)
1545 CButtonAction action;
1546 actionId = TranslateTouchCommand(pButton, action);
1547 if (actionId > 0)
1549 // check if there already is a mapping for the parsed action
1550 // and remove it if necessary
1551 buttonMap::iterator actionIt = map.find(actionId);
1552 if (actionIt != map.end())
1553 map.erase(actionIt);
1555 map.insert(std::make_pair(actionId, action));
1558 pButton = pButton->NextSiblingElement();
1561 // add the modified touch map with the window ID
1562 if (!map.empty())
1563 m_touchMap.insert(std::pair<int, buttonMap>(windowID, map));
1566 int CButtonTranslator::GetTouchActionCode(int window, int action)
1568 std::map<int, buttonMap>::const_iterator windowIt = m_touchMap.find(window);
1569 if (windowIt == m_touchMap.end())
1570 return ACTION_NONE;
1572 buttonMap::const_iterator touchIt = windowIt->second.find(action);
1573 if (touchIt == windowIt->second.end())
1574 return ACTION_NONE;
1576 return touchIt->second.id;