Files
thunderpad/src/main.cpp

272 lines
8.5 KiB
C++

#define MAIN
//to create a qapplication
#include <QFile>
//for ouput when there is no GUI going
#include <stdio.h>
#include <unistd.h>
//to create and handle signals for various events
#include <signal.h>
//to load layouts
#include "layout.h"
//to give event.h the current X11 display
#include "event.h"
//to update the joystick device list
#include "device.h"
//to produce errors!
#include "error.h"
#include <QX11Info>
#include <QSystemTrayIcon>
#include <QPointer>
#include <getopt.h>
//for making universally available variables
QHash<int, JoyPad*> available; //to device.h
QHash<int, JoyPad*> joypads; //to device.h
//variables needed in various functions in this file
QPointer<LayoutManager> layoutManagerPtr;
//signal handler for SIGIO
//SIGIO means that a new layout should be loaded. It is saved in
// ~/.qjoypad/layout, where the last used layout is put.
void catchSIGIO( int sig )
{
if (layoutManagerPtr) layoutManagerPtr->load();
//remember to catch this signal again next time.
signal( sig, catchSIGIO );
}
//signal handler for SIGUSR1
//SIGUSR1 means that we should update the available joystick device list.
void catchSIGUSR1( int sig ) {
//buildJoyDevices();
if (layoutManagerPtr) layoutManagerPtr->updateJoyDevs();
//remember to catch this signal again next time.
signal( sig, catchSIGUSR1 );
}
/* A new feature to add? We'll see.
void catchSIGUSR2( int sig ) {
lm->trayClick();
signal( sig, catchSIGUSR2 );
}
*/
int main( int argc, char **argv )
{
//create a new event loop. This will be captured by the QApplication
//when it gets created
QApplication a( argc, argv );
a.setQuitOnLastWindowClosed(false);
//where QJoyPad saves its settings!
const QString settingsDir(QDir::homePath() + "/.qjoypad3/");
//where to look for settings. If it does not exist, it will be created
QDir dir(settingsDir);
//the directory in wich the joystick devices are (e.g. "/dev/input")
QString devdir = DEVDIR;
//if there is no new directory and we can't make it, complain
if (!dir.exists() && !dir.mkdir(settingsDir)) {
printf("Couldn't create the QJoyPad save directory (%s)!", settingsDir.toStdString().c_str());
return 1;
}
//start out with no special layout.
QString layout;
//by default, we use a tray icon
bool useTrayIcon = true;
//this execution wasn't made to update the joystick device list.
bool update = false;
bool forceTrayIcon = false;
//parse command-line options
struct option long_options[] = {
{"help", no_argument, 0, 'h'},
{"device", required_argument, 0, 'd'},
{"force-tray", no_argument, 0, 't'},
{"notray", no_argument, 0, 'T'},
{"update", no_argument, 0, 'u'},
{0, 0, 0, 0 }
};
for (;;) {
int c = getopt_long(argc, argv, "hd:tTu", long_options, NULL);
if (c == -1)
break;
switch (c) {
case 'h':
printf("%s\n"
"Usage: %s [--device=\"/device/path\"] [--notray|--force-tray] [\"layout name\"]\n"
"\n"
"Options:\n"
" -h, --help Print this help message.\n"
" -d, --device=PATH Look for joystick devices in PATH. This should\n"
" be something like \"/dev/input\" if your game\n"
" devices are in /dev/input/js0, /dev/input/js1, etc.\n"
" -t, --force-tray Forece to use a system tray icon.\n"
" -T, --notray Do not use a system tray icon. This is useful for\n"
" window managers that don't support this feature.\n"
" -u, --update Force a running instance of QJoyPad to update its\n"
" list of devices and layouts.\n"
" \"layout name\" Load the given layout in an already running\n"
" instance of QJoyPad, or start QJoyPad using the\n"
" given layout.\n", NAME, argc > 0 ? argv[0] : "qjoypad");
return 0;
case 'd':
if (QFile::exists(optarg)) {
devdir = optarg;
}
else {
fprintf(stderr, "No such file or directory: %s\n", optarg);
return 1;
}
break;
case 'T':
useTrayIcon = false;
break;
case 't':
useTrayIcon = true;
forceTrayIcon = true;
break;
case 'u':
update = true;
break;
case '?':
fprintf(stderr,
"See `%s --help` for more information\n",
argc > 0 ? argv[0] : "qjoypad");
return 1;
}
}
if (optind < argc) {
layout = argv[optind ++];
if (optind < argc) {
fprintf(stderr,
"Too many arguments.\n"
"See `%s --help` for more information\n",
argc > 0 ? argv[0] : "qjoypad");
return 1;
}
}
//if the user specified a layout to use,
if (!layout.isEmpty())
{
//then we try to store that layout in the last-used layout spot, to be
//loaded by default.
QFile file( settingsDir + "layout");
if (file.open(QIODevice::WriteOnly))
{
QTextStream stream( &file );
stream << layout;
file.close();
}
}
//create a pid lock file.
QFile pidFile( "/tmp/qjoypad.pid" );
//if that file already exists, then qjoypad is already running!
if (pidFile.exists())
{
int pid = 0;
if (pidFile.open( QIODevice::ReadOnly ))
{
//try to get that pid...
QTextStream( &pidFile ) >> pid;
pidFile.close();
//if we can signal the pid (ie, if the process is active)
if (kill(pid,0) == 0)
{
//then prevent two instances from running at once.
//however, if we are setting the layout or updating the device
//list, this is not an error and we shouldn't make one!
if (layout.isEmpty() && update == false)
error("Instance Error","There is already a running instance of QJoyPad; please close\nthe old instance before starting a new one.");
else {
//if one of these is the case, send the approrpriate signal!
if (update == true)
kill(pid,SIGUSR1);
if (!layout.isEmpty())
kill(pid,SIGIO);
}
//and quit. We don't need two instances.
return 0;
}
}
}
//now we can try to create and write our pid to the lock file.
if (pidFile.open( QIODevice::WriteOnly ))
{
QTextStream( &pidFile ) << getpid();
pidFile.close();
}
if(forceTrayIcon) {
int sleepCounter = 0;
while(!QSystemTrayIcon::isSystemTrayAvailable()) {
sleep(1);
sleepCounter++;
if(sleepCounter > 20) {
printf("Error, we've waited more than 20 seconds, your sys tray probably isn't loading\n");
exit(1);
}
}
}
//capture the current display for event.h
display = QX11Info::display();
//create a new LayoutManager with a tray icon / floating icon, depending
//on the user's request
LayoutManager layoutManager(useTrayIcon,devdir,settingsDir);
layoutManagerPtr = &layoutManager;
//build the joystick device list for the first time,
//buildJoyDevices();
layoutManager.updateJoyDevs();
//load the last used layout (Or the one given as a command-line argument)
layoutManager.load();
//prepare the signal handlers
signal( SIGIO, catchSIGIO );
signal( SIGUSR1, catchSIGUSR1 );
// signal( SIGUSR2, catchSIGUSR2 );
//and run the program!
int result = a.exec();
//when everything is done, save the current layout for next time...
layoutManager.saveDefault();
//remove the lock file...
pidFile.remove();
//and terminate!
return result;
}