|
MythTV
0.26-pre
|
00001 /*---------------------------------------------------------------------------- 00002 ** jsmenu.cpp 00003 ** 00004 ** Description: 00005 ** Set of functions to generate key events based on 00006 ** input from a Joystick. 00007 ** 00008 ** Original Copyright 2004 by Jeremy White <jwhite@whitesen.org> 00009 ** 00010 ** License: 00011 ** This program is free software; you can redistribute it 00012 ** and/or modify it under the terms of the GNU General 00013 ** Public License as published bythe Free Software Foundation; 00014 ** either version 2, or (at your option) 00015 ** any later version. 00016 ** 00017 ** This program is distributed in the hope that it will be useful, 00018 ** but WITHOUT ANY WARRANTY; without even the implied warranty of 00019 ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 00020 ** GNU General Public License for more details. 00021 ** 00022 **--------------------------------------------------------------------------*/ 00023 00024 // Own header 00025 #include "jsmenu.h" 00026 00027 // QT headers 00028 #include <QCoreApplication> 00029 #include <QEvent> 00030 #include <QKeySequence> 00031 #include <QTextStream> 00032 #include <QStringList> 00033 00034 // C/C++ headers 00035 #include <cstdio> 00036 #include <cerrno> 00037 #include <sys/wait.h> 00038 #include <sys/types.h> 00039 #include <unistd.h> 00040 #include <fcntl.h> 00041 00042 // Kernel joystick header 00043 #include <linux/joystick.h> 00044 00045 // Myth headers 00046 #include "mythlogging.h" 00047 00048 // Mythui headers 00049 #include "jsmenuevent.h" 00050 00051 #define LOC QString("JoystickMenuThread: ") 00052 00053 JoystickMenuThread::JoystickMenuThread(QObject *main_window) 00054 : MThread("JoystickMenu"), 00055 m_mainWindow(main_window), m_devicename(""), 00056 m_fd(-1), m_buttonCount(0), 00057 m_axesCount(0), m_buttons(NULL), 00058 m_axes(NULL), m_bStop(false) 00059 { 00060 } 00061 00062 JoystickMenuThread::~JoystickMenuThread() 00063 { 00064 if (m_fd != -1) 00065 { 00066 close(m_fd); 00067 m_fd = -1; 00068 } 00069 00070 delete [] m_axes; 00071 m_axes = NULL; 00072 00073 delete [] m_buttons; 00074 m_buttons = NULL; 00075 } 00076 00080 int JoystickMenuThread::Init(QString &config_file) 00081 { 00082 int rc; 00083 00084 /*------------------------------------------------------------------------ 00085 ** Read the config file 00086 **----------------------------------------------------------------------*/ 00087 rc = ReadConfig(config_file); 00088 if (rc) 00089 { 00090 LOG(VB_GENERAL, LOG_ERR, LOC + 00091 QString("Joystick disabled - Failed to read %1") .arg(config_file)); 00092 return(rc); 00093 } 00094 00095 /*------------------------------------------------------------------------ 00096 ** Open the joystick device, retrieve basic info 00097 **----------------------------------------------------------------------*/ 00098 m_fd = open(qPrintable(m_devicename), O_RDONLY); 00099 if (m_fd == -1) 00100 { 00101 LOG(VB_GENERAL, LOG_ERR, LOC + 00102 QString("Joystick disabled - Failed to open device %1") 00103 .arg(m_devicename)); 00104 return -1; 00105 } 00106 00107 rc = ioctl(m_fd, JSIOCGAXES, &m_axesCount); 00108 if (rc == -1) 00109 { 00110 LOG(VB_GENERAL, LOG_ERR, LOC + 00111 "Joystick disabled - ioctl JSIOCGAXES failed"); 00112 return(rc); 00113 } 00114 00115 rc = ioctl(m_fd, JSIOCGBUTTONS, &m_buttonCount); 00116 if (rc == -1) 00117 { 00118 LOG(VB_GENERAL, LOG_ERR, LOC + 00119 "Joystick disabled - ioctl JSIOCGBUTTONS failed"); 00120 return(rc); 00121 } 00122 00123 /*------------------------------------------------------------------------ 00124 ** Allocate the arrays in which we track button and axis status 00125 **----------------------------------------------------------------------*/ 00126 m_buttons = new int[m_buttonCount]; 00127 memset(m_buttons, '\0', m_buttonCount * sizeof(*m_buttons)); 00128 00129 m_axes = new int[m_axesCount]; 00130 memset(m_axes, '\0', m_axesCount * sizeof(*m_axes)); 00131 00132 LOG(VB_GENERAL, LOG_INFO, LOC + 00133 QString("Initialization of %1 succeeded using config file %2") 00134 .arg(m_devicename) .arg(config_file)); 00135 return 0; 00136 } 00137 00151 int JoystickMenuThread::ReadConfig(QString config_file) 00152 { 00153 FILE *fp; 00154 00155 fp = fopen(qPrintable(config_file), "r"); 00156 if (!fp) 00157 return(-1); 00158 00159 QTextStream istream(fp); 00160 for (int line = 1; ! istream.atEnd(); line++) 00161 { 00162 QString rawline = istream.readLine(); 00163 QString simple_line = rawline.simplified(); 00164 if (simple_line.isEmpty() || simple_line.startsWith('#')) 00165 continue; 00166 00167 QStringList tokens = simple_line.split(" "); 00168 if (tokens.count() < 1) 00169 continue; 00170 00171 QString firstTok = tokens[0].toLower(); 00172 00173 if (firstTok.startsWith("devicename") && tokens.count() == 2) 00174 m_devicename = tokens[1]; 00175 else if (firstTok.startsWith("button") && tokens.count() == 3) 00176 m_map.AddButton(tokens[1].toInt(), tokens[2]); 00177 else if (firstTok.startsWith("axis") && tokens.count() == 5) 00178 m_map.AddAxis(tokens[1].toInt(), tokens[2].toInt(), 00179 tokens[3].toInt(), tokens[4]); 00180 else if (firstTok.startsWith("chord") && tokens.count() == 4) 00181 m_map.AddButton(tokens[2].toInt(), tokens[3], tokens[1].toInt()); 00182 else 00183 LOG(VB_GENERAL, LOG_ERR, LOC + 00184 QString("ReadConfig(%1) unrecognized or malformed line \"%2\" ") 00185 .arg(line) .arg(rawline)); 00186 } 00187 00188 fclose(fp); 00189 return(0); 00190 } 00191 00192 00197 void JoystickMenuThread::run(void) 00198 { 00199 RunProlog(); 00200 00201 int rc; 00202 00203 fd_set readfds; 00204 struct js_event js; 00205 struct timeval timeout; 00206 00207 while (!m_bStop) 00208 { 00209 00210 /*-------------------------------------------------------------------- 00211 ** Wait for activity from the joy stick (we wait a configurable 00212 ** poll time) 00213 **------------------------------------------------------------------*/ 00214 FD_ZERO(&readfds); 00215 FD_SET(m_fd, &readfds); 00216 00217 // the maximum time select() should wait 00218 timeout.tv_sec = 0; 00219 timeout.tv_usec = 100000; 00220 00221 rc = select(m_fd + 1, &readfds, NULL, NULL, &timeout); 00222 if (rc == -1) 00223 { 00224 /*---------------------------------------------------------------- 00225 ** TODO: In theory, we could recover from file errors 00226 ** (what happens when we unplug a joystick?) 00227 **--------------------------------------------------------------*/ 00228 LOG(VB_GENERAL, LOG_ERR, "select: " + ENO); 00229 return; 00230 } 00231 00232 if (rc == 1) 00233 { 00234 /*---------------------------------------------------------------- 00235 ** Read a joystick event 00236 **--------------------------------------------------------------*/ 00237 rc = read(m_fd, &js, sizeof(js)); 00238 if (rc != sizeof(js)) 00239 { 00240 LOG(VB_GENERAL, LOG_ERR, "error reading js:" + ENO); 00241 return; 00242 } 00243 00244 /*---------------------------------------------------------------- 00245 ** Events sent with the JS_EVENT_INIT flag are always sent 00246 ** right after you open the joy stick; they are useful 00247 ** for learning the initial state of buttons and axes 00248 **--------------------------------------------------------------*/ 00249 if (js.type & JS_EVENT_INIT) 00250 { 00251 if (js.type & JS_EVENT_BUTTON && js.number < m_buttonCount) 00252 m_buttons[js.number] = js.value; 00253 00254 if (js.type & JS_EVENT_AXIS && js.number < m_axesCount) 00255 m_axes[js.number] = js.value; 00256 } 00257 else 00258 { 00259 /*------------------------------------------------------------ 00260 ** Record new button states and look for triggers 00261 ** that would make us send a key. 00262 ** Things are a little tricky here; for buttons, we 00263 ** only act on button up events, not button down 00264 ** (this lets us implement the chord function). 00265 ** For axes, we only register a change if the 00266 ** Joystick moves into the specified range 00267 ** (that way, we only get one event per joystick 00268 ** motion). 00269 **----------------------------------------------------------*/ 00270 if (js.type & JS_EVENT_BUTTON && js.number < m_buttonCount) 00271 { 00272 if (js.value == 0 && m_buttons[js.number] == 1) 00273 ButtonUp(js.number); 00274 00275 m_buttons[js.number] = js.value; 00276 } 00277 00278 if (js.type & JS_EVENT_AXIS && js.number < m_axesCount) 00279 { 00280 AxisChange(js.number, js.value); 00281 m_axes[js.number] = js.value; 00282 } 00283 00284 } 00285 00286 } 00287 00288 } 00289 00290 RunEpilog(); 00291 } 00292 00296 void JoystickMenuThread::EmitKey(QString code) 00297 { 00298 QKeySequence a(code); 00299 00300 int keycode = 0; 00301 00302 // Send a dummy keycode if we couldn't convert the key sequence. 00303 // This is done so the main code can output a warning for bad 00304 // mappings. 00305 if (!a.count()) 00306 QCoreApplication::postEvent(m_mainWindow, new JoystickKeycodeEvent(code, 00307 keycode, true)); 00308 00309 for (unsigned int i = 0; i < a.count(); i++) 00310 { 00311 keycode = a[i]; 00312 00313 QCoreApplication::postEvent(m_mainWindow, new JoystickKeycodeEvent(code, 00314 keycode, true)); 00315 QCoreApplication::postEvent(m_mainWindow, new JoystickKeycodeEvent(code, 00316 keycode, false)); 00317 } 00318 } 00319 00320 00327 void JoystickMenuThread::ButtonUp(int button) 00328 { 00329 /*------------------------------------------------------------------------ 00330 ** Process chords first 00331 **----------------------------------------------------------------------*/ 00332 JoystickMap::button_map_t::const_iterator bmap; 00333 for (bmap = m_map.buttonMap().begin(); bmap != m_map.buttonMap().end(); 00334 ++bmap) 00335 { 00336 if (button == bmap->button && bmap->chord != -1 00337 && m_buttons[bmap->chord] == 1) 00338 { 00339 EmitKey(bmap->keystring); 00340 m_buttons[bmap->chord] = 0; 00341 return; 00342 } 00343 } 00344 00345 /*------------------------------------------------------------------------ 00346 ** Process everything else 00347 **----------------------------------------------------------------------*/ 00348 for (bmap = m_map.buttonMap().begin(); bmap != m_map.buttonMap().end(); 00349 ++bmap) 00350 { 00351 if (button == bmap->button && bmap->chord == -1) 00352 EmitKey(bmap->keystring); 00353 } 00354 } 00355 00359 void JoystickMenuThread::AxisChange(int axis, int value) 00360 { 00361 JoystickMap::axis_map_t::const_iterator amap; 00362 for (amap = m_map.axisMap().begin(); amap < m_map.axisMap().end(); ++amap) 00363 { 00364 if (axis == amap->axis) 00365 { 00366 /* If we're currently outside the range, and the move is 00367 ** into the range, then we trigger */ 00368 if (m_axes[axis] < amap->from || m_axes[axis] > amap->to) 00369 if (value >= amap->from && value <= amap->to) 00370 EmitKey(amap->keystring); 00371 } 00372 } 00373 }
1.7.6.1