version 0.3.0
[expresskeys.git] / src-expresskeys / main_setup.c
1 /*
2  main_setup.c -- Support ExpressKeys & Touch Strips on a Wacom Intuos3 tablet.
3
4  Copyright (C) 2005-2006 - Mats Johannesson
5
6  This program is free software; you can redistribute it and/or modify
7  it under the terms of the GNU General Public License as published by
8  the Free Software Foundation; either version 2 of the License, or
9  (at your option) any later version.
10
11  This program is distributed in the hope that it will be useful,
12  but WITHOUT ANY WARRANTY; without even the implied warranty of
13  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  GNU General Public License for more details.
15
16  You should have received a copy of the GNU General Public License
17  along with this program; if not, write to the Free Software
18  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307, USA.
19 */
20
21 #include "globals.h"
22
23 int main (int argc, char *argv[])
24 {
25
26         our_prog_name = basename(argv[0]);
27
28         int len = 0;
29
30         struct program *p;
31         struct configstrings *hp;
32
33         FILE *fp = NULL;
34         FILE *errorfp = NULL;
35
36 /* Locate the home directory of the user running this program */
37
38         char *user_homedir;
39         if ((user_homedir = getenv("HOME")) == NULL) {
40                 exit_on_error(errorfp, "%s ERROR: Can not find your HOME directory!\n", our_prog_name, "");
41         }
42
43 /* Concatenate the home directory string with the string of our preferred
44    configuration file directory. The address to the whole string is then
45    copied to a global pointer, so we won't have to perform this part again */
46
47         char *total_config_dir_block;
48         len = strlen(user_homedir) + strlen(config_dir) + 1;
49         if ((total_config_dir_block = (char *)malloc(len)) == NULL) {
50                 exit_on_error(errorfp, "%s ERROR: Memory allocation trouble at stage 1!\n", our_prog_name, "");
51         }
52         sprintf(total_config_dir_block, "%s%s", user_homedir, config_dir);
53         total_config_dir = total_config_dir_block;
54
55 /* Concatenate the full path with the pid file name. Store address */
56
57         char *total_pid_file_block;
58         len = strlen(total_config_dir) + strlen(pid_file) + 1;
59         if ((total_pid_file_block = (char *)malloc(len)) == NULL) {
60                 exit_on_error(errorfp, "%s ERROR: Memory allocation trouble at stage 2!\n", our_prog_name, "");
61         }
62         sprintf(total_pid_file_block, "%s%s", total_config_dir, pid_file);
63         total_pid_file = total_pid_file_block;
64
65 /* Concatenate the full path with the error file name. Store address */
66
67         char *total_error_file_block;
68         len = strlen(total_config_dir) + strlen(error_file) + 1;
69         if ((total_error_file_block = (char *)malloc(len)) == NULL) {
70                 exit_on_error(errorfp, "%s ERROR: Memory allocation trouble at stage 3!\n", our_prog_name, "");
71         }
72         sprintf(total_error_file_block, "%s%s", total_config_dir, error_file);
73         total_error_file = total_error_file_block;
74
75 /* Try to open the the configuration directory for reading, just as a
76    test to see if it exists. A failure here can mean many things, but we
77    then try to create it as a means to rule out a true lack of existence */
78
79         if ((fp = fopen(total_config_dir, "r")) == NULL) {
80                 if ((mkdir(total_config_dir, 0777)) == NON_VALID) {
81                         exit_on_error(errorfp, "%s ERROR: Can not read or create %s\n", our_prog_name, total_config_dir);
82                 }
83         } else {
84                 fclose(fp);
85         }
86
87 /* Open (and truncate) an error log for future reference */
88
89         if ((errorfp = fopen(total_error_file, "w")) == NULL) {
90                 exit_on_error(errorfp, "%s ERROR: Can not open %s in write mode\n", our_prog_name, total_error_file);
91         }
92
93 /* Prelaunch sanity checks: See if X is OK */
94
95         if ((display = XOpenDisplay(NULL)) == NULL) {
96                 exit_on_error(errorfp, "%s ERROR: Can not connect to your X Server\n", our_prog_name, "");
97         }
98         screen = DefaultScreen(display);
99
100 /* Can we use XTest to send fake key presses */
101
102         int event_base, error_base;
103         int major_version, minor_version;
104         if (!XTestQueryExtension(display, &event_base, &error_base,
105                 &major_version, &minor_version)) {
106                 exit_on_error(errorfp, "%s ERROR: XTest extension not present on your X server\n", our_prog_name, "");
107         }
108
109 /* Can we use XInput to talk with the tablet */
110
111         XExtensionVersion *xinputext;
112         xinputext = XGetExtensionVersion(display, INAME);
113         if (xinputext && (xinputext != (XExtensionVersion*) NoSuchExtension)) {
114                 XFree(xinputext);
115         } else {
116                 exit_on_error(errorfp, "%s ERROR: XInput extension not present on your X server\n", our_prog_name, "");
117         }
118
119 /* Automatically discover, open and register events with the first devices
120    that contain the string 'pad' and/or 'stylus' in their xorg.conf "Identifier"
121    entries (case insensitive). These devices will be invalidated if the user
122    specifies a named device on the command line (at least for now, since we only
123    allow one device of each kind to be registered) */
124
125         pad1_info = (void *) get_device_info(display, pad1_info_block, pad1_autoname);
126         if (pad1_info) {
127                 if (register_events(display, pad1_info, pad1_info->name)) {
128                         pad1_autoname = pad1_info->name;
129                 } else {
130                         pad1_autoname = 0;
131                 }
132         } else {
133                 pad1_autoname = 0;
134         }
135
136         stylus1_info = (void *) get_device_info(display, stylus1_info_block, stylus1_autoname);
137         if (stylus1_info) {
138                 if (register_events(display, stylus1_info, stylus1_info->name)) {
139                         stylus1_autoname = stylus1_info->name;
140                 } else {
141                         stylus1_autoname = 0;
142                 }
143         } else {
144                 stylus1_autoname = 0;
145         }
146
147 /* Free X memory allocations for missing devices */
148
149         if (!pad1_autoname) {
150                 XFreeDeviceList(pad1_info_block);
151                 pad1_info_block = 0;
152         }
153         if (!stylus1_autoname) {
154                 XFreeDeviceList(stylus1_info_block);
155                 stylus1_info_block = 0;
156         }
157
158 /* If NO device can be found or have its events registered, we exit. Fair? */
159
160         if ((!pad1_autoname) && (!stylus1_autoname)) {
161                 exit_on_error(errorfp, "%s ERROR: Absolutely NO 'pad' or 'stylus' device seem to be connected. Check your system (eg xorg.conf)! Verify this lack by running \"xsetwacom list\" or \"xidump -l\"\n", our_prog_name, "");
162         }
163
164 /* Command line handling */
165
166         char read_buffer [MAXBUFFER];
167         char write_buffer [MAXBUFFER];
168
169         int send_sigterm = 0;
170         int send_sigusr1 = 0;
171         int send_sigusr2 = 0;
172         int give_help = 0;
173
174         int i, c, d;
175
176         while ((--argc > 0) && (**++argv == '-' || **argv != '-')) {
177                 if (**argv == '-') {
178                         while (((c = *++argv[0])) && (argc != NON_VALID)) {
179                                 switch (c) {
180
181                                         case 'd':
182                                         go_daemon = 1;
183                                         break;
184
185                                         case 'k':
186                                         send_sigterm = 1;
187                                         break;
188
189                                         case 'r':
190                                         send_sigusr1 = 1;
191                                         break;
192
193                                         case 'v':
194                                         be_verbose = 1;
195                                         break;
196
197                                         case 'x':
198                                         just_exit = 1;
199                                         be_verbose = 1;
200                                         break;
201
202                                         case 's':
203                                         send_sigusr2 = 1;
204                                         break;
205
206                                         case 'h':
207                                         give_help = 1;
208                                         break;
209
210                                         default:
211                                         fprintf(stderr, "\n%s ERROR: Invalid switch '-%c' on command line.\n", our_prog_name, c);
212                                         argc = NON_VALID;
213                                         break;
214                                 }
215                         }
216                 } else {
217                         len = strlen(*argv);
218                         if (len < MAXBUFFER) {
219                                 sprintf(read_buffer, "%s", *argv);
220                                 for (i = 0; i < len; i++) {
221                                         d = tolower(read_buffer[i]); /* Turn everything into lower case */
222                                         sprintf(write_buffer+i, "%c", d);
223                                         ++read_buffer[i];
224                                 }
225                                 strncpy(write_buffer + len, "\0", 1);
226                         } else {
227                                 exit_on_error(errorfp, "%s ERROR: A device name on the command line was too long to handle!\n", our_prog_name, "");
228                         }
229                         if (!pad1_name) {
230                                 if ((strstr(write_buffer, "pad")) !=NULL) {
231                                         pad1_name = *argv;
232                                 }
233                         }
234                         if (!stylus1_name) {
235                                 if ((strstr(write_buffer, "stylus")) !=NULL) {
236                                         stylus1_name = *argv;
237                                 }
238                         }
239                         if ((!pad1_name) && (!stylus1_name)) {
240                                 argc = NON_VALID;
241                         }
242                 }
243         }
244         if ((argc != 0) || (give_help == 1)) {
245                 fprintf(stderr, "\n");
246                 fprintf(stderr, "%s Version: %s\n", our_prog_name, our_prog_version);
247                 fprintf(stderr, "\n");
248                 fprintf(stderr, "Usage: %s [OPTION]... [DEVICE]...\n", our_prog_name);
249                 fprintf(stderr, "Any combination of pad and/or stylus can be specified.\n");
250                 fprintf(stderr, "An empty command line means automatic device search.\n");
251                 fprintf(stderr, "Options can be written like -dv or -d -v in any place.\n");
252                 fprintf(stderr, "\n");
253                 fprintf(stderr, "  -d makes the program a daemon (runs in the background).\n");
254                 fprintf(stderr, "  -k terminates (kills) an already running daemon instance.\n");
255                 fprintf(stderr, "  -r re-reads the configuration file of a running daemon.\n");
256                 fprintf(stderr, "  -v prints info to the screen at many execution points.\n");
257                 fprintf(stderr, "  -x sets -v and exits after some important info blocks.\n");
258                 fprintf(stderr, "  -s tells a daemon instance to report status (info block).\n");
259                 fprintf(stderr, "  -h unconditionally brings up this help text.\n");
260                 fprintf(stderr, "\n");
261                 fprintf(stderr, "Example1: %s -d (first 'pad' and/or 'stylus' found get used)\n", our_prog_name);
262                 fprintf(stderr, "Example2: %s 1stIntuos3Pad 1stIntuos3Stylus2 -d (named devices)\n", our_prog_name);
263                 fprintf(stderr, "Example3: %s -rv (visibly re-read the configuration file)\n", our_prog_name);
264                 fprintf(stderr, "\n");
265                 fprintf(stderr, "Please direct any bug reports or questions to the top address\n");
266                 fprintf(stderr, "in the AUTHORS file. This program is _not_ a linuxwacom project.\n");
267                 exit_on_error(errorfp, "", "", "");
268         }
269
270 /* Prepare a scratch buffer for reading in an eventual PID */
271
272         char pid_buffer [MAXBUFFER];
273
274 /* The following routine handles both intentional re-runs of the program
275    (configuration file re-read requests or a terminate to a daemon), and
276    unintentional attempts to start another daemon. The latter goes like:
277
278    If a pid file exists it is a sign of either A) program already runs, or
279    B) a crash/brutal kill not handled by our exit routine has occured
280    previously. We therefore read in such a PID and perform a "fake" kill
281    with it (signal number 0). If -1 (error) is returned we just carry on.
282    Otherwise our kill test detected a process with that PID and we exit,
283    based on the assumption that another instance is running */
284
285         if ((fp = fopen(total_pid_file, "r")) != NULL) { /* File exists */
286                 fgets(pid_buffer, MAXBUFFER, fp);
287                 fclose(fp);
288                 if (((kill(atoi(pid_buffer), 0)) != NON_VALID)) { /* Live pid-file */
289                         if ((send_sigusr1) || (send_sigusr2) || (send_sigterm)) {
290                                 if (send_sigusr1) {
291                                         if ((kill(atoi(pid_buffer), SIGUSR1)) != NON_VALID) {
292                                                 if (be_verbose) {
293                                                         exit_on_error(errorfp, "%s SUCCESS: The %s configuration file has been re-read\n", our_prog_name, our_prog_name);
294                                                 }
295                                                 exit_on_error(errorfp, "", "", "");
296                                         } else {
297                                                 exit_on_error(errorfp, "%s FAILURE: The %s configuration file could not be re-read!\n", our_prog_name, our_prog_name);
298                                         }
299                                 }
300                                 if (send_sigusr2) {
301                                         if ((kill(atoi(pid_buffer), SIGUSR2)) != NON_VALID) {
302                                                 if (be_verbose) {
303                                                         exit_on_error(errorfp, "%s SUCCESS: The %s status has been reported\n", our_prog_name, our_prog_name);
304                                                 }
305                                                 exit_on_error(errorfp, "", "", "");
306                                         } else {
307                                                 exit_on_error(errorfp, "%s FAILURE: The %s status could not be reported!\n", our_prog_name, our_prog_name);
308                                         }
309                                 }
310                                 if (send_sigterm) {
311                                         if ((kill(atoi(pid_buffer), SIGTERM)) != NON_VALID) {
312                                                 if (be_verbose) {
313                                                         exit_on_error(errorfp, "%s SUCCESS: The %s daemon has been terminated\n", our_prog_name, our_prog_name);
314                                                 }
315                                                 exit_on_error(errorfp, "", "", "");
316                                         } else {
317                                                 exit_on_error(errorfp, "%s FAILURE: The %s daemon could not be terminated!\n", our_prog_name, our_prog_name);
318                                         }
319                                 }
320                         } else {
321                                 second_instance = 1; /* Flag prevents a PID-file deletion */
322                                 exit_on_error(errorfp, "%s ERROR: Another instance of %s seems to be running!\n", our_prog_name, our_prog_name);
323                         }
324                 } else { /* Dead pid-file */
325                         if ((send_sigusr1) || (send_sigusr2) || (send_sigterm)) {
326                                 exit_on_error(errorfp, "%s ERROR: No daemon instance of %s found to send a -r -s or -k\n", our_prog_name, our_prog_name);
327                         }
328                 }
329         } else { /* No file at all */
330                 if ((send_sigusr1) || (send_sigusr2) || (send_sigterm)) {
331                         exit_on_error(errorfp, "%s ERROR: No daemon instance of %s found to send a -r -s or -k\n", our_prog_name, our_prog_name);
332                 }
333         }
334
335 /* See if a requested pad is for real, and if events can be registered with it */
336
337         if (pad1_name) {
338                 if ((pad1_autoname) && ((strlen(pad1_autoname) != (strlen(pad1_name))))) {
339                         XFreeDeviceList(pad1_info_block);
340                         pad1_info_block = 0;
341                         pad1_autoname = 0;
342                 } else {
343                         if ((pad1_autoname) && ((strcmp(pad1_autoname, pad1_name)) != 0)) {
344                                 XFreeDeviceList(pad1_info_block);
345                                 pad1_info_block = 0;
346                                 pad1_autoname = 0;
347                         }
348                 }
349                 if (!pad1_info_block) {
350                         pad1_info = (void *) get_device_info(display, pad1_info_block, pad1_name);
351                         if (!pad1_info) {
352                                 exit_on_error(errorfp, "%s ERROR: Can not find pad device: %s\n", our_prog_name, pad1_name);
353                         }
354                         if (!register_events(display, pad1_info, pad1_name)) {
355                                 exit_on_error(errorfp, "%s ERROR: Could not register any pad events with %s\n", our_prog_name, pad1_name);
356                         }
357                 } else {
358                         pad1_name = pad1_autoname;
359                 }
360         } else {
361                 pad1_name = pad1_autoname;
362         }
363
364 /* See if a requested stylus is for real, and if events can be registered with it */
365
366         if (stylus1_name) {
367                 if ((stylus1_autoname) && ((strlen(stylus1_autoname) != (strlen(stylus1_name))))) {
368                         XFreeDeviceList(stylus1_info_block);
369                         stylus1_info_block = 0;
370                         stylus1_autoname = 0;
371                 } else {
372                         if ((stylus1_autoname) && ((strcmp(stylus1_autoname, stylus1_name)) != 0)) {
373                                 XFreeDeviceList(stylus1_info_block);
374                                 stylus1_info_block = 0;
375                                 stylus1_autoname = 0;
376                         }
377                 }
378                 if (!stylus1_info_block) {
379                         stylus1_info = (void *) get_device_info(display, stylus1_info_block, stylus1_name);
380                         if (!stylus1_info) {
381                                 exit_on_error(errorfp, "%s ERROR: Can not find stylus device: %s\n", our_prog_name, stylus1_name);
382                         }
383                         if (!register_events(display, stylus1_info, stylus1_name)) {
384                                 exit_on_error(errorfp, "%s ERROR: Could not register any stylus events with %s\n", our_prog_name, stylus1_name);
385                         }
386                 } else {
387                         stylus1_name = stylus1_autoname;
388                 }
389         } else {
390                 stylus1_name = stylus1_autoname;
391         }
392
393 /* Now determine what the config file will be named as */
394
395         if ((pad1_name) && (stylus1_name)) {
396                 config_file = config_file_intuos3;
397         } else {
398                 if (stylus1_name) {
399                         config_file = config_file_padless;
400                 } else {
401                         config_file = config_file_intuos3; /* User error? 'pad' without 'stylus'! */
402                 }
403         }
404
405 /* Concatenate the full path with the config file name. Store address */
406
407         char *total_config_file_block;
408         len = strlen(total_config_dir) + strlen(config_file) + 1;
409         if ((total_config_file_block = (char *)malloc(len)) == NULL) {
410                 exit_on_error(errorfp, "%s ERROR: Memory allocation trouble at stage 4!\n", our_prog_name, "");
411         }
412         sprintf(total_config_file_block, "%s%s", total_config_dir, config_file);
413         total_config_file = total_config_file_block;
414
415         if (be_verbose) {
416                 fprintf(stderr, "PGR VERSION = %s\n", our_prog_version);
417                 fprintf(stderr, "USR HOMEDIR = %s\n", user_homedir);
418                 fprintf(stderr, "OUR CNF-DIR = %s\n", total_config_dir);
419                 fprintf(stderr, "OUR CNFFILE = %s\n", total_config_file);
420                 fprintf(stderr, "OUR PIDFILE = %s\n", total_pid_file);
421                 fprintf(stderr, "OUR LOGFILE = %s\n", total_error_file);
422                 if (pad1_name) {
423                         fprintf(stderr, "OUR PD1NAME = %s\n", pad1_name);
424                 }
425                 if (stylus1_name) {
426                         fprintf(stderr, "OUR ST1NAME = %s\n", stylus1_name);
427                 }
428         }
429
430 /* If no configuration file exists, write out a short one from an internal
431    memory structure. Also tag it with a Config File Version number */
432
433         if ((fp = fopen(total_config_file, "a+")) == NULL) {
434                 exit_on_error(errorfp, "%s ERROR: Can not open %s in read/write mode\n", our_prog_name, total_config_file);
435         } else {
436                 rewind(fp);
437                 if (fgetc(fp) == EOF) {
438                         write_file_config_header(fp);
439                         for (p = internal_list; p < internal_list + num_list; p++) {
440                                 write_file_config((void *)&p, fp);
441                                 if (ferror(fp)) {
442                                         exit_on_error(errorfp, "%s ERROR: Write error in %s\n", our_prog_name, total_config_file);
443                                 }
444                         }
445                 }
446         }
447         fclose(fp);
448
449 /* Read in an existing configuration file */
450
451         p = external_list;
452         hp = human_readable;
453         if ((fp = fopen(total_config_file, "r")) == NULL) {
454                 exit_on_error(errorfp, "%s ERROR: Can not open %s in read mode\n", our_prog_name, total_config_file);
455         } else {
456                 switch (read_file_config((void *)&p, (void *)&hp, fp)){
457
458                         case 0: /* No errors */
459                         fclose(fp);
460                         break; /* OBS An identical list of error code returns exist in the on_signal.c
461                                         file (the re_read_file_config function). So change both when altering. */
462
463                         case 1:
464                         fclose(fp);
465                         exit_on_error(errorfp, "%s ERROR: No complete record found in %s\n", our_prog_name, total_config_file);
466
467                         case 2:
468                         fclose(fp);
469                         exit_on_error(errorfp, "%s ERROR: Memory allocation error while parsing %s\n", our_prog_name, total_config_file);
470
471                         case 3:
472                         fclose(fp);
473                         exit_on_error(errorfp, "%s ERROR: Config File Version 3 or higher not found\n", our_prog_name, "");
474
475                         case 4:
476                         fclose(fp);
477                         exit_on_error(errorfp, "%s ERROR: A line was too long to handle in the Config File %s\n", our_prog_name, total_config_file);
478
479                         case 5:
480                         fclose(fp);
481                         exit_on_error(errorfp, "%s ERROR: A program record named \"default\" must exist in %s\n", our_prog_name, total_config_file);
482
483                         default:
484                         fclose(fp);
485                         exit_on_error(errorfp, "%s ERROR: Unknown error while parsing %s\n", our_prog_name, total_config_file);
486                 }
487         }
488
489 /* Exit if all we wanted to see was the main 'debugging' info block */
490
491         if (just_exit) {
492                 exit_on_error(errorfp, "", "", "");
493         }
494
495 /* Replace some of the normal signal handlers with our own functions. We
496    want SIGUSR1 to read in the config file after a modification, SIGUSR2
497    to print status (the information we would get from -x) to the screen,
498    and all the normal program exits should first clean up a bit */
499
500         if ((signal(SIGUSR1, re_read_file_config) == SIG_ERR)
501                 || (signal(SIGUSR2, status_report) == SIG_ERR)
502                 || (signal(SIGINT, clean_up_exit) == SIG_ERR)
503                 || (signal(SIGHUP, clean_up_exit) == SIG_ERR)
504                 || (signal(SIGTERM, clean_up_exit) == SIG_ERR)) {
505                 exit_on_error(errorfp, "%s ERROR: Failed to modify signal handling!\n", our_prog_name, "");
506         }
507
508 /* Ready to launch in the foreground or as a daemon.
509    In daemon mode we also take care of storing our PID in the config dir
510    Observe that with a (0, 0) standard input/output/error goes to /dev/null
511    I've found it better to use (0, 1) and see errors while writing the code
512    It also comes in handy when running in (-v) verbose mode */
513
514                 if (go_daemon) {
515                         if ((daemon(0, 1)) == NON_VALID) {
516                                 exit_on_error(errorfp, "%s ERROR: Failed to fork into daemon mode! EXITING!\n", our_prog_name, "");
517                         } else {
518                                 sprintf(pid_buffer, "%d", getpid());
519                                 if ((fp = fopen(total_pid_file, "w")) == NULL) {
520                                         exit_on_error(errorfp, "%s ERROR: Can not open %s in write mode\n", our_prog_name, total_pid_file);
521                                 } else {
522                                         if (be_verbose) {
523                                                 fprintf(stderr, "OUR RUN-PID = %s\n", pid_buffer);
524                                         }
525                                         fprintf(fp, "%s", pid_buffer);
526                                         if (ferror(fp)) {
527                                                 exit_on_error(errorfp, "%s ERROR: Write error in %s\n", our_prog_name, total_pid_file);
528                                         } else {
529                                                 fclose(fp);
530                                         }
531                                 }
532                         }
533                 }
534                 fclose(errorfp);
535                 use_events(display); /* <-- Our true launch! The event loop */
536
537         exit(EXIT_OK); /* We should never reach this */
538
539 }
540
541 /* End Code */
542