1 /*
2     HOTKEYS - use keys on your multimedia keyboard to control your computer
3     Copyright (C) 2000-2002  Anthony Y P Wong <ypwong@ypwong.org>
4 
5     This program is free software; you can redistribute it and/or modify
6     it under the terms of the GNU General Public License as published by
7     the Free Software Foundation; either version 2 of the License, or
8     (at your option) any later version.
9 
10     This program is distributed in the hope that it will be useful,
11     but WITHOUT ANY WARRANTY; without even the implied warranty of
12     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13     GNU General Public License for more details.
14 
15     You should have received a copy of the GNU General Public License
16     along with this program; if not, write to the Free Software
17     Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
18 
19     $Id: hotkeys.c,v 1.32 2002/12/03 19:26:32 ypwong Exp $
20 
21     Reference:
22     http://www.win.tue.nl/math/dw/personalpages/aeb/linux/kbd/scancodes.html
23 */
24 
25 #if HAVE_CONFIG_H
26 #  include <config.h>
27 #endif
28 #include "common.h"
29 
30 #include <X11/Xosdefs.h>
31 #ifndef X_NOT_STDC_ENV
32 #include <stdlib.h>
33 #else
34 extern char *getenv();
35 #endif
36 
37 #include <stdio.h>
38 #include <unistd.h>
39 #include <stdarg.h>
40 #include <db.h>
41 #include <errno.h>
42 #ifdef HAVE_GETOPT_LONG
43 #include <getopt.h>
44 #endif /* HAVE_GETOPT_LONG */
45 #include <signal.h>
46 #include <syslog.h>
47 #include <pthread.h>
48 #include <sys/types.h>
49 #include <sys/stat.h>
50 #include <sys/wait.h>
51 #include <sys/time.h>
52 
53 /* Mixer related */
54 #include <fcntl.h>
55 #include <sys/ioctl.h>
56 #if defined (__FreeBSD__) || defined(__DragonFly__)
57 #include <sys/soundcard.h>
58 #else
59 #       if defined (__NetBSD__) || defined (__OpenBSD__)
60 #       include <soundcard.h>          /* OSS emulation */
61 #       undef ioctl
62 #       else
63 /* BSDI, Linux, Solaris */
64 #       include <sys/soundcard.h>
65 #       endif                          /* __NetBSD__ or __OpenBSD__ */
66 #endif                          /* __FreeBSD__ */
67 
68 /* CDROM related */
69 //#include <linux/cdrom.h>        /* FIXME: linux specific! */
70 /* APM (suspend/standby) support */
71 //#include "apm.h"
72 #if HAVE_GTK
73   #include "splash.h"
74 #endif
75 
76 #include "hotkeys.h"
77 #include "conf.h"
78 
79 #include <X11/Xmu/Error.h>
80 
81 #define	lowbit(x)	((x) & (-(x)))
82 #define	M(m)	fprintf(stderr,(m))
83 #define	M1(m,a)	fprintf(stderr,(m),(a))
84 
85 /***====================================================================***/
86 
87 char *      dpyName           = NULL;
88 Display *   dpy               = NULL;
89 char *      cfgFileName       = NULL;
90 int     	xkbOpcode         = 0;
91 int     	xkbEventCode      = 0;
92 XkbDescPtr  xkb               = NULL;
93 Bool        dummyErrFlag      = False;
94 
95 Bool            synch         = False;
96 int	            verbose       = 0;
97 int	            loglevel      = 0;
98 Bool            background    = True;
99 Bool            noSplash      = False;
100 
101 FILE *          errorFile     = NULL;
102 
103 char *          progname      = NULL;
104 
105 char *          cdromDevice   = CDROM_DEV;
106 
107 keyboard        kbd;                    /* the keyboard the user is using */
108 
109 #ifdef HAVE_LIBXOSD
110 xosd *          osd           = (xosd*)1;
111 #endif
112 
113 int             volUpAdj      = 2;
114 int             volDownAdj    = -2;
115 
116 /***====================================================================***/
117 
118 void
usage(int argc,char * argv[])119 usage(int argc, char *argv[])
120 {
121     char cmplStr[256] = {'\0'};
122     int  first = 1;
123 
124 #ifdef HAVE_LIBXOSD
125     strcat(cmplStr,"XOSD");
126     first = 0;
127 #endif
128 #if HAVE_GTK
129     if ( first == 0 )
130     {
131         strcat(cmplStr,",GTK");
132     }
133     else
134     {
135         strcat(cmplStr,"GTK");
136         first = 0;
137     }
138 #endif
139 
140     printf("HOTKEYS v" VERSION " -- use the hotkeys on your Internet/multimedia keyboard to control your computer");
141     if (strlen(cmplStr))
142         printf(" (compile option(s): %s)", cmplStr);
143     printf("\n");
144     printf("Usage: %s [options...]\n", argv[0]);
145     printf("Legal options:\n");
146 #ifdef HAVE_GETOPT_LONG
147     printf("    -t, --type=TYPE          Specify the keyboard type (refer to -l)\n");
148     printf("    -l, --kbd-list           Show all supported keyboards\n");
149     printf("    -d, --cdrom-dev=DEVICE   Specify the CDROM/DVDROM device, or 'none'\n");
150 #ifdef HAVE_LIBXOSD
151     printf("    -o, --osd=STATE          Turn off/on on-screen display\n");
152 #endif
153     printf("    -L, --loglevel=LEVEL     Set the log level in syslog [0-7]\n");
154     printf("    -b, --no-background      Do not run in the background\n");
155     printf("    -F, --fix-vmware=TIME    Use this if you use vmware at the same time\n");
156 #if HAVE_GTK
157     printf("    -Z, --no-splash          No splash screen\n");
158 #endif
159     printf("    -h, --help               Print this message\n");
160 /*
161     M("-cfg <file>          Specify a config file\n");
162     M("-d[isplay] <dpy>     Specify the display to watch\n");
163     M("-v                   Print verbose messages\n");
164 */
165 #else /* HAVE_GETOPT_LONG */
166     printf("    -t TYPE       Specify the keyboard type (refer to -l)\n");
167     printf("    -l            Show all supported keyboards\n");
168     printf("    -d DEVICE     Specify the CDROM/DVDROM device, 'none' for no device\n");
169 #ifdef HAVE_LIBXOSD
170     printf("    -o STATE      Turn off/on on-screen display\n");
171 #endif
172     printf("    -L LEVEL      Set the log level in syslog [0-7]\n");
173     printf("    -b            Do not run in the background\n");
174     printf("    -F TIME       Use this option if vmware is used concurrently\n");
175 #if HAVE_GTK
176     printf("    -Z            No splash screen\n");
177 #endif
178     printf("    -h            Print this message\n");
179 #endif /* HAVE_GETOPT_LONG */
180 }
181 
182 
183 void
showKbdList(int argc,char * argv[])184 showKbdList(int argc, char *argv[])
185 {
186     DIR*            dir;
187     struct dirent*  ent;
188     char*           homedir;
189     char*           h;
190     int             flag = 0;
191     int             len;
192 
193 #ifdef HAVE_GETOPT_LONG
194     printf("Supported keyboards: (with corresponding options to --kbd-list or -l)\n");
195 #else
196     printf("Supported keyboards: (with corresponding options to -l)\n");
197 #endif
198 
199     /* Read the definition files in $HOME/.hotkeys */
200     if ( (h = getenv("HOME")) != NULL )
201     {
202         homedir = XMALLOC( char, strlen(h) + strlen("/.hotkeys") + 1 );
203         strcpy( homedir, h );
204         strcat( homedir, "/.hotkeys" );
205         if ( ( dir = opendir(homedir) ) != NULL )
206         {
207             while ( ent = readdir(dir) )
208             {
209                 len = NLENGTH(ent);
210                 /* Show all files that ends with ".def" */
211                 if ( len > 4 &&
212                      strncmp( &(ent->d_name[len-4]), ".def", 4 ) == 0 )
213                 {
214                     ent->d_name[ len-4 ] = '\0';
215                     if ( setKbdType(NULL, ent->d_name) == True )
216                     {
217                         printf( "    %s\t- %s\n", ent->d_name, kbd.longName );
218                         flag = 1;
219                     }
220                 }
221             }
222         }
223         XFREE(homedir);
224     }
225     else
226     {
227         homedir = NULL;
228     }
229 
230     /* Read the default location: SHAREDIR */
231     if ( ( dir = opendir(SHAREDIR) ) != NULL )
232     {
233         while ( ent = readdir(dir) )
234         {
235             len = NLENGTH(ent);
236             /* Show all files that ends with ".def" in SHAREDIR */
237             if ( len > 4 &&
238                  strncmp( &(ent->d_name[len-4]), ".def", 4 ) == 0 )
239             {
240                 ent->d_name[ len-4 ] = '\0';
241                 if ( setKbdType(NULL, ent->d_name) == True )
242                 {
243                     printf( "    %s\t- %s\n", ent->d_name, kbd.longName );
244                     flag = 1;
245                 }
246             }
247         }
248     }
249 
250     if ( flag == 0 )
251     {
252         printf( "NONE FOUND. Probably due to an unsuccessful installation.\n");
253     }
254 
255     closedir(dir);
256 }
257 
258 
259 static Bool
setKbdType(const char * prog,const char * type)260 setKbdType(const char* prog, const char* type)
261 {
262     char*       defname;
263     char*       h;
264     Bool        ret = True;
265 
266     /* Make up the complete filename, try the user's HOME first */
267     if ( (h = getenv("HOME")) != NULL )
268     {
269         /* Make up the complete filename */
270         defname = XMALLOC( char, strlen(h) +
271                                  strlen("/.hotkeys/") +
272                                  strlen(type) + 5 );
273         strcpy( defname, h );
274         strcat( defname, "/.hotkeys/" );
275         strcat( defname, type );
276         strcat( defname, ".def" );
277         if ( testReadable(defname) )     /* if the file exists... */
278         {
279             ret = readDefFile( defname );
280         }
281         else
282         {
283             /* Now try if it exists in SHAREDIR or not */
284             XFREE( defname );
285             defname = XMALLOC( char, strlen(SHAREDIR)+strlen(type)+6 );
286             strcpy( defname, SHAREDIR );
287             strcat( defname, "/" );
288             strcat( defname, type );
289             strcat( defname, ".def" );
290 
291             if ( testReadable(defname) )     /* if the file exists... */
292             {
293                 ret = readDefFile( defname );
294             }
295             else
296             {
297                 /* No matching keyboard type */
298                 if ( prog != NULL )
299                 {
300                     uInfo("Keyboard type `%s' is not supported.\n"
301                           "Use %s --kbd-list to list all supported keyboard\n",
302                           type, prog);
303                     exit(0);
304                 }
305                 else
306                 {
307                     ret = False;
308                 }
309             }
310         }
311     }
312 
313     XFREE( defname );
314     return ret;
315 }
316 
317 /* Option is --cdrom-dev or -d */
318 void
setCDROMDevice(char * optarg)319 setCDROMDevice(char* optarg)
320 {
321     int fd;
322 
323     if ( strncasecmp( optarg, "none", 4 ) == 0 )
324     {
325         cdromDevice = NULL;
326         return;
327     }
328     if ( (fd = open( optarg, O_RDONLY|O_NONBLOCK )) == -1)
329     {
330         uInfo("Unable to open `%s', fall back to %s\n", optarg, CDROM_DEV);
331     }
332     else
333     {
334         if ( ( cdromDevice = (char*) xstrdup(optarg) ) == NULL )
335         {
336             uError("Insufficient memory");
337             bailout();
338         }
339     }
340     close (fd);
341 }
342 
343 
344 static void
setLoglevel(int level)345 setLoglevel(int level)
346 {
347     /* Map the supplied level to the one defined in syslog.h */
348     switch (level)
349     {
350         case 0:     loglevel = LOG_EMERG;       break;
351         case 1:     loglevel = LOG_ALERT;       break;
352         case 2:     loglevel = LOG_CRIT;        break;
353         case 3:     loglevel = LOG_ERR;         break;
354         case 4:     loglevel = LOG_WARNING;     break;
355         case 5:     loglevel = LOG_NOTICE;      break;
356         case 6:     loglevel = LOG_INFO;        break;
357         case 7:     loglevel = LOG_DEBUG;       break;
358         default:    loglevel = LOG_ERR;         break;
359     }
360     setlogmask( LOG_UPTO(loglevel) );
361 }
362 
363 #ifdef HAVE_LIBXOSD
364 static void
toggleOSD(char * optarg)365 toggleOSD(char* optarg)
366 {
367     int arg = 0;
368 
369     if ( strncasecmp( optarg, "on",  2 ) == 0 ||
370          strncasecmp( optarg, "1",   1 ) == 0 ||
371          strncasecmp( optarg, "yes", 3 ) == 0 )
372     {
373         arg = 1;
374     }
375     else
376     if ( strncasecmp( optarg, "off", 3 ) != 0 &&
377          strncasecmp( optarg, "0",   1 ) != 0 &&
378          strncasecmp( optarg, "no",  2 ) != 0 )
379     {
380         uInfo("Unknown argument: %s, assuming on\n", optarg);
381         arg = 1;
382     }
383 
384     if ( !arg )
385     {
386         osd = NULL;
387     }
388 }
389 #endif /* HAVE_LIBXOSD */
390 
391 /***====================================================================***/
392 
393 static Bool
parseArgs(int argc,char * argv[])394 parseArgs(int argc, char *argv[])
395 {
396     int     c, i;
397     int     digit_optind = 0;
398 
399     const char *flags = "hbt:d:lz:vL:F:"
400 #ifdef HAVE_LIBXOSD
401         "o:"
402 #endif
403 #if HAVE_GTK
404         "Z"
405 #endif
406     ;
407 #ifdef HAVE_GETOPT_LONG
408     int this_option_optind = optind ? optind : 1;
409     int option_index = 0;
410     static struct option long_options[] =
411     {
412         {"help",            0, 0, 'h'},
413         {"background",      0, 0, 'b'},
414         {"type",            1, 0, 't'},
415         {"cdrom-dev",       1, 0, 'd'},
416         {"kbd-list",        0, 0, 'l'},
417         {"verbose",         0, 0, 'v'},
418         {"loglevel",        1, 0, 'L'},
419 #ifdef HAVE_LIBXOSD
420         {"osd",             1, 0, 'o'},
421 #endif
422         {"fix-vmware",      2, 0, 'F'},
423 #if HAVE_GTK
424         {"no-splash",       0, 0, 'Z'},
425 #endif
426         {0, 0, 0, 0}
427     };
428 #endif /* HAVE_GETOPT_LONG */
429 
430     if ( strrchr( argv[0], '/' ) ) {
431         /* strip the directories */
432         progname = (char*) strrchr( argv[0], '/' ) + 1;
433     } else {
434         progname = argv[0];
435     }
436 
437 #ifdef HAVE_GETOPT_LONG
438     while ((c = getopt_long(argc, argv, flags, long_options, &option_index)) != -1) {
439 #else
440     while ((c = getopt(argc, argv, flags)) != -1) {
441 #endif /* HAVE_GETOPT_LONG */
442 
443         switch (c)
444         {
445 #if 0
446 #ifdef HAVE_GETOPT_LONG
447           case 0:
448 
449               if ( strncmp( long_options[option_index].name, "type", 4 ) == 0 )
450               {
451                   setKbdType(argv[0], optarg);
452               } else
453               if ( strncmp( long_options[option_index].name, "cdrom-dev", 9 ) == 0 )
454               {
455                   setCDROMDevice(optarg);
456               }
457               break;
458 #endif /* HAVE_GETOPT_LONG */
459 #endif
460 
461           case 't':
462               setConfig( "Kbd", optarg, 0 );
463               break;
464           case 'd':
465               setConfig( "CDROM", optarg, 0 );
466               break;
467           case 'h':
468               usage(argc, argv);
469               exit(0);
470               break;
471           case 'b':
472               background = False;
473               break;
474           case 'l':
475               showKbdList(argc, argv);
476               exit(0);
477               break;
478           case 'L':
479               setLoglevel(atoi(optarg));
480               break;
481 #ifdef HAVE_LIBXOSD
482           case 'o':
483               toggleOSD(optarg);
484               break;
485 #endif
486           case 'F':
487               fixVMware(optarg);
488               break;
489 #if HAVE_GTK
490           case 'Z':
491               noSplash=True;
492               break;
493 #endif
494           case 'z':
495               break;
496           case '?':
497               break;
498 
499         }
500     }
501 
502     if ( getConfig("Kbd")[0] )
503     {
504         setKbdType( argv[0], getConfig("Kbd") );
505     }
506     else
507     {
508         uInfo("You must set the keyboard type, use %s -t <type> to set it.\n", argv[0]);
509         exit(1);
510     }
511 
512     if ( getConfig("CDROM")[0] )
513         setCDROMDevice( getConfig("CDROM") );
514 
515     /* check for a single additional argument */
516     if ((argc - optind) > 1) {
517         fprintf( stderr, "%s: too many arguments\n", argv[0] );
518         bailout();
519     }
520 
521     return True;
522 }
523 
524 /* Copied from xkbevd */
525 static Display *
526 GetDisplay(char* program, char* dpyName, int* opcodeRtrn, int* evBaseRtrn)
527 {
528     int	mjr,mnr,error;
529     Display	*dpy;
530 
531     mjr = XkbMajorVersion;
532     mnr = XkbMinorVersion;
533     dpy = XkbOpenDisplay(dpyName,evBaseRtrn,NULL,&mjr,&mnr,&error);
534     if (dpy == NULL)
535     {
536         switch (error)
537         {
538             case XkbOD_BadLibraryVersion:
539                 uInfo("%s was compiled with XKB version %d.%02d\n",
540                         program,XkbMajorVersion,XkbMinorVersion);
541                 uInfo("X library supports incompatible version %d.%02d\n",
542                         mjr,mnr);
543                 break;
544             case XkbOD_ConnectionRefused:
545                 uInfo("Cannot open display \"%s\"\n",dpyName);
546                 break;
547             case XkbOD_NonXkbServer:
548                 uInfo("XKB extension not present on %s\n",dpyName);
549                 break;
550             case XkbOD_BadServerVersion:
551                 uInfo("%s was compiled with XKB version %d.%02d\n",
552                         program,XkbMajorVersion,XkbMinorVersion);
553                 uInfo("Server %s uses incompatible version %d.%02d\n",
554                         dpyName,mjr,mnr);
555                 break;
556             default:
557                 uInternalError("Unknown error %d from XkbOpenDisplay\n",error);
558         }
559     }
560     else
561     {
562         if (synch)
563             XSynchronize(dpy,True);
564         if (opcodeRtrn)
565             XkbQueryExtension(dpy,opcodeRtrn,evBaseRtrn,NULL,&mjr,&mnr);
566     }
567     return dpy;
568 }
569 
570 void
571 bailout(void)
572 {
573     if ( dpy != NULL )
574 	XCloseDisplay(dpy);
575     uInfo("Bailing out...\n");
576     exit(1);
577 }
578 
579 
580 /*
581  * adj is a percentage, can be +ve or -ve for louder and softer resp.
582  */
583 static int
584 adjustVol(int adj)
585 {
586     int         mixer_fd = -1, cdrom_fd = -1;
587     int         master_vol, cd_vol;
588 //    struct cdrom_volctrl cdrom_vol;
589     int         left, right;
590     static struct timeval last_time;
591     struct timeval this_time;
592     static int  multiplier = 0;   /* if 0 then it's first time to come in this func */
593     int         ret = 0;
594 
595     int sign = adj > 0 ? 1 : -1;
596 
597     if ( adj == 0 )
598         return 0;
599 
600     if ( multiplier == 0 )
601     {
602         gettimeofday(&last_time, NULL);
603         multiplier = sign;
604     }
605     else
606     {
607         gettimeofday(&this_time, NULL);
608         if ( (( adj > 0 && multiplier > 0 ) || ( adj < 0 && multiplier < 0 )) &&
609              ( (this_time.tv_sec - last_time.tv_sec) * 1000000 +
610              this_time.tv_usec - last_time.tv_usec < 500000 ) )
611         {
612             multiplier += sign;
613             adj *= abs(multiplier);
614             if ( adj > 10 )     /* should be a MAX value defined somewhere else FIXME */
615                 adj = 10;
616             else if ( adj < -10 )
617                 adj = -10;
618         }
619         else
620         {
621             multiplier = sign;
622         }
623         last_time.tv_sec = this_time.tv_sec;
624         last_time.tv_usec = this_time.tv_usec;
625     }
626 
627     /* open the mixer device */
628     if ( (mixer_fd = open( MIXER_DEV, O_RDWR|O_NONBLOCK )) == -1 )
629     {
630         uError("Unable to open `%s'", MIXER_DEV);
631     }
632     else
633     {
634         if ( SOUND_IOCTL(mixer_fd, SOUND_MIXER_READ_VOLUME, &master_vol) == -1)
635         {
636             uError("Unable to read the volume of `%s'", MIXER_DEV);
637             ret = -1;
638         }
639         else
640         {
641             /* Set the master volume */
642             left = (master_vol & 0xFF) + adj;
643             right = ((master_vol >> 8) & 0xFF) + adj;
644             left = (left>MAXLEVEL) ? MAXLEVEL : ((left<0) ? 0 : left);
645             right = (right>MAXLEVEL) ? MAXLEVEL : ((right<0) ? 0 : right);
646             master_vol = left + (right << 8);
647 
648             if (SOUND_IOCTL(mixer_fd, SOUND_MIXER_WRITE_VOLUME, &master_vol) == -1)
649             {
650                 uError("Unable to set the master volume");
651                 ret = -1;
652             }
653 #ifdef HAVE_LIBXOSD
654             else if (osd)
655             {
656                 xosd_display(osd, 0, XOSD_string, "Volume");
657                 xosd_display(osd, 1, XOSD_percentage, (((left+right)/2)*100/MAXLEVEL));
658             }
659 #endif
660         }
661 #if 0
662         if ( SOUND_IOCTL(mixer_fd, SOUND_MIXER_READ_CD, &cd_vol) == -1)
663         {
664             uError("Unable to read the CD volume of `%s'", MIXER_DEV);
665             ret = -1; goto LEAVE;
666         }
667         else
668         {
669             /* Set the CD volume */
670             left = (cd_vol & 0xFF) + adj;
671             right = ((cd_vol >> 8) & 0xFF) + adj;
672             left = (left>MAXLEVEL) ? MAXLEVEL : ((left<0) ? 0 : left);
673             right = (right>MAXLEVEL) ? MAXLEVEL : ((right<0) ? 0 : right);
674             cd_vol = left + (right << 8);
675 
676             if (SOUND_IOCTL(mixer_fd, SOUND_MIXER_WRITE_CD, &cd_vol) == -1)
677             {
678                 uError("Unable to set the CD volume");
679                 ret = -1; goto LEAVE;
680             }
681         }
682 #endif
683     }
684 
685     /* open the cdrom/dvdrom drive device */
686 /***** ANDY: No CD support yet
687     if ( cdromDevice != NULL )
688     {
689         if ( (cdrom_fd = open( cdromDevice, O_RDONLY|O_NONBLOCK )) == -1 )
690         {
691             uError("Unable to open `%s'", cdromDevice);
692         }
693         else
694         {
695             || read the cdrom volume ||
696             if ( ioctl(cdrom_fd, CDROMVOLREAD, &cdrom_vol) == -1 )
697             {
698                 uError("Unable to read the CDROM volume of `%s'", cdromDevice);
699                 ret = -1;
700             }
701             else
702             {
703                 || Set the CDROM volume ||
704                 int     t;
705                 float   myAdj;
706                 myAdj = 0xFF / 100.0 * adj;
707                 t = cdrom_vol.channel0 + myAdj;
708                 cdrom_vol.channel0 = (t < 0 ? 0 : (t > 0xFF ? 0xFF : t));
709                 t = cdrom_vol.channel1 + myAdj;
710                 cdrom_vol.channel1 = (t < 0 ? 0 : (t > 0xFF ? 0xFF : t));
711 #if 0
712                 t = cdrom_vol.channel2 + myAdj;
713                 cdrom_vol.channel2 = (t < 0 ? 0 : (t > 0xFF ? 0xFF : t));
714                 t = cdrom_vol.channel3 + myAdj;
715                 cdrom_vol.channel3 = (t < 0 ? 0 : (t > 0xFF ? 0xFF : t));
716 #endif
717                 if ( ioctl(cdrom_fd, CDROMVOLCTRL, &cdrom_vol) == -1 )
718                 {
719                     uError("Unable to set the volume of %s", cdromDevice);
720                     ret = -1;
721                 }
722             }
723         }
724     }
725 END Andy *****/
726 
727     if (mixer_fd != -1)     close(mixer_fd);
728 //Andy    if (cdrom_fd != -1)     close(cdrom_fd);
729 
730     return ret;
731 }
732 
733 
734 /*
735  *  Mute or un-mute the /dev/mixer master volume and CDROM drive's volume
736  */
737 static int
738 doMute(void)
739 {
740     static Bool             muted = False;
741     static int              last_mixer_vol, last_cd_vol;
742 //Andy    static struct cdrom_volctrl last_cdrom_vol;
743 
744     int                     vol, cd_vol;
745 //Andy    struct cdrom_volctrl    cdrom_vol;
746     int                     mixer_fd = -1, cdrom_fd = -1;
747 
748     short ret = 0;      /* return value */
749 
750     /* open the mixer device */
751     if ( (mixer_fd = open( MIXER_DEV, O_RDWR|O_NONBLOCK )) == -1 )
752     {
753         uError("Unable to open `%s'", MIXER_DEV);
754     }
755     /* open the cdrom/dvdrom drive device */
756 /***** Andy: No CD support
757     if ( cdromDevice != NULL )
758     {
759         if ( (cdrom_fd = open( cdromDevice, O_RDONLY|O_NONBLOCK )) == -1 )
760         {
761             uError("Unable to open `%s'", cdromDevice);
762         }
763     }
764 END Andy *****/
765 
766     if ( muted )
767     {
768         /* Un-mute them */
769         if (mixer_fd != -1)
770         {
771             if (SOUND_IOCTL(mixer_fd, SOUND_MIXER_WRITE_VOLUME, &last_mixer_vol) == -1)
772             {
773                 uError("Unable to un-mute the mixer");
774                 ret = -1;
775             }
776             else
777             {
778                 muted = False;
779 #ifdef HAVE_LIBXOSD
780                 if (osd)
781                 {
782                     int left = last_mixer_vol & 0xFF,
783                         right = (last_mixer_vol >> 8) & 0xFF;
784                     xosd_display(osd, 0, XOSD_string, "Unmute");
785 //                    xosd_display(osd, 1, XOSD_percentage, (((left+right)/2)*100/MAXLEVEL));
786                 }
787 #endif
788             }
789         }
790 /***** Andy: No CD support
791 #if 0
792         if (SOUND_IOCTL(mixer_fd, SOUND_MIXER_WRITE_CD, &last_cd_vol) == -1)
793         {
794             uError("Unable to un-mute the CD volume");
795             ret = -1;
796         } else
797             muted = False;
798 #endif
799         if (cdrom_fd != -1)
800         {
801             if ( ioctl(cdrom_fd, CDROMVOLCTRL, &last_cdrom_vol) == -1 )
802             {
803                 uError("Unable to un-mute `%s'", cdromDevice);
804                 ret = -1;
805             } else
806                 muted = False;
807         }
808 End Andy *****/
809     }
810     else    /* ! muted */
811     {
812 
813         /* Read and store the mixer volume, do not try to mute them
814          * if we cannot read any of their values. */
815 
816         if (mixer_fd != -1)
817         {
818             if ( SOUND_IOCTL(mixer_fd, SOUND_MIXER_READ_VOLUME, &last_mixer_vol) == -1)
819             {
820                 uError("Unable to read the mixer volume of `%s'", MIXER_DEV);
821                 ret = -1;
822             }
823             else
824             {
825                 /* Mute it! */
826                 vol = 0;
827                 if (SOUND_IOCTL(mixer_fd, SOUND_MIXER_WRITE_VOLUME, &vol) == -1)
828                 {
829                     uError("Unable to mute mixer volume of `%s'", MIXER_DEV);
830                     ret = -1;
831                 }
832                 else
833                 {
834                     muted = True;
835 #ifdef HAVE_LIBXOSD
836                     if (osd)
837                     {
838                         xosd_set_timeout(osd, -1);
839                         xosd_display(osd, 0, XOSD_string, "Mute");
840                         xosd_display(osd, 1, XOSD_string, "");
841                     }
842 #endif
843                 }
844             }
845         }
846 /***** Andy: No CD support
847 #if 0
848         if ( SOUND_IOCTL(mixer_fd, SOUND_MIXER_READ_CD, &last_cd_vol) == -1)
849         {
850             uError("Unable to read the CD volume of `%s'", MIXER_DEV);
851             ret = -1; goto LEAVE2;
852         }
853         else
854         {
855             if (SOUND_IOCTL(mixer_fd, SOUND_MIXER_WRITE_CD, &vol) == -1)
856             {
857                 uError("Unable to mute CD volume of `%s'", MIXER_DEV);
858                 ret = -1;
859             } else
860                 muted = True;
861         }
862 #endif
863 End Andy *****/
864         /* read and store the cdrom volume */
865 /***** Andy: No CD support
866         if (cdrom_fd != -1)
867         {
868             if ( ioctl(cdrom_fd, CDROMVOLREAD, &last_cdrom_vol) == -1 )
869             {
870                 uError("Unable to read the CDROM volume of `%s'", cdromDevice);
871                 ret = -1;
872             }
873             else
874             {
875                 || Set the volume to 0. FIXME: is this linux specific? Do
876                  * other platforms also have 4 channels? ||
877                 cdrom_vol.channel0 = cdrom_vol.channel1 = cdrom_vol.channel2 =
878                     cdrom_vol.channel3 = 0;
879                 if ( ioctl(cdrom_fd, CDROMVOLCTRL, &cdrom_vol) == -1 )
880                 {
881                     uError("Unable to mute `%s'", cdromDevice);
882                     ret = -1;
883                 } else
884                     muted = True;
885             }
886         }
887 End Andy *****/
888     }
889 
890     if (mixer_fd != -1)   close(mixer_fd);
891 //Andy    if (cdrom_fd != -1)   close(cdrom_fd);
892 
893     return ret;
894 }
895 
896 static int
897 ejectDisc(void)
898 {
899 /***** Andy: No CD support
900     int fd, status;
901 
902     if ( cdromDevice == NULL )
903         return 0;
904 
905     || the idea of this code is from xine's vcd plugin, mostly linux
906        specific FIXME ||
907     if ( (fd = open( cdromDevice, O_RDONLY | O_NONBLOCK)) > -1 ) {
908         status = ioctl(fd, CDROM_DRIVE_STATUS, CDSL_CURRENT);
909         switch (status)
910         {
911             || Looks like ATAPI drives doesn't return CDS_TRAY_OPEN,
912              * at least it's the case on my ASUS DVD drive... ||
913             case CDS_TRAY_OPEN:
914 #ifdef HAVE_LIBXOSD
915                 if ( osd )
916                 {
917                     xosd_display(osd, 0, XOSD_string, "Close tray");
918                     xosd_display(osd, 1, XOSD_string, "");
919                 }
920 #endif
921                 if ( (ioctl(fd, CDROMCLOSETRAY)) != 0 ) {
922                     SYSLOG( LOG_NOTICE, "CDROMCLOSETRAY failed: %s\n",
923                             strerror(errno) );
924                 }
925                 break;
926             case CDS_DISC_OK:
927             case CDS_NO_DISC:
928 #ifdef HAVE_LIBXOSD
929                 if ( osd )
930                 {
931                     xosd_display(osd, 0, XOSD_string, "Eject");
932                     xosd_display(osd, 1, XOSD_string, "");
933                 }
934 #endif
935                 if ( (ioctl(fd, CDROMEJECT)) != 0 ) {
936                     SYSLOG( LOG_NOTICE, "CDROMEJECT failed: %s\n",
937                             strerror(errno) );
938                 }
939                 break;
940             case CDS_NO_INFO:
941             case CDS_DRIVE_NOT_READY:
942             default:
943                 || Ignore ||
944                 break;
945         }
946         close(fd);
947         return 0;
948     }
949     else
950     {
951         SYSLOG(LOG_NOTICE, "CDROM_DRIVE_STATUS failed: %s\n", strerror(errno));
952         return -1;
953     }
954 End Andy *****/
955 }
956 
957 
958 static int
959 launchApp(int keycode)
960 {
961     int     pid;
962     char*   type = NULL;
963     int     i;
964 
965     /* Given the keycode, we find the string corresponding to it */
966     for ( i = 0; i < NUM_PREDEF_HOTKEYS; i++ )
967     {
968         if ( keycode == (kbd.defCmds)[i].key )
969         {
970             type = defStr[i].name;
971             break;
972         }
973     }
974     if ( type == NULL )
975         return -1;  /* this keycode is not associated with any app */
976 
977     if ( (pid=fork2()) == -1 )
978     {
979         uInfo("Cannot launch the %s\n", type);
980     }
981     else if ( pid == 0 )
982     {
983         /* Construct the argument arrays */
984         char**  arg_array;
985         char*   c = getConfig(type);
986         char*   cc;
987         int     noOfArgs = 1;   /* including the NULL element */
988         int     i = 0;
989         do {
990             c = strchr( c+1, ' ' );
991             noOfArgs++;
992         } while ( c != NULL );
993         arg_array = XMALLOC( char*, noOfArgs );
994         /* dup needed since strtok modifies the string */
995         c = (char*) xstrdup( getConfig(type) );
996         cc = c;         /* for free() */
997         arg_array[0] = strtok( c, " " );
998         while ( arg_array[i] != NULL )
999         {
1000             i++;
1001             arg_array[i] = strtok( NULL, " " );
1002         }
1003 
1004         if ( execvp(arg_array[0], arg_array) == -1 )
1005         {
1006             uError("Cannot launch %s", type);
1007             XFREE(cc);
1008             XFREE(arg_array);
1009             _exit(-1);
1010         }
1011     }
1012     else
1013     {
1014 #ifdef HAVE_LIBXOSD
1015         if ( osd )
1016         {
1017             xosd_display(osd, 0, XOSD_string, type);
1018             xosd_display(osd, 1, XOSD_string, "");
1019         }
1020 #endif
1021     }
1022 
1023     return 0;
1024 }
1025 
1026 
1027 int
1028 sleepState(int mode)
1029 {
1030 /***** Andy: No APM support
1031 #ifdef USE_APMD
1032     switch (mode)
1033     {
1034     //  case SUSPEND:
1035         error = system("apm -s");
1036         break;
1037     //  case STANDBY:
1038         error = system("apm -S");
1039         break;
1040       default:
1041         error = 0;
1042         break;
1043     }
1044 #else
1045     int fd;
1046     int error;
1047 
1048     if ( (fd = apm_open()) < 0 )
1049     {
1050         uError("unable to open APM device");
1051         exit(1);
1052     }
1053     switch (mode)
1054     {
1055      // case SUSPEND:
1056         error = apm_suspend(fd);
1057         break;
1058      // case STANDBY:
1059         error = apm_standby(fd);
1060         break;
1061       default:
1062         error = 0;
1063         break;
1064     }
1065     apm_close(fd);
1066 #endif
1067 End Andy *****/
1068 /* USE_APMD */
1069 }
1070 
1071 
1072 static void
1073 lookupUserCmd(const int keycode)
1074 {
1075     int     i;
1076 
1077     for ( i = 0; i < kbd.noOfCustomCmds; i++ )
1078     {
1079         if ( kbd.customCmds[i].keycode == keycode )
1080         {
1081             int pid;
1082 
1083             if ( (pid=fork2()) == -1 )
1084             {
1085                 uInfo("Cannot launch \"%s\"\n", kbd.customCmds[i].desc);
1086             }
1087             else if ( pid == 0 )
1088             {
1089                 /* Construct the argument arrays */
1090                 char**  arg_array;
1091                 char*   c = kbd.customCmds[i].command;
1092                 char*   cc;
1093                 int     noOfArgs = 1;   /* including the NULL element */
1094                 int     j = 0;
1095                 do {
1096                     c = strchr( c+1, ' ' );
1097                     noOfArgs++;
1098                 } while ( c != NULL );
1099                 arg_array = XMALLOC( char*, noOfArgs );
1100                 /* dup needed since strtok modifies the string */
1101                 c = (char*) xstrdup( kbd.customCmds[i].command );
1102                 cc = c;         /* cc is for free() later */
1103                 arg_array[0] = strtok( c, " " );
1104                 while ( arg_array[j] != NULL )
1105                 {
1106                     j++;
1107                     arg_array[j] = strtok( NULL, " " );
1108                 }
1109 
1110                 if ( execvp(arg_array[0], arg_array) == -1 )
1111                 {
1112                     uError("Cannot launch \"%s\"", kbd.customCmds[i].desc);
1113                     XFREE(cc);
1114                     XFREE(arg_array);
1115                     _exit(-1);
1116                 }
1117             }
1118             else
1119             {
1120 #ifdef HAVE_LIBXOSD
1121                 if ( osd )
1122                 {
1123                     xosd_display(osd, 0, XOSD_string, kbd.customCmds[i].desc);
1124                     xosd_display(osd, 1, XOSD_string, "");
1125                 }
1126 #endif
1127                 break;  /* break the for loop */
1128             }
1129         }
1130     }
1131 }
1132 
1133 /***====================================================================***/
1134 
1135 #ifdef DEBUG
1136 static void
1137 printXkbActionMessage(FILE* file,XkbEvent* xkbev)
1138 {
1139     XkbActionMessageEvent *msg= &xkbev->message;
1140     SYSLOG( LOG_DEBUG, "message: 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x\n",
1141                                         msg->message[0],msg->message[1],
1142                                         msg->message[2],msg->message[3],
1143                                         msg->message[4],msg->message[5]);
1144     SYSLOG( LOG_DEBUG, "key %d, event: %s,  follows: %s\n",msg->keycode,
1145                                      (msg->press?"press":"release"),
1146                                      (msg->key_event_follows?"yes":"no"));
1147     return;
1148 }
1149 #endif
1150 
1151 
1152 void
1153 uError(char* s,...)
1154 {
1155     va_list ap;
1156 
1157     va_start(ap, s);
1158     fprintf(errorFile,"%s: ", progname);
1159     vfprintf(errorFile,s,ap);
1160     fprintf(errorFile,": %s\n", strerror(errno));
1161     fflush(errorFile);
1162     va_end(ap);
1163     return;
1164 }
1165 
1166 void
1167 uInfo(char* s,...)
1168 {
1169     va_list ap;
1170 
1171     va_start(ap, s);
1172     fprintf(errorFile,"%s: ", progname);
1173     vfprintf(errorFile,s,ap);
1174     fflush(errorFile);
1175     va_end(ap);
1176     return;
1177 }
1178 
1179 void
1180 uInternalError(char* s,...)
1181 {
1182     va_list ap;
1183 
1184     va_start(ap, s);
1185     fprintf(errorFile,"%s: internal error: ", progname);
1186     vfprintf(errorFile,s,ap);
1187     fflush(errorFile);
1188     va_end(ap);
1189     return;
1190 }
1191 
1192 
1193 /*
1194  * Test whether filename is readable
1195  */
1196 int
1197 testReadable(const char* filename)
1198 {
1199     int fd;
1200 
1201     if ( (fd = open(filename, O_RDONLY)) == -1 )
1202     {
1203         return 0;
1204     }
1205     else
1206     {
1207         close(fd);
1208         return 1;
1209     }
1210 }
1211 
1212 /***====================================================================***/
1213 static int dummy() { /* grin */ }
1214 
1215 #include <X11/Xlibint.h>
1216 static int
1217 dummyHandler(Display* d, XErrorEvent* ev)
1218 {
1219     /* Code below is mainly from the function XmuPrintDefaultErrorMessage */
1220 
1221     _XExtension *ext = (_XExtension *)NULL;
1222     char major_op[128];
1223     char minor_op[128];
1224     char mesg[128];
1225 
1226     /* Get string of Major and Minor opcode */
1227     /* XXX this is non-portable */
1228     for (ext = d->ext_procs;
1229          ext && (ext->codes.major_opcode != ev->request_code);
1230          ext = ext->next)
1231       ;
1232     if (ext)
1233         XmuSnprintf(major_op, sizeof(major_op), "%s", ext->name);
1234     else
1235         major_op[0] = '\0';
1236 
1237     XmuSnprintf(mesg, sizeof(mesg),
1238                 "%s.%d", ext->name, ev->minor_code);
1239     XGetErrorDatabaseText(dpy, "XRequest", mesg, "", minor_op, BUFSIZ);
1240 
1241 
1242     if ( d == dpy && ev->error_code == BadValue &&
1243          strncmp( major_op, "XKEYBOARD", 9 ) == 0 &&
1244          strncmp( minor_op, "XkbSetMap", 9 ) == 0
1245 #if 0
1246          ev->request_code == 149 /* XKEYBOARD */ &&
1247          ev->minor_code == 9 /* XkbSetMap */
1248 #endif
1249        )
1250     {
1251         dummyErrFlag = True;
1252         SYSLOG( LOG_DEBUG, "X BadValue Error" );
1253     }
1254     else
1255     {
1256         XmuPrintDefaultErrorMessage(d, ev, stderr);
1257         uInfo("Program exiting...");
1258         bailout();
1259     }
1260     return 0;
1261 }
1262 
1263 static void
1264 commitXKBChanges(int tcode)
1265 {
1266     XkbMapChangesRec    mapChangeRec;
1267 
1268     /* Commit the change back to the server */
1269     bzero(&mapChangeRec, sizeof(mapChangeRec));
1270     mapChangeRec.changed = XkbKeySymsMask | XkbKeyTypesMask;
1271     mapChangeRec.first_key_sym = tcode;
1272     mapChangeRec.num_key_syms = 1;
1273     //        mapChangeRec.first_key_act = tcode;
1274     //        mapChangeRec.num_key_acts = 1;
1275     mapChangeRec.first_type = 0;
1276     mapChangeRec.num_types = xkb->map->num_types;
1277     if ( XkbChangeMap(dpy, xkb, &mapChangeRec) )
1278     {
1279 #ifdef DEBUG
1280         printf("map changed done: %d\n",tcode);
1281 #endif
1282     }
1283     else
1284     {
1285         uError("map changed failed\n"); bailout();
1286     }
1287 }
1288 
1289 void
1290 initializeX(char* prg)
1291 {
1292     KeySym              newKS;
1293     XkbMessageAction    xma;
1294     XkbMapChangesRec    mapChangeRec;
1295     int                 types[1];
1296     int                 i;
1297     int                 tcode;
1298 
1299     dpy = GetDisplay(prg, dpyName, &xkbOpcode, &xkbEventCode);
1300     if (!dpy)
1301         bailout();
1302     /* This function is NECESSARY to prevent the X BadValue error
1303      * when running in Synchronize mode */
1304     XSetAfterFunction(dpy, dummy);
1305     XSetErrorHandler(dummyHandler);
1306 
1307     /* Construct the Message Action struct */
1308     xma.type = XkbSA_ActionMessage;
1309     xma.flags = XkbSA_MessageOnPress;
1310     strcpy(xma.message," ");
1311 
1312 #if 0
1313 #ifdef DEBUG
1314     SYSLOG( LOG_DEBUG, "num_acts:%d size_acts:%d",
1315             xkb->server->num_acts, xkb->server->size_acts );
1316 #endif
1317 #endif
1318 
1319     /* Add KeySym to the key codes, as they don't have any KeySyms before */
1320     for ( i = 0; i < NUM_PREDEF_HOTKEYS + kbd.noOfCustomCmds; i++ )
1321     {
1322         if ( i < NUM_PREDEF_HOTKEYS )
1323         {
1324             tcode = (kbd.defCmds)[i].key;
1325             newKS = (kbd.defCmds)[i].keysym;
1326             if ( tcode == 0 )
1327                 continue;
1328         }
1329         else
1330         {
1331             tcode = kbd.customCmds[i-NUM_PREDEF_HOTKEYS].keycode;
1332             newKS = kbd.customCmds[i-NUM_PREDEF_HOTKEYS].keysym;
1333         }
1334 #if 0
1335         xkb= XkbGetKeyboard(dpy,XkbGBN_AllComponentsMask,XkbUseCoreKbd);
1336         xkb= XkbGetKeyboard(dpy,XkbAllComponentsMask,XkbUseCoreKbd);
1337 #endif
1338         xkb = XkbGetMap(dpy, XkbAllMapComponentsMask, XkbUseCoreKbd);
1339         if (!xkb)
1340         {
1341             uError("XkbGetMap failed\n"); bailout();
1342         }
1343 
1344         /* Check the keycode range */
1345         if ( ! XkbKeycodeInRange(xkb, tcode) )
1346         {
1347             uInfo("The keycode %d cannot be used, as it's not between the min(%d) and max(%d) keycode of your keyboard.\n"
1348                   "Please increase the 'maximum' value in /usr/X11R6/lib/X11/xkb/keycodes/xfree86, then restart X.",
1349                   tcode, xkb-> min_key_code, xkb->max_key_code);
1350             continue;
1351         }
1352 
1353         /* Assign a group to the key code */
1354         types[0] = XkbOneLevelIndex;
1355         if ( XkbChangeTypesOfKey(xkb, tcode, 1, XkbGroup1Mask, types, NULL)
1356                 != Success )
1357         {
1358             uError("XkbChangeTypesOfKey failed"); bailout();
1359         }
1360 /*
1361         types[XkbGroup1Index] = XkbKeyTypeIndex( xkb, code, XkbGroup1Index );
1362 */
1363 
1364         /* Change their Keysyms */
1365         if ( XkbResizeKeySyms( xkb, tcode, 1 ) == NULL )
1366         {
1367             uInfo("resize keysym failed\n"); bailout();
1368         }
1369         /* Assign a new keysym to the key code.  According to
1370          * XF86keysym.h, the vendor specific XFree86 keysym range is
1371          * 0x1008FF01 to 0x1008FFFF. So I just assign keysyms to the
1372          * internet keys starting from 0x1008FF01.  I think it doesn't
1373          * really matter to which one I use for the moment. For the
1374          * exact keysyms, please refer to XKeysymDB, */
1375 //        newKS = 0x1008FF01 + tcode;
1376         *XkbKeySymsPtr(xkb,tcode) = newKS;
1377 
1378 #if 0
1379 printf("keycode %d owns keysym %x\n", tcode,newKS);
1380         XChangeKeyboardMapping(dpy, tcode, 1, &newKS, 1);
1381 #endif
1382 
1383         /* Add one key action to it */
1384         if ( XkbResizeKeyActions( xkb, tcode, 1 ) == NULL )
1385         {
1386             uInfo("resize key action failed\n"); bailout();
1387         }
1388 
1389         commitXKBChanges(tcode);
1390         commitXKBChanges(tcode);    /* YES, we need to call it twice! */
1391 
1392         /* Assign the Message Action to the key code */
1393         (&(xkb->server->acts[ xkb->server->key_acts[tcode] ]))[0] = (XkbAction) xma;
1394 
1395         /* Commit the change back to the server. Yeah we need to do it
1396          * here instead of in commit XKBChanges(). Strange, eh?  But
1397          * you just can't, I wonder what the fsck X is doing.  I get
1398          * this just by lots of trial-and-error and many nights of no
1399          * sleeping to trace X with gdb. */
1400         bzero(&mapChangeRec, sizeof(mapChangeRec));
1401         mapChangeRec.changed = XkbKeyActionsMask;
1402         mapChangeRec.first_key_act = tcode;
1403         mapChangeRec.num_key_acts = 1;
1404         if ( XkbChangeMap(dpy, xkb, &mapChangeRec) )
1405         {
1406 #ifdef DEBUG
1407             printf("map changed done: %d\n",tcode);
1408 #endif
1409         }
1410         else
1411         {
1412             uError("map changed failed\n"); bailout();
1413         }
1414 #if 0
1415 #ifdef DEBUG
1416         SYSLOG( LOG_DEBUG, "idx:%d has action:%d no.:%d noOfGrps:%d\n",
1417                 xkb->server->key_acts[tcode], XkbKeyHasActions(xkb,tcode),
1418                 XkbKeyNumActions(xkb,tcode), XkbKeyNumGroups(xkb,tcode) );
1419         SYSLOG( LOG_DEBUG, "keycode %d\nbefore: %d",
1420                 tcode, XkbKeyActionsPtr(xkb,tcode)[0].type);
1421 #endif
1422 
1423 //    xkb = XkbGetMap(dpy, XkbAllMapComponentsMask, XkbUseCoreKbd);
1424 #ifdef DEBUG
1425         SYSLOG( LOG_DEBUG, "after: %d",XkbKeyActionsPtr(xkb,tcode)[0].type);
1426 #endif
1427 #endif
1428 
1429         if ( dummyErrFlag ) /* dummyHandler() have set it */
1430         {
1431             i--;            /* need to redo this round, as the action message
1432                                was not assigned to this keysym */
1433             dummyErrFlag == False;
1434         }
1435     }
1436 
1437     /* Select the ActionMessage event in any circumstances */
1438     if ( !XkbSelectEvents( dpy, XkbUseCoreKbd, XkbActionMessageMask,
1439                            XkbActionMessageMask ))
1440     {
1441         uInfo("Couldn't select desired XKB events\n");
1442         bailout();
1443     }
1444 }
1445 
1446 /* Initialize XOSD */
1447 void
1448 initXOSD(void)
1449 {
1450 #ifdef HAVE_LIBXOSD
1451     if ( osd )
1452     {
1453 	osd = xosd_create(3);
1454 	xosd_set_pos(osd, strncmp(getConfig("osd_position"),"top",3)?XOSD_bottom:XOSD_top);
1455 	xosd_set_bar_length(osd, atoi(getConfig("osd_bar_length")));
1456 	xosd_set_colour(osd, xstrdup(getConfig("osd_color")));
1457 	xosd_set_shadow_colour(osd, xstrdup(getConfig("osd_shadow_color")));
1458 	xosd_set_shadow_offset(osd, atoi(getConfig("osd_shadow_offset")));
1459 	xosd_set_horizontal_offset(osd, atoi(getConfig("osd_hoffset")));
1460 	xosd_set_vertical_offset(osd, atoi(getConfig("osd_voffset")));
1461 	xosd_set_font(osd, xstrdup(getConfig("osd_font")));
1462         xosd_set_align(osd, strncmp(getConfig("osd_align"),"left",4)?((!strncmp(getConfig("osd_align"),"center",6))?XOSD_center:XOSD_right):XOSD_left);
1463     }
1464 #endif
1465 }
1466 
1467 
1468 /* fork2() -- like fork, but the new process is immediately orphaned
1469  *            (won't leave a zombie when it exits)
1470  * Returns 1 to the parent, not any meaningful pid.
1471  * The parent cannot wait() for the new process (it's unrelated).
1472  */
1473 
1474 /* This version assumes that you *haven't* caught or ignored SIGCHLD. */
1475 /* If you have, then you should just be using fork() instead anyway.  */
1476 
1477 /* fork2() is from the Unix Programming FAQ */
1478 int
1479 fork2(void)
1480 {
1481     pid_t pid;
1482     int rc;
1483     int status;
1484 
1485     if (!(pid = fork()))
1486     {
1487         switch (fork())
1488         {
1489             case 0:  return 0;
1490             case -1: _exit(errno);    /* assumes all errnos are <256 */
1491             default: _exit(0);
1492         }
1493     }
1494 
1495     if (pid < 0 || waitpid(pid,&status,0) < 0)
1496         return -1;
1497 
1498     if (WIFEXITED(status))
1499         if (WEXITSTATUS(status) == 0)
1500             return 1;
1501         else
1502             errno = WEXITSTATUS(status);
1503     else
1504         errno = EINTR;  /* well, sort of :-) */
1505 
1506     return -1;
1507 }
1508 
1509 int
1510 main(int argc, char *argv[])
1511 {
1512     XkbEvent    ev;
1513     int         i, k;
1514 
1515     errorFile = stderr;
1516     openlog( PACKAGE, LOG_CONS | LOG_PID, LOG_USER );
1517 
1518     readConfigFile();
1519 
1520     /* initialize the kbd variable */
1521     kbd.noOfCustomCmds = 0;
1522     kbd.defCmds = XCALLOC( defEntry, NUM_PREDEF_HOTKEYS );
1523 
1524     if ( !parseArgs(argc,argv) )
1525         bailout();
1526 
1527 #if HAVE_GTK
1528     if ( !noSplash )
1529     {
1530         int pid;
1531 
1532         if ( (pid=fork2()) == -1 )
1533         {
1534             uInfo("Cannot spawn new process\n");
1535         }
1536         else if ( pid == 0 )
1537         {
1538             gtk_init(&argc,&argv);
1539             splash_create (SPLASH_IMAGE, 2000);   /* show splash for 2 sec */
1540             gtk_main();
1541         }
1542     }
1543 #endif /* HAVE_GTK */
1544 
1545     if (background)
1546     {
1547 
1548         if ( fork() !=0 )
1549         {
1550             SYSLOG( LOG_NOTICE, "Running in the background");
1551             _exit(0);
1552         }
1553         else
1554         {
1555             chdir("/");
1556         }
1557     }
1558 
1559     initializeX(argv[0]);
1560 #ifdef HAVE_LIBXOSD
1561     initXOSD();
1562 #endif
1563 
1564 #if HAVE_GTK
1565     if ( noSplash )
1566 #endif
1567         printf( "%s started successfully.\n", progname );
1568 
1569     /* Process the events in a forever loop */
1570     while (1)
1571     {
1572         XNextEvent( dpy, &ev.core );
1573 #ifdef DEBUG
1574         printXkbActionMessage( stdout, &ev );
1575 #endif
1576         if ( ev.type == xkbEventCode+XkbEventCode &&
1577              ev.any.xkb_type == XkbActionMessage )
1578         {
1579             SYSLOG( LOG_INFO, "Keycode %d pressed\n", ev.message.keycode );
1580 
1581 #ifdef HAVE_LIBXOSD
1582             if (osd)
1583                 xosd_set_timeout(osd, atoi(getConfig("osd_timeout")));
1584 #endif
1585             if ( keytypes[ev.message.keycode] == 1 )
1586             {
1587                 /* Apps stuffs */
1588                 launchApp(ev.message.keycode);
1589             } else
1590             /* Sound stuffs */
1591             if ( ev.message.keycode == (kbd.defCmds)[ejectKey].key )
1592             {
1593                 /* Use thread to improve the responsiveness */
1594                 pthread_t       tp;
1595                 pthread_attr_t  attr;
1596 
1597                 pthread_attr_init(&attr);
1598                 pthread_attr_setdetachstate(&attr,PTHREAD_CREATE_DETACHED);
1599                 pthread_create (&tp, &attr, ejectDisc, NULL);
1600             } else
1601             if ( ev.message.keycode == (kbd.defCmds)[volUpKey].key ) {
1602                 adjustVol(volUpAdj);
1603             } else
1604             if ( ev.message.keycode == (kbd.defCmds)[volDownKey].key ) {
1605                 adjustVol(volDownAdj);
1606             } else
1607             if ( ev.message.keycode == (kbd.defCmds)[muteKey].key ) {
1608                 doMute();
1609             } else
1610             /* APM stuffs */
1611 /**** Andy: No APM support
1612             if ( ev.message.keycode == (kbd.defCmds)[sleepKey].key ||
1613                  ev.message.keycode == (kbd.defCmds)[wakeupKey].key ) {
1614                 sleepState(STANDBY);
1615             } else
1616             if ( ev.message.keycode == (kbd.defCmds)[powerDownKey].key ) {
1617                 sleepState(SUSPEND);
1618             }
1619             else
1620             {
1621 End Andy *****/
1622                 lookupUserCmd(ev.message.keycode);  /* User-defined stuffs */
1623 //Andy            }
1624         }
1625     }
1626 
1627 #ifdef HAVE_LIBXOSD
1628     if (osd)
1629         xosd_destroy(osd);
1630 #endif
1631     XCloseDisplay(dpy);
1632     closelog();
1633     return 0;
1634 }
1635