Initial check-in
[him-cellwriter] / src / main.c
1
2 /*
3
4 cellwriter -- a character recognition input method
5 Copyright (C) 2007 Michael Levin <risujin@risujin.org>
6
7 This program is free software; you can redistribute it and/or
8 modify it under the terms of the GNU General Public License
9 as published by the Free Software Foundation; either version 2
10 of the License, or (at your option) any later version.
11
12 This program is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 GNU General Public License for more details.
16
17 You should have received a copy of the GNU General Public License
18 along with this program; if not, write to the Free Software
19 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
20
21 */
22
23 #include "config.h"
24 #include "common.h"
25 #include <stdlib.h>
26 #include <string.h>
27 #include <signal.h>
28 #include <stdio.h>
29 #include <errno.h>
30 #ifdef HAVE_GNOME
31 #include <libgnome/libgnome.h>
32 #endif
33
34 /* recognize.c */
35 extern int strength_sum;
36
37 void recognize_init(void);
38 void recognize_sync(void);
39 void samples_write(void);
40 void sample_read(void);
41 void update_enabled_samples(void);
42 int samples_loaded(void);
43
44 /* cellwidget.c */
45 extern int training, corrections, rewrites, characters, inputs;
46
47 void cell_widget_cleanup(void);
48
49 /* options.c */
50 void options_sync(void);
51
52 /* keyevent.c */
53 extern int key_recycles, key_overwrites, key_disable_overwrite;
54
55
56 void bad_keycodes_write(void);
57 void bad_keycodes_read(void);
58
59 /*
60         Variable argument parsing
61 */
62
63 char *nvav(int *plen, const char *fmt, va_list va)
64 {
65         static char buffer[2][16000];
66         static int which;
67         int len;
68
69         which = !which;
70         len = g_vsnprintf(buffer[which], sizeof(buffer[which]), fmt, va);
71         if (plen)
72                 *plen = len;
73         return buffer[which];
74 }
75
76 char *nva(int *plen, const char *fmt, ...)
77 {
78         va_list va;
79         char *string;
80
81         va_start(va, fmt);
82         string = nvav(plen, fmt, va);
83         va_end(va);
84         return string;
85 }
86
87 char *va(const char *fmt, ...)
88 {
89         va_list va;
90         char *string;
91
92         va_start(va, fmt);
93         string = nvav(NULL, fmt, va);
94         va_end(va);
95         return string;
96 }
97
98 /*
99         GDK colors
100 */
101
102 static int check_color_range(int value)
103 {
104         if (value < 0)
105                 value = 0;
106         if (value > 65535)
107                 value = 65535;
108         return value;
109 }
110
111 void scale_gdk_color(const GdkColor *base, GdkColor *out, double value)
112 {
113         out->red = check_color_range(base->red * value);
114         out->green = check_color_range(base->green * value);
115         out->blue = check_color_range(base->blue * value);
116 }
117
118 void gdk_color_to_hsl(const GdkColor *src,
119                       double *hue, double *sat, double *lit)
120 /* Source: http://en.wikipedia.org/wiki/HSV_color_space
121            #Conversion_from_RGB_to_HSL_or_HSV */
122 {
123         double max = src->red, min = src->red;
124
125         /* Find largest and smallest channel */
126         if (src->green > max)
127                 max = src->green;
128         if (src->green < min)
129                 min = src->green;
130         if (src->blue > max)
131                 max = src->blue;
132         if (src->blue < min)
133                 min = src->blue;
134
135         /* Hue depends on max/min */
136         if (max == min)
137                 *hue = 0;
138         else if (max == src->red) {
139                 *hue = (src->green - src->blue) / (max - min) / 6.;
140                 if (*hue < 0.)
141                         *hue += 1.;
142         } else if (max == src->green)
143                 *hue = ((src->blue - src->red) / (max - min) + 2.) / 6.;
144         else if (max == src->blue)
145                 *hue = ((src->red - src->green) / (max - min) + 4.) / 6.;
146
147         /* Lightness */
148         *lit = (max + min) / 2 / 65535;
149
150         /* Saturation depends on lightness */
151         if (max == min)
152                 *sat = 0.;
153         else if (*lit <= 0.5)
154                 *sat = (max - min) / (max + min);
155         else
156                 *sat = (max - min) / (65535 * 2 - (max + min));
157 }
158
159 void hsl_to_gdk_color(GdkColor *src, double hue, double sat, double lit)
160 /* Source: http://en.wikipedia.org/wiki/HSV_color_space
161            #Conversion_from_RGB_to_HSL_or_HSV */
162 {
163         double q, p, t[3];
164         int i;
165
166         /* Clamp ranges */
167         if (hue < 0)
168                 hue -= (int)hue - 1.;
169         if (hue > 1)
170                 hue -= (int)hue;
171         if (sat < 0)
172                 sat = 0;
173         if (sat > 1)
174                 sat = 1;
175         if (lit < 0)
176                 lit = 0;
177         if (lit > 1)
178                 lit = 1;
179
180         /* Special case for gray */
181         if (sat == 0.) {
182                 src->red = lit * 65535;
183                 src->green = lit * 65535;
184                 src->blue = lit * 65535;
185                 return;
186         }
187
188         q = (lit < 0.5) ? lit * (1 + sat) : lit + sat - (lit * sat);
189         p = 2 * lit - q;
190         t[0] = hue + 1 / 3.;
191         t[1] = hue;
192         t[2] = hue - 1 / 3.;
193         for (i = 0; i < 3; i++) {
194                 if (t[i] < 0.)
195                         t[i] += 1.;
196                 if (t[i] > 1.)
197                         t[i] -= 1.;
198                 if (t[i] >= 2 / 3.)
199                         t[i] = p;
200                 else if (t[i] >= 0.5)
201                         t[i] = p + ((q - p) * 6 * (2 / 3. - t[i]));
202                 else if (t[i] >= 1 / 6.)
203                         t[i] = q;
204                 else
205                         t[i] = p + ((q - p) * 6 * t[i]);
206         }
207         src->red = t[0] * 65535;
208         src->green = t[1] * 65535;
209         src->blue = t[2] * 65535;
210 }
211
212 void shade_gdk_color(const GdkColor *base, GdkColor *out, double value)
213 {
214         double hue, sat, lit;
215
216         gdk_color_to_hsl(base, &hue, &sat, &lit);
217         sat *= value;
218         lit += value - 1.;
219         hsl_to_gdk_color(out, hue, sat, lit);
220 }
221
222 void highlight_gdk_color(const GdkColor *base, GdkColor *out, double value)
223 /* Shades brighter or darker depending on the luminance of the base color */
224 {
225         double lum = (0.3 * base->red + 0.59 * base->green +
226                       0.11 * base->blue) / 65535;
227
228         value = lum < 0.5 ? 1. + value : 1. - value;
229         shade_gdk_color(base, out, value);
230 }
231
232 /*
233         Profile
234 */
235
236 /* Profile format version */
237 #define PROFILE_VERSION 0
238
239 int profile_read_only, keyboard_only = FALSE;
240
241 static GIOChannel *channel;
242 static char profile_buf[4096], *profile_end = NULL, profile_swap,
243             *force_profile = NULL, *profile_tmp = NULL;
244 static int force_read_only;
245
246 static int is_space(int ch)
247 {
248         return ch == ' ' || ch == '\t' || ch == '\r';
249 }
250
251 static int profile_open_channel(const char *type, const char *path)
252 /* Tries to open a profile channel, returns TRUE if it succeeds */
253 {
254         GError *error = NULL;
255
256         if (!g_file_test(path, G_FILE_TEST_IS_REGULAR) &&
257             g_file_test(path, G_FILE_TEST_EXISTS)) {
258                 g_warning("Failed to open %s profile '%s': Not a regular file",
259                           type, path);
260                 return FALSE;
261         }
262         channel = g_io_channel_new_file(path, profile_read_only ? "r" : "w",
263                                         &error);
264         if (!error)
265                 return TRUE;
266         g_warning("Failed to open %s profile '%s' for %s: %s",
267                   type, path, profile_read_only ? "reading" : "writing",
268                   error->message);
269         g_error_free(error);
270         return FALSE;
271 }
272
273 static void create_user_dir(void)
274 /* Make sure the user directory exists */
275 {
276         char *path;
277
278         path = g_build_filename(g_get_home_dir(), "." PACKAGE, NULL);
279         if (g_mkdir_with_parents(path, 0755))
280                 g_warning("Failed to create user directory '%s'", path);
281         g_free(path);
282 }
283
284 static int profile_open_read(void)
285 /* Open the profile file for reading. Returns TRUE if the profile was opened
286    successfully. */
287 {
288         char *path;
289
290         profile_read_only = TRUE;
291
292         /* Try opening a command-line specified profile first */
293         if (force_profile &&
294             profile_open_channel("command-line specified", force_profile))
295                 return TRUE;
296
297         /* Open user's profile */
298         path = g_build_filename(g_get_home_dir(), "." PACKAGE, "profile", NULL);
299         if (profile_open_channel("user's", path)) {
300                 g_free(path);
301                 return TRUE;
302         }
303         g_free(path);
304
305         /* Open system profile */
306         path = g_build_filename(PKGDATADIR, "profile", NULL);
307         if (profile_open_channel("system", path)) {
308                 g_free(path);
309                 return TRUE;
310         }
311         g_free(path);
312
313         return FALSE;
314 }
315
316 static int profile_open_write(void)
317 /* Open a temporary profile file for writing. Returns TRUE if the profile was
318    opened successfully. */
319 {
320         GError *error;
321         gint fd;
322
323         if (force_read_only) {
324                 g_debug("Not saving profile, opened in read-only mode");
325                 return FALSE;
326         }
327         profile_read_only = FALSE;
328
329         /* Open a temporary file as a channel */
330         error = NULL;
331         fd = g_file_open_tmp(PACKAGE ".XXXXXX", &profile_tmp, &error);
332         if (error) {
333                 g_warning("Failed to open tmp file while saving "
334                           "profile: %s", error->message);
335                 return FALSE;
336         }
337         channel = g_io_channel_unix_new(fd);
338         if (!channel) {
339                 g_warning("Failed to create channel from temporary file");
340                 return FALSE;
341         }
342
343         return TRUE;
344 }
345
346 static int move_file(char *from, char *to)
347 /* The standard library rename() cannot move across filesystems so we need a
348    function that can emulate that. This function will copy a file, byte-by-byte
349    but is not as safe as rename(). */
350 {
351         GError *error = NULL;
352         GIOChannel *src_channel, *dest_channel;
353         gchar buffer[4096];
354
355         /* Open source file for reading */
356         src_channel = g_io_channel_new_file(from, "r", &error);
357         if (error) {
358                 g_warning("move_file() failed to open src '%s': %s",
359                           from, error->message);
360                 return FALSE;
361         }
362
363         /* Open destination file for writing */
364         dest_channel = g_io_channel_new_file(to, "w", &error);
365         if (error) {
366                 g_warning("move_file() failed to open dest '%s': %s",
367                           to, error->message);
368                 g_io_channel_unref(src_channel);
369                 return FALSE;
370         }
371
372         /* Copy data in blocks */
373         for (;;) {
374                 gsize bytes_read, bytes_written;
375
376                 /* Read a block in */
377                 g_io_channel_read_chars(src_channel, buffer, sizeof (buffer),
378                                         &bytes_read, &error);
379                 if (bytes_read < 1 || error)
380                         break;
381
382                 /* Write the block out */
383                 g_io_channel_write_chars(dest_channel, buffer, bytes_read,
384                                          &bytes_written, &error);
385                 if (bytes_written < bytes_read || error) {
386                         g_warning("move_file() error writing to '%s': %s",
387                                   to, error->message);
388                         g_io_channel_unref(src_channel);
389                         g_io_channel_unref(dest_channel);
390                         return FALSE;
391                 }
392         }
393
394         /* Close channels */
395         g_io_channel_unref(src_channel);
396         g_io_channel_unref(dest_channel);
397
398         g_debug("move_file() copied '%s' to '%s'", from, to);
399
400         /* Should be safe to delete the old file now */
401         if (remove(from))
402                 log_errno("move_file() failed to delete src");
403
404         return TRUE;
405 }
406
407 static int profile_close(void)
408 /* Close the currently open profile and, if we just wrote the profile to a
409    temporary file, move it in place of the old profile */
410 {
411         char *path = NULL;
412
413         if (!channel)
414                 return FALSE;
415         g_io_channel_unref(channel);
416
417         if (!profile_tmp || profile_read_only)
418                 return TRUE;
419
420         /* For some bizarre reason we may not have managed to create the
421            temporary file */
422         if (!g_file_test(profile_tmp, G_FILE_TEST_EXISTS)) {
423                 g_warning("Tmp profile '%s' does not exist", profile_tmp);
424                 return FALSE;
425         }
426
427         /* Use command-line specified profile path first then the user's
428            home directory profile */
429         path = force_profile;
430         if (!path)
431                 path = g_build_filename(g_get_home_dir(),
432                                         "." PACKAGE, "profile", NULL);
433
434         if (g_file_test(path, G_FILE_TEST_EXISTS)) {
435                 g_message("Replacing '%s' with '%s'", path, profile_tmp);
436
437                 /* Don't write over non-regular files */
438                 if (!g_file_test(path, G_FILE_TEST_IS_REGULAR)) {
439                         g_warning("Old profile '%s' is not a regular file",
440                                   path);
441                         goto error_recovery;
442                 }
443
444                 /* Remove the old profile */
445                 if (remove(path)) {
446                         log_errno("Failed to delete old profile");
447                         goto error_recovery;
448                 }
449         }
450         else
451                 g_message("Creating new profile '%s'", path);
452
453         /* Move the temporary profile file in place of the old one */
454         if (rename(profile_tmp, path)) {
455                 log_errno("rename() failed to move tmp profile in place");
456                 if (!move_file(profile_tmp, path))
457                         goto error_recovery;
458         }
459
460         if (path != force_profile)
461                 g_free(path);
462         return TRUE;
463
464 error_recovery:
465         g_warning("Recover tmp profile at '%s'", profile_tmp);
466         return FALSE;
467 }
468
469 const char *profile_read(void)
470 /* Read a token from the open profile */
471 {
472         GError *error = NULL;
473         char *token;
474
475         if (!channel)
476                 return "";
477         if (!profile_end)
478                 profile_end = profile_buf;
479         *profile_end = profile_swap;
480
481 seek_profile_end:
482
483         /* Get the next token from the buffer */
484         for (; is_space(*profile_end); profile_end++);
485         token = profile_end;
486         for (; *profile_end && !is_space(*profile_end) && *profile_end != '\n';
487              profile_end++);
488
489         /* If we run out of buffer space, read a new chunk */
490         if (!*profile_end) {
491                 unsigned int token_size;
492                 gsize bytes_read;
493
494                 /* If we are out of space and we are not on the first or
495                    the last byte, then we have run out of things to read */
496                 if (profile_end > profile_buf &&
497                     profile_end < profile_buf + sizeof (profile_buf) - 1) {
498                         profile_swap = 0;
499                         return "";
500                 }
501
502                 /* Move what we have of the token to the start of the buffer,
503                    fill the rest of the buffer with new data and start reading
504                    from the beginning */
505                 token_size = profile_end - token;
506                 if (token_size >= sizeof (profile_buf) - 1) {
507                         g_warning("Oversize token in profile");
508                         return "";
509                 }
510                 memmove(profile_buf, token, token_size);
511                 g_io_channel_read_chars(channel, profile_buf + token_size,
512                                         sizeof (profile_buf) - token_size - 1,
513                                         &bytes_read, &error);
514                 if (error) {
515                         g_warning("Read error: %s", error->message);
516                         return "";
517                 }
518                 if (bytes_read < 1) {
519                         profile_swap = 0;
520                         return "";
521                 }
522                 profile_end = profile_buf;
523                 profile_buf[token_size + bytes_read] = 0;
524                 goto seek_profile_end;
525         }
526
527         profile_swap = *profile_end;
528         *profile_end = 0;
529         return token;
530 }
531
532 int profile_read_next(void)
533 /* Skip to the next line
534    FIXME should skip multiple blank lines */
535 {
536         const char *s;
537
538         do {
539                 s = profile_read();
540         } while (s[0]);
541         if (profile_swap == '\n') {
542                 profile_swap = ' ';
543                 return TRUE;
544         }
545         return FALSE;
546 }
547
548 int profile_write(const char *str)
549 /* Write a string to the open profile */
550 {
551         GError *error = NULL;
552         gsize bytes_written;
553
554         if (profile_read_only || !str)
555                 return 0;
556         if (!channel)
557                 return 1;
558         g_io_channel_write_chars(channel, str, strlen(str), &bytes_written,
559                                  &error);
560         if (error) {
561                 g_warning("Write error: %s", error->message);
562                 return 1;
563         }
564         return 0;
565 }
566
567 int profile_sync_int(int *var)
568 /* Read or write an integer variable depending on the profile mode */
569 {
570         if (profile_read_only) {
571                 const char *s;
572                 int n;
573
574                 s = profile_read();
575                 if (s[0]) {
576                         n = atoi(s);
577                         if (n || (s[0] == '0' && !s[1])) {
578                                 *var = n;
579                                 return 0;
580                         }
581                 }
582         } else
583                 return profile_write(va(" %d", *var));
584         return 1;
585 }
586
587 int profile_sync_short(short *var)
588 /* Read or write a short integer variable depending on the profile mode */
589 {
590         int value = *var;
591
592         if (profile_sync_int(&value))
593                 return 1;
594         if (!profile_read_only)
595                 return 0;
596         *var = (short)value;
597         return 0;
598 }
599
600 void version_read(void)
601 {
602         int version;
603
604         version = atoi(profile_read());
605         if (version != 0)
606                 g_warning("Loading a profile with incompatible version %d "
607                           "(expected %d)", version, PROFILE_VERSION);
608 }
609
610 /*
611         Main and signal handling
612 */
613
614 #define NUM_PROFILE_CMDS (sizeof (profile_cmds) / sizeof (*profile_cmds))
615
616 int profile_line, log_level = 4;
617
618 static char *log_filename = NULL;
619 static FILE *log_file = NULL;
620
621 /* Profile commands table */
622 static struct {
623         const char *name;
624         void (*read_func)(void);
625         void (*write_func)(void);
626 } profile_cmds[] = {
627         { "version",      version_read,      NULL               },
628         { "window",       window_sync,       window_sync        },
629         { "options",      options_sync,      options_sync       },
630         { "recognize",    recognize_sync,    recognize_sync     },
631         { "blocks",       blocks_sync,       blocks_sync        },
632         { "bad_keycodes", bad_keycodes_read, bad_keycodes_write },
633         { "sample",       sample_read,       samples_write      },
634 };
635
636 /* Command line arguments */
637 static GOptionEntry command_line_opts[] = {
638         { "log-level", 0, 0, G_OPTION_ARG_INT, &log_level,
639           "Log threshold (0=silent, 7=debug)", "4" },
640         { "log-file", 0, 0, G_OPTION_ARG_STRING, &log_filename,
641           "Log file to use instead of stdout", PACKAGE ".log" },
642         { "xid", 0, 0, G_OPTION_ARG_NONE, &window_embedded,
643           "Embed the main window (XEMBED)", NULL },
644         { "show-window", 0, 0, G_OPTION_ARG_NONE, &window_force_show,
645           "Show the main window", NULL },
646         { "hide-window", 0, 0, G_OPTION_ARG_NONE, &window_force_hide,
647           "Don't show the main window", NULL },
648         { "window-x", 0, 0, G_OPTION_ARG_INT, &window_force_x,
649           "Horizontal window position", "512" },
650         { "window-y", 0, 0, G_OPTION_ARG_INT, &window_force_y,
651           "Vertical window position", "768" },
652         { "dock-window", 0, 0, G_OPTION_ARG_INT, &window_force_docked,
653           "Docking (0=off, 1=top, 2=bottom)", "0" },
654         { "window-struts", 0, 0, G_OPTION_ARG_NONE, &window_struts,
655           "Reserve space when docking, see manpage", NULL },
656         { "keyboard-only", 0, 0, G_OPTION_ARG_NONE, &keyboard_only,
657           "Show on-screen keyboard only", NULL },
658         { "profile", 0, 0, G_OPTION_ARG_STRING, &force_profile,
659           "Full path to profile file to load", "profile" },
660         { "read-only", 0, 0, G_OPTION_ARG_NONE, &force_read_only,
661           "Do not save changes to the profile", NULL },
662         { "disable-overwrite", 0, 0, G_OPTION_ARG_NONE, &key_disable_overwrite,
663           "Do not modify the keymap", NULL },
664
665         /* Sentinel */
666         { NULL, 0, 0, 0, NULL, NULL, NULL }
667 };
668
669 /* If any of these things happen, try to save and exit cleanly -- gdb is not
670    affected by any of these signals being caught */
671 static int catch_signals[] = {
672         SIGSEGV,
673         SIGHUP,
674         SIGINT,
675         SIGTERM,
676         SIGQUIT,
677         SIGALRM,
678         -1
679 };
680
681 void save_profile(){
682         if (!window_embedded && profile_open_write()) {
683                 unsigned int i;
684
685                 profile_write(va("version %d\n", PROFILE_VERSION));
686                 for (i = 0; i < NUM_PROFILE_CMDS; i++)
687                         if (profile_cmds[i].write_func)
688                                 profile_cmds[i].write_func();
689                 if (profile_close())
690                         g_debug("Profile saved");
691         }
692 }
693
694 void cleanup(void)
695 {
696         static int finished;
697
698         /* Run once */
699         if (finished) {
700                 g_debug("Cleanup already called");
701                 return;
702         }
703         finished = TRUE;
704         g_message("Cleaning up");
705
706         /* Explicit cleanup */
707         cell_widget_cleanup();
708         window_cleanup();
709         key_event_cleanup();
710         if (!window_embedded)
711                 single_instance_cleanup();
712
713         /* Save profile */
714         save_profile();
715
716         /* Close log file */
717         if (log_file)
718                 fclose(log_file);
719 }
720
721 static void catch_sigterm(int sig)
722 /* Terminated by shutdown */
723 {
724         g_warning("Caught signal %d, cleaning up", sig);
725         cleanup();
726         exit(1);
727 }
728
729 static void hook_signals(void)
730 /* Setup signal catchers */
731 {
732         sigset_t sigset;
733         int *ps;
734
735         sigemptyset(&sigset);
736         ps = catch_signals;
737         while (*ps != -1) {
738                 signal(*ps, catch_sigterm);
739                 sigaddset(&sigset, *(ps++));
740         }
741         if (sigprocmask(SIG_UNBLOCK, &sigset, NULL) == -1)
742                 log_errno("Failed to set signal blocking mask");
743 }
744
745 void log_print(const char *format, ...)
746 /* Print to the log file or stderr */
747 {
748         FILE *file;
749         va_list va;
750
751         file = log_file;
752         if (!file) {
753                 if (window_embedded)
754                         return;
755                 file = stderr;
756         }
757         va_start(va, format);
758         vfprintf(file, format, va);
759         va_end(va);
760 }
761
762 void log_func(const gchar *domain, GLogLevelFlags level, const gchar *message)
763 {
764         const char *prefix = "", *postfix = "\n", *pmsg;
765
766         if (level > log_level)
767                 goto skip_print;
768
769         /* Do not print empty messages */
770         for (pmsg = message; *pmsg <= ' '; pmsg++)
771                 if (!*pmsg)
772                         goto skip_print;
773
774         /* Format the message */
775         switch (level & G_LOG_LEVEL_MASK) {
776         case G_LOG_LEVEL_DEBUG:
777                 prefix = "| ";
778                 break;
779         case G_LOG_LEVEL_INFO:
780         case G_LOG_LEVEL_MESSAGE:
781                 if (log_level > G_LOG_LEVEL_INFO) {
782                         prefix = "\n";
783                         postfix = ":\n";
784                 }
785                 break;
786         case G_LOG_LEVEL_WARNING:
787                 if (log_level > G_LOG_LEVEL_INFO)
788                         prefix = "* ";
789                 else if (log_level > G_LOG_LEVEL_WARNING)
790                         prefix = "WARNING: ";
791                 else
792                         prefix = PACKAGE ": ";
793                 break;
794         case G_LOG_LEVEL_CRITICAL:
795         case G_LOG_LEVEL_ERROR:
796                 if (log_level > G_LOG_LEVEL_INFO)
797                         prefix = "* ERROR! ";
798                 else if (log_level > G_LOG_LEVEL_WARNING)
799                         prefix = "ERROR: ";
800                 else
801                         prefix = PACKAGE ": ERROR! ";
802                 break;
803         default:
804                 break;
805         }
806         if (domain)
807                 log_print("%s[%s] %s%s", prefix, domain, message, postfix);
808         else
809                 log_print("%s%s%s", prefix, message, postfix);
810
811 skip_print:
812         if (level & G_LOG_FLAG_FATAL || level & G_LOG_LEVEL_ERROR)
813                 abort();
814 }
815
816 void trace_full(const char *file, const char *func, const char *format, ...)
817 {
818         char buf[256];
819         va_list va;
820
821         if (LOG_LEVEL_TRACE > log_level)
822                 return;
823         va_start(va, format);
824         vsnprintf(buf, sizeof(buf), format, va);
825         va_end(va);
826         log_print(": %s:%s() %s\n", file, func, buf);
827 }
828
829 void log_errno(const char *string)
830 {
831         log_func(NULL, G_LOG_LEVEL_WARNING,
832                  va("%s: %s", string, strerror(errno)));
833 }
834
835 static void second_instance(char *str)
836 {
837         g_debug("Received '%s' from fifo", str);
838         if (str[0] == '0' || str[0] == 'H' || str[0] == 'h')
839                 window_hide();
840         else if (str[0] == '1' || str[0] == 'S' || str[0] == 's')
841                 window_show();
842         else if (str[0] == 'T' || str[0] == 't')
843                 window_toggle();
844 }
845
846 void read_profile(){
847   const gchar *token;
848         if (profile_open_read()) {
849                 profile_line = 1;
850                 g_message("Parsing profile");
851                 do {
852                         unsigned int i;
853
854                         token = profile_read();
855                         if (!token[0]) {
856                                 if (profile_read_next())
857                                         continue;
858                                 break;
859                         }
860                         for (i = 0; i < NUM_PROFILE_CMDS; i++)
861                                 if (!g_ascii_strcasecmp(profile_cmds[i].name,
862                                                         token)) {
863                                         if (profile_cmds[i].read_func)
864                                                 profile_cmds[i].read_func();
865                                         break;
866                                 }
867                         if (i == NUM_PROFILE_CMDS)
868                                 g_warning("Unrecognized profile command '%s'",
869                                           token);
870                         profile_line++;
871                 } while (profile_read_next());
872                 profile_close();
873                 g_debug("Parsed %d commands", profile_line - 1);
874         }
875 }
876
877 int main(int argc, char *argv[])
878 {
879   /*
880         GError *error;
881         const char *token;
882
883         /* Initialize GTK+ 
884         error = NULL;
885         if (!gtk_init_with_args(&argc, &argv,
886                                 "grid-entry handwriting input panel",
887                                 command_line_opts, NULL, &error))
888                 return 0;
889
890         /* Setup log handler 
891         log_level = 1 << log_level;
892         g_log_set_handler(NULL, -1, (GLogFunc)log_func, NULL);
893
894         /* Try to open the log-file 
895         if (log_filename) {
896                 log_file = fopen(log_filename, "w");
897                 if (!log_file)
898                         g_warning("Failed to open log-file '%s'", log_filename);
899         }
900
901         /* See if the program is already running 
902         g_message("Starting " PACKAGE_STRING);
903         create_user_dir();
904         if (!window_embedded &&
905             single_instance_init((SingleInstanceFunc)second_instance,
906                                  window_force_hide ? "0" : "1")) {
907                 gdk_notify_startup_complete();
908                 g_message(PACKAGE_NAME " already running, quitting");
909                 return 0;
910         }
911
912 #ifdef HAVE_GNOME
913         /* Initialize GNOME for the Help button 
914         gnome_program_init(PACKAGE, VERSION, LIBGNOME_MODULE,
915                            argc, argv, NULL);
916 #endif
917
918         /* Component initilization 
919         if (key_event_init(NULL)) {
920                 GtkWidget *dialog;
921
922                 dialog = gtk_message_dialog_new(NULL, GTK_DIALOG_MODAL,
923                                                 GTK_MESSAGE_ERROR,
924                                                 GTK_BUTTONS_OK,
925                                                 "Xtest extension not "
926                                                 "supported");
927                 gtk_window_set_title(GTK_WINDOW(dialog), "Initilization Error");
928                 gtk_message_dialog_format_secondary_text(
929                         GTK_MESSAGE_DIALOG(dialog),
930                         "You\11r Xserver does not support the Xtest extension. "
931                         PACKAGE_NAME " cannot generate keystrokes without it.");
932                 gtk_dialog_run(GTK_DIALOG(dialog));
933                 gtk_widget_destroy(dialog);
934         }
935         recognize_init();
936
937         /* Read profile 
938         read_profile();
939
940         /* After loading samples and block enabled/disabled information,
941            update the samples 
942         update_enabled_samples();
943
944         /* Ensure that if there is a crash, data is saved properly 
945         hook_signals();
946         atexit(cleanup);
947
948         /* Initialize the interface 
949         g_message("Running interface");
950         window_create(NULL);
951
952         /* Startup notification is sent when the first window shows but in if
953            the window was closed during last start, it won't show at all so
954            we need to do this manually 
955         gdk_notify_startup_complete();
956
957         /* Run the interface 
958         if (!samples_loaded() && !keyboard_only)
959                 startup_splash_show();
960         gtk_main();
961         cleanup();
962
963         /* Session statistics 
964         if (characters && inputs && log_level >= G_LOG_LEVEL_DEBUG) {
965                 g_message("Session statistics --");
966                 g_debug("Average strength: %d%%", strength_sum / inputs);
967                 g_debug("Rewrites: %d out of %d inputs (%d%%)",
968                         rewrites, inputs, rewrites * 100 / inputs);
969                 g_debug("Corrections: %d out of %d characters (%d%%)",
970                         corrections, characters,
971                         corrections * 100 / characters);
972                 g_debug("KeyCodes overwrites: %d out of %d uses (%d%%)",
973                         key_overwrites, key_overwrites + key_recycles,
974                         key_recycles + key_overwrites ? key_overwrites * 100 /
975                         (key_recycles + key_overwrites) : 0);
976         }
977
978         return 0;
979         */
980 }