aboutsummaryrefslogtreecommitdiff
path: root/src/input/ButtonTranslator.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/input/ButtonTranslator.cpp')
-rw-r--r--src/input/ButtonTranslator.cpp1687
1 files changed, 1687 insertions, 0 deletions
diff --git a/src/input/ButtonTranslator.cpp b/src/input/ButtonTranslator.cpp
new file mode 100644
index 0000000000..c297408f36
--- /dev/null
+++ b/src/input/ButtonTranslator.cpp
@@ -0,0 +1,1687 @@
+/*
+ * Copyright (C) 2005-2013 Team XBMC
+ * http://xbmc.org
+ *
+ * This Program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This Program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with XBMC; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "system.h"
+#include "interfaces/Builtins.h"
+#include "ButtonTranslator.h"
+#include "profiles/ProfilesManager.h"
+#include "utils/URIUtils.h"
+#include "guilib/Key.h"
+#include "guilib/WindowIDs.h"
+#include "input/XBMC_keysym.h"
+#include "input/XBMC_keytable.h"
+#include "filesystem/File.h"
+#include "filesystem/Directory.h"
+#include "FileItem.h"
+#include "utils/StringUtils.h"
+#include "utils/log.h"
+#include "utils/XBMCTinyXML.h"
+#include "utils/RegExp.h"
+#include "XBIRRemote.h"
+#include "Util.h"
+#include <boost/shared_ptr.hpp>
+
+#if defined(TARGET_WINDOWS)
+#include "input/windows/WINJoystick.h"
+#elif defined(HAS_SDL_JOYSTICK) || defined(HAS_EVENT_SERVER)
+#include "SDLJoystick.h"
+#endif
+
+#define JOYSTICK_DEFAULT_MAP "_xbmc_"
+
+using namespace std;
+using namespace XFILE;
+
+typedef struct
+{
+ const char* name;
+ int action;
+} ActionMapping;
+
+typedef struct
+{
+ int origin;
+ int target;
+} WindowMapping;
+
+static const ActionMapping actions[] =
+{
+ {"left" , ACTION_MOVE_LEFT },
+ {"right" , ACTION_MOVE_RIGHT},
+ {"up" , ACTION_MOVE_UP },
+ {"down" , ACTION_MOVE_DOWN },
+ {"pageup" , ACTION_PAGE_UP },
+ {"pagedown" , ACTION_PAGE_DOWN},
+ {"select" , ACTION_SELECT_ITEM},
+ {"highlight" , ACTION_HIGHLIGHT_ITEM},
+ {"parentdir" , ACTION_NAV_BACK}, // backward compatibility
+ {"parentfolder" , ACTION_PARENT_DIR},
+ {"back" , ACTION_NAV_BACK},
+ {"previousmenu" , ACTION_PREVIOUS_MENU},
+ {"info" , ACTION_SHOW_INFO},
+ {"pause" , ACTION_PAUSE},
+ {"stop" , ACTION_STOP},
+ {"skipnext" , ACTION_NEXT_ITEM},
+ {"skipprevious" , ACTION_PREV_ITEM},
+ {"fullscreen" , ACTION_SHOW_GUI},
+ {"aspectratio" , ACTION_ASPECT_RATIO},
+ {"stepforward" , ACTION_STEP_FORWARD},
+ {"stepback" , ACTION_STEP_BACK},
+ {"bigstepforward" , ACTION_BIG_STEP_FORWARD},
+ {"bigstepback" , ACTION_BIG_STEP_BACK},
+ {"chapterorbigstepforward", ACTION_CHAPTER_OR_BIG_STEP_FORWARD},
+ {"chapterorbigstepback" , ACTION_CHAPTER_OR_BIG_STEP_BACK},
+ {"osd" , ACTION_SHOW_OSD},
+ {"showsubtitles" , ACTION_SHOW_SUBTITLES},
+ {"nextsubtitle" , ACTION_NEXT_SUBTITLE},
+ {"cyclesubtitle" , ACTION_CYCLE_SUBTITLE},
+ {"codecinfo" , ACTION_SHOW_CODEC},
+ {"nextpicture" , ACTION_NEXT_PICTURE},
+ {"previouspicture" , ACTION_PREV_PICTURE},
+ {"zoomout" , ACTION_ZOOM_OUT},
+ {"zoomin" , ACTION_ZOOM_IN},
+ {"playlist" , ACTION_SHOW_PLAYLIST},
+ {"queue" , ACTION_QUEUE_ITEM},
+ {"zoomnormal" , ACTION_ZOOM_LEVEL_NORMAL},
+ {"zoomlevel1" , ACTION_ZOOM_LEVEL_1},
+ {"zoomlevel2" , ACTION_ZOOM_LEVEL_2},
+ {"zoomlevel3" , ACTION_ZOOM_LEVEL_3},
+ {"zoomlevel4" , ACTION_ZOOM_LEVEL_4},
+ {"zoomlevel5" , ACTION_ZOOM_LEVEL_5},
+ {"zoomlevel6" , ACTION_ZOOM_LEVEL_6},
+ {"zoomlevel7" , ACTION_ZOOM_LEVEL_7},
+ {"zoomlevel8" , ACTION_ZOOM_LEVEL_8},
+ {"zoomlevel9" , ACTION_ZOOM_LEVEL_9},
+ {"nextcalibration" , ACTION_CALIBRATE_SWAP_ARROWS},
+ {"resetcalibration" , ACTION_CALIBRATE_RESET},
+ {"analogmove" , ACTION_ANALOG_MOVE},
+ {"rotate" , ACTION_ROTATE_PICTURE_CW},
+ {"rotateccw" , ACTION_ROTATE_PICTURE_CCW},
+ {"close" , ACTION_NAV_BACK}, // backwards compatibility
+ {"subtitledelayminus", ACTION_SUBTITLE_DELAY_MIN},
+ {"subtitledelay" , ACTION_SUBTITLE_DELAY},
+ {"subtitledelayplus" , ACTION_SUBTITLE_DELAY_PLUS},
+ {"audiodelayminus" , ACTION_AUDIO_DELAY_MIN},
+ {"audiodelay" , ACTION_AUDIO_DELAY},
+ {"audiodelayplus" , ACTION_AUDIO_DELAY_PLUS},
+ {"subtitleshiftup" , ACTION_SUBTITLE_VSHIFT_UP},
+ {"subtitleshiftdown" , ACTION_SUBTITLE_VSHIFT_DOWN},
+ {"subtitlealign" , ACTION_SUBTITLE_ALIGN},
+ {"audionextlanguage" , ACTION_AUDIO_NEXT_LANGUAGE},
+ {"verticalshiftup" , ACTION_VSHIFT_UP},
+ {"verticalshiftdown" , ACTION_VSHIFT_DOWN},
+ {"nextresolution" , ACTION_CHANGE_RESOLUTION},
+ {"audiotoggledigital", ACTION_TOGGLE_DIGITAL_ANALOG},
+ {"number0" , REMOTE_0},
+ {"number1" , REMOTE_1},
+ {"number2" , REMOTE_2},
+ {"number3" , REMOTE_3},
+ {"number4" , REMOTE_4},
+ {"number5" , REMOTE_5},
+ {"number6" , REMOTE_6},
+ {"number7" , REMOTE_7},
+ {"number8" , REMOTE_8},
+ {"number9" , REMOTE_9},
+ {"osdleft" , ACTION_OSD_SHOW_LEFT},
+ {"osdright" , ACTION_OSD_SHOW_RIGHT},
+ {"osdup" , ACTION_OSD_SHOW_UP},
+ {"osddown" , ACTION_OSD_SHOW_DOWN},
+ {"osdselect" , ACTION_OSD_SHOW_SELECT},
+ {"osdvalueplus" , ACTION_OSD_SHOW_VALUE_PLUS},
+ {"osdvalueminus" , ACTION_OSD_SHOW_VALUE_MIN},
+ {"smallstepback" , ACTION_SMALL_STEP_BACK},
+ {"fastforward" , ACTION_PLAYER_FORWARD},
+ {"rewind" , ACTION_PLAYER_REWIND},
+ {"play" , ACTION_PLAYER_PLAY},
+ {"playpause" , ACTION_PLAYER_PLAYPAUSE},
+ {"switchplayer" , ACTION_SWITCH_PLAYER},
+ {"delete" , ACTION_DELETE_ITEM},
+ {"copy" , ACTION_COPY_ITEM},
+ {"move" , ACTION_MOVE_ITEM},
+ {"mplayerosd" , ACTION_SHOW_MPLAYER_OSD},
+ {"hidesubmenu" , ACTION_OSD_HIDESUBMENU},
+ {"screenshot" , ACTION_TAKE_SCREENSHOT},
+ {"rename" , ACTION_RENAME_ITEM},
+ {"togglewatched" , ACTION_TOGGLE_WATCHED},
+ {"scanitem" , ACTION_SCAN_ITEM},
+ {"reloadkeymaps" , ACTION_RELOAD_KEYMAPS},
+ {"volumeup" , ACTION_VOLUME_UP},
+ {"volumedown" , ACTION_VOLUME_DOWN},
+ {"mute" , ACTION_MUTE},
+ {"backspace" , ACTION_BACKSPACE},
+ {"scrollup" , ACTION_SCROLL_UP},
+ {"scrolldown" , ACTION_SCROLL_DOWN},
+ {"analogfastforward" , ACTION_ANALOG_FORWARD},
+ {"analogrewind" , ACTION_ANALOG_REWIND},
+ {"moveitemup" , ACTION_MOVE_ITEM_UP},
+ {"moveitemdown" , ACTION_MOVE_ITEM_DOWN},
+ {"contextmenu" , ACTION_CONTEXT_MENU},
+ {"shift" , ACTION_SHIFT},
+ {"symbols" , ACTION_SYMBOLS},
+ {"cursorleft" , ACTION_CURSOR_LEFT},
+ {"cursorright" , ACTION_CURSOR_RIGHT},
+ {"showtime" , ACTION_SHOW_OSD_TIME},
+ {"analogseekforward" , ACTION_ANALOG_SEEK_FORWARD},
+ {"analogseekback" , ACTION_ANALOG_SEEK_BACK},
+ {"showpreset" , ACTION_VIS_PRESET_SHOW},
+ {"nextpreset" , ACTION_VIS_PRESET_NEXT},
+ {"previouspreset" , ACTION_VIS_PRESET_PREV},
+ {"lockpreset" , ACTION_VIS_PRESET_LOCK},
+ {"randompreset" , ACTION_VIS_PRESET_RANDOM},
+ {"increasevisrating" , ACTION_VIS_RATE_PRESET_PLUS},
+ {"decreasevisrating" , ACTION_VIS_RATE_PRESET_MINUS},
+ {"showvideomenu" , ACTION_SHOW_VIDEOMENU},
+ {"enter" , ACTION_ENTER},
+ {"increaserating" , ACTION_INCREASE_RATING},
+ {"decreaserating" , ACTION_DECREASE_RATING},
+ {"togglefullscreen" , ACTION_TOGGLE_FULLSCREEN},
+ {"nextscene" , ACTION_NEXT_SCENE},
+ {"previousscene" , ACTION_PREV_SCENE},
+ {"nextletter" , ACTION_NEXT_LETTER},
+ {"prevletter" , ACTION_PREV_LETTER},
+ {"jumpsms2" , ACTION_JUMP_SMS2},
+ {"jumpsms3" , ACTION_JUMP_SMS3},
+ {"jumpsms4" , ACTION_JUMP_SMS4},
+ {"jumpsms5" , ACTION_JUMP_SMS5},
+ {"jumpsms6" , ACTION_JUMP_SMS6},
+ {"jumpsms7" , ACTION_JUMP_SMS7},
+ {"jumpsms8" , ACTION_JUMP_SMS8},
+ {"jumpsms9" , ACTION_JUMP_SMS9},
+ {"filter" , ACTION_FILTER},
+ {"filterclear" , ACTION_FILTER_CLEAR},
+ {"filtersms2" , ACTION_FILTER_SMS2},
+ {"filtersms3" , ACTION_FILTER_SMS3},
+ {"filtersms4" , ACTION_FILTER_SMS4},
+ {"filtersms5" , ACTION_FILTER_SMS5},
+ {"filtersms6" , ACTION_FILTER_SMS6},
+ {"filtersms7" , ACTION_FILTER_SMS7},
+ {"filtersms8" , ACTION_FILTER_SMS8},
+ {"filtersms9" , ACTION_FILTER_SMS9},
+ {"firstpage" , ACTION_FIRST_PAGE},
+ {"lastpage" , ACTION_LAST_PAGE},
+ {"guiprofile" , ACTION_GUIPROFILE_BEGIN},
+ {"red" , ACTION_TELETEXT_RED},
+ {"green" , ACTION_TELETEXT_GREEN},
+ {"yellow" , ACTION_TELETEXT_YELLOW},
+ {"blue" , ACTION_TELETEXT_BLUE},
+ {"increasepar" , ACTION_INCREASE_PAR},
+ {"decreasepar" , ACTION_DECREASE_PAR},
+ {"volampup" , ACTION_VOLAMP_UP},
+ {"volampdown" , ACTION_VOLAMP_DOWN},
+ {"createbookmark" , ACTION_CREATE_BOOKMARK},
+ {"createepisodebookmark" , ACTION_CREATE_EPISODE_BOOKMARK},
+ {"settingsreset" , ACTION_SETTINGS_RESET},
+ {"settingslevelchange", ACTION_SETTINGS_LEVEL_CHANGE},
+
+ // 3D movie playback/GUI
+ {"stereomode" , ACTION_STEREOMODE_SELECT}, // cycle 3D modes, for now an alias for next
+ {"nextstereomode" , ACTION_STEREOMODE_NEXT},
+ {"previousstereomode" , ACTION_STEREOMODE_PREVIOUS},
+ {"togglestereomode" , ACTION_STEREOMODE_TOGGLE},
+ {"stereomodetomono" , ACTION_STEREOMODE_TOMONO},
+
+ // PVR actions
+ {"channelup" , ACTION_CHANNEL_UP},
+ {"channeldown" , ACTION_CHANNEL_DOWN},
+ {"previouschannelgroup" , ACTION_PREVIOUS_CHANNELGROUP},
+ {"nextchannelgroup" , ACTION_NEXT_CHANNELGROUP},
+ {"playpvr" , ACTION_PVR_PLAY},
+ {"playpvrtv" , ACTION_PVR_PLAY_TV},
+ {"playpvrradio" , ACTION_PVR_PLAY_RADIO},
+ {"record" , ACTION_RECORD},
+
+ // Mouse actions
+ {"leftclick" , ACTION_MOUSE_LEFT_CLICK},
+ {"rightclick" , ACTION_MOUSE_RIGHT_CLICK},
+ {"middleclick" , ACTION_MOUSE_MIDDLE_CLICK},
+ {"doubleclick" , ACTION_MOUSE_DOUBLE_CLICK},
+ {"longclick" , ACTION_MOUSE_LONG_CLICK},
+ {"wheelup" , ACTION_MOUSE_WHEEL_UP},
+ {"wheeldown" , ACTION_MOUSE_WHEEL_DOWN},
+ {"mousedrag" , ACTION_MOUSE_DRAG},
+ {"mousemove" , ACTION_MOUSE_MOVE},
+
+ // Touch
+ {"tap" , ACTION_TOUCH_TAP},
+ {"longpress" , ACTION_TOUCH_LONGPRESS},
+ {"pangesture" , ACTION_GESTURE_PAN},
+ {"zoomgesture" , ACTION_GESTURE_ZOOM},
+ {"rotategesture" , ACTION_GESTURE_ROTATE},
+ {"swipeleft" , ACTION_GESTURE_SWIPE_LEFT},
+ {"swiperight" , ACTION_GESTURE_SWIPE_RIGHT},
+ {"swipeup" , ACTION_GESTURE_SWIPE_UP},
+ {"swipedown" , ACTION_GESTURE_SWIPE_DOWN},
+
+ // Do nothing action
+ { "noop" , ACTION_NOOP}
+};
+
+static const ActionMapping windows[] =
+ {{"home" , WINDOW_HOME},
+ {"programs" , WINDOW_PROGRAMS},
+ {"pictures" , WINDOW_PICTURES},
+ {"filemanager" , WINDOW_FILES},
+ {"files" , WINDOW_FILES}, // backward compat
+ {"settings" , WINDOW_SETTINGS_MENU},
+ {"music" , WINDOW_MUSIC},
+ {"video" , WINDOW_VIDEOS},
+ {"videos" , WINDOW_VIDEO_NAV},
+ {"pvr" , WINDOW_TV_CHANNELS}, // backward compat
+ {"tvchannels" , WINDOW_TV_CHANNELS},
+ {"tvrecordings" , WINDOW_TV_RECORDINGS},
+ {"tvguide" , WINDOW_TV_GUIDE},
+ {"tvtimers" , WINDOW_TV_TIMERS},
+ {"tvsearch" , WINDOW_TV_SEARCH},
+ {"radiochannels" , WINDOW_RADIO_CHANNELS},
+ {"radiorecordings" , WINDOW_RADIO_RECORDINGS},
+ {"radioguide" , WINDOW_RADIO_GUIDE},
+ {"radiotimers" , WINDOW_RADIO_TIMERS},
+ {"radiosearch" , WINDOW_RADIO_SEARCH},
+ {"pvrguideinfo" , WINDOW_DIALOG_PVR_GUIDE_INFO},
+ {"pvrrecordinginfo" , WINDOW_DIALOG_PVR_RECORDING_INFO},
+ {"pvrtimersetting" , WINDOW_DIALOG_PVR_TIMER_SETTING},
+ {"pvrgroupmanager" , WINDOW_DIALOG_PVR_GROUP_MANAGER},
+ {"pvrchannelmanager" , WINDOW_DIALOG_PVR_CHANNEL_MANAGER},
+ {"pvrguidesearch" , WINDOW_DIALOG_PVR_GUIDE_SEARCH},
+ {"pvrchannelscan" , WINDOW_DIALOG_PVR_CHANNEL_SCAN},
+ {"pvrupdateprogress" , WINDOW_DIALOG_PVR_UPDATE_PROGRESS},
+ {"pvrosdchannels" , WINDOW_DIALOG_PVR_OSD_CHANNELS},
+ {"pvrosdguide" , WINDOW_DIALOG_PVR_OSD_GUIDE},
+ {"pvrosddirector" , WINDOW_DIALOG_PVR_OSD_DIRECTOR},
+ {"pvrosdcutter" , WINDOW_DIALOG_PVR_OSD_CUTTER},
+ {"pvrosdteletext" , WINDOW_DIALOG_OSD_TELETEXT},
+ {"systeminfo" , WINDOW_SYSTEM_INFORMATION},
+ {"testpattern" , WINDOW_TEST_PATTERN},
+ {"screencalibration" , WINDOW_SCREEN_CALIBRATION},
+ {"guicalibration" , WINDOW_SCREEN_CALIBRATION}, // backward compat
+ {"picturessettings" , WINDOW_SETTINGS_MYPICTURES},
+ {"programssettings" , WINDOW_SETTINGS_MYPROGRAMS},
+ {"weathersettings" , WINDOW_SETTINGS_MYWEATHER},
+ {"musicsettings" , WINDOW_SETTINGS_MYMUSIC},
+ {"systemsettings" , WINDOW_SETTINGS_SYSTEM},
+ {"videossettings" , WINDOW_SETTINGS_MYVIDEOS},
+ {"networksettings" , WINDOW_SETTINGS_SERVICE}, // backward compat
+ {"servicesettings" , WINDOW_SETTINGS_SERVICE},
+ {"appearancesettings" , WINDOW_SETTINGS_APPEARANCE},
+ {"pvrsettings" , WINDOW_SETTINGS_MYPVR},
+ {"tvsettings" , WINDOW_SETTINGS_MYPVR}, // backward compat
+ {"scripts" , WINDOW_PROGRAMS}, // backward compat
+ {"videofiles" , WINDOW_VIDEO_FILES},
+ {"videolibrary" , WINDOW_VIDEO_NAV},
+ {"videoplaylist" , WINDOW_VIDEO_PLAYLIST},
+ {"loginscreen" , WINDOW_LOGIN_SCREEN},
+ {"profiles" , WINDOW_SETTINGS_PROFILES},
+ {"skinsettings" , WINDOW_SKIN_SETTINGS},
+ {"addonbrowser" , WINDOW_ADDON_BROWSER},
+ {"yesnodialog" , WINDOW_DIALOG_YES_NO},
+ {"progressdialog" , WINDOW_DIALOG_PROGRESS},
+ {"virtualkeyboard" , WINDOW_DIALOG_KEYBOARD},
+ {"volumebar" , WINDOW_DIALOG_VOLUME_BAR},
+ {"submenu" , WINDOW_DIALOG_SUB_MENU},
+ {"favourites" , WINDOW_DIALOG_FAVOURITES},
+ {"contextmenu" , WINDOW_DIALOG_CONTEXT_MENU},
+ {"infodialog" , WINDOW_DIALOG_KAI_TOAST},
+ {"numericinput" , WINDOW_DIALOG_NUMERIC},
+ {"gamepadinput" , WINDOW_DIALOG_GAMEPAD},
+ {"shutdownmenu" , WINDOW_DIALOG_BUTTON_MENU},
+ {"mutebug" , WINDOW_DIALOG_MUTE_BUG},
+ {"playercontrols" , WINDOW_DIALOG_PLAYER_CONTROLS},
+ {"seekbar" , WINDOW_DIALOG_SEEK_BAR},
+ {"musicosd" , WINDOW_DIALOG_MUSIC_OSD},
+ {"addonsettings" , WINDOW_DIALOG_ADDON_SETTINGS},
+ {"visualisationsettings" , WINDOW_DIALOG_ADDON_SETTINGS}, // backward compat
+ {"visualisationpresetlist" , WINDOW_DIALOG_VIS_PRESET_LIST},
+ {"osdvideosettings" , WINDOW_DIALOG_VIDEO_OSD_SETTINGS},
+ {"osdaudiosettings" , WINDOW_DIALOG_AUDIO_OSD_SETTINGS},
+ {"videobookmarks" , WINDOW_DIALOG_VIDEO_BOOKMARKS},
+ {"filebrowser" , WINDOW_DIALOG_FILE_BROWSER},
+ {"networksetup" , WINDOW_DIALOG_NETWORK_SETUP},
+ {"mediasource" , WINDOW_DIALOG_MEDIA_SOURCE},
+ {"profilesettings" , WINDOW_DIALOG_PROFILE_SETTINGS},
+ {"locksettings" , WINDOW_DIALOG_LOCK_SETTINGS},
+ {"contentsettings" , WINDOW_DIALOG_CONTENT_SETTINGS},
+ {"songinformation" , WINDOW_DIALOG_SONG_INFO},
+ {"smartplaylisteditor" , WINDOW_DIALOG_SMART_PLAYLIST_EDITOR},
+ {"smartplaylistrule" , WINDOW_DIALOG_SMART_PLAYLIST_RULE},
+ {"busydialog" , WINDOW_DIALOG_BUSY},
+ {"pictureinfo" , WINDOW_DIALOG_PICTURE_INFO},
+ {"accesspoints" , WINDOW_DIALOG_ACCESS_POINTS},
+ {"fullscreeninfo" , WINDOW_DIALOG_FULLSCREEN_INFO},
+ {"karaokeselector" , WINDOW_DIALOG_KARAOKE_SONGSELECT},
+ {"karaokelargeselector" , WINDOW_DIALOG_KARAOKE_SELECTOR},
+ {"sliderdialog" , WINDOW_DIALOG_SLIDER},
+ {"addoninformation" , WINDOW_DIALOG_ADDON_INFO},
+ {"subtitlesearch" , WINDOW_DIALOG_SUBTITLES},
+ {"musicplaylist" , WINDOW_MUSIC_PLAYLIST},
+ {"musicfiles" , WINDOW_MUSIC_FILES},
+ {"musiclibrary" , WINDOW_MUSIC_NAV},
+ {"musicplaylisteditor" , WINDOW_MUSIC_PLAYLIST_EDITOR},
+ {"teletext" , WINDOW_DIALOG_OSD_TELETEXT},
+ {"selectdialog" , WINDOW_DIALOG_SELECT},
+ {"musicinformation" , WINDOW_DIALOG_MUSIC_INFO},
+ {"okdialog" , WINDOW_DIALOG_OK},
+ {"movieinformation" , WINDOW_DIALOG_VIDEO_INFO},
+ {"textviewer" , WINDOW_DIALOG_TEXT_VIEWER},
+ {"fullscreenvideo" , WINDOW_FULLSCREEN_VIDEO},
+ {"fullscreenlivetv" , WINDOW_FULLSCREEN_LIVETV}, // virtual window/keymap section for PVR specific bindings in fullscreen playback (which internally uses WINDOW_FULLSCREEN_VIDEO)
+ {"fullscreenradio" , WINDOW_FULLSCREEN_RADIO}, // virtual window for fullscreen radio, uses WINDOW_VISUALISATION as fallback
+ {"visualisation" , WINDOW_VISUALISATION},
+ {"slideshow" , WINDOW_SLIDESHOW},
+ {"filestackingdialog" , WINDOW_DIALOG_FILESTACKING},
+ {"karaoke" , WINDOW_KARAOKELYRICS},
+ {"weather" , WINDOW_WEATHER},
+ {"screensaver" , WINDOW_SCREENSAVER},
+ {"videoosd" , WINDOW_DIALOG_VIDEO_OSD},
+ {"videomenu" , WINDOW_VIDEO_MENU},
+ {"videotimeseek" , WINDOW_VIDEO_TIME_SEEK},
+ {"musicoverlay" , WINDOW_DIALOG_MUSIC_OVERLAY},
+ {"videooverlay" , WINDOW_DIALOG_VIDEO_OVERLAY},
+ {"startwindow" , WINDOW_START},
+ {"startup" , WINDOW_STARTUP_ANIM},
+ {"peripherals" , WINDOW_DIALOG_PERIPHERAL_MANAGER},
+ {"peripheralsettings" , WINDOW_DIALOG_PERIPHERAL_SETTINGS},
+ {"extendedprogressdialog" , WINDOW_DIALOG_EXT_PROGRESS},
+ {"mediafilter" , WINDOW_DIALOG_MEDIA_FILTER},
+ {"addon" , WINDOW_ADDON_START}};
+
+static const ActionMapping mousekeys[] =
+{
+ { "click", KEY_MOUSE_CLICK },
+ { "leftclick", KEY_MOUSE_CLICK },
+ { "rightclick", KEY_MOUSE_RIGHTCLICK },
+ { "middleclick", KEY_MOUSE_MIDDLECLICK },
+ { "doubleclick", KEY_MOUSE_DOUBLE_CLICK },
+ { "longclick", KEY_MOUSE_LONG_CLICK },
+ { "wheelup", KEY_MOUSE_WHEEL_UP },
+ { "wheeldown", KEY_MOUSE_WHEEL_DOWN },
+ { "mousedrag", KEY_MOUSE_DRAG },
+ { "mousemove", KEY_MOUSE_MOVE }
+};
+
+static const ActionMapping touchcommands[] =
+{
+ { "tap", ACTION_TOUCH_TAP },
+ { "longpress", ACTION_TOUCH_LONGPRESS },
+ { "pan", ACTION_GESTURE_PAN },
+ { "zoom", ACTION_GESTURE_ZOOM },
+ { "rotate", ACTION_GESTURE_ROTATE },
+ { "swipeleft", ACTION_GESTURE_SWIPE_LEFT },
+ { "swiperight", ACTION_GESTURE_SWIPE_RIGHT },
+ { "swipeup", ACTION_GESTURE_SWIPE_UP },
+ { "swipedown", ACTION_GESTURE_SWIPE_DOWN }
+};
+
+static const WindowMapping fallbackWindows[] =
+{
+ { WINDOW_FULLSCREEN_LIVETV, WINDOW_FULLSCREEN_VIDEO },
+ { WINDOW_DIALOG_FULLSCREEN_INFO, WINDOW_FULLSCREEN_VIDEO },
+ { WINDOW_FULLSCREEN_RADIO, WINDOW_VISUALISATION }
+};
+
+#ifdef TARGET_WINDOWS
+static const ActionMapping appcommands[] =
+{
+ { "browser_back", APPCOMMAND_BROWSER_BACKWARD },
+ { "browser_forward", APPCOMMAND_BROWSER_FORWARD },
+ { "browser_refresh", APPCOMMAND_BROWSER_REFRESH },
+ { "browser_stop", APPCOMMAND_BROWSER_STOP },
+ { "browser_search", APPCOMMAND_BROWSER_SEARCH },
+ { "browser_favorites", APPCOMMAND_BROWSER_FAVORITES },
+ { "browser_home", APPCOMMAND_BROWSER_HOME },
+ { "volume_mute", APPCOMMAND_VOLUME_MUTE },
+ { "volume_down", APPCOMMAND_VOLUME_DOWN },
+ { "volume_up", APPCOMMAND_VOLUME_UP },
+ { "next_track", APPCOMMAND_MEDIA_NEXTTRACK },
+ { "prev_track", APPCOMMAND_MEDIA_PREVIOUSTRACK },
+ { "stop", APPCOMMAND_MEDIA_STOP },
+ { "play_pause", APPCOMMAND_MEDIA_PLAY_PAUSE },
+ { "launch_mail", APPCOMMAND_LAUNCH_MAIL },
+ { "launch_media_select", APPCOMMAND_LAUNCH_MEDIA_SELECT },
+ { "launch_app1", APPCOMMAND_LAUNCH_APP1 },
+ { "launch_app2", APPCOMMAND_LAUNCH_APP2 },
+ { "play", APPCOMMAND_MEDIA_PLAY },
+ { "pause", APPCOMMAND_MEDIA_PAUSE },
+ { "fastforward", APPCOMMAND_MEDIA_FAST_FORWARD },
+ { "rewind", APPCOMMAND_MEDIA_REWIND },
+ { "channelup", APPCOMMAND_MEDIA_CHANNEL_UP },
+ { "channeldown", APPCOMMAND_MEDIA_CHANNEL_DOWN }
+};
+#endif
+
+CButtonTranslator& CButtonTranslator::GetInstance()
+{
+ static CButtonTranslator sl_instance;
+ return sl_instance;
+}
+
+CButtonTranslator::CButtonTranslator()
+{
+ m_deviceList.clear();
+ m_Loaded = false;
+}
+
+#if defined(HAS_LIRC) || defined(HAS_IRSERVERSUITE)
+void CButtonTranslator::ClearLircButtonMapEntries()
+{
+ vector<lircButtonMap*> maps;
+ for (map<std::string,lircButtonMap*>::iterator it = lircRemotesMap.begin();
+ it != lircRemotesMap.end();++it)
+ maps.push_back(it->second);
+ sort(maps.begin(),maps.end());
+ vector<lircButtonMap*>::iterator itend = unique(maps.begin(),maps.end());
+ for (vector<lircButtonMap*>::iterator it = maps.begin(); it != itend;++it)
+ delete *it;
+}
+#endif
+
+CButtonTranslator::~CButtonTranslator()
+{
+#if defined(HAS_LIRC) || defined(HAS_IRSERVERSUITE)
+ ClearLircButtonMapEntries();
+#endif
+}
+
+// Add the supplied device name to the list of connected devices
+void CButtonTranslator::AddDevice(std::string& strDevice)
+{
+ // Only add the device if it isn't already in the list
+ std::list<std::string>::iterator it;
+ for (it = m_deviceList.begin(); it != m_deviceList.end(); it++)
+ if (*it == strDevice)
+ return;
+
+ // Add the device
+ m_deviceList.push_back(strDevice);
+ m_deviceList.sort();
+
+ // New device added so reload the key mappings
+ Load();
+}
+
+void CButtonTranslator::RemoveDevice(std::string& strDevice)
+{
+ // Find the device
+ std::list<std::string>::iterator it;
+ for (it = m_deviceList.begin(); it != m_deviceList.end(); it++)
+ if (*it == strDevice)
+ break;
+ if (it == m_deviceList.end())
+ return;
+
+ // Remove the device
+ m_deviceList.remove(strDevice);
+
+ // Device removed so reload the key mappings
+ Load();
+}
+
+bool CButtonTranslator::Load(bool AlwaysLoad)
+{
+ m_translatorMap.clear();
+
+ // Directories to search for keymaps. They're applied in this order,
+ // so keymaps in profile/keymaps/ override e.g. system/keymaps
+ static const char* DIRS_TO_CHECK[] = {
+ "special://xbmc/system/keymaps/",
+ "special://masterprofile/keymaps/",
+ "special://profile/keymaps/"
+ };
+ bool success = false;
+
+ for (unsigned int dirIndex = 0; dirIndex < ARRAY_SIZE(DIRS_TO_CHECK); ++dirIndex)
+ {
+ if (XFILE::CDirectory::Exists(DIRS_TO_CHECK[dirIndex]))
+ {
+ CFileItemList files;
+ XFILE::CDirectory::GetDirectory(DIRS_TO_CHECK[dirIndex], files, ".xml");
+ // Sort the list for filesystem based priorities, e.g. 01-keymap.xml, 02-keymap-overrides.xml
+ files.Sort(SortByFile, SortOrderAscending);
+ for(int fileIndex = 0; fileIndex<files.Size(); ++fileIndex)
+ {
+ if (!files[fileIndex]->m_bIsFolder)
+ success |= LoadKeymap(files[fileIndex]->GetPath());
+ }
+
+ // Load mappings for any HID devices we have connected
+ std::list<std::string>::iterator it;
+ for (it = m_deviceList.begin(); it != m_deviceList.end(); it++)
+ {
+ std::string devicedir = DIRS_TO_CHECK[dirIndex];
+ devicedir.append(*it);
+ devicedir.append("/");
+ if( XFILE::CDirectory::Exists(devicedir) )
+ {
+ CFileItemList files;
+ XFILE::CDirectory::GetDirectory(devicedir, files, ".xml");
+ // Sort the list for filesystem based priorities, e.g. 01-keymap.xml, 02-keymap-overrides.xml
+ files.Sort(SortByFile, SortOrderAscending);
+ for(int fileIndex = 0; fileIndex<files.Size(); ++fileIndex)
+ {
+ if (!files[fileIndex]->m_bIsFolder)
+ success |= LoadKeymap(files[fileIndex]->GetPath());
+ }
+ }
+ }
+ }
+ }
+
+ if (!success)
+ {
+ CLog::Log(LOGERROR, "Error loading keymaps from: %s or %s or %s", DIRS_TO_CHECK[0], DIRS_TO_CHECK[1], DIRS_TO_CHECK[2]);
+ return false;
+ }
+
+#if defined(HAS_LIRC) || defined(HAS_IRSERVERSUITE)
+#ifdef TARGET_POSIX
+#define REMOTEMAP "Lircmap.xml"
+#else
+#define REMOTEMAP "IRSSmap.xml"
+#endif
+ std::string lircmapPath = URIUtils::AddFileToFolder("special://xbmc/system/", REMOTEMAP);
+ lircRemotesMap.clear();
+ if(CFile::Exists(lircmapPath))
+ success |= LoadLircMap(lircmapPath);
+ else
+ CLog::Log(LOGDEBUG, "CButtonTranslator::Load - no system %s found, skipping", REMOTEMAP);
+
+ lircmapPath = CProfilesManager::Get().GetUserDataItem(REMOTEMAP);
+ if(CFile::Exists(lircmapPath))
+ success |= LoadLircMap(lircmapPath);
+ else
+ CLog::Log(LOGDEBUG, "CButtonTranslator::Load - no userdata %s found, skipping", REMOTEMAP);
+
+ if (!success)
+ CLog::Log(LOGERROR, "CButtonTranslator::Load - unable to load remote map %s", REMOTEMAP);
+ // don't return false - it is to only indicate a fatal error (which this is not)
+#endif
+
+ // Done!
+ m_Loaded = true;
+ return true;
+}
+
+bool CButtonTranslator::LoadKeymap(const std::string &keymapPath)
+{
+ CXBMCTinyXML xmlDoc;
+
+ CLog::Log(LOGINFO, "Loading %s", keymapPath.c_str());
+ if (!xmlDoc.LoadFile(keymapPath))
+ {
+ CLog::Log(LOGERROR, "Error loading keymap: %s, Line %d\n%s", keymapPath.c_str(), xmlDoc.ErrorRow(), xmlDoc.ErrorDesc());
+ return false;
+ }
+ TiXmlElement* pRoot = xmlDoc.RootElement();
+ if (!pRoot)
+ {
+ CLog::Log(LOGERROR, "Error getting keymap root: %s", keymapPath.c_str());
+ return false;
+ }
+ std::string strValue = pRoot->Value();
+ if ( strValue != "keymap")
+ {
+ CLog::Log(LOGERROR, "%s Doesn't contain <keymap>", keymapPath.c_str());
+ return false;
+ }
+ // run through our window groups
+ TiXmlNode* pWindow = pRoot->FirstChild();
+ while (pWindow)
+ {
+ if (pWindow->Type() == TiXmlNode::TINYXML_ELEMENT)
+ {
+ int windowID = WINDOW_INVALID;
+ const char *szWindow = pWindow->Value();
+ if (szWindow)
+ {
+ if (strcmpi(szWindow, "global") == 0)
+ windowID = -1;
+ else
+ windowID = TranslateWindow(szWindow);
+ }
+ MapWindowActions(pWindow, windowID);
+ }
+ pWindow = pWindow->NextSibling();
+ }
+
+ return true;
+}
+
+bool CButtonTranslator::LoadLircMap(const std::string &lircmapPath)
+{
+#ifdef TARGET_POSIX
+#define REMOTEMAPTAG "lircmap"
+#else
+#define REMOTEMAPTAG "irssmap"
+#endif
+ // load our xml file, and fill up our mapping tables
+ CXBMCTinyXML xmlDoc;
+
+ // Load the config file
+ CLog::Log(LOGINFO, "Loading %s", lircmapPath.c_str());
+ if (!xmlDoc.LoadFile(lircmapPath))
+ {
+ CLog::Log(LOGERROR, "%s, Line %d\n%s", lircmapPath.c_str(), xmlDoc.ErrorRow(), xmlDoc.ErrorDesc());
+ return false; // This is so people who don't have the file won't fail, just warn
+ }
+
+ TiXmlElement* pRoot = xmlDoc.RootElement();
+ std::string strValue = pRoot->Value();
+ if (strValue != REMOTEMAPTAG)
+ {
+ CLog::Log(LOGERROR, "%sl Doesn't contain <%s>", lircmapPath.c_str(), REMOTEMAPTAG);
+ return false;
+ }
+
+ // run through our window groups
+ TiXmlNode* pRemote = pRoot->FirstChild();
+ while (pRemote)
+ {
+ if (pRemote->Type() == TiXmlNode::TINYXML_ELEMENT)
+ {
+ const char *szRemote = pRemote->Value();
+ if (szRemote)
+ {
+ TiXmlAttribute* pAttr = pRemote->ToElement()->FirstAttribute();
+ if (pAttr)
+ MapRemote(pRemote, pAttr->Value());
+ }
+ }
+ pRemote = pRemote->NextSibling();
+ }
+
+ return true;
+}
+
+void CButtonTranslator::MapRemote(TiXmlNode *pRemote, const char* szDevice)
+{
+ CLog::Log(LOGINFO, "* Adding remote mapping for device '%s'", szDevice);
+ vector<string> RemoteNames;
+ map<std::string, lircButtonMap*>::iterator it = lircRemotesMap.find(szDevice);
+ if (it == lircRemotesMap.end())
+ lircRemotesMap[szDevice] = new lircButtonMap;
+ lircButtonMap& buttons = *lircRemotesMap[szDevice];
+
+ TiXmlElement *pButton = pRemote->FirstChildElement();
+ while (pButton)
+ {
+ if (!pButton->NoChildren())
+ {
+ if (pButton->ValueStr() == "altname")
+ RemoteNames.push_back(pButton->FirstChild()->ValueStr());
+ else
+ buttons[pButton->FirstChild()->ValueStr()] = pButton->ValueStr();
+ }
+ pButton = pButton->NextSiblingElement();
+ }
+ for (vector<string>::iterator it = RemoteNames.begin();
+ it != RemoteNames.end();++it)
+ {
+ CLog::Log(LOGINFO, "* Linking remote mapping for '%s' to '%s'", szDevice, it->c_str());
+ lircRemotesMap[*it] = &buttons;
+ }
+}
+
+int CButtonTranslator::TranslateLircRemoteString(const char* szDevice, const char *szButton)
+{
+ // Find the device
+ map<std::string, lircButtonMap*>::iterator it = lircRemotesMap.find(szDevice);
+ if (it == lircRemotesMap.end())
+ return 0;
+
+ // Find the button
+ lircButtonMap::iterator it2 = (*it).second->find(szButton);
+ if (it2 == (*it).second->end())
+ return 0;
+
+ // Convert the button to code
+ if (strnicmp((*it2).second.c_str(), "obc", 3) == 0)
+ return TranslateUniversalRemoteString((*it2).second.c_str());
+
+ return TranslateRemoteString((*it2).second.c_str());
+}
+
+#if defined(HAS_SDL_JOYSTICK) || defined(HAS_EVENT_SERVER)
+void CButtonTranslator::MapJoystickActions(int windowID, TiXmlNode *pJoystick)
+{
+ string joyname = JOYSTICK_DEFAULT_MAP; // default global map name
+ vector<boost::shared_ptr<CRegExp> > joynames;
+ map<int, string> buttonMap;
+ map<int, string> axisMap;
+ AxesConfig axesConfig;
+ ActionMap hatMap;
+
+ TiXmlElement *pJoy = pJoystick->ToElement();
+ if (pJoy && pJoy->Attribute("name"))
+ joyname = pJoy->Attribute("name");
+ else
+ CLog::Log(LOGNOTICE, "No Joystick name specified, loading default map");
+
+ boost::shared_ptr<CRegExp> re(new CRegExp(true, CRegExp::asciiOnly));
+ if (!re->RegComp(JoynameToRegex(joyname), CRegExp::StudyRegExp))
+ {
+ CLog::Log(LOGNOTICE, "Invalid joystick regex specified: '%s'", joyname.c_str());
+ return;
+ }
+ else
+ joynames.push_back(re);
+
+ // parse map
+ TiXmlElement *pButton = pJoystick->FirstChildElement();
+ int id = 0;
+ while (pButton)
+ {
+ const std::string &type = pButton->ValueStr();
+ std::string action;
+ if (!pButton->NoChildren())
+ action = pButton->FirstChild()->ValueStr();
+
+ if ((pButton->QueryIntAttribute("id", &id) == TIXML_SUCCESS) && id>=0 && id<=256)
+ {
+ if (type == "button")
+ {
+ buttonMap[id] = action;
+ }
+ else if (type == "axis")
+ {
+ int limit = 0;
+ if (pButton->QueryIntAttribute("limit", &limit) == TIXML_SUCCESS)
+ {
+ if (limit==-1)
+ axisMap[-id] = action;
+ else if (limit==1)
+ axisMap[id] = action;
+ else if (limit==0)
+ axisMap[id|0xFFFF0000] = action;
+ else
+ {
+ axisMap[id] = action;
+ axisMap[-id] = action;
+ CLog::Log(LOGERROR, "Error in joystick map, invalid limit specified %d for axis %d", limit, id);
+ }
+ }
+ else
+ {
+ axisMap[id] = action;
+ axisMap[-id] = action;
+ }
+
+ if (windowID == -1) {
+ // in <global> we can override the rest state value of axes and whether they are triggers
+ bool trigger = false;
+ int restStateValue = 0;
+ pButton->QueryBoolAttribute("trigger", &trigger);
+ pButton->QueryIntAttribute("rest", &restStateValue);
+ // if it deviates from the defaults
+ if (trigger || restStateValue != 0)
+ axesConfig.push_back(AxisConfig(id, trigger, restStateValue));
+ }
+ }
+ else if (type == "hat")
+ {
+ string position;
+ if (pButton->QueryValueAttribute("position", &position) == TIXML_SUCCESS)
+ {
+ uint32_t hatID = id|0xFFF00000;
+ if (position.compare("up") == 0)
+ hatMap[(JACTIVE_HAT_UP<<16)|hatID] = action;
+ else if (position.compare("down") == 0)
+ hatMap[(JACTIVE_HAT_DOWN<<16)|hatID] = action;
+ else if (position.compare("right") == 0)
+ hatMap[(JACTIVE_HAT_RIGHT<<16)|hatID] = action;
+ else if (position.compare("left") == 0)
+ hatMap[(JACTIVE_HAT_LEFT<<16)|hatID] = action;
+ else
+ CLog::Log(LOGERROR, "Error in joystick map, invalid position specified %s for axis %d", position.c_str(), id);
+ }
+ }
+ else
+ CLog::Log(LOGERROR, "Error reading joystick map element, unknown button type: %s", type.c_str());
+ }
+ else if (type == "altname")
+ {
+ boost::shared_ptr<CRegExp> altRe(new CRegExp(true, CRegExp::asciiOnly));
+ if (!altRe->RegComp(JoynameToRegex(action), CRegExp::StudyRegExp))
+ CLog::Log(LOGNOTICE, "Ignoring invalid joystick altname regex: '%s'", action.c_str());
+ else
+ joynames.push_back(altRe);
+ }
+ else
+ CLog::Log(LOGERROR, "Error reading joystick map element, Invalid id: %d", id);
+
+ pButton = pButton->NextSiblingElement();
+ }
+ vector<boost::shared_ptr<CRegExp> >::iterator it = joynames.begin();
+ while (it!=joynames.end())
+ {
+ MergeMap(*it, &m_joystickButtonMap, windowID, buttonMap);
+ MergeMap(*it, &m_joystickAxisMap, windowID, axisMap);
+ MergeMap(*it, &m_joystickHatMap, windowID, hatMap);
+ if (windowID == -1)
+ m_joystickAxesConfigs[*it] = axesConfig;
+// CLog::Log(LOGDEBUG, "Found Joystick map for window %d using %s", windowID, it->c_str());
+ it++;
+ }
+}
+
+std::string CButtonTranslator::JoynameToRegex(const std::string& joyName) const
+{
+ if (joyName.empty())
+ return joyName;
+
+ // names already presented as regex are identified by a leading /
+ else if (joyName[0] == '/')
+ return joyName.substr(1);
+
+ // the others we'll have to escape
+ return "\\Q" + joyName + "\\E";
+}
+
+void CButtonTranslator::MergeMap(boost::shared_ptr<CRegExp> joyName, JoystickMap *joystick, int windowID, const ActionMap &map)
+{
+ // find or create WindowMap entry, match on pattern equality
+ JoystickMap::iterator jit;
+ for (jit = joystick->begin(); jit != joystick->end(); jit++)
+ {
+ if (jit->first->GetPattern() == joyName->GetPattern())
+ break;
+ }
+ WindowMap *w = (jit == joystick->end()) ? &(*joystick)[joyName] : &jit->second;
+
+ // find or create ActionMap, and merge/overwrite new entries
+ ActionMap *a = &(*w)[windowID];
+ for (ActionMap::const_iterator it = map.begin(); it != map.end(); it++)
+ (*a)[it->first] = it->second;
+}
+
+CButtonTranslator::JoystickMap::const_iterator CButtonTranslator::FindWindowMap(const std::string& joyName, const JoystickMap &maps) const
+{
+ JoystickMap::const_iterator it;
+ for (it = maps.begin(); it != maps.end(); it++)
+ {
+ if (it->first->RegFind(joyName) >= 0)
+ {
+ // CLog::Log(LOGDEBUG, "Regex %s matches joystick %s", it->first->GetPattern().c_str(), joyName.c_str());
+ break;
+ }
+ // CLog::Log(LOGDEBUG, "No match: %s for joystick %s", it->first->GetPattern().c_str(), joyName.c_str());
+ }
+ return it;
+}
+
+bool CButtonTranslator::TranslateJoystickString(int window, const std::string& joyName, int id, short inputType, int& action, std::string& strAction, bool &fullrange)
+{
+ fullrange = false;
+
+ // resolve the correct JoystickMap
+ JoystickMap *jmap;
+ if (inputType == JACTIVE_AXIS)
+ jmap = &m_joystickAxisMap;
+ else if (inputType == JACTIVE_BUTTON)
+ jmap = &m_joystickButtonMap;
+ else if (inputType == JACTIVE_HAT)
+ jmap = &m_joystickHatMap;
+ else
+ {
+ CLog::Log(LOGERROR, "Error reading joystick input type '%i'", (int) inputType);
+ return false;
+ }
+
+ JoystickMap::const_iterator it = FindWindowMap(joyName, *jmap);
+ if (it==jmap->end())
+ {
+ it = FindWindowMap(JOYSTICK_DEFAULT_MAP, *jmap); // default global map name
+ if (it==jmap->end())
+ return false;
+ }
+
+ const WindowMap *wmap = &it->second;
+
+ // try to get the action from the current window
+ action = GetActionCode(window, id, *wmap, strAction, fullrange);
+
+ // if it's invalid, try to get it from a fallback window or the global map
+ if (action == 0)
+ {
+ int fallbackWindow = GetFallbackWindow(window);
+ if (fallbackWindow > -1)
+ action = GetActionCode(fallbackWindow, id, *wmap, strAction, fullrange);
+ // still no valid action? use global map
+ if (action == 0)
+ action = GetActionCode(-1, id, *wmap, strAction, fullrange);
+ }
+
+ return (action > 0);
+}
+
+bool CButtonTranslator::TranslateTouchAction(int window, int touchAction, int touchPointers, int &action)
+{
+ action = 0;
+ if (touchPointers <= 0)
+ touchPointers = 1;
+
+ touchAction += touchPointers - 1;
+ touchAction |= KEY_TOUCH;
+
+ action = GetTouchActionCode(window, touchAction);
+ if (action <= 0)
+ {
+ int fallbackWindow = GetFallbackWindow(window);
+ if (fallbackWindow > -1)
+ action = GetTouchActionCode(fallbackWindow, touchAction);
+ if (action <= 0)
+ action = GetTouchActionCode(-1, touchAction);
+ }
+
+ return action > 0;
+}
+
+int CButtonTranslator::GetActionCode(int window, int action)
+{
+ map<int, buttonMap>::const_iterator it = m_translatorMap.find(window);
+ if (it == m_translatorMap.end())
+ return 0;
+
+ buttonMap::const_iterator it2 = it->second.find(action);
+ if (it2 == it->second.end())
+ return 0;
+
+ return it2->second.id;
+}
+
+/*
+ * Translates a joystick input to an action code
+ */
+int CButtonTranslator::GetActionCode(int window, int id, const WindowMap &wmap, std::string &strAction, bool &fullrange) const
+{
+ int action = 0;
+ bool found = false;
+
+ WindowMap::const_iterator it = wmap.find(window);
+ if (it != wmap.end())
+ {
+ const map<int, string> &windowbmap = it->second;
+ map<int, string>::const_iterator it2 = windowbmap.find(id);
+ if (it2 != windowbmap.end())
+ {
+ strAction = (it2->second).c_str();
+ found = true;
+ }
+
+ it2 = windowbmap.find(abs(id)|0xFFFF0000);
+ if (it2 != windowbmap.end())
+ {
+ strAction = (it2->second).c_str();
+ found = true;
+ fullrange = true;
+ }
+
+ // Hats joystick
+ it2 = windowbmap.find(id|0xFFF00000);
+ if (it2 != windowbmap.end())
+ {
+ strAction = (it2->second).c_str();
+ found = true;
+ }
+ }
+
+ if (found)
+ TranslateActionString(strAction.c_str(), action);
+ return action;
+}
+#endif
+
+void CButtonTranslator::GetActions(std::vector<std::string> &actionList)
+{
+ unsigned int size = sizeof(actions) / sizeof(ActionMapping);
+ actionList.clear();
+ actionList.reserve(size);
+ for (unsigned int index = 0; index < size; index++)
+ actionList.push_back(actions[index].name);
+}
+
+void CButtonTranslator::GetWindows(std::vector<std::string> &windowList)
+{
+ unsigned int size = sizeof(windows) / sizeof(ActionMapping);
+ windowList.clear();
+ windowList.reserve(size);
+ for (unsigned int index = 0; index < size; index++)
+ windowList.push_back(windows[index].name);
+}
+
+int CButtonTranslator::GetFallbackWindow(int windowID)
+{
+ for (unsigned int index = 0; index < ARRAY_SIZE(fallbackWindows); ++index)
+ {
+ if (fallbackWindows[index].origin == windowID)
+ return fallbackWindows[index].target;
+ }
+ // for addon windows use WINDOW_ADDON_START
+ // because id is dynamic
+ if (windowID >= WINDOW_ADDON_START && windowID <= WINDOW_ADDON_END)
+ return WINDOW_ADDON_START;
+
+ return -1;
+}
+
+CAction CButtonTranslator::GetAction(int window, const CKey &key, bool fallback)
+{
+ std::string strAction;
+ // try to get the action from the current window
+ int actionID = GetActionCode(window, key, strAction);
+ // if it's invalid, try to get it from the global map
+ if (actionID == 0 && fallback)
+ {
+ int fallbackWindow = GetFallbackWindow(window);
+ if (fallbackWindow > -1)
+ actionID = GetActionCode(fallbackWindow, key, strAction);
+ // still no valid action? use global map
+ if (actionID == 0)
+ actionID = GetActionCode( -1, key, strAction);
+ }
+ // Now fill our action structure
+ CAction action(actionID, strAction, key);
+ return action;
+}
+
+int CButtonTranslator::GetActionCode(int window, const CKey &key, std::string &strAction) const
+{
+ uint32_t code = key.GetButtonCode();
+
+ map<int, buttonMap>::const_iterator it = m_translatorMap.find(window);
+ if (it == m_translatorMap.end())
+ return 0;
+ buttonMap::const_iterator it2 = (*it).second.find(code);
+ int action = 0;
+ if (it2 != (*it).second.end())
+ {
+ action = (*it2).second.id;
+ strAction = (*it2).second.strID;
+ }
+#ifdef TARGET_POSIX
+ // Some buttoncodes changed in Hardy
+ if (action == 0 && (code & KEY_VKEY) == KEY_VKEY && (code & 0x0F00))
+ {
+ CLog::Log(LOGDEBUG, "%s: Trying Hardy keycode for %#04x", __FUNCTION__, code);
+ code &= ~0x0F00;
+ it2 = (*it).second.find(code);
+ if (it2 != (*it).second.end())
+ {
+ action = (*it2).second.id;
+ strAction = (*it2).second.strID;
+ }
+ }
+#endif
+ return action;
+}
+
+void CButtonTranslator::MapAction(uint32_t buttonCode, const char *szAction, buttonMap &map)
+{
+ int action = ACTION_NONE;
+ if (!TranslateActionString(szAction, action) || !buttonCode)
+ return; // no valid action, or an invalid buttoncode
+
+ // have a valid action, and a valid button - map it.
+ // check to see if we've already got this (button,action) pair defined
+ buttonMap::iterator it = map.find(buttonCode);
+ if (it == map.end() || (*it).second.id != action || (*it).second.strID != szAction)
+ {
+ // NOTE: This multimap is only being used as a normal map at this point (no support
+ // for multiple actions per key)
+ if (it != map.end())
+ map.erase(it);
+ CButtonAction button;
+ button.id = action;
+ button.strID = szAction;
+ map.insert(pair<uint32_t, CButtonAction>(buttonCode, button));
+ }
+}
+
+bool CButtonTranslator::HasDeviceType(TiXmlNode *pWindow, std::string type)
+{
+ return pWindow->FirstChild(type) != NULL;
+}
+
+void CButtonTranslator::MapWindowActions(TiXmlNode *pWindow, int windowID)
+{
+ if (!pWindow || windowID == WINDOW_INVALID)
+ return;
+
+ TiXmlNode* pDevice;
+
+ const char* types[] = {"gamepad", "remote", "universalremote", "keyboard", "mouse", "appcommand", NULL};
+ for (int i = 0; types[i]; ++i)
+ {
+ std::string type(types[i]);
+ if (HasDeviceType(pWindow, type))
+ {
+ buttonMap map;
+ std::map<int, buttonMap>::iterator it = m_translatorMap.find(windowID);
+ if (it != m_translatorMap.end())
+ {
+ map = it->second;
+ m_translatorMap.erase(it);
+ }
+
+ pDevice = pWindow->FirstChild(type);
+
+ TiXmlElement *pButton = pDevice->FirstChildElement();
+
+ while (pButton)
+ {
+ uint32_t buttonCode=0;
+ if (type == "gamepad")
+ buttonCode = TranslateGamepadString(pButton->Value());
+ else if (type == "remote")
+ buttonCode = TranslateRemoteString(pButton->Value());
+ else if (type == "universalremote")
+ buttonCode = TranslateUniversalRemoteString(pButton->Value());
+ else if (type == "keyboard")
+ buttonCode = TranslateKeyboardButton(pButton);
+ else if (type == "mouse")
+ buttonCode = TranslateMouseCommand(pButton);
+ else if (type == "appcommand")
+ buttonCode = TranslateAppCommand(pButton->Value());
+
+ if (buttonCode && pButton->FirstChild())
+ MapAction(buttonCode, pButton->FirstChild()->Value(), map);
+ pButton = pButton->NextSiblingElement();
+ }
+
+ // add our map to our table
+ if (!map.empty())
+ m_translatorMap.insert(pair<int, buttonMap>( windowID, map));
+ }
+ }
+
+#if defined(HAS_SDL_JOYSTICK) || defined(HAS_EVENT_SERVER)
+ if ((pDevice = pWindow->FirstChild("joystick")) != NULL)
+ {
+ // map joystick actions
+ while (pDevice)
+ {
+ MapJoystickActions(windowID, pDevice);
+ pDevice = pDevice->NextSibling("joystick");
+ }
+ }
+#endif
+
+ if ((pDevice = pWindow->FirstChild("touch")) != NULL)
+ {
+ // map touch actions
+ while (pDevice)
+ {
+ MapTouchActions(windowID, pDevice);
+ pDevice = pDevice->NextSibling("touch");
+ }
+ }
+}
+
+bool CButtonTranslator::TranslateActionString(const char *szAction, int &action)
+{
+ action = ACTION_NONE;
+ std::string strAction = szAction;
+ StringUtils::ToLower(strAction);
+ if (CBuiltins::HasCommand(strAction))
+ action = ACTION_BUILT_IN_FUNCTION;
+
+ for (unsigned int index=0;index < ARRAY_SIZE(actions);++index)
+ {
+ if (strAction == actions[index].name)
+ {
+ action = actions[index].action;
+ break;
+ }
+ }
+
+ if (action == ACTION_NONE)
+ {
+ CLog::Log(LOGERROR, "Keymapping error: no such action '%s' defined", strAction.c_str());
+ return false;
+ }
+
+ return true;
+}
+
+std::string CButtonTranslator::TranslateWindow(int windowID)
+{
+ for (unsigned int index = 0; index < ARRAY_SIZE(windows); ++index)
+ {
+ if (windows[index].action == windowID)
+ return windows[index].name;
+ }
+ return "";
+}
+
+int CButtonTranslator::TranslateWindow(const std::string &window)
+{
+ std::string strWindow(window);
+ if (strWindow.empty())
+ return WINDOW_INVALID;
+ StringUtils::ToLower(strWindow);
+ // eliminate .xml
+ if (StringUtils::EndsWith(strWindow, ".xml"))
+ strWindow = strWindow.substr(0, strWindow.size() - 4);
+
+ // window12345, for custom window to be keymapped
+ if (strWindow.length() > 6 && StringUtils::StartsWithNoCase(strWindow, "window"))
+ strWindow = strWindow.substr(6);
+ if (StringUtils::StartsWithNoCase(strWindow, "my")) // drop "my" prefix
+ strWindow = strWindow.substr(2);
+ if (StringUtils::IsNaturalNumber(strWindow))
+ {
+ // allow a full window id or a delta id
+ int iWindow = atoi(strWindow.c_str());
+ if (iWindow > WINDOW_INVALID)
+ return iWindow;
+ return WINDOW_HOME + iWindow;
+ }
+
+ // run through the window structure
+ for (unsigned int index = 0; index < ARRAY_SIZE(windows); ++index)
+ {
+ if (strWindow == windows[index].name)
+ return windows[index].action;
+ }
+
+ CLog::Log(LOGERROR, "Window Translator: Can't find window %s", strWindow.c_str());
+ return WINDOW_INVALID;
+}
+
+uint32_t CButtonTranslator::TranslateGamepadString(const char *szButton)
+{
+ if (!szButton)
+ return 0;
+ uint32_t buttonCode = 0;
+ std::string strButton = szButton;
+ StringUtils::ToLower(strButton);
+ if (strButton == "a") buttonCode = KEY_BUTTON_A;
+ else if (strButton == "b") buttonCode = KEY_BUTTON_B;
+ else if (strButton == "x") buttonCode = KEY_BUTTON_X;
+ else if (strButton == "y") buttonCode = KEY_BUTTON_Y;
+ else if (strButton == "white") buttonCode = KEY_BUTTON_WHITE;
+ else if (strButton == "black") buttonCode = KEY_BUTTON_BLACK;
+ else if (strButton == "start") buttonCode = KEY_BUTTON_START;
+ else if (strButton == "back") buttonCode = KEY_BUTTON_BACK;
+ else if (strButton == "leftthumbbutton") buttonCode = KEY_BUTTON_LEFT_THUMB_BUTTON;
+ else if (strButton == "rightthumbbutton") buttonCode = KEY_BUTTON_RIGHT_THUMB_BUTTON;
+ else if (strButton == "leftthumbstick") buttonCode = KEY_BUTTON_LEFT_THUMB_STICK;
+ else if (strButton == "leftthumbstickup") buttonCode = KEY_BUTTON_LEFT_THUMB_STICK_UP;
+ else if (strButton == "leftthumbstickdown") buttonCode = KEY_BUTTON_LEFT_THUMB_STICK_DOWN;
+ else if (strButton == "leftthumbstickleft") buttonCode = KEY_BUTTON_LEFT_THUMB_STICK_LEFT;
+ else if (strButton == "leftthumbstickright") buttonCode = KEY_BUTTON_LEFT_THUMB_STICK_RIGHT;
+ else if (strButton == "rightthumbstick") buttonCode = KEY_BUTTON_RIGHT_THUMB_STICK;
+ else if (strButton == "rightthumbstickup") buttonCode = KEY_BUTTON_RIGHT_THUMB_STICK_UP;
+ else if (strButton =="rightthumbstickdown") buttonCode = KEY_BUTTON_RIGHT_THUMB_STICK_DOWN;
+ else if (strButton == "rightthumbstickleft") buttonCode = KEY_BUTTON_RIGHT_THUMB_STICK_LEFT;
+ else if (strButton == "rightthumbstickright") buttonCode = KEY_BUTTON_RIGHT_THUMB_STICK_RIGHT;
+ else if (strButton == "lefttrigger") buttonCode = KEY_BUTTON_LEFT_TRIGGER;
+ else if (strButton == "righttrigger") buttonCode = KEY_BUTTON_RIGHT_TRIGGER;
+ else if (strButton == "leftanalogtrigger") buttonCode = KEY_BUTTON_LEFT_ANALOG_TRIGGER;
+ else if (strButton == "rightanalogtrigger") buttonCode = KEY_BUTTON_RIGHT_ANALOG_TRIGGER;
+ else if (strButton == "dpadleft") buttonCode = KEY_BUTTON_DPAD_LEFT;
+ else if (strButton == "dpadright") buttonCode = KEY_BUTTON_DPAD_RIGHT;
+ else if (strButton == "dpadup") buttonCode = KEY_BUTTON_DPAD_UP;
+ else if (strButton == "dpaddown") buttonCode = KEY_BUTTON_DPAD_DOWN;
+ else CLog::Log(LOGERROR, "Gamepad Translator: Can't find button %s", strButton.c_str());
+ return buttonCode;
+}
+
+uint32_t CButtonTranslator::TranslateRemoteString(const char *szButton)
+{
+ if (!szButton)
+ return 0;
+ uint32_t buttonCode = 0;
+ std::string strButton = szButton;
+ StringUtils::ToLower(strButton);
+ if (strButton == "left") buttonCode = XINPUT_IR_REMOTE_LEFT;
+ else if (strButton =="right") buttonCode = XINPUT_IR_REMOTE_RIGHT;
+ else if (strButton =="up") buttonCode = XINPUT_IR_REMOTE_UP;
+ else if (strButton == "down") buttonCode = XINPUT_IR_REMOTE_DOWN;
+ else if (strButton == "select") buttonCode = XINPUT_IR_REMOTE_SELECT;
+ else if (strButton == "back") buttonCode = XINPUT_IR_REMOTE_BACK;
+ else if (strButton == "menu") buttonCode = XINPUT_IR_REMOTE_MENU;
+ else if (strButton == "info") buttonCode = XINPUT_IR_REMOTE_INFO;
+ else if (strButton == "display") buttonCode = XINPUT_IR_REMOTE_DISPLAY;
+ else if (strButton == "title") buttonCode = XINPUT_IR_REMOTE_TITLE;
+ else if (strButton == "play") buttonCode = XINPUT_IR_REMOTE_PLAY;
+ else if (strButton == "pause") buttonCode = XINPUT_IR_REMOTE_PAUSE;
+ else if (strButton == "reverse") buttonCode = XINPUT_IR_REMOTE_REVERSE;
+ else if (strButton == "forward") buttonCode = XINPUT_IR_REMOTE_FORWARD;
+ else if (strButton == "skipplus") buttonCode = XINPUT_IR_REMOTE_SKIP_PLUS;
+ else if (strButton == "skipminus") buttonCode = XINPUT_IR_REMOTE_SKIP_MINUS;
+ else if (strButton == "stop") buttonCode = XINPUT_IR_REMOTE_STOP;
+ else if (strButton == "zero") buttonCode = XINPUT_IR_REMOTE_0;
+ else if (strButton == "one") buttonCode = XINPUT_IR_REMOTE_1;
+ else if (strButton == "two") buttonCode = XINPUT_IR_REMOTE_2;
+ else if (strButton == "three") buttonCode = XINPUT_IR_REMOTE_3;
+ else if (strButton == "four") buttonCode = XINPUT_IR_REMOTE_4;
+ else if (strButton == "five") buttonCode = XINPUT_IR_REMOTE_5;
+ else if (strButton == "six") buttonCode = XINPUT_IR_REMOTE_6;
+ else if (strButton == "seven") buttonCode = XINPUT_IR_REMOTE_7;
+ else if (strButton == "eight") buttonCode = XINPUT_IR_REMOTE_8;
+ else if (strButton == "nine") buttonCode = XINPUT_IR_REMOTE_9;
+ // additional keys from the media center extender for xbox remote
+ else if (strButton == "power") buttonCode = XINPUT_IR_REMOTE_POWER;
+ else if (strButton == "mytv") buttonCode = XINPUT_IR_REMOTE_MY_TV;
+ else if (strButton == "mymusic") buttonCode = XINPUT_IR_REMOTE_MY_MUSIC;
+ else if (strButton == "mypictures") buttonCode = XINPUT_IR_REMOTE_MY_PICTURES;
+ else if (strButton == "myvideo") buttonCode = XINPUT_IR_REMOTE_MY_VIDEOS;
+ else if (strButton == "record") buttonCode = XINPUT_IR_REMOTE_RECORD;
+ else if (strButton == "start") buttonCode = XINPUT_IR_REMOTE_START;
+ else if (strButton == "volumeplus") buttonCode = XINPUT_IR_REMOTE_VOLUME_PLUS;
+ else if (strButton == "volumeminus") buttonCode = XINPUT_IR_REMOTE_VOLUME_MINUS;
+ else if (strButton == "channelplus") buttonCode = XINPUT_IR_REMOTE_CHANNEL_PLUS;
+ else if (strButton == "channelminus") buttonCode = XINPUT_IR_REMOTE_CHANNEL_MINUS;
+ else if (strButton == "pageplus") buttonCode = XINPUT_IR_REMOTE_CHANNEL_PLUS;
+ else if (strButton == "pageminus") buttonCode = XINPUT_IR_REMOTE_CHANNEL_MINUS;
+ else if (strButton == "mute") buttonCode = XINPUT_IR_REMOTE_MUTE;
+ else if (strButton == "recordedtv") buttonCode = XINPUT_IR_REMOTE_RECORDED_TV;
+ else if (strButton == "guide") buttonCode = XINPUT_IR_REMOTE_GUIDE;
+ else if (strButton == "livetv") buttonCode = XINPUT_IR_REMOTE_LIVE_TV;
+ else if (strButton == "liveradio") buttonCode = XINPUT_IR_REMOTE_LIVE_RADIO;
+ else if (strButton == "epgsearch") buttonCode = XINPUT_IR_REMOTE_EPG_SEARCH;
+ else if (strButton == "star") buttonCode = XINPUT_IR_REMOTE_STAR;
+ else if (strButton == "hash") buttonCode = XINPUT_IR_REMOTE_HASH;
+ else if (strButton == "clear") buttonCode = XINPUT_IR_REMOTE_CLEAR;
+ else if (strButton == "enter") buttonCode = XINPUT_IR_REMOTE_ENTER;
+ else if (strButton == "xbox") buttonCode = XINPUT_IR_REMOTE_DISPLAY; // same as display
+ else if (strButton == "playlist") buttonCode = XINPUT_IR_REMOTE_PLAYLIST;
+ else if (strButton == "guide") buttonCode = XINPUT_IR_REMOTE_GUIDE;
+ else if (strButton == "teletext") buttonCode = XINPUT_IR_REMOTE_TELETEXT;
+ else if (strButton == "red") buttonCode = XINPUT_IR_REMOTE_RED;
+ else if (strButton == "green") buttonCode = XINPUT_IR_REMOTE_GREEN;
+ else if (strButton == "yellow") buttonCode = XINPUT_IR_REMOTE_YELLOW;
+ else if (strButton == "blue") buttonCode = XINPUT_IR_REMOTE_BLUE;
+ else if (strButton == "subtitle") buttonCode = XINPUT_IR_REMOTE_SUBTITLE;
+ else if (strButton == "language") buttonCode = XINPUT_IR_REMOTE_LANGUAGE;
+ else if (strButton == "eject") buttonCode = XINPUT_IR_REMOTE_EJECT;
+ else if (strButton == "contentsmenu") buttonCode = XINPUT_IR_REMOTE_CONTENTS_MENU;
+ else if (strButton == "rootmenu") buttonCode = XINPUT_IR_REMOTE_ROOT_MENU;
+ else if (strButton == "topmenu") buttonCode = XINPUT_IR_REMOTE_TOP_MENU;
+ else if (strButton == "dvdmenu") buttonCode = XINPUT_IR_REMOTE_DVD_MENU;
+ else if (strButton == "print") buttonCode = XINPUT_IR_REMOTE_PRINT;
+ else CLog::Log(LOGERROR, "Remote Translator: Can't find button %s", strButton.c_str());
+ return buttonCode;
+}
+
+uint32_t CButtonTranslator::TranslateUniversalRemoteString(const char *szButton)
+{
+ if (!szButton || strlen(szButton) < 4 || strnicmp(szButton, "obc", 3))
+ return 0;
+ const char *szCode = szButton + 3;
+ // Button Code is 255 - OBC (Original Button Code) of the button
+ uint32_t buttonCode = 255 - atol(szCode);
+ if (buttonCode > 255)
+ buttonCode = 0;
+ return buttonCode;
+}
+
+uint32_t CButtonTranslator::TranslateKeyboardString(const char *szButton)
+{
+ uint32_t buttonCode = 0;
+ XBMCKEYTABLE keytable;
+
+ // Look up the key name
+ if (KeyTableLookupName(szButton, &keytable))
+ {
+ buttonCode = keytable.vkey;
+ }
+
+ // The lookup failed i.e. the key name wasn't found
+ else
+ {
+ CLog::Log(LOGERROR, "Keyboard Translator: Can't find button %s", szButton);
+ }
+
+ buttonCode |= KEY_VKEY;
+
+ return buttonCode;
+}
+
+uint32_t CButtonTranslator::TranslateKeyboardButton(TiXmlElement *pButton)
+{
+ uint32_t button_id = 0;
+ const char *szButton = pButton->Value();
+
+ if (!szButton)
+ return 0;
+ const std::string strKey = szButton;
+ if (strKey == "key")
+ {
+ std::string strID;
+ if (pButton->QueryValueAttribute("id", &strID) == TIXML_SUCCESS)
+ {
+ const char *str = strID.c_str();
+ char *endptr;
+ long int id = strtol(str, &endptr, 0);
+ if (endptr - str != (int)strlen(str) || id <= 0 || id > 0x00FFFFFF)
+ CLog::Log(LOGDEBUG, "%s - invalid key id %s", __FUNCTION__, strID.c_str());
+ else
+ button_id = (uint32_t) id;
+ }
+ else
+ CLog::Log(LOGERROR, "Keyboard Translator: `key' button has no id");
+ }
+ else
+ button_id = TranslateKeyboardString(szButton);
+
+ // Process the ctrl/shift/alt modifiers
+ std::string strMod;
+ if (pButton->QueryValueAttribute("mod", &strMod) == TIXML_SUCCESS)
+ {
+ StringUtils::ToLower(strMod);
+
+ vector<string> modArray = StringUtils::Split(strMod, ",");
+ for (vector<string>::const_iterator i = modArray.begin(); i != modArray.end(); ++i)
+ {
+ string substr = *i;
+ StringUtils::Trim(substr);
+
+ if (substr == "ctrl" || substr == "control")
+ button_id |= CKey::MODIFIER_CTRL;
+ else if (substr == "shift")
+ button_id |= CKey::MODIFIER_SHIFT;
+ else if (substr == "alt")
+ button_id |= CKey::MODIFIER_ALT;
+ else if (substr == "super" || substr == "win")
+ button_id |= CKey::MODIFIER_SUPER;
+ else if (substr == "meta" || substr == "cmd")
+ button_id |= CKey::MODIFIER_META;
+ else
+ CLog::Log(LOGERROR, "Keyboard Translator: Unknown key modifier %s in %s", substr.c_str(), strMod.c_str());
+ }
+ }
+
+ return button_id;
+}
+
+uint32_t CButtonTranslator::TranslateAppCommand(const char *szButton)
+{
+#ifdef TARGET_WINDOWS
+ std::string strAppCommand = szButton;
+ StringUtils::ToLower(strAppCommand);
+
+ for (int i = 0; i < ARRAY_SIZE(appcommands); i++)
+ if (strAppCommand == appcommands[i].name)
+ return appcommands[i].action | KEY_APPCOMMAND;
+
+ CLog::Log(LOGERROR, "%s: Can't find appcommand %s", __FUNCTION__, szButton);
+#endif
+
+ return 0;
+}
+
+uint32_t CButtonTranslator::TranslateMouseCommand(TiXmlElement *pButton)
+{
+ uint32_t buttonId = 0;
+
+ if (pButton)
+ {
+ std::string szKey = pButton->ValueStr();
+ if (!szKey.empty())
+ {
+ StringUtils::ToLower(szKey);
+ for (unsigned int i = 0; i < ARRAY_SIZE(mousekeys); i++)
+ {
+ if (szKey == mousekeys[i].name)
+ {
+ buttonId = mousekeys[i].action;
+ break;
+ }
+ }
+ if (!buttonId)
+ {
+ CLog::Log(LOGERROR, "Unknown mouse action (%s), skipping", pButton->Value());
+ }
+ else
+ {
+ int id = 0;
+ if ((pButton->QueryIntAttribute("id", &id) == TIXML_SUCCESS) && id>=0 && id<=4)
+ {
+ buttonId += id;
+ }
+ }
+ }
+ }
+
+ return buttonId;
+}
+
+void CButtonTranslator::Clear()
+{
+ m_translatorMap.clear();
+#if defined(HAS_LIRC) || defined(HAS_IRSERVERSUITE)
+ ClearLircButtonMapEntries();
+ lircRemotesMap.clear();
+#endif
+
+#if defined(HAS_SDL_JOYSTICK) || defined(HAS_EVENT_SERVER)
+ m_joystickButtonMap.clear();
+ m_joystickAxisMap.clear();
+ m_joystickHatMap.clear();
+#endif
+
+ m_Loaded = false;
+}
+
+uint32_t CButtonTranslator::TranslateTouchCommand(TiXmlElement *pButton, CButtonAction &action)
+{
+ const char *szButton = pButton->Value();
+ if (szButton == NULL || pButton->FirstChild() == NULL)
+ return ACTION_NONE;
+
+ const char *szAction = pButton->FirstChild()->Value();
+ if (szAction == NULL)
+ return ACTION_NONE;
+
+ std::string strTouchCommand = szButton;
+ StringUtils::ToLower(strTouchCommand);
+
+ const char *attrVal = pButton->Attribute("direction");
+ if (attrVal != NULL)
+ strTouchCommand += attrVal;
+
+ uint32_t actionId = ACTION_NONE;
+ for (unsigned int i = 0; i < ARRAY_SIZE(touchcommands); i++)
+ {
+ if (strTouchCommand == touchcommands[i].name)
+ {
+ actionId = touchcommands[i].action;
+ break;
+ }
+ }
+
+ if (actionId <= ACTION_NONE)
+ {
+ CLog::Log(LOGERROR, "%s: Can't find touch command %s", __FUNCTION__, szButton);
+ return ACTION_NONE;
+ }
+
+ attrVal = pButton->Attribute("pointers");
+ if (attrVal != NULL)
+ {
+ int pointers = (int)strtol(attrVal, NULL, 0);
+ if (pointers >= 1)
+ actionId += pointers - 1;
+ }
+
+ action.strID = szAction;
+ if (!TranslateActionString(szAction, action.id) || action.id <= ACTION_NONE)
+ return ACTION_NONE;
+
+ return actionId | KEY_TOUCH;
+}
+
+void CButtonTranslator::MapTouchActions(int windowID, TiXmlNode *pTouch)
+{
+ if (pTouch == NULL)
+ return;
+
+ buttonMap map;
+ // check if there already is a touch map for the window ID
+ std::map<int, buttonMap>::iterator it = m_touchMap.find(windowID);
+ if (it != m_touchMap.end())
+ {
+ // get the existing touch map and remove it from the window mapping
+ // as it will be inserted later on
+ map = it->second;
+ m_touchMap.erase(it);
+ }
+
+ uint32_t actionId = 0;
+ TiXmlElement *pTouchElem = pTouch->ToElement();
+ if (pTouchElem == NULL)
+ return;
+
+ TiXmlElement *pButton = pTouchElem->FirstChildElement();
+ while (pButton != NULL)
+ {
+ CButtonAction action;
+ actionId = TranslateTouchCommand(pButton, action);
+ if (actionId > 0)
+ {
+ // check if there already is a mapping for the parsed action
+ // and remove it if necessary
+ buttonMap::iterator actionIt = map.find(actionId);
+ if (actionIt != map.end())
+ map.erase(actionIt);
+
+ map.insert(std::make_pair(actionId, action));
+ }
+
+ pButton = pButton->NextSiblingElement();
+ }
+
+ // add the modified touch map with the window ID
+ if (!map.empty())
+ m_touchMap.insert(std::pair<int, buttonMap>(windowID, map));
+}
+
+int CButtonTranslator::GetTouchActionCode(int window, int action)
+{
+ std::map<int, buttonMap>::const_iterator windowIt = m_touchMap.find(window);
+ if (windowIt == m_touchMap.end())
+ return ACTION_NONE;
+
+ buttonMap::const_iterator touchIt = windowIt->second.find(action);
+ if (touchIt == windowIt->second.end())
+ return ACTION_NONE;
+
+ return touchIt->second.id;
+}