1/************************************************************
2 Copyright (c) 1995 by Silicon Graphics Computer Systems, Inc.
3
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.
15
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.
24
25 ********************************************************/
26
27#include <stdlib.h>
28#include <X11/X.h>
29#include <X11/Xlib.h>
30#include <X11/XKBlib.h>
31#include <X11/Intrinsic.h>
32#include <X11/StringDefs.h>
33#include <X11/Shell.h>
34#include <X11/Xaw/Cardinals.h>
35#include <X11/Xaw/Box.h>
36
37#define	BOOLEAN_DEFINED
38#include "utils.h"
39#include "LED.h"
40
41/***====================================================================***/
42
43#define	YES		1
44#define	NO		0
45#define	DONT_CARE	-1
46
47static Display *inDpy, *outDpy;
48static unsigned long wanted, real, named, explicit, automatic, virtual;
49static char *inDpyName;
50static int wantNamed = DONT_CARE;
51static int wantExplicit = DONT_CARE;
52static int wantAutomatic = DONT_CARE;
53static int wantReal = DONT_CARE;
54static int wantVirtual = DONT_CARE;
55static int evBase, errBase;
56static Bool synch;
57static Bool useUnion = True;
58
59/***====================================================================***/
60
61static void
62usage(char *program)
63{
64    uInformation("Usage: %s <options>\n", program);
65    uInformation("Legal options include the usual X toolkit options plus:\n"
66                 "  -help           Print this message\n"
67                 "  -version        Print the program version\n"
68                 "  -indpy <name>   Name of display to watch\n"
69                 "  -watch <leds>   Mask of LEDs to watch\n"
70                 "  [-+]automatic   (Don't) watch automatic LEDs\n"
71                 "  [-+]explicit    (Don't) watch explicit LEDs\n"
72                 "  [-+]name        (Don't) watch named LEDs\n"
73                 "  [-+]real        (Don't) watch real LEDs\n"
74                 "  [-+]virtual     (Don't) watch virtual LEDs\n"
75                 "  -intersection   Watch only LEDs in all desired sets\n"
76                 "  -union          Watch LEDs in any desired sets\n"
77                 "The default set of LEDs is -union +name +automatic +real\n");
78    return;
79}
80
81static Bool
82parseArgs(int argc, char *argv[])
83{
84    register int i;
85
86    for (i = 1; i < argc; i++) {
87        if (uStrCaseEqual(argv[i], "-indpy")) {
88            if (i < argc - 1)
89                inDpyName = argv[++i];
90            else {
91                uWarning("No name specified for input display\n");
92                uAction("Ignoring trailing -indpy argument\n");
93            }
94        }
95        else if (uStrCaseEqual(argv[i], "-watch")) {
96            if (i < argc - 1) {
97                int tmp;
98
99                if (sscanf(argv[++i], "%i", &tmp) != 1) {
100                    uWarning("Set of LEDs must be specified as an integer\n");
101                    uAction("Ignoring bogus value \"%s\" for -watch flag\n",
102                            argv[i]);
103                }
104                else
105                    wanted = tmp;
106            }
107            else {
108                uWarning("Didn't specify any LEDs to watch\n");
109                uAction("Ignoring trailing -watch argument\n");
110            }
111        }
112        else if (uStrCaseEqual(argv[i], "-union")) {
113            useUnion = True;
114        }
115        else if (uStrCaseEqual(argv[i], "-intersection")) {
116            useUnion = False;
117        }
118        else if (uStrCaseEqual(argv[i], "-help")) {
119            usage(argv[0]);
120            exit(0);
121        }
122        else if (uStrCaseEqual(argv[i], "-version")) {
123            printf("xkbvleds (%s) %s\n", PACKAGE_NAME, PACKAGE_VERSION);
124            exit(0);
125        }
126        else if ((argv[i][0] == '+') || (argv[i][0] == '-')) {
127            Bool onoff;
128            int *which;
129
130            onoff = (argv[i][0] == '+');
131            which = NULL;
132            if (uStrCaseEqual(&argv[i][1], "name"))
133                which = &wantNamed;
134            else if (uStrCaseEqual(&argv[i][1], "explicit"))
135                which = &wantExplicit;
136            else if (uStrCaseEqual(&argv[i][1], "automatic"))
137                which = &wantAutomatic;
138            else if (uStrCaseEqual(&argv[i][1], "real"))
139                which = &wantReal;
140            else if (uStrCaseEqual(&argv[i][1], "virtual"))
141                which = &wantVirtual;
142            if (which != NULL) {
143                if (*which != DONT_CARE) {
144                    uWarning("Multiple settings for [+-]%s\n", &argv[i][1]);
145                    uAction("Using %c%s, ignoring %c%s\n",
146                            (onoff ? '+' : '-'), &argv[i][1],
147                            (onoff ? '-' : '+'), &argv[i][1]);
148                }
149                *which = (onoff ? YES : NO);
150            }
151        }
152    }
153    return True;
154}
155
156/***====================================================================***/
157
158static Display *
159GetDisplay(char *program, char *dpyName)
160{
161    int mjr, mnr, error;
162    Display *dpy;
163
164    mjr = XkbMajorVersion;
165    mnr = XkbMinorVersion;
166    dpy = XkbOpenDisplay(dpyName, &evBase, &errBase, &mjr, &mnr, &error);
167    if (dpy == NULL) {
168        switch (error) {
169        case XkbOD_BadLibraryVersion:
170            uInformation("%s was compiled with XKB version %d.%02d\n",
171                         program, XkbMajorVersion, XkbMinorVersion);
172            uError("X library supports incompatible version %d.%02d\n",
173                   mjr, mnr);
174            break;
175        case XkbOD_ConnectionRefused:
176            uError("Cannot open display \"%s\"\n", dpyName);
177            break;
178        case XkbOD_NonXkbServer:
179            uError("XKB extension not present on %s\n", dpyName);
180            break;
181        case XkbOD_BadServerVersion:
182            uInformation("%s was compiled with XKB version %d.%02d\n",
183                         program, XkbMajorVersion, XkbMinorVersion);
184            uError("Server %s uses incompatible version %d.%02d\n",
185                   dpyName, mjr, mnr);
186            break;
187        default:
188            uInternalError("Unknown error %d from XkbOpenDisplay\n", error);
189        }
190    }
191    else if (synch)
192        XSynchronize(dpy, True);
193    return dpy;
194}
195
196/***====================================================================***/
197
198int
199main(int argc, char *argv[])
200{
201    Widget toplevel;
202    XtAppContext app_con;
203    Widget panel;
204    Widget leds[XkbNumIndicators];
205    register int i;
206    unsigned bit;
207    unsigned n;
208    XkbDescPtr xkb;
209    XkbEvent ev;
210    static Arg boxArgs[] = { {XtNorientation, (XtArgVal) XtorientHorizontal} };
211    static Arg onArgs[] = { {XtNon, (XtArgVal) True} };
212    static Arg offArgs[] = { {XtNon, (XtArgVal) False} };
213    static String fallback_resources[] = {
214        "*Box*background: grey40",
215        NULL
216    };
217
218    bzero(leds, XkbNumIndicators * sizeof(Widget));
219    toplevel = XtOpenApplication(&app_con, "XkbLEDPanel", NULL, 0, &argc, argv,
220                                 fallback_resources,
221                                 sessionShellWidgetClass, NULL, ZERO);
222    if (toplevel == NULL) {
223        uFatalError("Couldn't create application top level\n");
224        return 1;
225    }
226    if ((argc > 1) && (!parseArgs(argc, argv))) {
227        usage(argv[0]);
228        return 1;
229    }
230    if ((wanted == 0) && (wantNamed == DONT_CARE) && (wantExplicit == DONT_CARE)
231        && (wantAutomatic == DONT_CARE) && (wantReal == DONT_CARE)) {
232        wantNamed = YES;
233        wantReal = YES;
234        wantAutomatic = YES;
235    }
236    outDpy = XtDisplay(toplevel);
237    if (inDpyName != NULL) {
238        inDpy = GetDisplay(argv[0], inDpyName);
239        if (!inDpy)
240            return 1;
241    }
242    else {
243        inDpy = outDpy;
244    }
245    if (inDpy) {
246        int i1, mn, mj;
247
248        mj = XkbMajorVersion;
249        mn = XkbMinorVersion;
250        if (!XkbLibraryVersion(&mj, &mn)) {
251            uInformation("%s was compiled with XKB version %d.%02d\n",
252                         argv[0], XkbMajorVersion, XkbMinorVersion);
253            uError("X library supports incompatible version %d.%02d\n", mj, mn);
254        }
255        if (!XkbQueryExtension(inDpy, &i1, &evBase, &errBase, &mj, &mn)) {
256            uFatalError("Server doesn't support a compatible XKB\n");
257            return 1;
258        }
259    }
260    else {
261        uFatalError("No input display\n");
262        return 1;
263    }
264    panel =
265        XtCreateManagedWidget("xkbleds", boxWidgetClass, toplevel, boxArgs, 1);
266    if (panel == NULL) {
267        uFatalError("Couldn't create list of leds\n");
268        return 1;
269    }
270    real = virtual = named = explicit = automatic = 0;
271    if (wantReal || wantNamed || wantAutomatic || wantExplicit || wantVirtual) {
272        register int i, bit;
273
274        xkb = XkbGetMap(inDpy, 0, XkbUseCoreKbd);
275        if (!xkb) {
276            uFatalError("Couldn't read keymap\n");
277            return 1;
278        }
279        if (XkbGetIndicatorMap(inDpy, XkbAllIndicatorsMask, xkb) != Success) {
280            uFatalError("Couldn't read indicator map\n");
281            return 1;
282        }
283        if (XkbGetNames(inDpy, XkbAllNamesMask, xkb) != Success) {
284            uFatalError("Couldn't read indicator names\n");
285            return 1;
286        }
287        for (i = 0, bit = 1; i < XkbNumIndicators; i++, bit <<= 1) {
288            XkbIndicatorMapPtr map = &xkb->indicators->maps[i];
289
290            if (xkb->names->indicators[i] != None)
291                named |= bit;
292            if (xkb->indicators->phys_indicators & bit)
293                real |= bit;
294            if ((((map->which_groups != 0) && (map->groups != 0)) ||
295                 ((map->which_mods != 0) &&
296                  ((map->mods.real_mods != 0) || (map->mods.vmods != 0))) ||
297                 (map->ctrls != 0)) &&
298                ((map->flags & XkbIM_NoAutomatic) == 0)) {
299                automatic |= bit;
300            }
301            else
302                explicit |= bit;
303        }
304        virtual = ~real;
305        if (wantReal == NO)
306            real = ~real;
307        else if (wantReal == DONT_CARE)
308            real = (useUnion ? 0 : ~0);
309        if (wantVirtual == NO)
310            virtual = ~virtual;
311        else if (wantVirtual == DONT_CARE)
312            virtual = (useUnion ? 0 : ~0);
313        if (wantNamed == NO)
314            named = ~named;
315        else if (wantNamed == DONT_CARE)
316            named = (useUnion ? 0 : ~0);
317        if (wantAutomatic == NO)
318            automatic = ~automatic;
319        else if (wantAutomatic == DONT_CARE)
320            automatic = (useUnion ? 0 : ~0);
321        if (wantExplicit == NO)
322            explicit = ~explicit;
323        else if (wantExplicit == DONT_CARE)
324            explicit = (useUnion ? 0 : ~0);
325        if (useUnion)
326            wanted |= real | virtual | named | automatic | explicit;
327        else
328            wanted &= real & virtual & named & automatic & explicit;
329    }
330    else
331        xkb = NULL;
332    if (wanted == 0) {
333        uError("No indicator maps match the selected criteria\n");
334        uAction("Exiting\n");
335        return 1;
336    }
337
338    XkbSelectEvents(inDpy, XkbUseCoreKbd, XkbIndicatorStateNotifyMask,
339                    XkbIndicatorStateNotifyMask);
340    XkbGetIndicatorState(inDpy, XkbUseCoreKbd, &n);
341    bit = (1U << (XkbNumIndicators - 1));
342    for (i = XkbNumIndicators - 1; i >= 0; i--, bit >>= 1) {
343        if (wanted & bit) {
344            char buf[12];
345            ArgList list;
346
347            snprintf(buf, sizeof(buf), "led%d", i + 1);
348            if (n & bit)
349                list = onArgs;
350            else
351                list = offArgs;
352            leds[i] =
353                XtCreateManagedWidget(buf, ledWidgetClass, panel, list, 1);
354        }
355    }
356    XtRealizeWidget(toplevel);
357    while (1) {
358        XtAppNextEvent(app_con, &ev.core);
359        if (ev.core.type == evBase + XkbEventCode) {
360            if (ev.any.xkb_type == XkbIndicatorStateNotify) {
361                for (i = 0, bit = 1; i < XkbNumIndicators; i++, bit <<= 1) {
362                    if ((ev.indicators.changed & bit) && (leds[i])) {
363                        ArgList list;
364
365                        if (ev.indicators.state & bit)
366                            list = onArgs;
367                        else
368                            list = offArgs;
369                        XtSetValues(leds[i], list, 1);
370                    }
371                }
372            }
373        }
374        else
375            XtDispatchEvent(&ev.core);
376    }
377/* BAIL: */
378    if (inDpy)
379        XCloseDisplay(inDpy);
380    if (outDpy != inDpy)
381        XCloseDisplay(outDpy);
382    inDpy = outDpy = NULL;
383    return 0;
384}
385