version 0.2.0
[expresskeys.git] / src-server / main_setup.c
1 /*
2  main_setup.c -- Support ExpressKeys & Touch Strips on a Wacom Intuos3 tablet.
3  
4  Copyright (C) 2005 - 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         struct program *p;
29
30         int i = 0;
31         int len = 0;
32
33         FILE *fp = NULL;
34         FILE *errorfp = NULL;
35
36 /* Prelaunch sanity checks: See if X is OK */
37
38         if ((display = XOpenDisplay(NULL)) == NULL) {
39                 exit_on_error(errorfp, "%s ERROR: Can not connect to your X Server\n", our_prog_name, "");
40                 exit(EXIT_KO); 
41         }
42         screen = DefaultScreen(display);
43
44 /* Locate the home directory of the user running this program */
45
46         char *user_homedir;
47         if ((user_homedir = getenv("HOME")) == NULL) {
48                 exit_on_error(errorfp, "%s ERROR: Can not find your HOME directory!\n", our_prog_name, "");
49         }
50
51 /* Concatenate the home directory string with the string of our preferred
52    configuration file directory. The address to the whole string is then
53    copied to a global pointer, so we won't have to perform this part again */
54
55         char *total_config_dir_block;
56         len = strlen(user_homedir) + strlen(config_dir) + 1;
57         if ((total_config_dir_block = (char *)malloc(len)) == NULL) {
58                 exit_on_error(errorfp, "%s ERROR: Memory allocation trouble at stage 2!\n", our_prog_name, "");
59         }
60         sprintf(total_config_dir_block, "%s%s", user_homedir, config_dir);
61         total_config_dir = total_config_dir_block;
62
63 /* Concatenate the full path with the config file name. Store address */
64
65         char *total_config_file_block;
66         len = strlen(total_config_dir) + strlen(config_file) + 1;
67         if ((total_config_file_block = (char *)malloc(len)) == NULL) {
68                 exit_on_error(errorfp, "%s ERROR: Memory allocation trouble at stage 3!\n", our_prog_name, "");
69         }
70         sprintf(total_config_file_block, "%s%s", total_config_dir, config_file);
71         total_config_file = total_config_file_block;
72
73 /* Concatenate the full path with the pid file name. Store address */
74
75         char *total_pid_file_block;
76         len = strlen(total_config_dir) + strlen(pid_file) + 1;
77         if ((total_pid_file_block = (char *)malloc(len)) == NULL) {
78                 exit_on_error(errorfp, "%s ERROR: Memory allocation trouble at stage 4!\n", our_prog_name, "");
79         }
80         sprintf(total_pid_file_block, "%s%s", total_config_dir, pid_file);
81         total_pid_file = total_pid_file_block;
82
83 /* Concatenate the full path with the error file name. Store address */
84
85         char *total_error_file_block;
86         len = strlen(total_config_dir) + strlen(error_file) + 1;
87         if ((total_error_file_block = (char *)malloc(len)) == NULL) {
88                 exit_on_error(errorfp, "%s ERROR: Memory allocation trouble at stage 5!\n", our_prog_name, "");
89         }
90         sprintf(total_error_file_block, "%s%s", total_config_dir, error_file);
91         total_error_file = total_error_file_block;
92
93 /* Prepare a scratch buffer for reading in an eventual PID */
94
95         char pid_buffer [MAXBUFFER];
96
97 /* Try to open the the configuration directory for reading, just as a
98    test to see if it exists. A failure here can mean many things, but we
99    then try to create it as a means to rule out a true lack of existence */
100
101         if ((fp = fopen(total_config_dir, "r")) == NULL) {
102                 if ((mkdir(total_config_dir, 0777)) == NON_VALID) {
103                         exit_on_error(errorfp, "%s ERROR: Can not read or create %s\n", our_prog_name, total_config_dir);
104                 }
105         } else {
106                 fclose(fp);
107         }
108
109 /* If a pid file exists it is a sign of either A) program already runs, or
110    B) a crash/brutal kill not handled by our exit routine has occured
111    previously. We therefore read in such a PID and perform a "fake" kill
112    with it (signal number 0). If -1 (error) is returned we just carry on.
113    Otherwise our kill test detected a process with that PID and we exit,
114    based on the assumption that another instance is running */
115
116         if ((fp = fopen(total_pid_file, "r")) != NULL) {
117                 fgets(pid_buffer, MAXBUFFER, fp);
118                 fclose(fp);
119                 if ((kill(atoi(pid_buffer), 0)) != NON_VALID) {
120                         exit_on_error(errorfp, "%s ERROR: Another instance of %s seems to be running!\n", our_prog_name, our_prog_name);
121                 }
122         }
123
124 /* Open (and truncate) an error log for future reference */
125
126         if ((errorfp = fopen(total_error_file, "w")) == NULL) {
127                 exit_on_error(errorfp, "%s ERROR: Can not open %s in write mode\n", our_prog_name, total_error_file);
128         }
129
130 /* Can we use XTest to send fake key presses */
131
132         int event_base, error_base;
133         int major_version, minor_version;
134         if (!XTestQueryExtension(display, &event_base, &error_base,
135                 &major_version, &minor_version)) {
136                 exit_on_error(errorfp, "%s ERROR: XTest extension not present on your X server\n", our_prog_name, "");
137         }
138
139 /* Can we use XInput to talk with the tablet */
140
141         XExtensionVersion *xinputext;
142         xinputext = XGetExtensionVersion(display, INAME);
143         if (xinputext && (xinputext != (XExtensionVersion*) NoSuchExtension)) {
144                 XFree(xinputext);
145         } else {
146                 exit_on_error(errorfp, "%s ERROR: XInput extension not present on your X server\n", our_prog_name, "");
147         }
148
149 /* We need at least the pad name specified... */
150
151         if (argc < 2) {
152                 fprintf(stderr, "\n");
153                 fprintf(stderr, "Usage: %s <pad-device-name> [<pen-device-name>] [-d] [-v]\n", our_prog_name);
154                 fprintf(stderr, "\n");
155                 fprintf(stderr, "Where the pad name is mandatory. Specify a pen name\n");
156                 fprintf(stderr, "if you want the program to handle pen mode switches.\n");
157                 fprintf(stderr, "Use -d to make the program a daemon (run in the background).\n");
158                 fprintf(stderr, "Use -v to print info to the screen at many execution points\n");
159                 fprintf(stderr, "\n");
160                 fprintf(stderr, "Example: %s pad stylus -d\n", our_prog_name);
161                 fprintf(stderr, "\n");
162                 exit_on_error(errorfp, "", "", "");
163         }
164
165 /* See if the pad is for real, and if it is active */
166
167         pad_info = (void *) get_device_info(display, pad_info_block, argv[1]);
168         if (!pad_info) {
169                 exit_on_error(errorfp, "%s ERROR: Can not find pad device: %s\n", our_prog_name, argv[1]);
170         }
171
172 /* Set a flag if we should run as a daemon or/and in verbose mode. Also
173    register and check a pen device, should such an action be requested */
174
175         if (argc > 2) {
176                 for (i = 2; i < argc; i++) {
177                         if (strcmp(argv[i], "-d") == 0) {
178                                 go_daemon = 1;
179                                 break;
180                         }
181                 }
182                 for (i = 2; i < argc; i++) {
183                         if (strcmp(argv[i], "-v") == 0) {
184                                 be_verbose = 1;
185                                 break;
186                         }
187                 }
188                 for (i = 2; i < argc; i++) {
189                         if ((strcmp(argv[i], "-d")) != 0 &&
190                         ((strcmp(argv[i], "-v")) != 0)) {
191                                 pen_name = argv[i];
192                                 handle_pen = 1;
193                                 pen_info = (void *) get_device_info(display, pen_info_block, argv[i]);
194                                 if (!pen_info) {
195                                         exit_on_error(errorfp, "%s ERROR: Can not find pen device: %s\n", our_prog_name, pen_name);
196                                 }
197                                 break;
198                         }
199                 }
200         }
201
202         if (be_verbose) {
203                 fprintf(stderr, "USR HOMEDIR = %s\n", user_homedir);
204                 fprintf(stderr, "OUR CNF-DIR = %s\n", total_config_dir);
205                 fprintf(stderr, "OUR CNFFILE = %s\n", total_config_file);
206                 fprintf(stderr, "OUR PIDFILE = %s\n", total_pid_file);
207                 fprintf(stderr, "OUR LOGFILE = %s\n", total_error_file);
208                 fprintf(stderr, "OUR PADNAME = %s\n", argv[1]);
209                 if (pen_name) {
210                         fprintf(stderr, "OUR PENNAME = %s\n", pen_name);
211                 }
212         }
213
214 /* Make sure we can open a requested pen */
215
216         if (handle_pen) {
217                 pen_mode = Relative;
218                 if (toggle_pen_mode(display, pen_name)) {
219                         exit_on_error(errorfp, "%s ERROR: Can not open pen device: %s\n", our_prog_name, pen_name);
220                 }
221         }
222
223 /* If no configuration file exists, write out a short one from an internal
224    memory structure. Also tag it with a Config File Version number */
225
226         if ((fp = fopen(total_config_file, "a+")) == NULL) {
227                 exit_on_error(errorfp, "%s ERROR: Can not open %s in read/write mode\n", our_prog_name, total_config_file);
228         } else {
229                 rewind(fp);
230                 if (fgetc(fp) == EOF) {
231                         write_file_config_header(fp);
232                         for (p = internal_list; p < internal_list + num_list; p++) {
233                                 write_file_config((void *)&p, fp);
234                                 if (ferror(fp)) {
235                                         exit_on_error(errorfp, "%s ERROR: Write error in %s\n", our_prog_name, total_config_file);
236                                 }
237                         }
238                 }
239         }
240         fclose(fp);
241
242 /* Read in an existing configuration file */
243
244         p = external_list;
245         if ((fp = fopen(total_config_file, "r")) == NULL) {
246                 exit_on_error(errorfp, "%s ERROR: Can not open %s in read mode\n", our_prog_name, total_config_file);
247         } else {
248                 switch (read_file_config((void *)&p, fp)){
249                         
250                         case 0:
251                         fclose(fp);
252                         break;
253
254                         case 1:
255                         fclose(fp);
256                         exit_on_error(errorfp, "%s ERROR: No complete record found in %s\n", our_prog_name, total_config_file);
257                         
258                         case 2:
259                         fclose(fp);
260                         exit_on_error(errorfp, "%s ERROR: Memory allocation error while parsing %s\n", our_prog_name, total_config_file);
261
262                         case 3:
263                         fclose(fp);
264                         exit_on_error(errorfp, "%s ERROR: Config File Version %d not found\n", our_prog_name, (void *)CONFIG_VERSION);
265
266                         default:
267                         fclose(fp);
268                         exit_on_error(errorfp, "%s ERROR: Unknown error while parsing %s\n", our_prog_name, total_config_file);
269                 }
270         }
271
272 /* Replace some of the normal signal handlers with our own functions. We
273    want SIGUSR1 and SIGUSR2 to read in the config file after a modification,
274    and all the normal program exits should first clean up a bit */
275
276         if ((signal(SIGUSR1, re_read_file_config) == SIG_ERR)
277                 || (signal(SIGUSR2, re_read_file_config) == SIG_ERR)
278                 || (signal(SIGINT, clean_up_exit) == SIG_ERR)
279                 || (signal(SIGHUP, clean_up_exit) == SIG_ERR)
280                 || (signal(SIGTERM, clean_up_exit) == SIG_ERR)) {
281                 exit_on_error(errorfp, "%s ERROR: Failed to modify signal handling!\n", our_prog_name, "");
282         }
283
284 /* Ready to launch in the foreground or as a daemon after one last check.
285    In daemon mode we also take care of storing our PID in the config dir
286    Observe that with a (0, 0) standard input/output/error goes to /dev/null
287    I've found it better to use (0, 1) and see errors while writing the code
288    It also comes in handy when running in (-v) verbose mode */
289
290         if (register_events(display, pad_info, argv[1])) {
291                 if (go_daemon) {
292                         if ((daemon(0, 1)) == NON_VALID) {
293                                 exit_on_error(errorfp, "%s ERROR: Failed to fork into daemon mode! EXITING!\n", our_prog_name, "");
294                         } else {
295                                 sprintf(pid_buffer, "%d", getpid());
296                                 if ((fp = fopen(total_pid_file, "w")) == NULL) {
297                                         exit_on_error(errorfp, "%s ERROR: Can not open %s in write mode\n", our_prog_name, total_pid_file);
298                                 } else {
299                                         if (be_verbose) {
300                                                 fprintf(stderr, "OUR RUN-PID = %s\n", pid_buffer);
301                                         }
302                                         fprintf(fp, "%s", pid_buffer);
303                                         if (ferror(fp)) {
304                                                 exit_on_error(errorfp, "%s ERROR: Write error in %s\n", our_prog_name, total_pid_file);
305                                         } else {
306                                                 fclose(fp);
307                                         }
308                                 }
309                         }
310                 }
311                 fclose(errorfp);
312                 use_events(display); /* <-- Our true launch! The event loop */
313         } else {
314                 exit_on_error(errorfp, "%s ERROR: Could not register any events!\n", our_prog_name, "");
315         }
316
317         exit(EXIT_OK);
318
319 }
320
321 /* End Code */
322