1: /* 2: * Copyright 1985, Cognition Inc. 3: * This software is not supported by Cognition Inc. and is 4: * provided "as is". To keep the X revolution growing, please forward 5: * enhancements and bug fixes to anyone who is interested. 6: */ 7: 8: /* 9: * XShell - a quick "shell" to allow you to cons up programs on the fly. The 10: * program looks at Button and Key Pressed events and looks up Xshell.name in 11: * $HOME/.Xdefaults where name is: 12: * 13: * LeftButton, MiddleButton, RightButton - on button events. 14: * ascii (e.g. [aB^9]) - on key pressed events. 15: * PF# and various special names - on special keys. 16: * 17: * The idea is that the user can define any set of key/button bindings which 18: * can be invoked by simply pressing in the XShell window. This can be very 19: * useful for times when you have filled all of your windows with things that 20: * you don't want to (or can't) suspend. 21: * 22: * I find it useful to have a large and small terminal window, a dc, and 23: * sometimes an editor that I can pop up on demand. This program was written 24: * because I was tired of getting into situations where I didn't have a window 25: * handy and I needed to run some little calculator or editor. Since I use 26: * a small, terse window manager I didn't just stick a bag on the side of it, 27: * but wrote a separate program instead. 28: * 29: * Apologies to anyone who has the scallop shell as a trademark. 30: * 31: * Author: Jim Fulton, Cognition Inc. 32: */ 33: #ifndef lint 34: static char *rcsid_xshell_c = "$Header: xshell.c,v 10.6 86/02/01 16:19:36 tony Rel $"; 35: #endif 36: 37: #include <stdio.h> 38: #include <X/Xlib.h> 39: #include <X/Xkeyboard.h> 40: #include <signal.h> 41: #include <ctype.h> 42: #include <strings.h> 43: #include <sys/file.h> 44: #include <sys/ioctl.h> 45: #include <sys/wait.h> 46: #include <sys/time.h> 47: #include <sys/resource.h> 48: #include <sys/types.h> 49: #include "scallopshell.h" 50: #include "largescallopshell.h" 51: 52: extern KeyMapEntry StdMap[]; 53: 54: extern char *malloc(); 55: 56: #define strneq(a,b) (strncmp ((a), (b), strlen(a)) == 0) 57: #define streq(a,b) (strcmp ((a), (b)) == 0) 58: #define isopt(optname) (strneq(*argv, optname)) /* does partial matching */ 59: #define hasarg ((argv[1] != (char *) NULL) && (argv[1][0] != '-')) 60: #define NO 0 61: #define YES 1 62: #define DEFAULT_FONT "helv12b" 63: #define micro2centi (10000) /* microsecond to 1/100 second */ 64: 65: char *ProgramName; 66: 67: Window ShellWindow; 68: Display *dpy; 69: int fg = BlackPixel; /* print bits in black */ 70: int bg = WhitePixel; /* print background in white */ 71: int bd = BlackPixel; /* print border in black */ 72: int bw = 0; /* no border */ 73: int volume = 1; 74: WindowInfo RootWindowInfo; 75: Bitmap IconBitmap; 76: Pixmap IconPixmap; 77: XEvent inputevent; 78: int width, height; 79: unsigned long code; 80: int nflash = 3; 81: struct timeval delaytime = {0, 5*micro2centi}; 82: int quiet = NO; 83: short *icon_bits; 84: int icon_width, icon_height; 85: 86: char **actionvector[256]; /* command table */ 87: char actionfound[256]; /* see flags below */ 88: #define ACTION_NEW ((char) 0) 89: #define ACTION_FOUND ((char) 1) 90: #define ACTION_NOT_FOUND ((char) 2) 91: 92: char *yes[] = {"y", "yes", "YES", "on", "ON", "On", 93: "t", "true", "TRUE", "True", (char *) NULL}; 94: 95: char *small[] = {"s", "S", "small", "SMALL", "Small", "sm", "SM", "Sm", 96: (char *) NULL}; 97: 98: int IsMemberOf (list, string) 99: register char *list[]; 100: register char *string; 101: { 102: for (; *list; list++) { 103: if (streq (string, *list)) return (YES); 104: } 105: return (NO); 106: } 107: 108: reapchild () 109: { 110: union wait status; 111: 112: while (wait3 (&status, WNOHANG, (struct rusage *) 0) > 0) ; 113: return; 114: } 115: 116: static void Error (msg, arg) 117: char *msg, *arg; 118: { 119: fprintf (stderr, "%s: error with %s", msg); 120: if (arg != (char *) NULL && *arg != '\0') 121: fprintf (stderr, ": %s\n", arg); 122: else 123: fprintf (stderr, "\n"); 124: exit (1); 125: } 126: 127: static void Usage (msg) 128: char *msg; 129: { 130: fprintf (stderr, "%s: error with \"%s\". Usage is\n", 131: ProgramName, msg); 132: fprintf (stderr, "\n\t\t%s [-flags] [=WxH+X+Y] [host:displaynum]\n\n", 133: ProgramName); 134: fprintf (stderr, "where -flags are:\n"); 135: fprintf (stderr, "\t-fg color Foreground color.\n"); 136: fprintf (stderr, "\t-bg color Background color.\n"); 137: fprintf (stderr, "\t-bd color Border color.\n"); 138: fprintf (stderr, "\t-bw[idth]n Border width in pixels.\n"); 139: fprintf (stderr, "\t-v[olume] n Bell volume.\n"); 140: fprintf (stderr, "\t-fl[ash] n Number of times to flash icon.\n"); 141: fprintf (stderr, "\t-d[elay] n 1/10ths of a second flashes.\n"); 142: fprintf (stderr, "\t-r[everse] Reverse video.\n"); 143: fprintf (stderr, "\t-q[uiet] Don\'t feep on errors.\n"); 144: fprintf (stderr, "\t-s[mall] Use a small icon instead of a big one.\n"); 145: fprintf (stderr, "\n"); 146: exit (1); 147: } 148: 149: quit () 150: { 151: XUnmapWindow (ShellWindow); 152: XCloseDisplay (dpy); 153: exit (0); 154: } 155: 156: main (argc, argv) 157: int argc; 158: char *argv[]; 159: { 160: register int i; 161: register char *cp; 162: char *fgname, *bgname, *bdname; 163: int reverse; 164: char *displayname; 165: Color colorstruct; 166: int c; 167: char cbuf[2]; 168: int xoff, yoff, xsign, ysign; 169: 170: (void) signal (SIGINT, quit); 171: (void) signal (SIGHUP, quit); 172: (void) signal (SIGCHLD, reapchild); 173: 174: setlinebuf (stderr); 175: 176: /* set program name for errors */ 177: ProgramName = argv[0]; 178: cp = rindex (ProgramName, '/'); 179: if (cp) ProgramName = ++cp; /* strip off directory name */ 180: 181: /* Initialize variables */ 182: fgname = bgname = bdname = (char *) NULL; 183: reverse = NO; 184: displayname = (char *) NULL; 185: bzero (actionfound, sizeof (actionfound)); 186: icon_bits = largescallopshell_bits; 187: width = -1; 188: height = -1; 189: icon_width = largescallopshell_width; 190: icon_height = largescallopshell_height; 191: 192: /* read in defaults from .Xdefaults */ 193: 194: fgname = XGetDefault (ProgramName, "Foreground"); 195: 196: bgname = XGetDefault (ProgramName, "Background"); 197: 198: bdname = XGetDefault (ProgramName, "Border"); 199: 200: cp = XGetDefault (ProgramName, "BorderWidth"); 201: if (cp) bw = atoi (cp); 202: 203: cp = XGetDefault (ProgramName, "Volume"); 204: if (cp) volume = atoi (cp); 205: 206: cp = XGetDefault (ProgramName, "Flash"); 207: if (cp) nflash = atoi (cp); 208: 209: cp = XGetDefault (ProgramName, "Delay"); 210: if (cp) delaytime.tv_usec = atoi (cp) * micro2centi; 211: 212: if ((cp = XGetDefault (ProgramName, "ReverseVideo")) != NULL) 213: if (IsMemberOf (yes, cp)) reverse = YES; 214: 215: if ((cp = XGetDefault (ProgramName, "Quiet")) != NULL) 216: if (IsMemberOf (yes, cp)) quiet = YES; 217: 218: if ((cp = XGetDefault (ProgramName, "IconSize")) != NULL) { 219: if (IsMemberOf(small, cp)) { 220: icon_bits = scallopshell_bits; 221: icon_width = scallopshell_width; 222: icon_height = scallopshell_height; 223: } 224: } 225: 226: cp = XGetDefault (ProgramName, "WindowGeometry"); 227: if (cp) { 228: if (!XParse_Window_Geometry (cp, &width, &height, 229: &xsign, &xoff, &ysign, &yoff)) Usage ("default =WxH+XOFF+YOFF"); 230: } 231: 232: /* read command arguments */ 233: argv++; /* advance past command name */ 234: for (; *argv; argv++) { /* iterate over arguments */ 235: if (**argv == '-') { 236: if (isopt ("-fg")) { 237: if (hasarg) { 238: fgname = *++argv; 239: } else Usage ("-fg foreground"); 240: } else 241: if (isopt ("-bg")) { 242: if (hasarg) { 243: bgname = *++argv; 244: } else Usage ("-bg background"); 245: } else 246: if (isopt ("-bd")) { 247: if (hasarg) { 248: bdname = *++argv; 249: } else Usage ("-bd border color"); 250: } else 251: if (isopt ("-bwidth")) { 252: if (hasarg) { 253: bw = atoi (*++argv); 254: } else Usage ("-bwidth borderwidth"); 255: } else 256: if (isopt ("-volume")) { 257: if (hasarg) { 258: volume = atoi (*++argv); 259: } else Usage ("-volume volume"); 260: } else 261: if (isopt ("-flash")) { 262: if (hasarg) { 263: nflash = atoi (*++argv); 264: } else Usage ("-flash n"); 265: } else 266: if (isopt ("-delay")) { 267: if (hasarg) { 268: delaytime.tv_usec = atoi (*++argv) * micro2centi; 269: } else Usage ("-delay n"); 270: } else 271: if (isopt ("-reverse")) { 272: reverse = YES; 273: } else 274: if (isopt ("-quiet")) { 275: quiet = YES; 276: } else 277: if (isopt ("-small")) { 278: icon_bits = scallopshell_bits; 279: icon_width = scallopshell_width; 280: icon_height = scallopshell_height; 281: } else 282: Usage ("-unknown flag"); /* end if command line options */ 283: } else if (**argv == '=') { 284: if (!XParse_Window_Geometry (*argv, &width, &height, 285: &xsign, &xoff, &ysign, &yoff)) Usage ("=WxH+XOFF+YOFF"); 286: } else 287: displayname = *argv; 288: } /*end for*/ 289: 290: 291: if (width == -1) width = icon_width; 292: if (height == -1) height = icon_height; 293: 294: /* okay, now set things up */ 295: dpy = XOpenDisplay (displayname); 296: if (!dpy) 297: Error ("opening display", ""); 298: 299: if (!XQueryWindow (RootWindow, &RootWindowInfo)) 300: Error ("query root", ""); 301: 302: if (xsign < 0) xoff = RootWindowInfo.width - xoff - width - 2*bw; 303: if (ysign < 0) yoff = RootWindowInfo.height - yoff - height - 2*bw; 304: 305: /* set the colors for the various parts */ 306: 307: #define setcolor(colorname,colornum) \ 308: if (colorname && DisplayCells() > 2 && \ 309: XParseColor(colorname, &colorstruct) && \ 310: XGetHardwareColor(&colorstruct)) { \ 311: colornum = colorstruct.pixel; \ 312: reverse = NO; \ 313: } 314: 315: setcolor (fgname, fg); 316: setcolor (bgname, bg); 317: setcolor (bdname, bd); 318: 319: #undef setcolor 320: 321: if (reverse) { 322: i = fg; 323: fg = bg; 324: bg = i; 325: } 326: /* now, make up the icon pixmap */ 327: 328: IconBitmap = XStoreBitmap (icon_width, icon_height, icon_bits); 329: if (!IconBitmap) Error ("storing icon bitmap", ""); 330: 331: IconPixmap = XMakePixmap (IconBitmap, fg, bg); 332: if (!IconPixmap) Error ("storing icon pixmap", ""); 333: 334: 335: /* make the window */ 336: 337: ShellWindow = XCreateWindow (RootWindow, xoff, yoff, width, height, 338: bw, XMakeTile(bd), XMakeTile(bg)); 339: if (!ShellWindow) Error ("creating shell window", ""); 340: 341: /* and store away the program name in the window */ 342: 343: XStoreName (ShellWindow, ProgramName); 344: 345: /* select the window events */ 346: 347: XSelectInput (ShellWindow, KeyPressed | ButtonPressed | ExposeWindow); 348: 349: /* and map it, this should generate an Expose event */ 350: XMapWindow (ShellWindow); 351: 352: while (1) { /* loop forever */ 353: XNextEvent (&inputevent); 354: code = ((XKeyOrButtonEvent *) &inputevent)->detail; 355: switch ((int) inputevent.type) { 356: case ExposeWindow: /* repaint the icon */ 357: if (inputevent.window == ShellWindow) 358: XPixmapPut (ShellWindow, 0, 0, 0, 0, 359: icon_width, icon_height, 360: IconPixmap, GXcopy, AllPlanes); 361: break; 362: 363: case KeyPressed: 364: c = StdMap [code & ValueMask] [KeyState(code)]; 365: switch (c) { 366: case -1: 367: feep (); 368: break; 369: case SHFT: 370: perform ("SHIFT"); 371: break; 372: case CNTL: 373: perform ("CONTROL"); 374: break; 375: case LOCK: 376: perform ("LOCK"); 377: break; 378: case SYMBOL: 379: perform ("SYMBOL"); 380: break; 381: case KEYPAD: 382: switch (code & ValueMask) { 383: case KC_KEYPAD_0: 384: perform ("KEYPAD0"); 385: break; 386: case KC_KEYPAD_PERIOD: 387: perform ("KEYPAD."); 388: break; 389: case KC_ENTER: 390: perform ("ENTER"); 391: break; 392: case KC_KEYPAD_1: 393: perform ("KEYPAD1"); 394: break; 395: case KC_KEYPAD_2: 396: perform ("KEYPAD2"); 397: break; 398: case KC_KEYPAD_3: 399: perform ("KEYPAD3"); 400: break; 401: case KC_KEYPAD_4: 402: perform ("KEYPAD4"); 403: break; 404: case KC_KEYPAD_5: 405: perform ("KEYPAD5"); 406: break; 407: case KC_KEYPAD_6: 408: perform ("KEYPAD6"); 409: break; 410: case KC_KEYPAD_COMMA: 411: perform ("KEYPAD,"); 412: break; 413: case KC_KEYPAD_7: 414: perform ("KEYPAD7"); 415: break; 416: case KC_KEYPAD_8: 417: perform ("KEYPAD8"); 418: break; 419: case KC_KEYPAD_9: 420: perform ("KEYPAD9"); 421: break; 422: case KC_KEYPAD_MINUS: 423: perform ("KEYPAD-"); 424: break; 425: default: 426: feep (); 427: break; 428: } /*end switch*/ 429: break; 430: case CURSOR: 431: switch (code & ValueMask) { 432: case KC_CURSOR_LEFT: 433: perform ("LEFTARROW"); 434: break; 435: case KC_CURSOR_RIGHT: 436: perform ("RIGHTARROW"); 437: break; 438: case KC_CURSOR_DOWN: 439: perform ("DOWNARROW"); 440: break; 441: case KC_CURSOR_UP: 442: perform ("UPARROW"); 443: break; 444: default: 445: feep (); 446: break; 447: } 448: break; 449: case PFX: 450: switch (code & ValueMask) { 451: case KC_PF1: 452: perform ("PF1"); 453: case KC_PF2: 454: perform ("PF2"); 455: case KC_PF3: 456: perform ("PF3"); 457: case KC_PF4: 458: perform ("PF4"); 459: default: 460: feep (); 461: break; 462: } 463: break; 464: case FUNC1: 465: perform ("FUNC1"); 466: break; 467: case FUNC2: 468: perform ("FUNC2"); 469: break; 470: case FUNC3: 471: perform ("FUNC3"); 472: break; 473: case FUNC4: 474: perform ("FUNC4"); 475: break; 476: case FUNC5: 477: perform ("FUNC5"); 478: break; 479: case FUNC6: 480: perform ("FUNC6"); 481: break; 482: case FUNC7: 483: perform ("FUNC7"); 484: break; 485: case FUNC8: 486: perform ("FUNC8"); 487: break; 488: case FUNC9: 489: perform ("FUNC9"); 490: break; 491: case FUNC10: 492: perform ("FUNC10"); 493: break; 494: case FUNC11: 495: perform ("FUNC11"); 496: break; 497: case FUNC12: 498: perform ("FUNC12"); 499: break; 500: case FUNC13: 501: perform ("FUNC13"); 502: break; 503: case FUNC14: 504: perform ("FUNC14"); 505: break; 506: case FUNC15: 507: perform ("FUNC15"); 508: break; 509: case FUNC16: 510: perform ("FUNC16"); 511: break; 512: case FUNC17: 513: perform ("FUNC17"); 514: break; 515: case FUNC18: 516: perform ("FUNC18"); 517: break; 518: case FUNC19: 519: perform ("FUNC19"); 520: break; 521: case FUNC20: 522: perform ("FUNC20"); 523: break; 524: case E1: 525: perform ("E1"); 526: break; 527: case E2: 528: perform ("E2"); 529: break; 530: case E3: 531: perform ("E3"); 532: break; 533: case E4: 534: perform ("E4"); 535: break; 536: case E5: 537: perform ("E5"); 538: break; 539: case E6: 540: perform ("E6"); 541: break; 542: default: /* must be ascii */ 543: cbuf[0] = (char) c; 544: cbuf[1] = '\0'; 545: perform (cbuf); 546: break; 547: } /*end switch on keycode*/ 548: break; 549: 550: case ButtonPressed: 551: switch (code & ValueMask) { 552: case LeftButton: 553: perform ("LEFTBUTTON"); 554: break; 555: case MiddleButton: 556: perform ("MIDDLEBUTTON"); 557: break; 558: case RightButton: 559: perform ("RIGHTBUTTON"); 560: break; 561: default: 562: feep (); 563: break; 564: } 565: break; 566: 567: default: 568: feep (); 569: break; 570: } /*end switch on event type*/ 571: } /*end while forever getting input events*/ 572: } /*end main*/ 573: 574: /**************************************************************************** 575: * perform - This routine looks in its table to see if it already has a key 576: * code, else it does an XGetDefault of the keyname. 577: */ 578: 579: static perform (keyname) 580: char *keyname; 581: { 582: char buf[32]; 583: register char *cp; 584: 585: if (actionfound [code] == ACTION_NEW) { 586: (void) strcpy (buf, "action."); 587: (void) strcat (buf, keyname); 588: cp = XGetDefault (ProgramName, buf); 589: if (!cp) 590: actionfound [code] = ACTION_NOT_FOUND; 591: else { /* else we have to parse the string */ 592: parseaction (cp); 593: } /*end if we have an action*/ 594: } /*end if we needed to look up an action*/ 595: 596: if (actionfound [code] == ACTION_FOUND) { 597: if (vfork() == 0) /* in child, start program */ 598: execvp (actionvector [code] [0], actionvector [code]); 599: else /* in parent, flash icon */ 600: flash (); 601: } else { 602: if (!quiet) feep (); 603: } 604: 605: return; 606: } 607: 608: 609: static parseaction (actionstring) 610: char *actionstring; 611: { 612: register char *cp; 613: register int wc = 0; /* word count */ 614: register int inword; 615: register char **actionlist; 616: register int i; 617: 618: inword = 0; 619: for (cp = actionstring; *cp; cp++) { /* iterate over string */ 620: if (isspace(*cp)) { 621: if (inword) inword = 0; /* no longer in a word */ 622: } else { 623: if (!inword) { /* weren't in word */ 624: inword = 1; /* but now we are */ 625: wc++; /* so increment counter */ 626: } 627: } 628: } 629: /* wc now contains the number of separate words */ 630: actionlist = (char **) malloc ((unsigned)sizeof (char *) * (wc + 1)); 631: if (!actionlist) 632: Error ("allocating memory for command list", actionstring); 633: 634: i = 0; 635: inword = 0; 636: for (cp = actionstring; *cp; cp++) { 637: if (isspace(*cp)) { 638: if (inword) { /* were in a word */ 639: inword = 0; /* but now we're not */ 640: } 641: *cp = '\0'; /* and null out space */ 642: } else { 643: if (!inword) { /* weren't in a word */ 644: inword = 1; /* but now we are */ 645: actionlist [i++] = cp; /* store pointer to start of word */ 646: } 647: } 648: } 649: actionlist [wc] = (char *) NULL; /* execv wants this */ 650: 651: actionfound [code] = ACTION_FOUND; 652: actionvector [code] = actionlist; /* store the action */ 653: return; 654: } 655: 656: 657: /**************************************************************************** 658: * feep - is designed to alert the user that something went wrong. It could 659: * put up a dialog box if it were smart.... 660: */ 661: 662: static feep () 663: { 664: XFeep (volume); 665: XFlush (); 666: return; 667: } 668: 669: 670: /**************************************************************************** 671: * flash - this just flashes the shell box a couple of times 672: */ 673: 674: flash () 675: { 676: register int i, j; 677: 678: for (i = 0; i < nflash; i++) { 679: for (j = 0; j < 2; j++) { 680: XPixFill (ShellWindow, 0, 0, width, height, BlackPixel, 681: (Bitmap) 0, GXinvert, AllPlanes); 682: XFlush (); 683: (void) select (0, (fd_set *) 0, (fd_set *) 0, (fd_set *) 0, &delaytime); 684: } 685: } 686: return; 687: }