1: #ifndef lint
2: static char *rcsid_uwm_c = "$Header: uwm.c,v 10.4 86/02/01 16:24:27 tony Rel $";
3: #endif lint
4:
5: /************************************************************************
6: * *
7: * Copyright (c) 1986 by *
8: * Digital Equipment Corporation, Maynard, MA *
9: * All Rights Reserved. *
10: * *
11: * Permission to use, copy, modify, and distribute this software *
12: * and its documentation is hereby granted only to licensees of *
13: * The Regents of the University of California pursuant to their *
14: * license agreement for the Berkeley Software Distribution *
15: * provided that the following appears on all copies. *
16: * *
17: * "LICENSED FROM DIGITAL EQUIPMENT CORPORATION *
18: * COPYRIGHT (C) 1986 *
19: * DIGITAL EQUIPMENT CORPORATION *
20: * MAYNARD, MA *
21: * ALL RIGHTS RESERVED. *
22: * *
23: * THE INFORMATION IN THIS SOFTWARE IS SUBJECT TO CHANGE WITHOUT *
24: * NOTICE AND SHOULD NOT BE CONSTRUED AS A COMMITMENT BY DIGITAL *
25: * EQUIPMENT CORPORATION. DIGITAL MAKES NO REPRESENTATIONS *
26: * ABOUT SUITABILITY OF THIS SOFTWARE FOR ANY PURPOSE. IT IS *
27: * SUPPLIED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY. *
28: * *
29: * IF THE UNIVERSITY OF CALIFORNIA OR ITS LICENSEES MODIFY *
30: * THE SOFTWARE IN A MANNER CREATING DERIVATIVE COPYRIGHT *
31: * RIGHTS APPROPRIATE COPYRIGHT LEGENDS MAY BE PLACED ON THE *
32: * DERIVATIVE WORK IN ADDITION TO THAT SET FORTH ABOVE." *
33: * *
34: ************************************************************************/
35:
36:
37: /*
38: * MODIFICATION HISTORY
39: *
40: * 000 -- M. Gancarz, DEC Ultrix Engineering Group
41: */
42:
43: #ifndef lint
44: static char *sccsid = "@(#)uwm.c 3.8 1/24/86";
45: #endif
46:
47: #include <sys/time.h>
48: #include "uwm.h"
49:
50: #ifdef PROFIL
51: #include <signal.h>
52: /*
53: * Dummy handler for profiling.
54: */
55: ptrap()
56: {
57: exit(0);
58: }
59: #endif
60:
61: #include <fcntl.h>
62:
63: static short gray_bits[16] = {
64: 0xaaaa, 0x5555, 0xaaaa, 0x5555,
65: 0xaaaa, 0x5555, 0xaaaa, 0x5555,
66: 0xaaaa, 0x5555, 0xaaaa, 0x5555,
67: 0xaaaa, 0x5555, 0xaaaa, 0x5555
68: };
69:
70: Bool ChkMline();
71: char *sfilename;
72: extern FILE *yyin;
73:
74: /*
75: * Main program.
76: */
77: main(argc, argv, environ)
78: int argc;
79: char **argv;
80: char **environ;
81: {
82: short hi; /* Button event high detail. */
83: short lo; /* Button event low detail. */
84: int x, y; /* Mouse X and Y coordinates. */
85: int cur_x, cur_y; /* Current mouse X and Y coordinates. */
86: int str_width; /* Width in pixels of output string. */
87: int pop_width, pop_height; /* Pop up window width and height. */
88: int context; /* Root, window, or icon context. */
89: Bool func_stat; /* If true, function swallowed a ButtonUp. */
90: Bool delta_done; /* If true, then delta functions are done. */
91: register Binding *bptr; /* Pointer to Bindings list. */
92: char *root_name; /* Root window name. */
93: char *display = NULL; /* Display name pointer. */
94: char message[128]; /* Error message buffer. */
95: char *rc_file; /* Pointer to $HOME/.uwmrc. */
96: Bitmap gray_bitmap; /* Gray bitmap used for gray pixmap. */
97: Display *dpy; /* Display info pointer. */
98: Window event_win; /* Event window. */
99: Window sub_win; /* Subwindow for XUpdateMouse calls. */
100: WindowInfo root_info; /* Root window info. */
101: WindowInfo event_info; /* Event window info. */
102: XButtonEvent button_event; /* Button input event. */
103: char *malloc();
104:
105:
106: #ifdef PROFIL
107: signal(SIGTERM, ptrap);
108: #endif
109:
110: /*
111: * Set up internal defaults.
112: */
113: strcpy(IFontName, DEF_FONT);
114: strcpy(PFontName, DEF_FONT);
115: strcpy(MFontName, DEF_FONT);
116: CursorFunc = DEF_FUNC;
117: Delta = DEF_DELTA;
118: IBorderWidth = DEF_ICON_BORDER_WIDTH;
119: HIconPad = DEF_ICON_PADDING;
120: VIconPad = DEF_ICON_PADDING;
121: PBorderWidth = DEF_POP_BORDER_WIDTH;
122: PPadding = DEF_POP_PADDING;
123: MBorderWidth = DEF_MENU_BORDER_WIDTH;
124: HMenuPad = DEF_MENU_PADDING;
125: VMenuPad = DEF_MENU_PADDING;
126: Volume = DEF_VOLUME;
127:
128: /*
129: * Set XErrorFunction to be non-terminating.
130: */
131: XErrorHandler(XError);
132:
133: /*
134: * Parse the command line arguments.
135: */
136: Argv = argv;
137: Environ = environ;
138: argc--, argv++;
139: while (argc) {
140: if (!(strcmp(*argv, "-f"))) {
141: argc--, argv++;
142: if ((argc == 0) || (Startup_File[0] != '\0'))
143: Usage();
144: strncpy(Startup_File, *argv, NAME_LEN);
145: }
146: else display = *argv;
147: argc--, argv++;
148: }
149:
150: /*
151: * Initialize the default bindings.
152: */
153: InitBindings();
154:
155: /*
156: * Read in and parse $HOME/.uwmrc, if it exists.
157: */
158: sfilename = rc_file = malloc(NAME_LEN);
159: sprintf(rc_file, "%s/.uwmrc", getenv("HOME"));
160: if ((yyin = fopen(rc_file, "r")) != NULL) {
161: Lineno = 1;
162: yyparse();
163: fclose(yyin);
164: if (Startup_File_Error)
165: Error("Bad .uwmrc file...aborting");
166: }
167:
168: /*
169: * Read in and parse the startup file from the command line, if
170: * specified.
171: */
172: if (Startup_File[0] != '\0') {
173: sfilename = Startup_File;
174: if ((yyin = fopen(Startup_File, "r")) == NULL) {
175: sprintf(message, "Cannot open startup file '%s'", Startup_File);
176: Error(message);
177: }
178: Lineno = 1;
179: yyparse();
180: fclose(yyin);
181: if (Startup_File_Error)
182: Error("Bad startup file...aborting");
183: }
184:
185: /*
186: * Verify the menu bindings.
187: */
188: VerifyMenuBindings();
189: if (Startup_File_Error)
190: Error("Bad startup file...aborting");
191:
192: /*
193: * Open the display.
194: */
195: if ((dpy = XOpenDisplay(display)) == NULL)
196: Error("Unable to open display");
197:
198: /*
199: * Force child processes to disinherit the TCP file descriptor.
200: * This helps shell commands forked and exec'ed from menus
201: * to work properly.
202: */
203: if ((status = fcntl(dpyno(), F_SETFD, 1)) == -1) {
204: perror("uwm: child cannot disinherit TCP fd");
205: Error("TCP file descriptor problems");
206: }
207:
208: /*
209: * If the root window has not been named, name it.
210: */
211: status = XFetchName(RootWindow, &root_name);
212: if (status == FAILURE) Error("Can't fetch Root Window name string");
213: if (root_name == NULL) XStoreName(RootWindow, " X Root Window ");
214: if (root_name) free(root_name);
215:
216: /*
217: * Gather information about the root window.
218: */
219: status = XQueryWindow(RootWindow, &root_info);
220: if (status == FAILURE)
221: Error("Can't acquire root window information from X server");
222:
223: ScreenHeight = root_info.height; /* True height of entire screen */
224: ScreenWidth = root_info.width; /* True width of entire screen */
225:
226: /*
227: * Create and store the icon background pixmap.
228: */
229: gray_bitmap = XStoreBitmap(16, 16, gray_bits);
230: GrayPixmap = XMakePixmap(gray_bitmap, BlackPixel, WhitePixel);
231:
232: /*
233: * Set up icon window, icon cursor and pop-up window color parameters.
234: */
235: if (Reverse) {
236: IconCursorFunc = GXcopyInverted;
237: IBorder = WhitePixmap;
238: IBackground = GrayPixmap;
239: ITextForground = WhitePixel;
240: ITextBackground = BlackPixel;
241: PBorder = BlackPixmap;
242: PBackground = WhitePixmap;
243: PTextForground = BlackPixel;
244: PTextBackground = WhitePixel;
245: MBorder = WhitePixmap;
246: MBackground = BlackPixmap;
247: MTextForground = WhitePixel;
248: MTextBackground = BlackPixel;
249: }
250: else {
251: IconCursorFunc = GXcopy;
252: IBorder = BlackPixmap;
253: IBackground = GrayPixmap;
254: ITextForground = BlackPixel;
255: ITextBackground = WhitePixel;
256: PBorder = WhitePixmap;
257: PBackground = BlackPixmap;
258: PTextForground = WhitePixel;
259: PTextBackground = BlackPixel;
260: MBorder = BlackPixmap;
261: MBackground = WhitePixmap;
262: MTextForground = BlackPixel;
263: MTextBackground = WhitePixel;
264: }
265:
266: /*
267: * Store all the cursors.
268: */
269: StoreCursors();
270:
271: /*
272: * grab the mouse buttons according to the map structure
273: */
274: Grab_Buttons();
275:
276: /*
277: * Load the selected fonts.
278: */
279: IFont = XGetFont(IFontName);
280: if (IFont == FAILURE) {
281: sprintf(message, "Unable to get font '%s'.", IFontName);
282: Error(message);
283: }
284: PFont = XGetFont(PFontName);
285: if (PFont == FAILURE) {
286: sprintf(message, "Unable to get font '%s'.", PFontName);
287: Error(message);
288: }
289: MFont = XGetFont(MFontName);
290: if (MFont == FAILURE) {
291: sprintf(message, "Unable to get font '%s'.", MFontName);
292: Error(message);
293: }
294:
295: /*
296: * Retrieve the information structure for the specifed fonts and
297: * set the global font information pointers.
298: */
299: status = XQueryFont(IFont, &IFontInfo);
300: if (status == FAILURE) {
301: sprintf(message, "Unable to query X server for info on font '%s'.",
302: IFontName);
303: Error(message);
304: }
305: status = XQueryFont(PFont, &PFontInfo);
306: if (status == FAILURE) {
307: sprintf(message, "Unable to query X server for info on font '%s'.",
308: PFontName);
309: Error(message);
310: }
311: status = XQueryFont(MFont, &MFontInfo);
312: if (status == FAILURE) {
313: sprintf(message, "Unable to query X server for info on font '%s'.",
314: MFontName);
315: Error(message);
316: }
317:
318: /*
319: * Calculate size of the resize pop-up window.
320: */
321: str_width = XQueryWidth(PText, PFont);
322: pop_width = str_width + (PPadding << 1);
323: PWidth = pop_width + (PBorderWidth << 1);
324: pop_height = PFontInfo.height + (PPadding << 1);
325: PHeight = pop_height + (PBorderWidth << 1);
326:
327: /*
328: * Create the pop-up window. Create it at (0, 0) for now. We will
329: * move it where we want later.
330: */
331: Pop = XCreateWindow(RootWindow,
332: 0, 0,
333: pop_width, pop_height,
334: PBorderWidth,
335: PBorder, PBackground);
336: if (Pop == FAILURE) Error("Can't create pop-up dimension display window.");
337:
338: /*
339: * Create the menus for later use.
340: */
341: CreateMenus();
342:
343: /*
344: * Tell the user we're alive and well.
345: */
346: XFeep(Volume);
347:
348: /*
349: * Main command loop.
350: */
351: while (TRUE) {
352:
353: delta_done = func_stat = FALSE;
354:
355: /*
356: * Get the next mouse button event. Spin our wheels until
357: * a ButtonPressed event is returned.
358: * Note that mouse events within an icon window are handled
359: * in the "GetButton" function or by the icon's owner if
360: * it is not uwm.
361: */
362: while (TRUE) {
363: if (!GetButton(&button_event)) continue;
364: if (button_event.type == ButtonPressed) break;
365: }
366:
367: /*
368: * Okay, determine the event window and mouse coordinates.
369: */
370: status = XInterpretLocator(RootWindow,
371: &x, &y,
372: &event_win,
373: button_event.location);
374:
375: if (status == FAILURE) continue;
376:
377: /*
378: * Determine the event window and context.
379: */
380: if (event_win == 0) {
381: event_win = RootWindow;
382: context = ROOT;
383: } else {
384: status = XQueryWindow(event_win, &event_info);
385: if (status == FAILURE) continue;
386: if (event_info.type & IsIcon)
387: context = ICON;
388: else context = WINDOW;
389: }
390:
391: /*
392: * Get the button event detail.
393: */
394: lo = (button_event.detail & ValueMask);
395: hi = KeyMask(button_event.detail);
396:
397: /*
398: * Determine which function was selected and invoke it.
399: */
400: for(bptr = Blist; bptr; bptr = bptr->next) {
401:
402: if ((bptr->button != lo) ||
403: (KeyMask(bptr->mask) != hi))
404: continue;
405:
406: if (bptr->context != context)
407: continue;
408:
409: if (!(bptr->mask & ButtonDown))
410: continue;
411:
412: /*
413: * Found a match! Invoke the function.
414: */
415: if ((*bptr->func)(event_win,
416: (int)bptr->mask & ~ButtonMods,
417: bptr->button,
418: x, y,
419: bptr->menu)) {
420: func_stat = TRUE;
421: break;
422: }
423: }
424:
425: /*
426: * If the function ate the ButtonUp event, then restart the loop.
427: */
428: if (func_stat) continue;
429:
430: while(TRUE) {
431: /*
432: * Wait for the next button event.
433: */
434: if (XPending() && GetButton(&button_event)) {
435:
436: /*
437: * If it's not a release of the same button that was pressed,
438: * don't do the function bound to 'ButtonUp'.
439: */
440: if (button_event.type != ButtonReleased)
441: break;
442: if (lo != (button_event.detail & ValueMask))
443: break;
444: if (hi != KeyMask(button_event.detail))
445: break;
446:
447: /*
448: * Okay, determine the event window and mouse coordinates.
449: */
450: status = XInterpretLocator(RootWindow,
451: &x, &y,
452: &event_win,
453: button_event.location);
454:
455: if (status == FAILURE) break;
456:
457: if (event_win == 0) {
458: event_win = RootWindow;
459: context = ROOT;
460: } else {
461: status = XQueryWindow(event_win, &event_info);
462: if (status == FAILURE) break;
463: if (event_info.type & IsIcon)
464: context = ICON;
465: else context = WINDOW;
466: }
467:
468: /*
469: * Determine which function was selected and invoke it.
470: */
471: for(bptr = Blist; bptr; bptr = bptr->next) {
472:
473: if ((bptr->button != lo) ||
474: (KeyMask(bptr->mask) != hi))
475: continue;
476:
477: if (bptr->context != context)
478: continue;
479:
480: if (!(bptr->mask & ButtonUp))
481: continue;
482:
483: /*
484: * Found a match! Invoke the function.
485: */
486: (*bptr->func)(event_win,
487: (int)bptr->mask & ~ButtonMods,
488: bptr->button,
489: x, y,
490: bptr->menu);
491: }
492: break;
493: }
494:
495: XUpdateMouse(RootWindow, &cur_x, &cur_y, &sub_win);
496: if (!delta_done &&
497: ((abs(cur_x - x) > Delta) || (abs(cur_y - y) > Delta))) {
498: /*
499: * Delta functions are done once (and only once.)
500: */
501: delta_done = TRUE;
502:
503: /*
504: * Determine the new event window's coordinates.
505: */
506: status = XInterpretLocator(RootWindow,
507: &x, &y,
508: &event_win,
509: button_event.location);
510: if (status == FAILURE) break;
511:
512: /*
513: * Determine the event window and context.
514: */
515: if (event_win == 0) {
516: event_win = RootWindow;
517: context = ROOT;
518: } else {
519: status = XQueryWindow(event_win, &event_info);
520: if (status == FAILURE) break;
521: if (event_info.type & IsIcon)
522: context = ICON;
523: else context = WINDOW;
524: }
525:
526: /*
527: * Determine which function was selected and invoke it.
528: */
529: for(bptr = Blist; bptr; bptr = bptr->next) {
530:
531: if ((bptr->button != lo) ||
532: (KeyMask(bptr->mask) != hi))
533: continue;
534:
535: if (bptr->context != context)
536: continue;
537:
538: if (!(bptr->mask & DeltaMotion))
539: continue;
540:
541: /*
542: * Found a match! Invoke the function.
543: */
544: if ((*bptr->func)(event_win,
545: (int)bptr->mask & ~ButtonMods,
546: bptr->button,
547: x, y,
548: bptr->menu)) {
549: func_stat = TRUE;
550: break;
551: }
552: }
553: /*
554: * If the function ate the ButtonUp event,
555: * then restart the loop.
556: */
557: if (func_stat) break;
558: }
559: }
560: }
561: }
562:
563: /*
564: * Initialize the default bindings. First, write the character array
565: * out to a temp file, then point the parser to it and read it in.
566: * Afterwards, we unlink the temp file.
567: */
568: InitBindings()
569: {
570: char *mktemp();
571: char *tempfile = TEMPFILE; /* Temporary filename. */
572: register FILE *fp; /* Temporary file pointer. */
573: register char **ptr; /* Default bindings string array pointer. */
574:
575: /*
576: * Create and write the temp file.
577: */
578: sfilename = mktemp(tempfile);
579: if ((fp = fopen(tempfile, "w")) == NULL) {
580: perror("uwm: cannot create temp file");
581: exit(1);
582: }
583: for (ptr = DefaultBindings; *ptr; ptr++) {
584: fputs(*ptr, fp);
585: fputc('\n', fp);
586: }
587: fclose(fp);
588:
589: /*
590: * Read in the bindings from the temp file and parse them.
591: */
592: if ((yyin = fopen(tempfile, "r")) == NULL) {
593: perror("uwm: cannot open temp file");
594: exit(1);
595: }
596: Lineno = 1;
597: yyparse();
598: fclose(yyin);
599: unlink(tempfile);
600: if (Startup_File_Error)
601: Error("Bad default bindings...aborting");
602:
603: /*
604: * Parse the system startup file, if one exists.
605: */
606: if ((yyin = fopen(SYSFILE, "r")) != NULL) {
607: sfilename = SYSFILE;
608: Lineno = 1;
609: yyparse();
610: fclose(yyin);
611: if (Startup_File_Error)
612: Error("Bad system startup file...aborting");
613: }
614: }
615:
616: /*
617: * Verify menu bindings by checking that a menu that is mapped actually
618: * exists. Stash a pointer in the binding to the relevant menu info data
619: * structure.
620: * Check nested menu consistency.
621: */
622: ()
623: {
624: Binding *bptr;
625: MenuLink *mptr;
626:
627: for(bptr = Blist; bptr; bptr = bptr->next) {
628: if (bptr->func == Menu) {
629: for(mptr = Menus; mptr; mptr = mptr->next) {
630: if(!(strcmp(bptr->menuname, mptr->menu->name))) {
631: bptr->menu = mptr->menu;
632: break;
633: }
634: }
635: if (mptr == NULL) {
636: fprintf(stderr,
637: "uwm: non-existent menu reference: \"%s\"\n",
638: bptr->menuname);
639: Startup_File_Error = TRUE;
640: }
641: }
642: }
643: CheckMenus();
644: }
645:
646: /*
647: * Check nested menu consistency by verifying that every menu line that
648: * calls another menu references a menu that actually exists.
649: */
650: ()
651: {
652: MenuLink *ptr;
653: Bool errflag = FALSE;
654:
655: for(ptr = Menus; ptr; ptr = ptr->next) {
656: if (ChkMline(ptr->menu))
657: errflag = TRUE;
658: }
659: if (errflag)
660: Error("Nested menu inconsistency");
661: }
662:
663: Bool ChkMline(menu)
664: MenuInfo *menu;
665: {
666: MenuLine *ptr;
667: MenuLink *lptr;
668: Bool errflag = FALSE;
669:
670: for(ptr = menu->line; ptr; ptr = ptr->next) {
671: if (ptr->type == IsMenuFunction) {
672: for(lptr = Menus; lptr; lptr = lptr->next) {
673: if(!(strcmp(ptr->text, lptr->menu->name))) {
674: ptr->menu = lptr->menu;
675: break;
676: }
677: }
678: if (lptr == NULL) {
679: fprintf(stderr,
680: "uwm: non-existent menu reference: \"%s\"\n",
681: ptr->text);
682: errflag = TRUE;
683: }
684: }
685: }
686: return(errflag);
687: }
688:
689: /*
690: * Grab the mouse buttons according to the bindings list.
691: */
692: Grab_Buttons()
693: {
694: Binding *bptr;
695:
696: for(bptr = Blist; bptr; bptr = bptr->next)
697: Grab(bptr->mask);
698: }
699:
700: /*
701: * Grab a mouse button according to the given mask.
702: */
703: Grab(mask)
704: short mask;
705: {
706: short m = LeftMask | MiddleMask | RightMask;
707:
708: switch (mask & m) {
709: case LeftMask:
710: status = XGrabButton(RootWindow, LeftButtonCursor,
711: mask & ~ButtonMods,
712: EVENTMASK);
713: if (status == FAILURE)
714: Error("Can't grab left mouse button.");
715: break;
716:
717: case MiddleMask:
718: status = XGrabButton(RootWindow, MiddleButtonCursor,
719: mask & ~ButtonMods,
720: EVENTMASK);
721: if (status == FAILURE)
722: Error("Can't grab middle mouse button.");
723: break;
724:
725: case RightMask:
726: status = XGrabButton(RootWindow, RightButtonCursor,
727: mask & ~ButtonMods,
728: EVENTMASK);
729: if (status == FAILURE)
730: Error("Can't grab right mouse button.");
731: break;
732: }
733: }
734:
735: /*
736: * error routine for .uwmrc parser
737: */
738: yyerror(s)
739: char*s;
740: {
741: fprintf(stderr, "uwm: %s: %d: %s\n", sfilename, Lineno, s);
742: Startup_File_Error = TRUE;
743: }
744:
745: /*
746: * Print usage message and quit.
747: */
748: Usage()
749: {
750: fputs("Usage: uwm [-f <file>] [<host>:<display>]\n", stderr);
751: exit(1);
752: }
753:
754: /*
755: * error handler for X I/O errors
756: */
757: XIOError(dsp)
758: Display *dsp;
759: {
760: perror("uwm");
761: exit(3);
762: }