1#include <stdio.h>
2#include <stdlib.h>
3#include <X11/Xlib.h>
4#include <X11/Intrinsic.h>
5#include <X11/XKBlib.h>
6#include <Xm/MainW.h>
7#include <Xm/RowColumn.h>
8#include <Xm/ToggleB.h>
9
10Display         *theDisplay;
11XtAppContext    appContext;
12int		xkbEventBase;
13Widget		topLevel;
14Widget		leds[XkbNumIndicators];
15Atom		ledAtoms[XkbNumIndicators];
16XmString	ledNames[XkbNumIndicators];
17XkbDescPtr	xkb_desc;
18
19void            valueChangedProc(Widget,XtPointer,XmToggleButtonCallbackStruct *);
20XtCallbackRec   valueChangedCB[2]={(XtCallbackProc)valueChangedProc,NULL};
21
22/************************************************************************/
23/*									*/
24/* Application Resources						*/
25/*									*/
26/************************************************************************/
27#define	YES		1
28#define	NO		0
29#define	DONT_CARE	-1
30
31typedef struct
32{
33    int		wanted;
34    int		wantAutomatic;
35    int		wantExplicit;
36    int		wantNamed;
37    int		wantReal;
38    int		wantVirtual;
39    int		useUnion;
40} OptionsRec;
41
42OptionsRec options;
43
44#define Offset(field) XtOffsetOf(OptionsRec,field)
45XtResource resources[] =
46{
47    {"wanted", "Wanted", XtRInt, sizeof(int),
48     Offset(wanted), XtRImmediate, (XtPointer) DONT_CARE },
49    {"wantAutomatic", "WantAutomatic", XtRInt, sizeof(int),
50     Offset(wantAutomatic), XtRImmediate, (XtPointer)   DONT_CARE},
51    {"wantExplicit", "WantExplicit", XtRInt, sizeof(int),
52     Offset(wantExplicit), XtRImmediate, (XtPointer)    DONT_CARE},
53    {"wantNamed", "WantNamed", XtRInt, sizeof(int),
54     Offset(wantNamed), XtRImmediate, (XtPointer)       DONT_CARE},
55    {"wantReal", "WantReal", XtRInt, sizeof(int),
56     Offset(wantReal), XtRImmediate, (XtPointer)        DONT_CARE},
57    {"wantVirtual", "WantVirtual", XtRInt, sizeof(int),
58     Offset(wantVirtual), XtRImmediate, (XtPointer)     DONT_CARE},
59    {"useUnion", "UseUnion", XtRInt, sizeof(int),
60     Offset(useUnion), XtRImmediate, (XtPointer)        YES},
61    NULL
62};
63#undef Offset
64
65String fallbackResources[] =
66{
67    "*mainWindow.width: 100",
68    "*mainWindow.height: 50",
69    NULL
70};
71
72XrmOptionDescRec optionDesc[] =
73{
74    {"-watch", "*wanted", XrmoptionSepArg, (XtPointer) "0"},
75    {"-automatic", "*wantAutomatic", XrmoptionNoArg, (XtPointer) "0"},
76    {"+automatic", "*wantAutomatic", XrmoptionNoArg, (XtPointer) "1"},
77    {"-explicit", "*wantExplicit", XrmoptionNoArg, (XtPointer) "0"},
78    {"+explicit", "*wantExplicit", XrmoptionNoArg, (XtPointer) "1"},
79    {"-named", "*wantNamed", XrmoptionNoArg, (XtPointer) "0"},
80    {"+named", "*wantNamed", XrmoptionNoArg, (XtPointer) "1"},
81    {"-real", "*wantReal", XrmoptionNoArg, (XtPointer) "0"},
82    {"+real", "*wantReal", XrmoptionNoArg, (XtPointer) "1"},
83    {"-virtual", "*wantVirtual", XrmoptionNoArg, (XtPointer) "0"},
84    {"+virtual", "*wantVirtual", XrmoptionNoArg, (XtPointer) "1"},
85    {"-intersection", "*useUnion", XrmoptionNoArg, (XtPointer) "0"},
86    {"-union", "*useUnion", XrmoptionNoArg, (XtPointer) "1"}
87};
88
89/************************************************************************/
90/*									*/
91/* usage								*/
92/*									*/
93/************************************************************************/
94void usage(char *program)
95{
96    printf("Usage: %s <options>\n",program);
97    printf("Legal options include the usual X toolkit options plus:\n");
98    printf("  -help           Print this message\n");
99    printf("  -indpy <name>   Name of display to watch\n");
100    printf("  -watch <leds>   Mask of LEDs to watch\n");
101    printf("  [-+]automatic   (Don't) watch automatic LEDs\n");
102    printf("  [-+]explicit    (Don't) watch explicit LEDs\n");
103    printf("  [-+]named       (Don't) watch named LEDs\n");
104    printf("  [-+]real        (Don't) watch real LEDs\n");
105    printf("  [-+]virtual     (Don't) watch virtual LEDs\n");
106    printf("  -intersection   Watch only LEDs in all desired sets\n");
107    printf("  -union          Watch LEDs in any desired sets\n");
108    printf("The default set of LEDs is -intersection +named +virtual\n");
109    return;
110}
111/************************************************************************/
112/*									*/
113/*  XkbEventHandler	 						*/
114/*									*/
115/*  DESCRIPTION:							*/
116/*									*/
117/*      Handles events generated by the Xkb server extension.		*/
118/*									*/
119/************************************************************************/
120Boolean XkbEventHandler(XEvent *event)
121{
122    XkbEvent 		*xkbEv = (XkbEvent *) event;
123
124    if (xkbEv->any.xkb_type==XkbIndicatorStateNotify)  {
125	register int 		i;
126	register unsigned	bit;
127	for (i=0,bit=1;i<XkbNumIndicators;i++,bit<<=1)
128	    if ((xkbEv->indicators.changed&bit)&&(leds[i]))
129	    {
130		if (xkbEv->indicators.state&bit)
131		    XmToggleButtonSetState(leds[i],True,False);
132		else
133		    XmToggleButtonSetState(leds[i],False,False);
134	    }
135    }
136    else if (xkbEv->any.xkb_type==XkbIndicatorMapNotify) {
137	    unsigned change= xkbEv->indicators.changed;
138
139	    if (XkbGetIndicatorMap(theDisplay,change,xkb_desc)!=Success)
140		fprintf(stderr,"Couldn't get changed indicator maps\n");
141    }
142
143    return True;
144
145} /* XkbEventHandler */
146
147/************************************************************************/
148/*									*/
149/* InitXkb								*/
150/*									*/
151/************************************************************************/
152Boolean InitXkb(Display *theDisplay)
153{
154    int			i,opcode,errorBase,major,minor;
155    XkbDescPtr 		xkb;
156    unsigned int	bit;
157    unsigned int	real,virtual,named,explicit,automatic;
158    char 		*name;
159
160    if (!XkbQueryExtension(theDisplay,
161			   &opcode,
162			   &xkbEventBase,
163			   &errorBase,
164			   &major,
165			   &minor))
166	return False;
167
168    if (!XkbUseExtension(theDisplay,&major,&minor))
169	return False;
170
171    XkbSelectEvents(theDisplay,
172		    XkbUseCoreKbd,
173		    XkbIndicatorStateNotifyMask|XkbIndicatorMapNotifyMask,
174		    XkbIndicatorStateNotifyMask|XkbIndicatorMapNotifyMask);
175
176    XtSetEventDispatcher(theDisplay,
177			 xkbEventBase+XkbEventCode,
178			 XkbEventHandler);
179
180    xkb=XkbGetMap(theDisplay,0,XkbUseCoreKbd);
181    real=virtual=named=explicit=automatic=0;
182
183    if (!xkb)
184    {
185	fprintf(stderr,"Couldn't get keymap\n");
186	return False;
187    }
188    if (XkbGetIndicatorMap(theDisplay,XkbAllIndicatorsMask,xkb)!=Success)
189    {
190	fprintf(stderr,"Couldn't read indicator map\n");
191	XkbFreeKeyboard(xkb,XkbAllComponentsMask,True);
192	return False;
193    }
194    real=virtual=named=explicit=automatic=0;
195
196    if (XkbGetNames(theDisplay,XkbIndicatorNamesMask,xkb)!=Success)
197    {
198	fprintf(stderr,"Couldn't read indicator names\n");
199	XkbFreeKeyboard(xkb,XkbAllComponentsMask,True);
200	return False;
201    }
202    real=virtual=named=explicit=automatic=0;
203
204    for (i=0,bit=1;i<XkbNumIndicators;i++,bit<<=1)
205    {
206	XkbIndicatorMapPtr map= &xkb->indicators->maps[i];
207	name = NULL;
208	if (xkb->names->indicators[i]!=None)
209	{
210	    named|= bit;
211	    name = XGetAtomName(theDisplay,xkb->names->indicators[i]);
212	}
213	if (name != NULL)
214        {
215	    ledAtoms[i] = xkb->names->indicators[i];
216	    ledNames[i] = XmStringCreate(name,XmSTRING_DEFAULT_CHARSET);
217	}
218	else
219	{
220	    char temp[12];
221	    sprintf(temp,"led%d\0",i+1);
222	    ledAtoms[i] = None;
223	    ledNames[i] = XmStringCreate(temp,XmSTRING_DEFAULT_CHARSET);
224	}
225	if (xkb->indicators->phys_indicators&bit)
226	    real|= bit;
227	if ((((map->which_groups!=0)&&(map->groups!=0))||
228	     ((map->which_mods!=0)&&
229			((map->mods.real_mods!=0)||(map->mods.vmods!=0)))||
230	     (map->ctrls!=0))&&
231	    ((map->flags&XkbIM_NoAutomatic)==0)) {
232	    automatic|= bit;
233	}
234	else explicit|= bit;
235    }
236
237    virtual = ~real;
238
239    if (options.useUnion)
240    {
241        if ((options.wantReal==NO)      || (options.wantReal==DONT_CARE))
242	    real = 0;
243        if ((options.wantVirtual==NO)   || (options.wantVirtual==DONT_CARE))
244	    virtual = 0;
245        if ((options.wantNamed==NO)     || (options.wantNamed==DONT_CARE))
246	    named = 0;
247        if ((options.wantAutomatic==NO) || (options.wantAutomatic==DONT_CARE))
248	    automatic = 0;
249        if ((options.wantExplicit==NO)  || (options.wantExplicit==DONT_CARE))
250	    explicit = 0;
251
252	options.wanted |= real|virtual|named|automatic|explicit;
253    }
254    else
255    {
256	if (options.wanted == DONT_CARE)
257	    options.wanted = ~0;
258
259        if (options.wantReal==NO)
260	    real = ~real;
261	else if (options.wantReal==DONT_CARE)
262	    real = ~0;
263
264        if (options.wantVirtual==NO)
265	    virtual = ~virtual;
266        else if (options.wantVirtual==DONT_CARE)
267	    virtual = ~0;
268
269        if (options.wantNamed==NO)
270	    named = ~named;
271        else if (options.wantNamed==DONT_CARE)
272	    named = ~0;
273
274        if (options.wantAutomatic==NO)
275	    automatic = ~automatic;
276        else if (options.wantAutomatic==DONT_CARE)
277	    automatic = ~0;
278
279        if (options.wantExplicit==NO)
280	    explicit = ~explicit;
281        else if (options.wantExplicit==DONT_CARE)
282	    explicit = ~0;
283
284        options.wanted &= real&virtual&named&automatic&explicit;
285    }
286
287    XkbFreeKeyboard(xkb,XkbAllComponentsMask,True);
288    return True;
289
290} /* InitXkb */
291
292/************************************************************************/
293/*                                                                      */
294/* valueChangedProc - called when a toggle button is pressed.           */
295/*                                                                      */
296/************************************************************************/
297void valueChangedProc(Widget                    	w,
298                      XtPointer                 	clientData,
299                      XmToggleButtonCallbackStruct      *callbackData)
300{
301    int         	led = (int) clientData;
302    XkbDescPtr		xkb;
303
304    xkb = XkbGetMap(theDisplay,0,XkbUseCoreKbd);
305    if (!xkb)
306    {
307	fprintf(stderr,"XkbGetMap failed\n");
308        return;
309    }
310
311    if (XkbGetIndicatorMap(theDisplay,XkbAllIndicatorsMask,xkb)!=Success)
312    {
313	fprintf(stderr,"GetIndicatorMap failed\n");
314	XkbFreeKeyboard(xkb,XkbAllComponentsMask,True);
315	return;
316    }
317
318    /* The 'flags' field tells whether this indicator is automatic
319     * (XkbIM_NoExplicit - 0x80), explicit (XkbIM_NoAutomatic - 0x40),
320     * or neither (both - 0xC0).
321     *
322     * If NoAutomatic is set, the server ignores the rest of the
323     * fields in the indicator map (i.e. it disables automatic control
324     * of the LED).   If NoExplicit is set, the server prevents clients
325     * from explicitly changing the value of the LED (using the core
326     * protocol *or* XKB).   If NoAutomatic *and* NoExplicit are set,
327     * the LED cannot be changed (unless you change the map first).
328     * If neither NoAutomatic nor NoExplicit are set, the server will
329     * change the LED according to the indicator map, but clients can
330     * override that (until the next automatic change) using the core
331     * protocol or XKB.
332     */
333    switch (xkb->indicators->maps[led].flags &
334	    (XkbIM_NoExplicit|XkbIM_NoAutomatic))
335    {
336	case XkbIM_NoExplicit|XkbIM_NoAutomatic:
337	{
338	    XmToggleButtonSetState(w,!callbackData->set,FALSE);
339	    XkbFreeKeyboard(xkb,XkbAllComponentsMask,True);
340	    return;
341	}
342
343	case XkbIM_NoAutomatic:
344	{
345	    if (ledAtoms[led] != None)
346		XkbSetNamedIndicator(theDisplay,XkbUseCoreKbd,
347				     ledAtoms[led],callbackData->set,
348				     FALSE,NULL);
349	    else
350	    {
351		XKeyboardControl	xkc;
352		xkc.led= led;
353		if (callbackData->set)
354		     xkc.led_mode= LedModeOn;
355		else xkc.led_mode= LedModeOff;
356		XChangeKeyboardControl(theDisplay,KBLed|KBLedMode,&xkc);
357		XSync(theDisplay,0);
358	    }
359
360	    XkbFreeKeyboard(xkb,XkbAllComponentsMask,True);
361	    return;
362	}
363
364	case XkbIM_NoExplicit:
365	break;
366    }
367
368    /* The 'ctrls' field tells what controls tell this indicator to
369     * to turn on:  RepeatKeys (0x1), SlowKeys (0x2), BounceKeys (0x4),
370     *              StickyKeys (0x8), MouseKeys (0x10), AccessXKeys (0x20),
371     *		    TimeOut (0x40), Feedback (0x80), ToggleKeys (0x100),
372     *		    Overlay1 (0x200), Overlay2 (0x400), GroupsWrap (0x800),
373     *		    InternalMods (0x1000), IgnoreLockMods (0x2000),
374     *		    PerKeyRepeat (0x3000), or ControlsEnabled (0x4000)
375     */
376    if (xkb->indicators->maps[led].ctrls)
377    {
378	unsigned long which = xkb->indicators->maps[led].ctrls;
379
380	XkbGetControls(theDisplay,XkbAllControlsMask,xkb);
381	if (callbackData->set)
382	    xkb->ctrls->enabled_ctrls |= which;
383	else
384	    xkb->ctrls->enabled_ctrls &= ~which;
385	XkbSetControls(theDisplay,which|XkbControlsEnabledMask,xkb);
386    }
387
388    /* The 'which_groups' field tells when this indicator turns on
389     * for the 'groups' field:  base (0x1), latched (0x2), locked (0x4),
390     *                          or effective (0x8).
391     */
392    if (xkb->indicators->maps[led].groups)
393    {
394	int		i;
395       	unsigned int	group = 1;
396
397	/* Turning on a group indicator is kind of tricky.  For
398	 * now, we will just Latch or Lock the first group we find
399	 * if that is what this indicator does.  Otherwise, we're
400	 * just going to punt and get out of here.
401	 */
402	if (callbackData->set)
403	{
404	    for (i = XkbNumKbdGroups-1; i >= 0; i--)
405		if ((1 << i) &
406		    xkb->indicators->maps[led].groups)
407		    group = i;
408	    if (xkb->indicators->maps[led].which_groups &
409		(XkbIM_UseLocked | XkbIM_UseEffective))
410		XkbLockGroup(theDisplay,XkbUseCoreKbd,group);
411	    else if (xkb->indicators->maps[led].which_groups&XkbIM_UseLatched)
412		XkbLatchGroup(theDisplay,XkbUseCoreKbd,group);
413	    else
414	    {
415		XmToggleButtonSetState(w,!callbackData->set,FALSE);
416		XkbFreeKeyboard(xkb,XkbAllComponentsMask,True);
417		return;
418	    }
419	}
420	/* Turning off a group indicator will mean that we just
421	 * Lock the first group that this indicator doesn't watch.
422	 */
423	else
424	{
425	    for (i = XkbNumKbdGroups-1; i >= 0; i--)
426		if (!((1 << i) &
427		      xkb->indicators->maps[led].groups))
428		    group = i;
429	    XkbLockGroup(theDisplay,XkbUseCoreKbd,group);
430	}
431    }
432
433    /* The 'which_mods' field tells when this indicator turns on
434     * for the modifiers:  base (0x1), latched (0x2), locked (0x4),
435     *                     or effective (0x8).
436     *
437     * The 'real_mods' field tells whether this turns on when one of
438     * the real X modifiers is set:  Shift (0x1), Lock (0x2), Control (0x4),
439     * Mod1 (0x8), Mod2 (0x10), Mod3 (0x20), Mod4 (0x40), or Mod5 (0x80).
440     *
441     * The 'virtual_mods' field tells whether this turns on when one of
442     * the virtual modifiers is set.
443     *
444     * The 'mask' field tells what real X modifiers the virtual_modifiers
445     * map to?
446     */
447    if (xkb->indicators->maps[led].mods.real_mods ||
448	xkb->indicators->maps[led].mods.mask)
449    {
450	XkbStateRec 	state;
451	unsigned int	affect,mods;
452
453	affect = (xkb->indicators->maps[led].mods.real_mods |
454		  xkb->indicators->maps[led].mods.mask);
455
456	if (callbackData->set)
457	    mods = affect;
458	else
459	    mods = 0;
460
461	if (xkb->indicators->maps[led].which_mods &
462	    (XkbIM_UseLocked | XkbIM_UseEffective))
463	    XkbLockModifiers(theDisplay,XkbUseCoreKbd,affect,mods);
464	else if (xkb->indicators->maps[led].which_mods &
465		 XkbIM_UseLatched)
466	    XkbLatchModifiers(theDisplay,XkbUseCoreKbd,affect,mods);
467	else
468	{
469	    XmToggleButtonSetState(w,!callbackData->set,FALSE);
470	    XkbFreeKeyboard(xkb,XkbAllComponentsMask,True);
471	    return;
472	}
473    }
474
475    XkbFreeKeyboard(xkb,XkbAllComponentsMask,True);
476
477} /* valueChangedProc */
478
479/************************************************************************/
480/*									*/
481/* InitializeUI								*/
482/*									*/
483/************************************************************************/
484void InitializeUI(Widget topLevel)
485{
486    Arg 		argList[3];
487    char 		buf[256];
488    int 		i;
489    unsigned int	bit,n;
490    Widget 		mainWindow,rowColumn;
491    XmString		tempString;
492
493    mainWindow = (Widget) XmCreateMainWindow(topLevel,"mainWindow",NULL,0);
494    XtManageChild(mainWindow);
495    rowColumn  = (Widget) XmCreateRowColumn(mainWindow,"rowColumn",NULL,0);
496    XtManageChild(rowColumn);
497
498    XkbGetIndicatorState(theDisplay,XkbUseCoreKbd,&n);
499    for (i=0,bit=1;i<XkbNumIndicators;i++,bit<<=1)
500    {
501	if (options.wanted&bit)
502	{
503            /* [[[ WDW - If we wanted to be really fancy, we
504	     *     would look for a "*ledxx.labelString" value
505	     *     in the resource database so the I18N dudes
506	     *	   can see localized strings. ]]]
507	     */
508	    XtSetArg(argList[0], XmNlabelString,ledNames[i]);
509	    if (n&bit) XtSetArg(argList[1], XmNset, True);
510	    else       XtSetArg(argList[1], XmNset, False);
511	    sprintf(buf,"led%d\0",i);
512            valueChangedCB[0].closure = (XtPointer) i;
513	    XtSetArg(argList[2], XmNvalueChangedCallback, valueChangedCB);
514	    leds[i]= XmCreateToggleButton(rowColumn,buf,argList,3);
515	    XtManageChild(leds[i]);
516	}
517	else
518	    leds[i]=0;
519    }
520
521} /* InitializeUI */
522
523/************************************************************************/
524/*									*/
525/* main									*/
526/*									*/
527/************************************************************************/
528#if NeedFunctionPrototypes
529int main(int	argc,
530	  char	*argv[])
531#else
532int main(argc, argv)
533    int		argc;
534    char	*argv[];
535#endif
536{
537    /********************************************************************/
538    /*									*/
539    /* Initialize the toolkit 						*/
540    /*									*/
541    /********************************************************************/
542    Arg argList[2];
543    topLevel = XtAppInitialize(&appContext, "xkbleds",
544			       optionDesc, XtNumber(optionDesc),
545			       &argc, argv,
546			       fallbackResources,
547			       NULL, 0);
548    XtSetArg(argList[0], XtNallowShellResize, TRUE);
549    XtSetValues(topLevel,argList,1);
550    XtGetApplicationResources(topLevel, (XtPointer)&options, resources,
551			      XtNumber(resources), NULL, 0);
552
553    if (argc > 1)
554    {
555	usage(argv[0]);
556	exit(0);
557    }
558
559    /* Defaults
560     */
561    if ((options.wanted == DONT_CARE) &&
562	(options.wantReal == DONT_CARE) &&
563	(options.wantVirtual == DONT_CARE) &&
564	(options.wantNamed == DONT_CARE) &&
565	(options.wantAutomatic == DONT_CARE) &&
566	(options.wantExplicit == DONT_CARE) &&
567	(options.useUnion == YES))
568    {
569	options.wanted 		= 0;
570	options.wantReal	= YES;
571	options.wantNamed 	= YES;
572	options.wantAutomatic	= YES;
573    }
574
575    /********************************************************************/
576    /*									*/
577    /* See if the server has XKB.					*/
578    /*									*/
579    /********************************************************************/
580    theDisplay = XtDisplay(topLevel);
581    if (!InitXkb(theDisplay))
582    {
583	fprintf(stderr,"Could not initialize XKB extension.\n");
584	exit(0);
585    }
586
587    if (options.wanted == 0)
588    {
589	fprintf(stderr,"No LED's were selected.\n\n");
590	usage(argv[0]);
591	exit(0);
592    }
593
594    /********************************************************************/
595    /*									*/
596    /* Set up the UI and go.						*/
597    /*									*/
598    /********************************************************************/
599    XtRealizeWidget(topLevel);
600    InitializeUI(topLevel);
601    XtAppMainLoop(appContext);
602
603    /* NOT REACHED */
604    exit(0L);
605}
606