diff options
Diffstat (limited to 'xkbevd.c')
-rw-r--r-- | xkbevd.c | 562 |
1 files changed, 562 insertions, 0 deletions
diff --git a/xkbevd.c b/xkbevd.c new file mode 100644 index 0000000..4776570 --- /dev/null +++ b/xkbevd.c @@ -0,0 +1,562 @@ +/************************************************************ + Copyright (c) 1995 by Silicon Graphics Computer Systems, Inc. + + Permission to use, copy, modify, and distribute this + software and its documentation for any purpose and without + fee is hereby granted, provided that the above copyright + notice appear in all copies and that both that copyright + notice and this permission notice appear in supporting + documentation, and that the name of Silicon Graphics not be + used in advertising or publicity pertaining to distribution + of the software without specific prior written permission. + Silicon Graphics makes no representation about the suitability + of this software for any purpose. It is provided "as is" + without any express or implied warranty. + + SILICON GRAPHICS DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS + SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY + AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT SHALL SILICON + GRAPHICS BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL + DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, + DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE + OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH + THE USE OR PERFORMANCE OF THIS SOFTWARE. + + ********************************************************/ + +#define DEBUG_VAR xkbevdDebug +#include <X11/Xosdefs.h> +#include <stdlib.h> +#include "xkbevd.h" + + +#define lowbit(x) ((x) & (-(x))) + +/***====================================================================***/ + +#ifndef DFLT_XKBEVD_CONFIG +#define DFLT_XKBEVD_CONFIG "%s/.xkb/xkbevd.cf" +#endif /* DFLT_XKBEVD_CONFIG */ + +#ifndef DFLT_XKB_CONFIG_ROOT +#define DFLT_XKB_CONFIG_ROOT "/usr/X11R6/lib/xkb" +#endif + +#ifndef DFLT_SYS_XKBEVD_CONFIG +#define DFLT_SYS_XKBEVD_CONFIG "%s/xkbevd.cf" +#endif /* DFLT_SYS_XKBEVD_CONFIG */ + +#ifndef DFLT_SOUND_CMD +#define DFLT_SOUND_CMD "/usr/sbin/sfplay -q" +#endif /* DFLT_SOUND_CMD */ + +#ifndef DFLT_SOUND_DIR +#define DFLT_SOUND_DIR "/usr/share/data/sounds/prosonus/" +#endif /* DFLT_SOUND_DIR */ + +/***====================================================================***/ + +static char * dpyName= NULL; +Display * dpy= NULL; +static const char * cfgFileName= NULL; +int xkbOpcode= 0; +int xkbEventCode= 0; +Bool detectableRepeat= False; + +static +CfgEntryPtr config= NULL; +static +unsigned long eventMask= 0; + +static Bool synch= False; +static int verbose= 0; +static Bool background= False; + +static const char * soundCmd= NULL; +static const char * soundDir= NULL; + +XkbDescPtr xkb= NULL; + +/***====================================================================***/ + +static void +Usage(int argc, char *argv[]) +{ + fprintf(stderr, "Usage: %s [options]...\n%s", argv[0], + "Legal options:\n" + "-?, -help Print this message\n" + "-cfg <file> Specify a config file\n" + "-sc <cmd> Specify the command to play sounds\n" + "-sd <dir> Specify the root directory for sound files\n" + "-d[isplay] <dpy> Specify the display to watch\n" + "-bg Run in background\n" + "-synch Force synchronization\n" + "-v Print verbose messages\n"); + return; +} + +/***====================================================================***/ + +static Bool +parseArgs(int argc, char *argv[]) +{ +register int i; + + for (i=1;i<argc;i++) { + if (strcmp(argv[i],"-bg")==0) { + background= True; + } + else if (strcmp(argv[i],"-cfg")==0) { + if (i>=(argc-1)) { + uError("No configuration file specified on command line\n"); + uAction("Trailing %s argument ignored\n",argv[i]); + } + else { + char *name= argv[++i]; + if (cfgFileName!=NULL) { + if (uStringEqual(cfgFileName,name)) + uWarning("Config file \"%s\" specified twice!\n", + name); + else { + uWarning("Multiple config files on command line\n"); + uAction("Using \"%s\", ignoring \"%s\"\n",name, + cfgFileName); + } + } + cfgFileName= name; + } + } + else if ((strcmp(argv[i],"-d")==0)||(strcmp(argv[i],"-display")==0)) { + if (i>=(argc-1)) { + uError("No display specified on command line\n"); + uAction("Trailing %s argument ignored\n",argv[i]); + } + else { + char *name= argv[++i]; + if (dpyName!=NULL) { + if (uStringEqual(dpyName,name)) + uWarning("Display \"%s\" specified twice!\n", + name); + else { + uWarning("Multiple displays on command line\n"); + uAction("Using \"%s\", ignoring \"%s\"\n",name, + dpyName); + } + } + dpyName= name; + } + } + else if (strcmp(argv[i],"-sc")==0) { + if (i>=(argc-1)) { + uError("No sound command specified on command line\n"); + uAction("Trailing %s argument ignored\n",argv[i]); + } + else { + char *name= argv[++i]; + if (soundCmd!=NULL) { + if (uStringEqual(soundCmd,name)) + uWarning("Sound command \"%s\" specified twice!\n", + name); + else { + uWarning("Multiple sound commands on command line\n"); + uAction("Using \"%s\", ignoring \"%s\"\n",name, + soundCmd); + } + } + soundCmd= name; + } + } + else if (strcmp(argv[i],"-sd")==0) { + if (i>=(argc-1)) { + uError("No sound directory specified on command line\n"); + uAction("Trailing %s argument ignored\n",argv[i]); + } + else { + char *name= argv[++i]; + if (soundDir!=NULL) { + if (uStringEqual(soundDir,name)) + uWarning("Sound directory \"%s\" specified twice!\n", + name); + else { + uWarning("Multiple sound dirs on command line\n"); + uAction("Using \"%s\", ignoring \"%s\"\n",name, + soundDir); + } + } + soundDir= name; + } + } + else if ((strcmp(argv[i],"-synch")==0)||(strcmp(argv[i],"-s")==0)) { + synch= True; + } + else if (strcmp(argv[i],"-v")==0) { + verbose++; + } + else if ((strcmp(argv[i],"-?")==0)||(strcmp(argv[i],"-help")==0)) { + Usage(argc,argv); + exit(0); + } + else { + uError("Unknown flag \"%s\" on command line\n",argv[i]); + Usage(argc,argv); + return False; + } + } + if (background == False) { + eventMask = XkbAllEventsMask; + verbose++; + } + + return True; +} + +static Display * +GetDisplay(char *program, char *dpyName, int *opcodeRtrn, int *evBaseRtrn) +{ +int mjr,mnr,error; +Display *dpy; + + mjr= XkbMajorVersion; + mnr= XkbMinorVersion; + dpy= XkbOpenDisplay(dpyName,evBaseRtrn,NULL,&mjr,&mnr,&error); + if (dpy==NULL) { + switch (error) { + case XkbOD_BadLibraryVersion: + uInformation("%s was compiled with XKB version %d.%02d\n", + program,XkbMajorVersion,XkbMinorVersion); + uError("X library supports incompatible version %d.%02d\n", + mjr,mnr); + break; + case XkbOD_ConnectionRefused: + uError("Cannot open display \"%s\"\n",dpyName); + break; + case XkbOD_NonXkbServer: + uError("XKB extension not present on %s\n",dpyName); + break; + case XkbOD_BadServerVersion: + uInformation("%s was compiled with XKB version %d.%02d\n", + program,XkbMajorVersion,XkbMinorVersion); + uError("Server %s uses incompatible version %d.%02d\n", + dpyName,mjr,mnr); + break; + default: + uInternalError("Unknown error %d from XkbOpenDisplay\n",error); + } + } + else if (synch) + XSynchronize(dpy,True); + if (opcodeRtrn) + XkbQueryExtension(dpy,opcodeRtrn,evBaseRtrn,NULL,&mjr,&mnr); + return dpy; +} + +/***====================================================================***/ + +void +InterpretConfigs(CfgEntryPtr cfg) +{ +char * name; +unsigned priv= 0; + + config= cfg; + while (cfg!=NULL) { + name= cfg->name.str; + if (cfg->entry_type==VariableDef) { + if (uStrCaseEqual(name,"sounddirectory")|| + uStrCaseEqual(name,"sounddir")) { + if (soundDir==NULL) { + soundDir= cfg->action.text; + cfg->name.str= NULL; + cfg->action.text= NULL; + } + } + else if (uStrCaseEqual(name,"soundcommand")|| + uStrCaseEqual(name,"soundcmd")) { + if (soundCmd==NULL) { + soundCmd= cfg->action.text; + cfg->name.str= NULL; + cfg->action.text= NULL; + } + } + else { + uWarning("Assignment to unknown variable \"%s\"\n", name); + uAction("Ignored\n"); + } + } + else if (cfg->entry_type==EventDef) switch (cfg->event_type) { + case XkbBellNotify: + if (name!=NULL) cfg->name.atom= XInternAtom(dpy,name,False); + else cfg->name.atom= None; + if (name) free(name); + break; + case XkbAccessXNotify: + priv= 0; + if (name==NULL) + priv= XkbAllNewKeyboardEventsMask; + else if (uStrCaseEqual(name,"skpress")) + priv= XkbAXN_SKPressMask; + else if (uStrCaseEqual(name,"skaccept")) + priv= XkbAXN_SKAcceptMask; + else if (uStrCaseEqual(name,"skreject")) + priv= XkbAXN_SKRejectMask; + else if (uStrCaseEqual(name,"skrelease")) + priv= XkbAXN_SKReleaseMask; + else if (uStrCaseEqual(name,"bkaccept")) + priv= XkbAXN_BKAcceptMask; + else if (uStrCaseEqual(name,"bkreject")) + priv= XkbAXN_BKRejectMask; + else if (uStrCaseEqual(name,"warning")) + priv= XkbAXN_AXKWarningMask; + if (name) free(name); + cfg->name.priv= priv; + break; + case XkbActionMessage: + /* nothing to do */ + break; + } + eventMask|= (1L<<cfg->event_type); + cfg= cfg->next; + } + while ((config)&&(config->entry_type!=EventDef)) { + CfgEntryPtr next; + if (config->name.str) free(config->name.str); + if (config->action.text) free(config->action.text); + config->name.str= NULL; + config->action.text= NULL; + next= config->next; + free(config); + config= next; + } + cfg= config; + while ((cfg!=NULL)&&(cfg->next!=NULL)) { + CfgEntryPtr next; + next= cfg->next; + if (next->entry_type!=EventDef) { + if (next->name.str) free(config->name.str); + if (next->action.text) free(config->action.text); + next->name.str= NULL; + next->action.text= NULL; + cfg->next= next->next; + next->next= NULL; + free(next); + } + else cfg= next; + } + return; +} + +static CfgEntryPtr +FindMatchingConfig(XkbEvent *ev) +{ +CfgEntryPtr cfg,dflt; + + dflt= NULL; + for (cfg= config;(cfg!=NULL);cfg=cfg->next) { + if ((ev->type!=xkbEventCode)||(cfg->event_type!=ev->any.xkb_type)) + continue; + switch (ev->any.xkb_type) { + case XkbBellNotify: + if (ev->bell.name==cfg->name.atom) + return cfg; + else if ((cfg->name.atom==None)&&(dflt==NULL)) + dflt= cfg; + break; + case XkbAccessXNotify: + if (cfg->name.priv&(1L<<ev->accessx.detail)) + return cfg; + break; + case XkbActionMessage: + if (cfg->name.str==NULL) + dflt= cfg; + else if (strncmp(cfg->name.str,ev->message.message, + XkbActionMessageLength)==0) + return cfg; + break; + default: + uInternalError("Can't handle type %d XKB events yet, Sorry.\n", + ev->any.xkb_type); + break; + } + } + return dflt; +} + +static Bool +ProcessMatchingConfig(XkbEvent *ev) +{ +CfgEntryPtr cfg; +char buf[1024],*cmd; +int ok; + + cfg= FindMatchingConfig(ev); + if (!cfg) { + if (verbose) + PrintXkbEvent(stdout,ev); + return False; + } + if (cfg->action.type==UnknownAction) { + if (cfg->action.text==NULL) + cfg->action.type= NoAction; + else if (cfg->action.text[0]=='!') { + char *tmp; + cfg->action.type= ShellAction; + tmp= uStringDup(&cfg->action.text[1]); + free(cfg->action.text); + cfg->action.text= tmp; + } + else cfg->action.type= SoundAction; + } + switch (cfg->action.type) { + case NoAction: + return True; + case EchoAction: + if (cfg->action.text!=NULL) { + sprintf(buf,"%s",cfg->action.text); + cmd= SubstituteEventArgs(buf,ev); + printf("%s",cmd); + } + return True; + case PrintEvAction: + PrintXkbEvent(stdout,ev); + return True; + case ShellAction: + if (cfg->action.text==NULL) { + uWarning("Empty shell command!\n"); + uAction("Ignored\n"); + return True; + } + cmd= cfg->action.text; + break; + case SoundAction: + if (cfg->action.text==NULL) { + uWarning("Empty sound command!\n"); + uAction("Ignored\n"); + return True; + } + sprintf(buf,"%s %s%s",soundCmd,soundDir,cfg->action.text); + cmd= buf; + break; + default: + uInternalError("Unknown error action type %d\n",cfg->action.type); + return False; + } + cmd= SubstituteEventArgs(cmd,ev); + if (verbose) + uInformation("Executing shell command \"%s\"\n",cmd); + ok= (system(cmd)==0); + return ok; +} + +/***====================================================================***/ + +int +main(int argc, char *argv[]) +{ +FILE * file; +static char buf[1024]; +XkbEvent ev; +Bool ok; + + + yyin = stdin; + uSetEntryFile(NullString); + uSetDebugFile(NullString); + uSetErrorFile(NullString); + + if (!parseArgs(argc,argv)) + exit(1); + file= NULL; + XkbInitAtoms(NULL); + if (cfgFileName==NULL) { + char *home; + home= (char *)getenv("HOME"); + sprintf(buf,DFLT_XKBEVD_CONFIG,(home?home:"")); + cfgFileName= buf; + } + if (uStringEqual(cfgFileName,"-")) { + static const char *in= "stdin"; + file= stdin; + cfgFileName= in; + } + else { + file= fopen(cfgFileName,"r"); + if (file==NULL) { /* no personal config, try for a system one */ + if (cfgFileName!=buf) { /* user specified a file. bail */ + uError("Can't open config file \"%s\n",cfgFileName); + uAction("Exiting\n"); + exit(1); + } + sprintf(buf,DFLT_SYS_XKBEVD_CONFIG,DFLT_XKB_CONFIG_ROOT); + file= fopen(cfgFileName,"r"); + if (file==NULL && !eventMask) { + if (verbose) { + uError("Couldn't find a config file anywhere\n"); + uAction("Exiting\n"); + exit(1); + } + exit(0); + } + } + } + + if (background) { + if (fork()!=0) { + if (verbose) + uInformation("Running in the background\n"); + exit(0); + } + } + dpy= GetDisplay(argv[0],dpyName,&xkbOpcode,&xkbEventCode); + if (!dpy) + goto BAILOUT; + ok= True; + setScanState(cfgFileName,1); + CFGParseFile(file); + if (!config && !eventMask) { + uError("No configuration specified in \"%s\"\n",cfgFileName); + goto BAILOUT; + } + if (eventMask==0) { + uError("No events to watch in \"%s\"\n",cfgFileName); + goto BAILOUT; + } + if (!XkbSelectEvents(dpy,XkbUseCoreKbd,eventMask,eventMask)) { + uError("Couldn't select desired XKB events\n"); + goto BAILOUT; + } + xkb= XkbGetKeyboard(dpy,XkbGBN_AllComponentsMask,XkbUseCoreKbd); + if (eventMask&XkbBellNotifyMask) { + unsigned ctrls,vals; + if (verbose) + uInformation("Temporarily disabling the audible bell\n"); + if (!XkbChangeEnabledControls(dpy,XkbUseCoreKbd,XkbAudibleBellMask,0)) { + uError("Couldn't disable audible bell\n"); + goto BAILOUT; + } + ctrls= vals= XkbAudibleBellMask; + if (!XkbSetAutoResetControls(dpy,XkbAudibleBellMask,&ctrls,&vals)) { + uWarning("Couldn't configure audible bell to reset on exit\n"); + uAction("Audible bell might remain off\n"); + } + } + if (soundCmd==NULL) soundCmd= DFLT_SOUND_CMD; + if (soundDir==NULL) soundDir= DFLT_SOUND_DIR; + XkbStdBellEvent(dpy,None,0,XkbBI_ImAlive); + while (1) { + XNextEvent(dpy,&ev.core); + if ((!ProcessMatchingConfig(&ev))&&(ev.type==xkbEventCode)&& + (ev.any.xkb_type==XkbBellNotify)) { + XkbForceDeviceBell(dpy,ev.bell.device, + ev.bell.bell_class,ev.bell.bell_id, + ev.bell.percent); + } + } + + XCloseDisplay(dpy); + return (ok==0); +BAILOUT: + uAction("Exiting\n"); + if (dpy!=NULL) + XCloseDisplay(dpy); + exit(1); +} |