MythTV  0.26-pre
jsmenu.cpp
Go to the documentation of this file.
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 }
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends