ckb-next  v0.2.8 at branch master
ckb-next driver for corsair devices
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
main.cpp
Go to the documentation of this file.
1 #include "mainwindow.h"
2 #include <QApplication>
3 #include <QDateTime>
4 #include <QSharedMemory>
5 #include <QCommandLineParser>
6 #include <cstring>
7 #include <unistd.h>
8 #include <signal.h>
9 #include <errno.h>
10 
11 QSharedMemory appShare("ckb");
12 
13 #ifdef Q_OS_MACX
14 // App Nap is an OSX feature designed to save power by putting background apps to sleep.
15 // However, it interferes with ckb's animations, so we want it off
16 extern "C" void disableAppNap();
17 #endif
18 
19 // Shared memory sizes
20 #define SHMEM_SIZE 128 * 1024
21 #define SHMEM_SIZE_V015 16
22 
23 // Command line options
31 };
32 
41 CommandLineParseResults parseCommandLine(QCommandLineParser &parser, QString *errorMessage) {
42  // setup parser to interpret -abc as -a -b -c
43  parser.setSingleDashWordOptionMode(QCommandLineParser::ParseAsCompactedShortOptions);
44 
45  /* add command line options */
46  // add -v, --version
47  const QCommandLineOption versionOption = parser.addVersionOption();
48  // add -h, --help (/? on windows)
49  const QCommandLineOption helpOption = parser.addHelpOption();
50  // add -b, --background
51  const QCommandLineOption backgroundOption(QStringList() << "b" << "background",
52  "Starts in background, without displaying the main window.");
53  parser.addOption(backgroundOption);
54  // add -c, --close
55  const QCommandLineOption closeOption(QStringList() << "c" << "close",
56  "Causes already running instance (if any) to exit.");
57  parser.addOption(closeOption);
58 
59  /* parse arguments */
60  if (!parser.parse(QCoreApplication::arguments())) {
61  // set error, if there already are some
62  *errorMessage = parser.errorText();
63  return CommandLineError;
64  }
65 
66  /* return requested operation/setup */
67  if (parser.isSet(versionOption)) {
68  // print version and exit
70  }
71 
72  if (parser.isSet(helpOption)) {
73  // print help and exit
75  }
76 
77  if (parser.isSet(backgroundOption)) {
78  // open application in background
79  return CommandLineBackground;
80  }
81 
82  if (parser.isSet(closeOption)) {
83  // close already running application instances, if any
84  return CommandLineClose;
85  }
86 
87  /* no explicit argument was passed */
88  return CommandLineOK;
89 };
90 
91 // Scan shared memory for an active PID
92 static bool pidActive(const QStringList& lines){
93  foreach(const QString& line, lines){
94  if(line.startsWith("PID ")){
95  bool ok;
96  pid_t pid;
97  // Valid PID found?
98  if((pid = line.split(" ")[1].toLong(&ok)) > 0 && ok){
99  // kill -0 does nothing to the application, but checks if it's running
100  return (kill(pid, 0) == 0 || errno != ESRCH);
101  }
102  }
103  }
104  // If the PID wasn't listed in the shmem, assume it is running
105  return true;
106 }
107 
108 // Check if the application is running. Optionally write something to its shared memory.
109 static bool isRunning(const char* command){
110  // Try to create shared memory (if successful, application was not already running)
111  if(!appShare.create(SHMEM_SIZE) || !appShare.lock()){
112  // Lock existing shared memory
113  if(!appShare.attach() || !appShare.lock())
114  return true;
115  bool running = false;
116  if(appShare.size() == SHMEM_SIZE_V015){
117  // Old shmem - no PID listed so assume the process is running, and print the command directly to the buffer
118  if(command){
119  void* data = appShare.data();
120  snprintf((char*)data, SHMEM_SIZE_V015, "%s", command);
121  }
122  running = true;
123  } else {
124  // New shmem. Scan the contents of the shared memory for a PID
125  QStringList lines = QString((const char*)appShare.constData()).split("\n");
126  if(pidActive(lines)){
127  running = true;
128  // Append the command
129  if(command){
130  lines.append(QString("Option ") + command);
131  QByteArray newMem = lines.join("\n").left(SHMEM_SIZE).toLatin1();
132  // Copy the NUL byte as well as the string
133  memcpy(appShare.data(), newMem.constData(), newMem.length() + 1);
134  }
135  }
136  }
137  if(running){
138  appShare.unlock();
139  return true;
140  }
141  }
142  // Not already running. Initialize the shared memory with our PID
143  snprintf((char*)appShare.data(), appShare.size(), "PID %ld", (long)getpid());
144  appShare.unlock();
145  return false;
146 }
147 
148 int main(int argc, char *argv[]){
149  // Setup main application
150  QApplication a(argc, argv);
151 
152  // Setup names and versions
153  QCoreApplication::setOrganizationName("ckb");
154  QCoreApplication::setApplicationVersion(CKB_VERSION_STR);
155  QCoreApplication::setApplicationName("ckb");
156 
157  // Setup argument parser
158  QCommandLineParser parser;
159  QString errorMessage;
160  parser.setApplicationDescription("Open Source Corsair Input Device Driver for Linux and OSX.");
161  bool background = 0;
162 
163  // Although the daemon runs as root, the GUI needn't and shouldn't be, as it has the potential to corrupt settings data.
164  if(getuid() == 0){
165  printf("The ckb GUI should not be run as root.\n");
166  return 0;
167  }
168 
169  // Seed the RNG for UsbIds
170  qsrand(QDateTime::currentMSecsSinceEpoch());
171 #ifdef Q_OS_MACX
172  disableAppNap();
173 
174  FILE *fp = fopen("/tmp/ckb", "w");
175  fprintf(fp, "%d", getpid());
176  fclose(fp);
177 #endif
178 #if QT_VERSION >= QT_VERSION_CHECK(5, 3, 0)
179  // Enable HiDPI support
180  qApp->setAttribute(Qt::AA_UseHighDpiPixmaps);
181 #endif
182 
183  // Parse arguments
184  switch (parseCommandLine(parser, &errorMessage)) {
185  case CommandLineOK:
186  // If launched with no argument
187  break;
188  case CommandLineError:
189  fputs(qPrintable(errorMessage), stderr);
190  fputs("\n\n", stderr);
191  fputs(qPrintable(parser.helpText()), stderr);
192  return 1;
194  // If launched with --version, print version info and then quit
195  printf("%s %s\n", qPrintable(QCoreApplication::applicationName()),
196  qPrintable(QCoreApplication::applicationVersion()));
197  return 0;
199  // If launched with --help, print help and then quit
200  parser.showHelp();
201  return 0;
202  case CommandLineClose:
203  // If launched with --close, kill existing app
204  if (isRunning("Close"))
205  printf("Asking existing instance to close.\n");
206  else
207  printf("ckb is not running.\n");
208  return 0;
210  // If launched with --background, launch in background
211  background = 1;
212  break;
213  }
214 
215  // Launch in background if requested, or if re-launching a previous session
216  if(qApp->isSessionRestored())
217  background = 1;
218  if(isRunning(background ? 0 : "Open")){
219  printf("ckb is already running. Exiting.\n");
220  return 0;
221  }
222  MainWindow w;
223  if(!background)
224  w.show();
225 
226  return a.exec();
227 }
QSharedMemory appShare("ckb")
void disableAppNap()
Definition: media_mac.m:34
#define SHMEM_SIZE_V015
Definition: main.cpp:21
static bool isRunning(const char *command)
Definition: main.cpp:109
CommandLineParseResults
Definition: main.cpp:24
#define SHMEM_SIZE
Definition: main.cpp:20
int main(int argc, char *argv[])
Definition: main.cpp:148
CommandLineParseResults parseCommandLine(QCommandLineParser &parser, QString *errorMessage)
parseCommandLine - Setup options and parse command line arguments.
Definition: main.cpp:41
static bool pidActive(const QStringList &lines)
Definition: main.cpp:92