version 0.4.0
[expresskeys.git] / src-expresskeys / main_setup.c
1 /*
2  expresskeys - Support ExpressKeys, Touch Strips, Scroll Wheel on Wacom tablets.
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 along
17  with this program; if not, write to the Free Software Foundation, Inc.,
18  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19 */
20
21 #include <stdio.h> /* NULL, FILE, fopen, fclose, fprintf */
22 #include <stdlib.h> /* exit, getenv */
23 #include <libgen.h> /* basename */
24 #include <sys/stat.h> /* mkdir */
25 #include <sys/types.h> /* mkdir, getpid */
26 #include <unistd.h> /* getpid, daemon */
27 #include <signal.h> /* signal */
28 #include <X11/extensions/XInput.h>
29
30 #include "defines.h"
31
32 /* Globals: */
33
34 /* Flag to mark that a tablet with no 'pad' device is present: */
35 int have_padless;
36
37 /* The basename call will provide our runtime program name: */
38 const char* our_prog_name;
39
40 /* Path to the configuration directory and some information files: */
41 const char* total_config_dir;
42 const char* total_pid_file;
43 const char* total_error_file;
44 const char* total_status_file;
45
46 /* Externals */
47
48 extern int live_pid;
49 extern int have_pad;
50
51 extern int ok_xsetwacom;
52 extern int ok_config;
53
54 extern int go_daemon;
55 extern int be_verbose;
56 extern int just_exit;
57
58 extern int send_sigterm;
59 extern int send_sigusr1;
60 extern int send_sigusr2;
61
62 extern Display* display;
63
64 extern const char* user_pad;
65 extern const char* user_stylus1;
66 extern const char* user_stylus2;
67
68 extern XDevice* stylus_xdevice[MAXSTYLUS][MAXMODEL * MAXPAD];
69
70 extern char* path_malloc(char* path, char* file);
71 extern void parse_command(FILE* errorfp, int argc, char* argv[]);
72 extern void check_pid(const char* pidfile, const char* statusfile,
73                                                 const char* pgr_runline);
74 extern void send_command(FILE* errorfp);
75 extern void check_x_sanity(FILE* errorfp);
76 extern int xerror_handler(Display* display, XErrorEvent* error_event);
77 extern void get_device_info();
78 extern void check_user(FILE* errorfp);
79 extern void shuffle_styli();
80 extern void device_to_structure();
81 extern void report_header(FILE* reportfp);
82 extern void write_config(FILE* errorfp);
83 extern void read_config(FILE* errorfp);
84 extern void clean_up_exit(int signum);
85 extern void re_read_config(int signum);
86 extern void status_report(int signum);
87 extern void use_events();
88
89 /*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
90  Like most main() functions, this is primarily a place to call other functions
91  from and to check some important return results (through flags and arrays):
92  +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
93
94 int main(int argc, char* argv[])
95 {
96         int i, j;
97
98         our_prog_name = basename(argv[0]);
99
100         const char* warn_xsetwacom =
101 "\nOBSERVE: xsetwacom 0.0.7 or greater was not detected on this machine\n"
102 "(linuxwacom-0.7.5-2 or higher is required). Since we need at least that\n"
103 "version for identification and other functionality, all tablets will be\n"
104 "treated as having no 'pad' (being 'padless'). A very limited set of\n"
105 "features, or none at all, will be available.\n\n";
106
107         const char* no_tablet =
108 "ERROR: Tablet not attached, OR (in case of Cintiq 21UX, Intuos3\n"
109 "and Graphire4) the 'pad' device has not been specified in xorg.conf, OR is\n"
110 "lacking on the command line when using \"named devices\".\n";
111
112         const char* config_dir = "/.expresskeys";
113         const char* pid_file = "/expresskeys.pid";
114         const char* error_file = "/error.log";
115         const char* status_file = "/status.log";
116
117 /* String (and name) read from the status log while searching for a daemon: */
118         const char* pgr_runline = "PGR RUNNAME =";
119
120         FILE* fp = NULL;
121         FILE* errorfp = NULL;
122
123         char pid_buffer[MAXBUFFER];
124         const char* user_homedir;
125
126         if ((user_homedir = getenv("HOME")) == NULL) {
127                 exit_on_error(errorfp,
128         "\n%s ERROR: Can not find your HOME directory!\n", our_prog_name, "");
129         }
130
131 /* Malloc strings for the configuration directory and information files: */
132         total_config_dir = path_malloc((void*)user_homedir, (void*)config_dir);
133         total_pid_file = path_malloc((void*)total_config_dir, (void*)pid_file);
134         total_error_file = path_malloc((void*)total_config_dir,
135                                                         (void*)error_file);
136         total_status_file = path_malloc((void*)total_config_dir,
137                                                         (void*)status_file);
138
139 /* Try to open the the configuration directory for reading, just as a test
140  to see if it exists. A failure here can mean many things, but we then try to
141  create it as a means to rule out a true lack of existence: */
142         if ((fp = fopen(total_config_dir, "r")) == NULL) {
143                 if ((mkdir(total_config_dir, 0777)) == NON_VALID) {
144                         exit_on_error(errorfp,
145 "\n%s ERROR: Can not read or create %s\n", our_prog_name, total_config_dir);
146                 }
147         } else {
148                 fclose(fp);
149         }
150
151 /* Open (and truncate) an error log for future reference: */
152         if ((errorfp = fopen(total_error_file, "w")) == NULL) {
153                 exit_on_error(errorfp,
154 "\n%s ERROR: Can not open %s in write mode\n", our_prog_name, total_error_file);
155         }
156
157 /* Check for flags and named devices on the command line: */
158         parse_command(errorfp, argc, argv);
159
160 /* Look for a daemon instance of the program: */
161         check_pid(total_pid_file, total_status_file, pgr_runline);
162
163 /* Act on signal flags from the user: */
164         if ((live_pid) && ((send_sigusr1) || (send_sigusr2) ||
165                                                 (send_sigterm))) {
166                 send_command(errorfp);
167         } else if ((!live_pid) && ((send_sigusr1) || (send_sigusr2) ||
168                                                 (send_sigterm))) {
169                 exit_on_error(errorfp,
170 "\n%s ERROR: No daemon instance of expresskeys found to send a -r -s or -k\n",
171                                                         our_prog_name, "");
172         }
173
174 /* Try to hinder several instances of the program to run simultaneously: */
175         if (live_pid) {
176                 exit_on_error(errorfp,
177 "\n%s ERROR: A daemon instance of expresskeys seems to be running!\n",
178                                                         our_prog_name, "");
179         }
180
181 /* See if X is running (open a connection to the X display) and that both
182  XInput and XTest extensions are present. The tests should not be performed
183  prior to this point, since we both want to be able to send signals from a
184  client running in a pure text terminal, _and_ prevent the same client from
185  deleting a live PID-file (test of opened X display is used as trigger): */
186         check_x_sanity(errorfp);
187
188 /* Register an error handler for X server error returns: */
189         XSetErrorHandler(xerror_handler);
190
191 /* Search for and open appropriate devices. Register events and fill arrays: */
192         get_device_info();
193
194 /* Compare device names specified on the command line with found devices: */
195         if ((user_pad) || (user_stylus1) || (user_stylus2)) {
196                 check_user(errorfp);
197         }
198
199 /* See if a tablet with no 'pad' device has been detected: */
200         for (i = 0; i < MAXSTYLUS && have_padless == 0; i++) {
201                 for (j = 0; j < MAXPAD; j++) {
202                         if (stylus_xdevice[i][j]) {
203                                 have_padless = 1;
204                                 break;
205                         }
206                 }
207         }
208
209 /* Exit if we are totally void of tablets: */
210         if ((!have_pad) && (!have_padless)) {
211                 fp = stderr;
212                 for (i = 0; i < 2; i++) {
213                         fprintf(fp, "\n%s %s", our_prog_name, no_tablet);
214                         fp = errorfp;
215                 }
216                 fclose(errorfp);
217                 errorfp = NULL;
218                 exit_on_error(errorfp, "", "", "");
219         }
220
221 /* Adjust possible misappropriations when filling in the stylus arrays: */
222         shuffle_styli();
223
224 /* Transfer obtained knowledge about the devices from arrays to structures: */
225         device_to_structure();
226
227 /* Aid in debugging/tuning or just satisfy curiosity if asked to: */
228         if (be_verbose) {
229                 report_header(stderr);
230         }
231
232 /* Write out a minimal configuration file for any tablet that is missing one: */
233         write_config(errorfp);
234
235 /* Read in existing configuration files for currently connected tablets: */
236         read_config(errorfp);
237
238 /* Sanity verifications: */
239         if (!ok_config) {
240                 exit_on_error(errorfp,
241 "\n%s ERROR: No active file with a valid ConfigVersion found!\n",
242                                                         our_prog_name, "");
243         }
244
245         if (!ok_xsetwacom) {
246                 fp = stderr;
247                 for (i = 0; i < 2; i++) {
248                         fprintf(fp, "%s", warn_xsetwacom);
249                         fp = errorfp;
250                 }
251         }
252
253 /* Exit if all the user wanted to see was the main 'debugging' info block: */
254         if (just_exit) {
255                 exit_on_error(errorfp, "", "", "");
256         }
257
258 /* Replace some of the normal signal handlers with our own functions. We want
259  SIGUSR1 to read in all the configuration files after a modification, SIGUSR2
260  to print status (the information we would get from -s) to file/screen, and
261  all the normal program exits should first clean up a bit: */
262         if ((signal(SIGUSR1, re_read_config) == SIG_ERR)
263                 || (signal(SIGUSR2, status_report) == SIG_ERR)
264                 || (signal(SIGINT, clean_up_exit) == SIG_ERR)
265                 || (signal(SIGHUP, clean_up_exit) == SIG_ERR)
266                 || (signal(SIGTERM, clean_up_exit) == SIG_ERR)) {
267                 exit_on_error(errorfp,
268 "\n%s ERROR: Failed to modify signal handling!\n", our_prog_name, "");
269         }
270
271 /* Store information we have in the status file (silent use of the -s switch):*/
272         if (go_daemon) {
273                 go_daemon = 0;
274                 status_report(0);
275                 go_daemon = 1;
276         }
277
278 /* Ready to launch in the foreground or as a daemon. In daemon mode we also
279  take care of storing our PID in the config dir. Observe that with a (0, 0)
280  standard input/output/error goes to /dev/null. I've found it better to use
281  (0, 1) and see errors while writing the code. It also comes in handy when
282  running in -v (verbose) mode: */
283         if (go_daemon) {
284                 if ((daemon(0, 1)) == NON_VALID) {
285                         exit_on_error(errorfp,
286 "\n%s ERROR: Failed to fork into daemon mode! EXITING!\n", our_prog_name, "");
287                 } else {
288                         sprintf(pid_buffer, "%i", getpid());
289                         if ((fp = fopen(total_pid_file, "w")) == NULL) {
290                                 exit_on_error(errorfp,
291 "\n%s ERROR: Can not open %s in write mode\n", our_prog_name, total_pid_file);
292                         } else {
293                                 if (be_verbose) {
294                                         fprintf(stderr,
295                                         "OUR RUN-PID = %s\n", pid_buffer);
296                                 }
297                                 fprintf(fp, "%s", pid_buffer);
298                                 if (ferror(fp)) {
299                                         fclose(fp);
300                                         exit_on_error(errorfp,
301 "\n%s ERROR: Write error in %s\n", our_prog_name, total_pid_file);
302                                 } else {
303                                         fclose(fp);
304                                 }
305                         }
306                 }
307         }
308         fclose(errorfp);
309
310 /* Our true launch, the event loop: */
311         use_events();
312
313 /* We should never reach this: */
314         exit(0);
315
316 }
317
318 /* End Code */
319