176910425Smrg/************************************************************
276910425Smrg Copyright (c) 1995 by Silicon Graphics Computer Systems, Inc.
376910425Smrg
476910425Smrg Permission to use, copy, modify, and distribute this
576910425Smrg software and its documentation for any purpose and without
676910425Smrg fee is hereby granted, provided that the above copyright
776910425Smrg notice appear in all copies and that both that copyright
876910425Smrg notice and this permission notice appear in supporting
99ff100acSmrg documentation, and that the name of Silicon Graphics not be
109ff100acSmrg used in advertising or publicity pertaining to distribution
1176910425Smrg of the software without specific prior written permission.
129ff100acSmrg Silicon Graphics makes no representation about the suitability
1376910425Smrg of this software for any purpose. It is provided "as is"
1476910425Smrg without any express or implied warranty.
159ff100acSmrg
169ff100acSmrg SILICON GRAPHICS DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
179ff100acSmrg SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
1876910425Smrg AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT SHALL SILICON
199ff100acSmrg GRAPHICS BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL
209ff100acSmrg DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
219ff100acSmrg DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
2276910425Smrg OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION  WITH
2376910425Smrg THE USE OR PERFORMANCE OF THIS SOFTWARE.
2476910425Smrg
2576910425Smrg ********************************************************/
2676910425Smrg
2776910425Smrg#include <X11/Xosdefs.h>
2876910425Smrg#include <stdlib.h>
2976910425Smrg#include "xkbevd.h"
3076910425Smrg
3176910425Smrg/***====================================================================***/
3276910425Smrg
3376910425Smrg#ifndef DFLT_XKBEVD_CONFIG
3476910425Smrg#define DFLT_XKBEVD_CONFIG "%s/.xkb/xkbevd.cf"
3576910425Smrg#endif /* DFLT_XKBEVD_CONFIG */
3676910425Smrg
3776910425Smrg#ifndef DFLT_XKB_CONFIG_ROOT
38a67f45c3Smrg#define	DFLT_XKB_CONFIG_ROOT "/usr/share/X11/xkb"
3976910425Smrg#endif
4076910425Smrg
4176910425Smrg#ifndef DFLT_SYS_XKBEVD_CONFIG
4276910425Smrg#define DFLT_SYS_XKBEVD_CONFIG "%s/xkbevd.cf"
4376910425Smrg#endif /* DFLT_SYS_XKBEVD_CONFIG */
4476910425Smrg
4576910425Smrg#ifndef DFLT_SOUND_CMD
4676910425Smrg#define	DFLT_SOUND_CMD "/usr/sbin/sfplay -q"
4776910425Smrg#endif /* DFLT_SOUND_CMD */
4876910425Smrg
4976910425Smrg#ifndef DFLT_SOUND_DIR
5076910425Smrg#define	DFLT_SOUND_DIR "/usr/share/data/sounds/prosonus/"
5176910425Smrg#endif /* DFLT_SOUND_DIR */
5276910425Smrg
5376910425Smrg/***====================================================================***/
5476910425Smrg
559ff100acSmrgstatic char *   dpyName = NULL;
569ff100acSmrgDisplay *       dpy = NULL;
579ff100acSmrgstatic const char *cfgFileName = NULL;
589ff100acSmrgint             xkbOpcode = 0;
599ff100acSmrgint             xkbEventCode = 0;
6076910425Smrg
619ff100acSmrgstatic CfgEntryPtr config = NULL;
629ff100acSmrgstatic unsigned long eventMask = 0;
6376910425Smrg
649ff100acSmrgstatic Bool     synch = False;
659ff100acSmrgstatic int      verbose = 0;
669ff100acSmrgstatic Bool     background = False;
6776910425Smrg
689ff100acSmrgstatic const char *soundCmd = NULL;
699ff100acSmrgstatic const char *soundDir = NULL;
7076910425Smrg
719ff100acSmrgXkbDescPtr      xkb = NULL;
7276910425Smrg
7376910425Smrg/***====================================================================***/
7476910425Smrg
7576910425Smrgstatic void
7676910425SmrgUsage(int argc, char *argv[])
7776910425Smrg{
789ff100acSmrg    fprintf(stderr, "Usage: %s [options]...\n%s", argv[0],
799ff100acSmrg            "Legal options:\n"
809ff100acSmrg            "-?, -help            Print this message\n"
819ff100acSmrg            "-cfg <file>          Specify a config file\n"
829ff100acSmrg            "-sc <cmd>            Specify the command to play sounds\n"
839ff100acSmrg            "-sd <dir>            Specify the root directory for sound files\n"
849ff100acSmrg            "-d[isplay] <dpy>     Specify the display to watch\n"
859ff100acSmrg            "-bg                  Run in background\n"
869ff100acSmrg            "-synch               Force synchronization\n"
879ff100acSmrg            "-v                   Print verbose messages\n"
889ff100acSmrg            "-version             Print program version\n"
899ff100acSmrg        );
9076910425Smrg    return;
9176910425Smrg}
9276910425Smrg
9376910425Smrg/***====================================================================***/
9476910425Smrg
9576910425Smrgstatic Bool
9676910425SmrgparseArgs(int argc, char *argv[])
9776910425Smrg{
989ff100acSmrg    register int i;
999ff100acSmrg
1009ff100acSmrg    for (i = 1; i < argc; i++) {
1019ff100acSmrg        if (strcmp(argv[i], "-bg") == 0) {
1029ff100acSmrg            background = True;
1039ff100acSmrg        }
1049ff100acSmrg        else if (strcmp(argv[i], "-cfg") == 0) {
1059ff100acSmrg            if (i >= (argc - 1)) {
1069ff100acSmrg                uError("No configuration file specified on command line\n");
1079ff100acSmrg                uAction("Trailing %s argument ignored\n", argv[i]);
1089ff100acSmrg            }
1099ff100acSmrg            else {
1109ff100acSmrg                char *name = argv[++i];
1119ff100acSmrg
1129ff100acSmrg                if (cfgFileName != NULL) {
1139ff100acSmrg                    if (uStringEqual(cfgFileName, name))
1149ff100acSmrg                        uWarning("Config file \"%s\" specified twice!\n", name);
1159ff100acSmrg                    else {
1169ff100acSmrg                        uWarning("Multiple config files on command line\n");
1179ff100acSmrg                        uAction("Using \"%s\", ignoring \"%s\"\n", name,
1189ff100acSmrg                                cfgFileName);
1199ff100acSmrg                    }
1209ff100acSmrg                }
1219ff100acSmrg                cfgFileName = name;
1229ff100acSmrg            }
1239ff100acSmrg        }
1249ff100acSmrg        else if ((strcmp(argv[i], "-d") == 0) ||
1259ff100acSmrg                 (strcmp(argv[i], "-display") == 0)) {
1269ff100acSmrg            if (i >= (argc - 1)) {
1279ff100acSmrg                uError("No display specified on command line\n");
1289ff100acSmrg                uAction("Trailing %s argument ignored\n", argv[i]);
1299ff100acSmrg            }
1309ff100acSmrg            else {
1319ff100acSmrg                char *name = argv[++i];
1329ff100acSmrg
1339ff100acSmrg                if (dpyName != NULL) {
1349ff100acSmrg                    if (uStringEqual(dpyName, name))
1359ff100acSmrg                        uWarning("Display \"%s\" specified twice!\n", name);
1369ff100acSmrg                    else {
1379ff100acSmrg                        uWarning("Multiple displays on command line\n");
1389ff100acSmrg                        uAction("Using \"%s\", ignoring \"%s\"\n", name,
1399ff100acSmrg                                dpyName);
1409ff100acSmrg                    }
1419ff100acSmrg                }
1429ff100acSmrg                dpyName = name;
1439ff100acSmrg            }
1449ff100acSmrg        }
1459ff100acSmrg        else if (strcmp(argv[i], "-sc") == 0) {
1469ff100acSmrg            if (i >= (argc - 1)) {
1479ff100acSmrg                uError("No sound command specified on command line\n");
1489ff100acSmrg                uAction("Trailing %s argument ignored\n", argv[i]);
1499ff100acSmrg            }
1509ff100acSmrg            else {
1519ff100acSmrg                char *name = argv[++i];
1529ff100acSmrg
1539ff100acSmrg                if (soundCmd != NULL) {
1549ff100acSmrg                    if (uStringEqual(soundCmd, name))
1559ff100acSmrg                        uWarning("Sound command \"%s\" specified twice!\n",
1569ff100acSmrg                                 name);
1579ff100acSmrg                    else {
1589ff100acSmrg                        uWarning("Multiple sound commands on command line\n");
1599ff100acSmrg                        uAction("Using \"%s\", ignoring \"%s\"\n", name,
1609ff100acSmrg                                soundCmd);
1619ff100acSmrg                    }
1629ff100acSmrg                }
1639ff100acSmrg                soundCmd = name;
1649ff100acSmrg            }
1659ff100acSmrg        }
1669ff100acSmrg        else if (strcmp(argv[i], "-sd") == 0) {
1679ff100acSmrg            if (i >= (argc - 1)) {
1689ff100acSmrg                uError("No sound directory specified on command line\n");
1699ff100acSmrg                uAction("Trailing %s argument ignored\n", argv[i]);
1709ff100acSmrg            }
1719ff100acSmrg            else {
1729ff100acSmrg                char *name = argv[++i];
1739ff100acSmrg
1749ff100acSmrg                if (soundDir != NULL) {
1759ff100acSmrg                    if (uStringEqual(soundDir, name))
1769ff100acSmrg                        uWarning("Sound directory \"%s\" specified twice!\n",
1779ff100acSmrg                                 name);
1789ff100acSmrg                    else {
1799ff100acSmrg                        uWarning("Multiple sound dirs on command line\n");
1809ff100acSmrg                        uAction("Using \"%s\", ignoring \"%s\"\n", name,
1819ff100acSmrg                                soundDir);
1829ff100acSmrg                    }
1839ff100acSmrg                }
1849ff100acSmrg                soundDir = name;
1859ff100acSmrg            }
1869ff100acSmrg        }
1879ff100acSmrg        else if ((strcmp(argv[i], "-synch") == 0) ||
1889ff100acSmrg                 (strcmp(argv[i], "-s") == 0)) {
1899ff100acSmrg            synch = True;
1909ff100acSmrg        }
1919ff100acSmrg        else if (strcmp(argv[i], "-v") == 0) {
1929ff100acSmrg            verbose++;
1939ff100acSmrg        }
1948bfe6addSmrg        else if ((strcmp(argv[i], "-version") == 0) ||
1958bfe6addSmrg                 (strcmp(argv[i], "--version") == 0)) {
1969ff100acSmrg            puts(PACKAGE_STRING);
1979ff100acSmrg            exit(0);
1989ff100acSmrg        }
1999ff100acSmrg        else if ((strcmp(argv[i], "-?") == 0) ||
2008bfe6addSmrg                 (strcmp(argv[i], "-help") == 0) ||
2018bfe6addSmrg                 (strcmp(argv[i], "--help") == 0)) {
2029ff100acSmrg            Usage(argc, argv);
2039ff100acSmrg            exit(0);
2049ff100acSmrg        }
2059ff100acSmrg        else {
2069ff100acSmrg            uError("Unknown flag \"%s\" on command line\n", argv[i]);
2079ff100acSmrg            Usage(argc, argv);
2089ff100acSmrg            return False;
2099ff100acSmrg        }
21076910425Smrg    }
211db17cd6dSmrg    if (background == False) {
2129ff100acSmrg        eventMask = XkbAllEventsMask;
2139ff100acSmrg        verbose++;
214db17cd6dSmrg    }
215db17cd6dSmrg
21676910425Smrg    return True;
21776910425Smrg}
21876910425Smrg
21976910425Smrgstatic Display *
220a67f45c3SmrgGetDisplay(const char *program, const char *displayName,
221a67f45c3Smrg           int *opcodeRtrn, int *evBaseRtrn)
22276910425Smrg{
2239ff100acSmrg    int mjr, mnr, error;
224a67f45c3Smrg    Display *display;
2259ff100acSmrg
2269ff100acSmrg    mjr = XkbMajorVersion;
2279ff100acSmrg    mnr = XkbMinorVersion;
228a67f45c3Smrg    display = XkbOpenDisplay(displayName, evBaseRtrn, NULL, &mjr, &mnr, &error);
229a67f45c3Smrg    if (display == NULL) {
2309ff100acSmrg        switch (error) {
2319ff100acSmrg        case XkbOD_BadLibraryVersion:
2329ff100acSmrg            uInformation("%s was compiled with XKB version %d.%02d\n",
2339ff100acSmrg                         program, XkbMajorVersion, XkbMinorVersion);
2349ff100acSmrg            uError("X library supports incompatible version %d.%02d\n",
2359ff100acSmrg                   mjr, mnr);
2369ff100acSmrg            break;
2379ff100acSmrg        case XkbOD_ConnectionRefused:
238a67f45c3Smrg            uError("Cannot open display \"%s\"\n", displayName);
2399ff100acSmrg            break;
2409ff100acSmrg        case XkbOD_NonXkbServer:
241a67f45c3Smrg            uError("XKB extension not present on %s\n", displayName);
2429ff100acSmrg            break;
2439ff100acSmrg        case XkbOD_BadServerVersion:
2449ff100acSmrg            uInformation("%s was compiled with XKB version %d.%02d\n",
2459ff100acSmrg                         program, XkbMajorVersion, XkbMinorVersion);
2469ff100acSmrg            uError("Server %s uses incompatible version %d.%02d\n",
247a67f45c3Smrg                   displayName, mjr, mnr);
2489ff100acSmrg            break;
2499ff100acSmrg        default:
2509ff100acSmrg            uInternalError("Unknown error %d from XkbOpenDisplay\n", error);
2519ff100acSmrg        }
25276910425Smrg    }
25376910425Smrg    else if (synch)
254a67f45c3Smrg        XSynchronize(display, True);
25576910425Smrg    if (opcodeRtrn)
256a67f45c3Smrg        XkbQueryExtension(display, opcodeRtrn, evBaseRtrn, NULL, &mjr, &mnr);
257a67f45c3Smrg    return display;
25876910425Smrg}
25976910425Smrg
26076910425Smrg/***====================================================================***/
26176910425Smrg
26276910425Smrgvoid
26376910425SmrgInterpretConfigs(CfgEntryPtr cfg)
26476910425Smrg{
2659ff100acSmrg    config = cfg;
2669ff100acSmrg    while (cfg != NULL) {
267a67f45c3Smrg        char *name = cfg->name.str;
2689ff100acSmrg        if (cfg->entry_type == VariableDef) {
2699ff100acSmrg            if (uStrCaseEqual(name, "sounddirectory") ||
2709ff100acSmrg                uStrCaseEqual(name, "sounddir")) {
2719ff100acSmrg                if (soundDir == NULL) {
2729ff100acSmrg                    soundDir = cfg->action.text;
2739ff100acSmrg                    cfg->name.str = NULL;
2749ff100acSmrg                    cfg->action.text = NULL;
2759ff100acSmrg                }
2769ff100acSmrg            }
2779ff100acSmrg            else if (uStrCaseEqual(name, "soundcommand") ||
2789ff100acSmrg                     uStrCaseEqual(name, "soundcmd")) {
2799ff100acSmrg                if (soundCmd == NULL) {
2809ff100acSmrg                    soundCmd = cfg->action.text;
2819ff100acSmrg                    cfg->name.str = NULL;
2829ff100acSmrg                    cfg->action.text = NULL;
2839ff100acSmrg                }
2849ff100acSmrg            }
2859ff100acSmrg            else {
2869ff100acSmrg                uWarning("Assignment to unknown variable \"%s\"\n", name);
2879ff100acSmrg                uAction("Ignored\n");
2889ff100acSmrg            }
2899ff100acSmrg        }
290a67f45c3Smrg        else if (cfg->entry_type == EventDef) {
291a67f45c3Smrg            unsigned int priv;
292a67f45c3Smrg
2939ff100acSmrg            switch (cfg->event_type) {
2949ff100acSmrg            case XkbBellNotify:
2959ff100acSmrg                if (name != NULL)
2969ff100acSmrg                    cfg->name.atom = XInternAtom(dpy, name, False);
2979ff100acSmrg                else
2989ff100acSmrg                    cfg->name.atom = None;
2999ff100acSmrg                if (name)
3009ff100acSmrg                    free(name);
3019ff100acSmrg                break;
3029ff100acSmrg            case XkbAccessXNotify:
3039ff100acSmrg                priv = 0;
3049ff100acSmrg                if (name == NULL)
3059ff100acSmrg                    priv = XkbAllNewKeyboardEventsMask;
3069ff100acSmrg                else if (uStrCaseEqual(name, "skpress"))
3079ff100acSmrg                    priv = XkbAXN_SKPressMask;
3089ff100acSmrg                else if (uStrCaseEqual(name, "skaccept"))
3099ff100acSmrg                    priv = XkbAXN_SKAcceptMask;
3109ff100acSmrg                else if (uStrCaseEqual(name, "skreject"))
3119ff100acSmrg                    priv = XkbAXN_SKRejectMask;
3129ff100acSmrg                else if (uStrCaseEqual(name, "skrelease"))
3139ff100acSmrg                    priv = XkbAXN_SKReleaseMask;
3149ff100acSmrg                else if (uStrCaseEqual(name, "bkaccept"))
3159ff100acSmrg                    priv = XkbAXN_BKAcceptMask;
3169ff100acSmrg                else if (uStrCaseEqual(name, "bkreject"))
3179ff100acSmrg                    priv = XkbAXN_BKRejectMask;
3189ff100acSmrg                else if (uStrCaseEqual(name, "warning"))
3199ff100acSmrg                    priv = XkbAXN_AXKWarningMask;
3209ff100acSmrg                if (name)
3219ff100acSmrg                    free(name);
3229ff100acSmrg                cfg->name.priv = priv;
3239ff100acSmrg                break;
3249ff100acSmrg            case XkbActionMessage:
3259ff100acSmrg                /* nothing to do */
3269ff100acSmrg                break;
3279ff100acSmrg            }
328a67f45c3Smrg        }
3299ff100acSmrg        eventMask |= (1L << cfg->event_type);
3309ff100acSmrg        cfg = cfg->next;
33176910425Smrg    }
3329ff100acSmrg    while ((config) && (config->entry_type != EventDef)) {
3339ff100acSmrg        CfgEntryPtr next;
3349ff100acSmrg
3359ff100acSmrg        if (config->name.str)
3369ff100acSmrg            free(config->name.str);
3379ff100acSmrg        if (config->action.text)
3389ff100acSmrg            free(config->action.text);
3399ff100acSmrg        config->name.str = NULL;
3409ff100acSmrg        config->action.text = NULL;
3419ff100acSmrg        next = config->next;
3429ff100acSmrg        free(config);
3439ff100acSmrg        config = next;
34476910425Smrg    }
3459ff100acSmrg    cfg = config;
3469ff100acSmrg    while ((cfg != NULL) && (cfg->next != NULL)) {
3479ff100acSmrg        CfgEntryPtr next;
3489ff100acSmrg
3499ff100acSmrg        next = cfg->next;
3509ff100acSmrg        if (next->entry_type != EventDef) {
3519ff100acSmrg            if (next->name.str)
3529ff100acSmrg                free(config->name.str);
3539ff100acSmrg            if (next->action.text)
3549ff100acSmrg                free(config->action.text);
3559ff100acSmrg            next->name.str = NULL;
3569ff100acSmrg            next->action.text = NULL;
3579ff100acSmrg            cfg->next = next->next;
3589ff100acSmrg            next->next = NULL;
3599ff100acSmrg            free(next);
3609ff100acSmrg        }
3619ff100acSmrg        else
3629ff100acSmrg            cfg = next;
36376910425Smrg    }
36476910425Smrg    return;
36576910425Smrg}
36676910425Smrg
36776910425Smrgstatic CfgEntryPtr
3689ff100acSmrgFindMatchingConfig(XkbEvent * ev)
36976910425Smrg{
3709ff100acSmrg    CfgEntryPtr cfg, dflt;
3719ff100acSmrg
3729ff100acSmrg    dflt = NULL;
3739ff100acSmrg    for (cfg = config; (cfg != NULL); cfg = cfg->next) {
3749ff100acSmrg        if ((ev->type != xkbEventCode) || (cfg->event_type != ev->any.xkb_type))
3759ff100acSmrg            continue;
3769ff100acSmrg        switch (ev->any.xkb_type) {
3779ff100acSmrg        case XkbBellNotify:
3789ff100acSmrg            if (ev->bell.name == cfg->name.atom)
3799ff100acSmrg                return cfg;
3809ff100acSmrg            else if ((cfg->name.atom == None) && (dflt == NULL))
3819ff100acSmrg                dflt = cfg;
3829ff100acSmrg            break;
3839ff100acSmrg        case XkbAccessXNotify:
3849ff100acSmrg            if (cfg->name.priv & (1L << ev->accessx.detail))
3859ff100acSmrg                return cfg;
3869ff100acSmrg            break;
3879ff100acSmrg        case XkbActionMessage:
3889ff100acSmrg            if (cfg->name.str == NULL)
3899ff100acSmrg                dflt = cfg;
3909ff100acSmrg            else if (strncmp(cfg->name.str, ev->message.message,
3919ff100acSmrg                             XkbActionMessageLength) == 0)
3929ff100acSmrg                return cfg;
3939ff100acSmrg            break;
3949ff100acSmrg        default:
3959ff100acSmrg            uInternalError("Can't handle type %d XKB events yet, Sorry.\n",
3969ff100acSmrg                           ev->any.xkb_type);
3979ff100acSmrg            break;
3989ff100acSmrg        }
39976910425Smrg    }
40076910425Smrg    return dflt;
40176910425Smrg}
40276910425Smrg
40376910425Smrgstatic Bool
4049ff100acSmrgProcessMatchingConfig(XkbEvent * ev)
40576910425Smrg{
4069ff100acSmrg    CfgEntryPtr cfg;
4078bfe6addSmrg    char buf[1024];
4088bfe6addSmrg    const char *cmd;
4099ff100acSmrg    int ok;
41076910425Smrg
4119ff100acSmrg    cfg = FindMatchingConfig(ev);
412db17cd6dSmrg    if (!cfg) {
4139ff100acSmrg        if (verbose)
4149ff100acSmrg            PrintXkbEvent(stdout, ev);
4159ff100acSmrg        return False;
416db17cd6dSmrg    }
4179ff100acSmrg    if (cfg->action.type == UnknownAction) {
4189ff100acSmrg        if (cfg->action.text == NULL)
4199ff100acSmrg            cfg->action.type = NoAction;
4209ff100acSmrg        else if (cfg->action.text[0] == '!') {
4219ff100acSmrg            char *tmp;
4229ff100acSmrg
4239ff100acSmrg            cfg->action.type = ShellAction;
4249ff100acSmrg            tmp = uStringDup(&cfg->action.text[1]);
4259ff100acSmrg            free(cfg->action.text);
4269ff100acSmrg            cfg->action.text = tmp;
4279ff100acSmrg        }
4289ff100acSmrg        else
4299ff100acSmrg            cfg->action.type = SoundAction;
43076910425Smrg    }
43176910425Smrg    switch (cfg->action.type) {
4329ff100acSmrg    case NoAction:
4339ff100acSmrg        return True;
4349ff100acSmrg    case EchoAction:
4359ff100acSmrg        if (cfg->action.text != NULL) {
4369ff100acSmrg            snprintf(buf, sizeof(buf), "%s", cfg->action.text);
4379ff100acSmrg            cmd = SubstituteEventArgs(buf, ev);
4389ff100acSmrg            printf("%s", cmd);
4399ff100acSmrg        }
4409ff100acSmrg        return True;
4419ff100acSmrg    case PrintEvAction:
4429ff100acSmrg        PrintXkbEvent(stdout, ev);
4439ff100acSmrg        return True;
4449ff100acSmrg    case ShellAction:
4459ff100acSmrg        if (cfg->action.text == NULL) {
4469ff100acSmrg            uWarning("Empty shell command!\n");
4479ff100acSmrg            uAction("Ignored\n");
4489ff100acSmrg            return True;
4499ff100acSmrg        }
4509ff100acSmrg        cmd = cfg->action.text;
4519ff100acSmrg        break;
4529ff100acSmrg    case SoundAction:
4539ff100acSmrg        if (cfg->action.text == NULL) {
4549ff100acSmrg            uWarning("Empty sound command!\n");
4559ff100acSmrg            uAction("Ignored\n");
4569ff100acSmrg            return True;
4579ff100acSmrg        }
4589ff100acSmrg        snprintf(buf, sizeof(buf), "%s %s%s", soundCmd, soundDir,
4599ff100acSmrg                 cfg->action.text);
4609ff100acSmrg        cmd = buf;
4619ff100acSmrg        break;
4629ff100acSmrg    default:
4639ff100acSmrg        uInternalError("Unknown error action type %d\n", cfg->action.type);
4649ff100acSmrg        return False;
46576910425Smrg    }
4669ff100acSmrg    cmd = SubstituteEventArgs(cmd, ev);
46776910425Smrg    if (verbose)
4689ff100acSmrg        uInformation("Executing shell command \"%s\"\n", cmd);
4699ff100acSmrg    ok = (system(cmd) == 0);
47076910425Smrg    return ok;
47176910425Smrg}
47276910425Smrg
47376910425Smrg/***====================================================================***/
47476910425Smrg
47576910425Smrgint
47676910425Smrgmain(int argc, char *argv[])
47776910425Smrg{
4789ff100acSmrg    FILE *      file;
4799ff100acSmrg    static char buf[1024];
4809ff100acSmrg    XkbEvent    ev;
4819ff100acSmrg    Bool        ok;
48276910425Smrg
48376910425Smrg    yyin = stdin;
48476910425Smrg    uSetErrorFile(NullString);
48576910425Smrg
4869ff100acSmrg    if (!parseArgs(argc, argv))
4879ff100acSmrg        exit(1);
4889ff100acSmrg    file = NULL;
48976910425Smrg    XkbInitAtoms(NULL);
4909ff100acSmrg    if (cfgFileName == NULL) {
4919ff100acSmrg        char *home = getenv("HOME");
4929ff100acSmrg        snprintf(buf, sizeof(buf), DFLT_XKBEVD_CONFIG, (home ? home : ""));
4939ff100acSmrg        cfgFileName = buf;
49476910425Smrg    }
4959ff100acSmrg    if (uStringEqual(cfgFileName, "-")) {
4969ff100acSmrg        static const char *in = "stdin";
4979ff100acSmrg
4989ff100acSmrg        file = stdin;
4999ff100acSmrg        cfgFileName = in;
50076910425Smrg    }
50176910425Smrg    else {
5029ff100acSmrg        file = fopen(cfgFileName, "r");
5039ff100acSmrg        if (file == NULL) {     /* no personal config, try for a system one */
5049ff100acSmrg            if (cfgFileName != buf) {   /* user specified a file.  bail */
5059ff100acSmrg                uError("Can't open config file \"%s\n", cfgFileName);
5069ff100acSmrg                uAction("Exiting\n");
5079ff100acSmrg                exit(1);
5089ff100acSmrg            }
5099ff100acSmrg            snprintf(buf, sizeof(buf), DFLT_SYS_XKBEVD_CONFIG,
5109ff100acSmrg                     DFLT_XKB_CONFIG_ROOT);
5119ff100acSmrg            file = fopen(cfgFileName, "r");
5129ff100acSmrg            if (file == NULL && !eventMask) {
5139ff100acSmrg                if (verbose) {
5149ff100acSmrg                    uError("Couldn't find a config file anywhere\n");
5159ff100acSmrg                    uAction("Exiting\n");
5169ff100acSmrg                    exit(1);
5179ff100acSmrg                }
5189ff100acSmrg                exit(0);
5199ff100acSmrg            }
5209ff100acSmrg        }
52176910425Smrg    }
52276910425Smrg
52376910425Smrg    if (background) {
5249ff100acSmrg        if (fork() != 0) {
5259ff100acSmrg            if (verbose)
5269ff100acSmrg                uInformation("Running in the background\n");
5279ff100acSmrg            exit(0);
5289ff100acSmrg        }
52976910425Smrg    }
5309ff100acSmrg    dpy = GetDisplay(argv[0], dpyName, &xkbOpcode, &xkbEventCode);
53176910425Smrg    if (!dpy)
5329ff100acSmrg        goto BAILOUT;
5339ff100acSmrg    ok = True;
5349ff100acSmrg    setScanState(cfgFileName, 1);
53576910425Smrg    CFGParseFile(file);
536db17cd6dSmrg    if (!config && !eventMask) {
5379ff100acSmrg        uError("No configuration specified in \"%s\"\n", cfgFileName);
5389ff100acSmrg        goto BAILOUT;
53976910425Smrg    }
5409ff100acSmrg    if (eventMask == 0) {
5419ff100acSmrg        uError("No events to watch in \"%s\"\n", cfgFileName);
5429ff100acSmrg        goto BAILOUT;
54376910425Smrg    }
5449ff100acSmrg    if (!XkbSelectEvents(dpy, XkbUseCoreKbd, eventMask, eventMask)) {
5459ff100acSmrg        uError("Couldn't select desired XKB events\n");
5469ff100acSmrg        goto BAILOUT;
54776910425Smrg    }
5489ff100acSmrg    xkb = XkbGetKeyboard(dpy, XkbGBN_AllComponentsMask, XkbUseCoreKbd);
5499ff100acSmrg    if (eventMask & XkbBellNotifyMask) {
5509ff100acSmrg        unsigned ctrls, vals;
5519ff100acSmrg
5529ff100acSmrg        if (verbose)
5539ff100acSmrg            uInformation("Temporarily disabling the audible bell\n");
5549ff100acSmrg        if (!XkbChangeEnabledControls
5559ff100acSmrg            (dpy, XkbUseCoreKbd, XkbAudibleBellMask, 0)) {
5569ff100acSmrg            uError("Couldn't disable audible bell\n");
5579ff100acSmrg            goto BAILOUT;
5589ff100acSmrg        }
5599ff100acSmrg        ctrls = vals = XkbAudibleBellMask;
5609ff100acSmrg        if (!XkbSetAutoResetControls(dpy, XkbAudibleBellMask, &ctrls, &vals)) {
5619ff100acSmrg            uWarning("Couldn't configure audible bell to reset on exit\n");
5629ff100acSmrg            uAction("Audible bell might remain off\n");
5639ff100acSmrg        }
56476910425Smrg    }
5659ff100acSmrg    if (soundCmd == NULL)
5669ff100acSmrg        soundCmd = DFLT_SOUND_CMD;
5679ff100acSmrg    if (soundDir == NULL)
5689ff100acSmrg        soundDir = DFLT_SOUND_DIR;
5699ff100acSmrg    XkbStdBellEvent(dpy, None, 0, XkbBI_ImAlive);
57076910425Smrg    while (1) {
5719ff100acSmrg        XNextEvent(dpy, &ev.core);
5729ff100acSmrg        if ((!ProcessMatchingConfig(&ev)) && (ev.type == xkbEventCode) &&
5739ff100acSmrg            (ev.any.xkb_type == XkbBellNotify)) {
5749ff100acSmrg            XkbForceDeviceBell(dpy, ev.bell.device,
5759ff100acSmrg                               ev.bell.bell_class, ev.bell.bell_id,
5769ff100acSmrg                               ev.bell.percent);
5779ff100acSmrg        }
57876910425Smrg    }
57976910425Smrg
58076910425Smrg    XCloseDisplay(dpy);
5819ff100acSmrg    return (ok == 0);
5829ff100acSmrg BAILOUT:
58376910425Smrg    uAction("Exiting\n");
5849ff100acSmrg    if (dpy != NULL)
5859ff100acSmrg        XCloseDisplay(dpy);
58676910425Smrg    exit(1);
58776910425Smrg}
588