1 /************************************************************
2 Copyright (c) 1995 by Silicon Graphics Computer Systems, Inc.
4 Permission to use, copy, modify, and distribute this
5 software and its documentation for any purpose and without
6 fee is hereby granted, provided that the above copyright
7 notice appear in all copies and that both that copyright
8 notice and this permission notice appear in supporting
9 documentation, and that the name of Silicon Graphics not be
10 used in advertising or publicity pertaining to distribution
11 of the software without specific prior written permission.
12 Silicon Graphics makes no representation about the suitability
13 of this software for any purpose. It is provided "as is"
14 without any express or implied warranty.
16 SILICON GRAPHICS DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
17 SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
18 AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT SHALL SILICON
19 GRAPHICS BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL
20 DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
21 DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
22 OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH
23 THE USE OR PERFORMANCE OF THIS SOFTWARE.
25 ********************************************************/
27 #define DEBUG_VAR xkbevdDebug
28 #include <X11/Xosdefs.h>
33 #define lowbit(x) ((x) & (-(x)))
35 /***====================================================================***/
37 #ifndef DFLT_XKBEVD_CONFIG
38 #define DFLT_XKBEVD_CONFIG "%s/.xkb/xkbevd.cf"
39 #endif /* DFLT_XKBEVD_CONFIG */
41 #ifndef DFLT_XKB_CONFIG_ROOT
42 #define DFLT_XKB_CONFIG_ROOT "/usr/X11R6/lib/xkb"
45 #ifndef DFLT_SYS_XKBEVD_CONFIG
46 #define DFLT_SYS_XKBEVD_CONFIG "%s/xkbevd.cf"
47 #endif /* DFLT_SYS_XKBEVD_CONFIG */
49 #ifndef DFLT_SOUND_CMD
50 #define DFLT_SOUND_CMD "/usr/sbin/sfplay -q"
51 #endif /* DFLT_SOUND_CMD */
53 #ifndef DFLT_SOUND_DIR
54 #define DFLT_SOUND_DIR "/usr/share/data/sounds/prosonus/"
55 #endif /* DFLT_SOUND_DIR */
57 /***====================================================================***/
59 static char * dpyName= NULL;
61 static char * cfgFileName= NULL;
64 Bool detectableRepeat= False;
67 CfgEntryPtr config= NULL;
69 unsigned long eventMask= 0;
71 static Bool synch= False;
72 static int verbose= 0;
73 static Bool background= False;
75 static char * soundCmd= NULL;
76 static char * soundDir= NULL;
80 /***====================================================================***/
83 Usage(int argc, char *argv[])
85 fprintf(stderr, "Usage: %s [options]...\n%s", argv[0],
87 "-?, -help Print this message\n"
88 "-cfg <file> Specify a config file\n"
89 "-sc <cmd> Specify the command to play sounds\n"
90 "-sd <dir> Specify the root directory for sound files\n"
91 "-d[isplay] <dpy> Specify the display to watch\n"
92 "-bg Run in background\n"
93 "-synch Force synchronization\n"
94 "-v Print verbose messages\n");
98 /***====================================================================***/
101 parseArgs(int argc, char *argv[])
105 for (i=1;i<argc;i++) {
106 if (strcmp(argv[i],"-bg")==0) {
109 else if (strcmp(argv[i],"-cfg")==0) {
111 uError("No configuration file specified on command line\n");
112 uAction("Trailing %s argument ignored\n",argv[i]);
115 char *name= argv[++i];
116 if (cfgFileName!=NULL) {
117 if (uStringEqual(cfgFileName,name))
118 uWarning("Config file \"%s\" specified twice!\n",
121 uWarning("Multiple config files on command line\n");
122 uAction("Using \"%s\", ignoring \"%s\"\n",name,
129 else if ((strcmp(argv[i],"-d")==0)||(strcmp(argv[i],"-display")==0)) {
131 uError("No display specified on command line\n");
132 uAction("Trailing %s argument ignored\n",argv[i]);
135 char *name= argv[++i];
137 if (uStringEqual(dpyName,name))
138 uWarning("Display \"%s\" specified twice!\n",
141 uWarning("Multiple displays on command line\n");
142 uAction("Using \"%s\", ignoring \"%s\"\n",name,
149 else if (strcmp(argv[i],"-sc")==0) {
151 uError("No sound command specified on command line\n");
152 uAction("Trailing %s argument ignored\n",argv[i]);
155 char *name= argv[++i];
156 if (soundCmd!=NULL) {
157 if (uStringEqual(soundCmd,name))
158 uWarning("Sound command \"%s\" specified twice!\n",
161 uWarning("Multiple sound commands on command line\n");
162 uAction("Using \"%s\", ignoring \"%s\"\n",name,
169 else if (strcmp(argv[i],"-sd")==0) {
171 uError("No sound directory specified on command line\n");
172 uAction("Trailing %s argument ignored\n",argv[i]);
175 char *name= argv[++i];
176 if (soundDir!=NULL) {
177 if (uStringEqual(soundDir,name))
178 uWarning("Sound directory \"%s\" specified twice!\n",
181 uWarning("Multiple sound dirs on command line\n");
182 uAction("Using \"%s\", ignoring \"%s\"\n",name,
189 else if ((strcmp(argv[i],"-synch")==0)||(strcmp(argv[i],"-s")==0)) {
192 else if (strcmp(argv[i],"-v")==0) {
195 else if ((strcmp(argv[i],"-?")==0)||(strcmp(argv[i],"-help")==0)) {
200 uError("Unknown flag \"%s\" on command line\n",argv[i]);
205 if (background == False) {
206 eventMask = XkbAllEventsMask;
214 GetDisplay(char *program, char *dpyName, int *opcodeRtrn, int *evBaseRtrn)
219 mjr= XkbMajorVersion;
220 mnr= XkbMinorVersion;
221 dpy= XkbOpenDisplay(dpyName,evBaseRtrn,NULL,&mjr,&mnr,&error);
224 case XkbOD_BadLibraryVersion:
225 uInformation("%s was compiled with XKB version %d.%02d\n",
226 program,XkbMajorVersion,XkbMinorVersion);
227 uError("X library supports incompatible version %d.%02d\n",
230 case XkbOD_ConnectionRefused:
231 uError("Cannot open display \"%s\"\n",dpyName);
233 case XkbOD_NonXkbServer:
234 uError("XKB extension not present on %s\n",dpyName);
236 case XkbOD_BadServerVersion:
237 uInformation("%s was compiled with XKB version %d.%02d\n",
238 program,XkbMajorVersion,XkbMinorVersion);
239 uError("Server %s uses incompatible version %d.%02d\n",
243 uInternalError("Unknown error %d from XkbOpenDisplay\n",error);
247 XSynchronize(dpy,True);
249 XkbQueryExtension(dpy,opcodeRtrn,evBaseRtrn,NULL,&mjr,&mnr);
253 /***====================================================================***/
256 InterpretConfigs(CfgEntryPtr cfg)
264 if (cfg->entry_type==VariableDef) {
265 if (uStrCaseEqual(name,"sounddirectory")||
266 uStrCaseEqual(name,"sounddir")) {
267 if (soundDir==NULL) {
268 soundDir= cfg->action.text;
270 cfg->action.text= NULL;
273 else if (uStrCaseEqual(name,"soundcommand")||
274 uStrCaseEqual(name,"soundcmd")) {
275 if (soundCmd==NULL) {
276 soundCmd= cfg->action.text;
278 cfg->action.text= NULL;
282 uWarning("Assignment to unknown variable \"%s\"\n", name);
283 uAction("Ignored\n");
286 else if (cfg->entry_type==EventDef) switch (cfg->event_type) {
288 if (name!=NULL) cfg->name.atom= XInternAtom(dpy,name,False);
289 else cfg->name.atom= None;
290 if (name) uFree(name);
292 case XkbAccessXNotify:
295 priv= XkbAllNewKeyboardEventsMask;
296 else if (uStrCaseEqual(name,"skpress"))
297 priv= XkbAXN_SKPressMask;
298 else if (uStrCaseEqual(name,"skaccept"))
299 priv= XkbAXN_SKAcceptMask;
300 else if (uStrCaseEqual(name,"skreject"))
301 priv= XkbAXN_SKRejectMask;
302 else if (uStrCaseEqual(name,"skrelease"))
303 priv= XkbAXN_SKReleaseMask;
304 else if (uStrCaseEqual(name,"bkaccept"))
305 priv= XkbAXN_BKAcceptMask;
306 else if (uStrCaseEqual(name,"bkreject"))
307 priv= XkbAXN_BKRejectMask;
308 else if (uStrCaseEqual(name,"warning"))
309 priv= XkbAXN_AXKWarningMask;
310 if (name) uFree(name);
311 cfg->name.priv= priv;
313 case XkbActionMessage:
317 eventMask|= (1L<<cfg->event_type);
320 while ((config)&&(config->entry_type!=EventDef)) {
322 if (config->name.str) uFree(config->name.str);
323 if (config->action.text) uFree(config->action.text);
324 config->name.str= NULL;
325 config->action.text= NULL;
331 while ((cfg!=NULL)&&(cfg->next!=NULL)) {
334 if (next->entry_type!=EventDef) {
335 if (next->name.str) uFree(config->name.str);
336 if (next->action.text) uFree(config->action.text);
337 next->name.str= NULL;
338 next->action.text= NULL;
339 cfg->next= next->next;
349 FindMatchingConfig(XkbEvent *ev)
351 CfgEntryPtr cfg,dflt;
354 for (cfg= config;(cfg!=NULL);cfg=cfg->next) {
355 if ((ev->type!=xkbEventCode)||(cfg->event_type!=ev->any.xkb_type))
357 switch (ev->any.xkb_type) {
359 if (ev->bell.name==cfg->name.atom)
361 else if ((cfg->name.atom==None)&&(dflt==NULL))
364 case XkbAccessXNotify:
365 if (cfg->name.priv&(1L<<ev->accessx.detail))
368 case XkbActionMessage:
369 if (cfg->name.str==NULL)
371 else if (strncmp(cfg->name.str,ev->message.message,
372 XkbActionMessageLength)==0)
376 uInternalError("Can't handle type %d XKB events yet, Sorry.\n",
385 ProcessMatchingConfig(XkbEvent *ev)
391 cfg= FindMatchingConfig(ev);
394 PrintXkbEvent(stdout,ev);
397 if (cfg->action.type==UnknownAction) {
398 if (cfg->action.text==NULL)
399 cfg->action.type= NoAction;
400 else if (cfg->action.text[0]=='!') {
402 cfg->action.type= ShellAction;
403 tmp= uStringDup(&cfg->action.text[1]);
404 uFree(cfg->action.text);
405 cfg->action.text= tmp;
407 else cfg->action.type= SoundAction;
409 switch (cfg->action.type) {
413 if (cfg->action.text!=NULL) {
414 sprintf(buf,"%s",cfg->action.text);
415 cmd= SubstituteEventArgs(buf,ev);
420 PrintXkbEvent(stdout,ev);
423 if (cfg->action.text==NULL) {
424 uWarning("Empty shell command!\n");
425 uAction("Ignored\n");
428 cmd= cfg->action.text;
431 if (cfg->action.text==NULL) {
432 uWarning("Empty sound command!\n");
433 uAction("Ignored\n");
436 sprintf(buf,"%s %s%s",soundCmd,soundDir,cfg->action.text);
440 uInternalError("Unknown error action type %d\n",cfg->action.type);
443 cmd= SubstituteEventArgs(cmd,ev);
445 uInformation("Executing shell command \"%s\"\n",cmd);
446 ok= (system(cmd)==0);
450 /***====================================================================***/
453 main(int argc, char *argv[])
456 static char buf[1024];
462 uSetEntryFile(NullString);
463 uSetDebugFile(NullString);
464 uSetErrorFile(NullString);
466 if (!parseArgs(argc,argv))
470 if (cfgFileName==NULL) {
472 home= (char *)getenv("HOME");
473 sprintf(buf,DFLT_XKBEVD_CONFIG,(home?home:""));
476 if (uStringEqual(cfgFileName,"-")) {
477 static char *in= "stdin";
482 file= fopen(cfgFileName,"r");
483 if (file==NULL) { /* no personal config, try for a system one */
484 if (cfgFileName!=buf) { /* user specified a file. bail */
485 uError("Can't open config file \"%s\n",cfgFileName);
486 uAction("Exiting\n");
489 sprintf(buf,DFLT_SYS_XKBEVD_CONFIG,DFLT_XKB_CONFIG_ROOT);
490 file= fopen(cfgFileName,"r");
491 if (file==NULL && !eventMask) {
493 uError("Couldn't find a config file anywhere\n");
494 uAction("Exiting\n");
505 uInformation("Running in the background\n");
509 dpy= GetDisplay(argv[0],dpyName,&xkbOpcode,&xkbEventCode);
513 setScanState(cfgFileName,1);
515 if (!config && !eventMask) {
516 uError("No configuration specified in \"%s\"\n",cfgFileName);
520 uError("No events to watch in \"%s\"\n",cfgFileName);
523 if (!XkbSelectEvents(dpy,XkbUseCoreKbd,eventMask,eventMask)) {
524 uError("Couldn't select desired XKB events\n");
527 xkb= XkbGetKeyboard(dpy,XkbGBN_AllComponentsMask,XkbUseCoreKbd);
528 if (eventMask&XkbBellNotifyMask) {
531 uInformation("Temporarily disabling the audible bell\n");
532 if (!XkbChangeEnabledControls(dpy,XkbUseCoreKbd,XkbAudibleBellMask,0)) {
533 uError("Couldn't disable audible bell\n");
536 ctrls= vals= XkbAudibleBellMask;
537 if (!XkbSetAutoResetControls(dpy,XkbAudibleBellMask,&ctrls,&vals)) {
538 uWarning("Couldn't configure audible bell to reset on exit\n");
539 uAction("Audible bell might remain off\n");
542 if (soundCmd==NULL) soundCmd= DFLT_SOUND_CMD;
543 if (soundDir==NULL) soundDir= DFLT_SOUND_DIR;
544 XkbStdBellEvent(dpy,None,0,XkbBI_ImAlive);
546 XNextEvent(dpy,&ev.core);
547 if ((!ProcessMatchingConfig(&ev))&&(ev.type==xkbEventCode)&&
548 (ev.any.xkb_type==XkbBellNotify)) {
549 XkbForceDeviceBell(dpy,ev.bell.device,
550 ev.bell.bell_class,ev.bell.bell_id,
558 uAction("Exiting\n");