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