xkbevd.c revision 76910425
1/* $Xorg: xkbevd.c,v 1.4 2000/08/17 19:54:49 cpqbld Exp $ */ 2/************************************************************ 3 Copyright (c) 1995 by Silicon Graphics Computer Systems, Inc. 4 5 Permission to use, copy, modify, and distribute this 6 software and its documentation for any purpose and without 7 fee is hereby granted, provided that the above copyright 8 notice appear in all copies and that both that copyright 9 notice and this permission notice appear in supporting 10 documentation, and that the name of Silicon Graphics not be 11 used in advertising or publicity pertaining to distribution 12 of the software without specific prior written permission. 13 Silicon Graphics makes no representation about the suitability 14 of this software for any purpose. It is provided "as is" 15 without any express or implied warranty. 16 17 SILICON GRAPHICS DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS 18 SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY 19 AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT SHALL SILICON 20 GRAPHICS BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL 21 DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, 22 DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE 23 OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH 24 THE USE OR PERFORMANCE OF THIS SOFTWARE. 25 26 ********************************************************/ 27/* $XFree86: xc/programs/xkbevd/xkbevd.c,v 3.8 2001/01/17 23:46:09 dawes Exp $ */ 28 29#define DEBUG_VAR xkbevdDebug 30#include <X11/Xosdefs.h> 31#include <stdlib.h> 32#include "xkbevd.h" 33 34 35#define lowbit(x) ((x) & (-(x))) 36 37/***====================================================================***/ 38 39#ifndef DFLT_XKBEVD_CONFIG 40#define DFLT_XKBEVD_CONFIG "%s/.xkb/xkbevd.cf" 41#endif /* DFLT_XKBEVD_CONFIG */ 42 43#ifndef DFLT_XKB_CONFIG_ROOT 44#define DFLT_XKB_CONFIG_ROOT "/usr/X11R6/lib/xkb" 45#endif 46 47#ifndef DFLT_SYS_XKBEVD_CONFIG 48#define DFLT_SYS_XKBEVD_CONFIG "%s/xkbevd.cf" 49#endif /* DFLT_SYS_XKBEVD_CONFIG */ 50 51#ifndef DFLT_SOUND_CMD 52#define DFLT_SOUND_CMD "/usr/sbin/sfplay -q" 53#endif /* DFLT_SOUND_CMD */ 54 55#ifndef DFLT_SOUND_DIR 56#define DFLT_SOUND_DIR "/usr/share/data/sounds/prosonus/" 57#endif /* DFLT_SOUND_DIR */ 58 59/***====================================================================***/ 60 61char * dpyName= NULL; 62Display * dpy= NULL; 63char * cfgFileName= NULL; 64int xkbOpcode= 0; 65int xkbEventCode= 0; 66Bool detectableRepeat= False; 67 68CfgEntryPtr config= NULL; 69unsigned long eventMask= 0; 70 71Bool synch= False; 72int verbose= 0; 73Bool background= False; 74 75char * soundCmd= NULL; 76char * soundDir= NULL; 77 78XkbDescPtr xkb= NULL; 79 80/***====================================================================***/ 81 82#define M(m) fprintf(stderr,(m)) 83#define M1(m,a) fprintf(stderr,(m),(a)) 84 85static void 86Usage(int argc, char *argv[]) 87{ 88 M1("Usage: %s [options]...\n",argv[0]); 89 M("Legal options:\n"); 90 M("-?,-help Print this message\n"); 91 M("-cfg <file> Specify a config file\n"); 92 M("-sc <cmd> Specify the command to play sounds\n"); 93 M("-sd <dir> Specify the root directory for sound files\n"); 94 M("-d[isplay] <dpy> Specify the display to watch\n"); 95 M("-bg Run in background\n"); 96 M("-synch Force synchronization\n"); 97 M("-v Print verbose messages\n"); 98 return; 99} 100 101/***====================================================================***/ 102 103static Bool 104parseArgs(int argc, char *argv[]) 105{ 106register int i; 107 108 for (i=1;i<argc;i++) { 109 if (strcmp(argv[i],"-bg")==0) { 110 background= True; 111 } 112 else if (strcmp(argv[i],"-cfg")==0) { 113 if (i>=(argc-1)) { 114 uError("No configuration file specified on command line\n"); 115 uAction("Trailing %s argument ignored\n",argv[i]); 116 } 117 else { 118 char *name= argv[++i]; 119 if (cfgFileName!=NULL) { 120 if (uStringEqual(cfgFileName,name)) 121 uWarning("Config file \"%s\" specified twice!\n"); 122 else { 123 uWarning("Multiple config files on command line\n"); 124 uAction("Using \"%s\", ignoring \"%s\"\n",name, 125 cfgFileName); 126 } 127 } 128 cfgFileName= name; 129 } 130 } 131 else if ((strcmp(argv[i],"-d")==0)||(strcmp(argv[i],"-display")==0)) { 132 if (i>=(argc-1)) { 133 uError("No display specified on command line\n"); 134 uAction("Trailing %s argument ignored\n",argv[i]); 135 } 136 else { 137 char *name= argv[++i]; 138 if (dpyName!=NULL) { 139 if (uStringEqual(dpyName,name)) 140 uWarning("Display \"%s\" specified twice!\n"); 141 else { 142 uWarning("Multiple displays on command line\n"); 143 uAction("Using \"%s\", ignoring \"%s\"\n",name, 144 dpyName); 145 } 146 } 147 dpyName= name; 148 } 149 } 150 else if (strcmp(argv[i],"-sc")==0) { 151 if (i>=(argc-1)) { 152 uError("No sound command specified on command line\n"); 153 uAction("Trailing %s argument ignored\n",argv[i]); 154 } 155 else { 156 char *name= argv[++i]; 157 if (soundCmd!=NULL) { 158 if (uStringEqual(soundCmd,name)) 159 uWarning("Sound command \"%s\" specified twice!\n"); 160 else { 161 uWarning("Multiple sound commands on command line\n"); 162 uAction("Using \"%s\", ignoring \"%s\"\n",name, 163 soundCmd); 164 } 165 } 166 soundCmd= name; 167 } 168 } 169 else if (strcmp(argv[i],"-sd")==0) { 170 if (i>=(argc-1)) { 171 uError("No sound directory specified on command line\n"); 172 uAction("Trailing %s argument ignored\n",argv[i]); 173 } 174 else { 175 char *name= argv[++i]; 176 if (soundDir!=NULL) { 177 if (uStringEqual(soundDir,name)) 178 uWarning("Sound directory \"%s\" specified twice!\n"); 179 else { 180 uWarning("Multiple sound dirs on command line\n"); 181 uAction("Using \"%s\", ignoring \"%s\"\n",name, 182 soundDir); 183 } 184 } 185 soundDir= name; 186 } 187 } 188 else if ((strcmp(argv[i],"-synch")==0)||(strcmp(argv[i],"-s")==0)) { 189 synch= True; 190 } 191 else if (strcmp(argv[i],"-v")==0) { 192 verbose++; 193 } 194 else if ((strcmp(argv[i],"-?")==0)||(strcmp(argv[i],"-help")==0)) { 195 Usage(argc,argv); 196 exit(0); 197 } 198 else { 199 uError("Unknown flag \"%s\" on command line\n",argv[i]); 200 Usage(argc,argv); 201 return False; 202 } 203 } 204 return True; 205} 206 207static Display * 208GetDisplay(char *program, char *dpyName, int *opcodeRtrn, int *evBaseRtrn) 209{ 210int mjr,mnr,error; 211Display *dpy; 212 213 mjr= XkbMajorVersion; 214 mnr= XkbMinorVersion; 215 dpy= XkbOpenDisplay(dpyName,evBaseRtrn,NULL,&mjr,&mnr,&error); 216 if (dpy==NULL) { 217 switch (error) { 218 case XkbOD_BadLibraryVersion: 219 uInformation("%s was compiled with XKB version %d.%02d\n", 220 program,XkbMajorVersion,XkbMinorVersion); 221 uError("X library supports incompatible version %d.%02d\n", 222 mjr,mnr); 223 break; 224 case XkbOD_ConnectionRefused: 225 uError("Cannot open display \"%s\"\n",dpyName); 226 break; 227 case XkbOD_NonXkbServer: 228 uError("XKB extension not present on %s\n",dpyName); 229 break; 230 case XkbOD_BadServerVersion: 231 uInformation("%s was compiled with XKB version %d.%02d\n", 232 program,XkbMajorVersion,XkbMinorVersion); 233 uError("Server %s uses incompatible version %d.%02d\n", 234 dpyName,mjr,mnr); 235 break; 236 default: 237 uInternalError("Unknown error %d from XkbOpenDisplay\n",error); 238 } 239 } 240 else if (synch) 241 XSynchronize(dpy,True); 242 if (opcodeRtrn) 243 XkbQueryExtension(dpy,opcodeRtrn,evBaseRtrn,NULL,&mjr,&mnr); 244 return dpy; 245} 246 247/***====================================================================***/ 248 249void 250InterpretConfigs(CfgEntryPtr cfg) 251{ 252char * name; 253unsigned priv= 0; 254 255 config= cfg; 256 while (cfg!=NULL) { 257 name= cfg->name.str; 258 if (cfg->entry_type==VariableDef) { 259 if (uStrCaseEqual(name,"sounddirectory")|| 260 uStrCaseEqual(name,"sounddir")) { 261 if (soundDir==NULL) { 262 soundDir= cfg->action.text; 263 cfg->name.str= NULL; 264 cfg->action.text= NULL; 265 } 266 } 267 else if (uStrCaseEqual(name,"soundcommand")|| 268 uStrCaseEqual(name,"soundcmd")) { 269 if (soundCmd==NULL) { 270 soundCmd= cfg->action.text; 271 cfg->name.str= NULL; 272 cfg->action.text= NULL; 273 } 274 } 275 else { 276 uWarning("Assignment to unknown variable \"%s\"\n",cfg->name); 277 uAction("Ignored\n"); 278 } 279 } 280 else if (cfg->entry_type==EventDef) switch (cfg->event_type) { 281 case XkbBellNotify: 282 if (name!=NULL) cfg->name.atom= XInternAtom(dpy,name,False); 283 else cfg->name.atom= None; 284 if (name) uFree(name); 285 break; 286 case XkbAccessXNotify: 287 priv= 0; 288 if (name==NULL) 289 priv= XkbAllNewKeyboardEventsMask; 290 else if (uStrCaseEqual(name,"skpress")) 291 priv= XkbAXN_SKPressMask; 292 else if (uStrCaseEqual(name,"skaccept")) 293 priv= XkbAXN_SKAcceptMask; 294 else if (uStrCaseEqual(name,"skreject")) 295 priv= XkbAXN_SKRejectMask; 296 else if (uStrCaseEqual(name,"skrelease")) 297 priv= XkbAXN_SKReleaseMask; 298 else if (uStrCaseEqual(name,"bkaccept")) 299 priv= XkbAXN_BKAcceptMask; 300 else if (uStrCaseEqual(name,"bkreject")) 301 priv= XkbAXN_BKRejectMask; 302 else if (uStrCaseEqual(name,"warning")) 303 priv= XkbAXN_AXKWarningMask; 304 if (name) uFree(name); 305 cfg->name.priv= priv; 306 break; 307 case XkbActionMessage: 308 /* nothing to do */ 309 break; 310 } 311 eventMask|= (1L<<cfg->event_type); 312 cfg= cfg->next; 313 } 314 while ((config)&&(config->entry_type!=EventDef)) { 315 CfgEntryPtr next; 316 if (config->name.str) uFree(config->name.str); 317 if (config->action.text) uFree(config->action.text); 318 config->name.str= NULL; 319 config->action.text= NULL; 320 next= config->next; 321 uFree(config); 322 config= next; 323 } 324 cfg= config; 325 while ((cfg!=NULL)&&(cfg->next!=NULL)) { 326 CfgEntryPtr next; 327 next= cfg->next; 328 if (next->entry_type!=EventDef) { 329 if (next->name.str) uFree(config->name.str); 330 if (next->action.text) uFree(config->action.text); 331 next->name.str= NULL; 332 next->action.text= NULL; 333 cfg->next= next->next; 334 next->next= NULL; 335 uFree(next); 336 } 337 else cfg= next; 338 } 339 return; 340} 341 342static CfgEntryPtr 343FindMatchingConfig(XkbEvent *ev) 344{ 345CfgEntryPtr cfg,dflt; 346 347 dflt= NULL; 348 for (cfg= config;(cfg!=NULL);cfg=cfg->next) { 349 if ((ev->type!=xkbEventCode)||(cfg->event_type!=ev->any.xkb_type)) 350 continue; 351 switch (ev->any.xkb_type) { 352 case XkbBellNotify: 353 if (ev->bell.name==cfg->name.atom) 354 return cfg; 355 else if ((cfg->name.atom==None)&&(dflt==NULL)) 356 dflt= cfg; 357 break; 358 case XkbAccessXNotify: 359 if (cfg->name.priv&(1L<<ev->accessx.detail)) 360 return cfg; 361 break; 362 case XkbActionMessage: 363 if (cfg->name.str==NULL) 364 dflt= cfg; 365 else if (strncmp(cfg->name.str,ev->message.message, 366 XkbActionMessageLength)==0) 367 return cfg; 368 break; 369 default: 370 uInternalError("Can't handle type %d XKB events yet, Sorry.\n"); 371 break; 372 } 373 } 374 return dflt; 375} 376 377static Bool 378ProcessMatchingConfig(XkbEvent *ev) 379{ 380CfgEntryPtr cfg; 381char buf[1024],*cmd; 382int ok; 383 384 cfg= FindMatchingConfig(ev); 385 if (!cfg) 386 return False; 387 if (cfg->action.type==UnknownAction) { 388 if (cfg->action.text==NULL) 389 cfg->action.type= NoAction; 390 else if (cfg->action.text[0]=='!') { 391 char *tmp; 392 cfg->action.type= ShellAction; 393 tmp= uStringDup(&cfg->action.text[1]); 394 uFree(cfg->action.text); 395 cfg->action.text= tmp; 396 } 397 else cfg->action.type= SoundAction; 398 } 399 switch (cfg->action.type) { 400 case NoAction: 401 return True; 402 case EchoAction: 403 if (cfg->action.text!=NULL) { 404 sprintf(buf,cfg->action.text); 405 cmd= SubstituteEventArgs(buf,ev); 406 printf("%s",cmd); 407 } 408 return True; 409 case PrintEvAction: 410 PrintXkbEvent(stdout,ev); 411 return True; 412 case ShellAction: 413 if (cfg->action.text==NULL) { 414 uWarning("Empty shell command!\n"); 415 uAction("Ignored\n"); 416 return True; 417 } 418 cmd= cfg->action.text; 419 break; 420 case SoundAction: 421 if (cfg->action.text==NULL) { 422 uWarning("Empty sound command!\n"); 423 uAction("Ignored\n"); 424 return True; 425 } 426 sprintf(buf,"%s %s%s",soundCmd,soundDir,cfg->action.text); 427 cmd= buf; 428 break; 429 default: 430 uInternalError("Unknown error action type %d\n",cfg->action.type); 431 return False; 432 } 433 cmd= SubstituteEventArgs(cmd,ev); 434 if (verbose) 435 uInformation("Executing shell command \"%s\"\n",cmd); 436 ok= (system(cmd)==0); 437 return ok; 438} 439 440/***====================================================================***/ 441 442int 443main(int argc, char *argv[]) 444{ 445FILE * file; 446static char buf[1024]; 447XkbEvent ev; 448Bool ok; 449 450 451 yyin = stdin; 452 uSetEntryFile(NullString); 453 uSetDebugFile(NullString); 454 uSetErrorFile(NullString); 455 456 if (!parseArgs(argc,argv)) 457 exit(1); 458 file= NULL; 459 XkbInitAtoms(NULL); 460 if (cfgFileName==NULL) { 461 char *home; 462 home= (char *)getenv("HOME"); 463 sprintf(buf,DFLT_XKBEVD_CONFIG,(home?home:"")); 464 cfgFileName= buf; 465 } 466 if (uStringEqual(cfgFileName,"-")) { 467 static char *in= "stdin"; 468 file= stdin; 469 cfgFileName= in; 470 } 471 else { 472 file= fopen(cfgFileName,"r"); 473 if (file==NULL) { /* no personal config, try for a system one */ 474 if (cfgFileName!=buf) { /* user specified a file. bail */ 475 uError("Can't open config file \"%s\n",cfgFileName); 476 uAction("Exiting\n"); 477 exit(1); 478 } 479 sprintf(buf,DFLT_SYS_XKBEVD_CONFIG,DFLT_XKB_CONFIG_ROOT); 480 file= fopen(cfgFileName,"r"); 481 if (file==NULL) { 482 if (verbose) { 483 uError("Couldn't find a config file anywhere\n"); 484 uAction("Exiting\n"); 485 exit(1); 486 } 487 exit(0); 488 } 489 } 490 } 491 492 if (background) { 493 if (fork()!=0) { 494 if (verbose) 495 uInformation("Running in the background\n"); 496 exit(0); 497 } 498 } 499 dpy= GetDisplay(argv[0],dpyName,&xkbOpcode,&xkbEventCode); 500 if (!dpy) 501 goto BAILOUT; 502 ok= True; 503 setScanState(cfgFileName,1); 504 CFGParseFile(file); 505 if (!config) { 506 uError("No configuration specified in \"%s\"\n",cfgFileName); 507 goto BAILOUT; 508 } 509 if (eventMask==0) { 510 uError("No events to watch in \"%s\"\n",cfgFileName); 511 goto BAILOUT; 512 } 513 if (!XkbSelectEvents(dpy,XkbUseCoreKbd,eventMask,eventMask)) { 514 uError("Couldn't select desired XKB events\n"); 515 goto BAILOUT; 516 } 517 xkb= XkbGetKeyboard(dpy,XkbGBN_AllComponentsMask,XkbUseCoreKbd); 518 if (eventMask&XkbBellNotifyMask) { 519 unsigned ctrls,vals; 520 if (verbose) 521 uInformation("Temporarily disabling the audible bell\n"); 522 if (!XkbChangeEnabledControls(dpy,XkbUseCoreKbd,XkbAudibleBellMask,0)) { 523 uError("Couldn't disable audible bell\n"); 524 goto BAILOUT; 525 } 526 ctrls= vals= XkbAudibleBellMask; 527 if (!XkbSetAutoResetControls(dpy,XkbAudibleBellMask,&ctrls,&vals)) { 528 uWarning("Couldn't configure audible bell to reset on exit\n"); 529 uAction("Audible bell might remain off\n"); 530 } 531 } 532 if (soundCmd==NULL) soundCmd= DFLT_SOUND_CMD; 533 if (soundDir==NULL) soundDir= DFLT_SOUND_DIR; 534 XkbStdBellEvent(dpy,None,0,XkbBI_ImAlive); 535 while (1) { 536 XNextEvent(dpy,&ev.core); 537 if ((!ProcessMatchingConfig(&ev))&&(ev.type==xkbEventCode)&& 538 (ev.any.xkb_type==XkbBellNotify)) { 539 XkbForceDeviceBell(dpy,ev.bell.device, 540 ev.bell.bell_class,ev.bell.bell_id, 541 ev.bell.percent); 542 } 543 } 544 545 XCloseDisplay(dpy); 546 return (ok==0); 547BAILOUT: 548 uAction("Exiting\n"); 549 if (dpy!=NULL) 550 XCloseDisplay(dpy); 551 exit(1); 552} 553