]> asedeno.scripts.mit.edu Git - 1ts-debian.git/blob - zephyr/zwgc/X_gram.c
need automake as a build-dep, even though we don't use most of it
[1ts-debian.git] / zephyr / zwgc / X_gram.c
1 /* This file is part of the Project Athena Zephyr Notification System.
2  * It is one of the source files comprising zwgc, the Zephyr WindowGram
3  * client.
4  *
5  *      Created by:     Marc Horowitz <marc@athena.mit.edu>
6  *
7  *      $Id$
8  *
9  *      Copyright (c) 1989 by the Massachusetts Institute of Technology.
10  *      For copying and distribution information, see the file
11  *      "mit-copyright.h".
12  */
13
14 #include <sysdep.h>
15
16 #if (!defined(lint) && !defined(SABER))
17 static const char rcsid_X_gram_c[] = "$Id$";
18 #endif
19
20 #include <zephyr/mit-copyright.h>
21
22 #ifndef X_DISPLAY_MISSING
23
24 #include <zephyr/zephyr.h>
25 #include "X_gram.h"
26 #include "xmark.h"
27 #include <X11/Xutil.h>
28 #include <X11/cursorfont.h>
29 #include <X11/Xatom.h>
30 #include "zwgc.h"
31 #include "X_driver.h"
32 #include "X_fonts.h"
33 #include "error.h"
34 #include "new_string.h"
35 #include "xrevstack.h"
36 #include "xerror.h"
37 #include "xselect.h"
38 #ifdef CMU_ZWGCPLUS
39 #include "plus.h"
40 #endif
41
42 extern XContext desc_context;
43 extern char *app_instance;
44
45 /*
46  *
47  */
48
49 int internal_border_width = 2;
50
51 unsigned long default_fgcolor;
52 unsigned long default_bgcolor;
53 unsigned long default_bordercolor;
54 long ttl = 0;
55 static int reset_saver;
56 static int border_width = 1;
57 static int cursor_code = XC_sailboat;
58 static int set_transient;
59 static int enable_delete;
60 static char *title_name,*icon_name;
61 static Cursor cursor;
62 static Window group_leader; /* In order to have transient windows,
63                              * I need a top-level window to always exist
64                              */
65 static XClassHint classhint;
66 static XSetWindowAttributes xattributes;
67 static unsigned long xattributes_mask;
68 static int set_all_desktops = True;
69 static Atom net_wm_desktop = None;
70 static Atom net_wm_window_type = None;
71 static Atom net_wm_window_type_utility = None;
72
73 /* ICCCM note:
74  *
75  * the following properties must be set on all top-level windows:
76  *
77  * WM_NAME                  XStoreName(dpy,w,name);
78  * WM_ICON_NAME             XSetIconName(dpy,w,name);
79  * WM_NORMAL_HINTS          XSetNormalHints(dpy,w,sizehints);
80  * WM_HINTS                 XSetWMHints(dpy,w,wmhints);
81  * WM_CLASS                 XSetClassHint(dpy,w,classhint);
82  *
83  * and for individual zgrams:
84  *
85  * WM_TRANSIENT_FOR         XSetTransientForHint(dpy,w,main_window);
86  * WM_PROTOCOLS             XSetWMProtocols(dpy,w,protocols,cnt);
87  */
88
89 /* set all properties defined in ICCCM.  If main_window == 0,
90  * per-zgram initialization is not done.
91  */
92
93 /*ARGSUSED*/
94 void
95 x_set_icccm_hints(Display *dpy,
96                   Window w,
97                   char *name,
98                   char *icon_name,
99                   XSizeHints *psizehints,
100                   XWMHints *pwmhints,
101                   Window main_window)
102 {
103    XStoreName(dpy,w,name);
104    XSetIconName(dpy,w,icon_name);
105    XSetWMNormalHints(dpy,w,psizehints);
106    XSetWMHints(dpy,w,pwmhints);
107    XSetClassHint(dpy,w,&classhint);
108    /* in order for some wm's to iconify, the window shouldn't be transient.
109       e.g. Motif wm */
110    if (main_window != None) {
111       if (set_transient)
112           XSetTransientForHint(dpy,w,main_window);
113    }
114    if (enable_delete)
115       XSetWMProtocols(dpy,w,&XA_WM_DELETE_WINDOW,1);
116 }
117
118 void
119 x_gram_init(Display *dpy)
120 {
121     char *temp;
122     XSizeHints sizehints;
123     XWMHints wmhints;
124     unsigned long rv,tc;
125
126     default_fgcolor = BlackPixelOfScreen(DefaultScreenOfDisplay(dpy));
127     default_bgcolor = WhitePixelOfScreen(DefaultScreenOfDisplay(dpy));
128     rv = get_bool_resource("reverseVideo", "ReverseVideo", 0);
129     if (rv) {
130        tc = default_fgcolor;
131        default_fgcolor = default_bgcolor;
132        default_bgcolor = tc;
133     }
134     temp = get_string_resource("foreground", "Foreground");
135     if (temp)
136       default_fgcolor = x_string_to_color(temp, default_fgcolor);
137     temp = get_string_resource("background", "Background");
138     if (temp)
139       default_bgcolor = x_string_to_color(temp, default_bgcolor);
140     default_bordercolor = default_fgcolor;
141     temp = get_string_resource("borderColor", "BorderColor");
142     if (temp)
143       default_bordercolor = x_string_to_color(temp, default_bordercolor);
144
145     temp = get_string_resource("minTimeToLive", "MinTimeToLive");
146     if (temp && atoi(temp)>=0)
147        ttl = atoi(temp);
148
149 #ifdef CMU_ZWGCPLUS
150     if (ttl == 0) {
151       temp = get_string_resource("lifespan", "LifeSpan");
152       if (temp && atoi(temp)>=0)
153         ttl = atoi(temp);
154     }
155
156     get_full_names = get_bool_resource("getFullNames", "GetFullNames", 0);
157 #endif
158
159     reverse_stack = get_bool_resource("reverseStack", "ReverseStack", 0);
160     reset_saver =  get_bool_resource("resetSaver", "ResetSaver", 1);
161     /* The default here should be 1, but mwm sucks */
162     set_transient = get_bool_resource("transient", "Transient", 0);
163     enable_delete = get_bool_resource("enableDelete", "EnableDelete", 1);
164
165     temp = get_string_resource("borderWidth", "BorderWidth");
166     /* <<<>>> */
167     if (temp && atoi(temp)>=0)
168       border_width = atoi(temp);
169
170     temp = get_string_resource("internalBorder", "InternalBorder");
171     /* <<<>>> */
172     if (temp && atoi(temp)>=0)
173       internal_border_width = atoi(temp);
174
175     temp = get_string_resource("cursorCode", "CursorCode");
176     /* <<<>>> */
177     if (temp && atoi(temp))
178       cursor_code = atoi(temp);
179
180     cursor = XCreateFontCursor(dpy, cursor_code);
181     if (!cursor)
182       cursor = XCreateFontCursor(dpy, XC_sailboat);
183
184     temp = get_string_resource("pointerColor", "Foreground");
185     if (temp) {
186         char *temp2;
187         XColor cursor_fore, cursor_back;
188         /* XXX need to do our own parsing here, since the RecolorCursor
189            routine requires an XColor, not an unsigned long (pixel) */
190         if (!(temp2 = get_string_resource("background","Background"))) {
191             if (default_bgcolor == WhitePixelOfScreen(DefaultScreenOfDisplay(dpy)))
192                 temp2 = "white";
193             else
194                 temp2 = "black";
195         }
196         if (XParseColor(dpy,
197                         DefaultColormapOfScreen(DefaultScreenOfDisplay(dpy)),
198                         temp, &cursor_fore) &&
199             XParseColor(dpy,
200                         DefaultColormapOfScreen(DefaultScreenOfDisplay(dpy)),
201                         temp2, &cursor_back)) {
202               XRecolorCursor(dpy, cursor, &cursor_fore, &cursor_back);
203           }
204     }
205     if (!(title_name=get_string_resource("title","Title")))
206       if (!(title_name=get_string_resource("name","Name")))
207         title_name=app_instance;
208
209     if (!(icon_name=get_string_resource("iconName","IconName")))
210       if (!(icon_name=get_string_resource("name","Name")))
211         icon_name=app_instance;
212
213     if (!(temp=get_string_resource("name","Name")))
214       if (!(temp=(char *) getenv("RESOURCE_NAME")))
215         temp=app_instance;
216     classhint.res_name=string_Copy(temp);
217     classhint.res_class="Zwgc";
218
219     if (set_transient) {
220        group_leader=XCreateSimpleWindow(dpy,DefaultRootWindow(dpy),0,0,100,100,
221                                         0,default_bordercolor,default_bgcolor);
222        sizehints.x = 0;
223        sizehints.y = 0;
224        sizehints.width = 100;
225        sizehints.height = 100;
226        sizehints.flags = PPosition | PSize;
227
228        wmhints.input = False;
229        wmhints.initial_state = DontCareState;
230        wmhints.flags = InputHint | StateHint;
231
232        x_set_icccm_hints(dpy,group_leader,"ZwgcGroup","ZwgcGroup",&sizehints,
233                          &wmhints,0);
234     }
235     xattributes.border_pixel = default_bordercolor;
236     xattributes.cursor = cursor;
237     xattributes.event_mask = (ExposureMask|ButtonReleaseMask|ButtonPressMask
238                               |LeaveWindowMask|Button1MotionMask
239 #ifdef CMU_ZWGCPLUS
240                               |KeyPressMask
241 #endif
242                               |Button3MotionMask|StructureNotifyMask);
243     xattributes_mask = (CWBackPixel|CWBorderPixel|CWEventMask|CWCursor);
244
245     set_all_desktops = get_bool_resource("allDesktops", "AllDesktops", True);
246     net_wm_desktop = XInternAtom(dpy, "_NET_WM_DESKTOP", False);
247     net_wm_window_type = XInternAtom(dpy, "_NET_WM_WINDOW_TYPE", False);
248     net_wm_window_type_utility = XInternAtom(dpy,
249                                              "_NET_WM_WINDOW_TYPE_UTILITY",
250                                              False);
251
252     temp = get_string_resource ("backingStore", "BackingStore");
253     if (!temp)
254         return;
255     xattributes_mask |= CWBackingStore;
256     if (!strcasecmp (temp, "notuseful"))
257         xattributes.backing_store = NotUseful;
258     else if (!strcasecmp (temp, "whenmapped"))
259         xattributes.backing_store = WhenMapped;
260     else if (!strcasecmp (temp, "always"))
261         xattributes.backing_store = Always;
262     else if (!strcasecmp (temp, "default"))
263         xattributes_mask &= ~CWBackingStore;
264     else {
265         switch (get_bool_resource ("backingStore", "BackingStore", -1)) {
266         case 0:
267             xattributes.backing_store = NotUseful;
268             break;
269         case 1:
270             xattributes.backing_store = WhenMapped;
271             break;
272         case -1:
273             fprintf (stderr,
274                  "zwgc: Cannot interpret backing-store resource value `%s'.\n",
275                      temp);
276             xattributes_mask &= ~CWBackingStore;
277             break;
278         }
279     }
280 }
281
282 int
283 x_calc_gravity(int xalign,
284                int yalign)
285 {
286     if (yalign > 0) {                                   /* North */
287         return (xalign > 0)  ? NorthWestGravity
288              : (xalign == 0) ? NorthGravity
289              :                 NorthEastGravity;
290     } else if (yalign == 0) {                           /* Center */
291         return (xalign > 0)  ? WestGravity
292              : (xalign == 0) ? CenterGravity
293              :                 EastGravity;
294     } else {                                            /* South */
295         return (xalign > 0)  ? SouthWestGravity
296              : (xalign == 0) ? SouthGravity
297              :                 SouthEastGravity;
298     }
299 }
300
301 void
302 x_gram_create(Display *dpy,
303               x_gram *gram,
304               int xalign,
305               int yalign,
306               int xpos,
307               int ypos,
308               int xsize,
309               int ysize,
310               int beepcount)
311 {
312     Window w;
313     XSizeHints sizehints;
314     XWMHints wmhints;
315     XSetWindowAttributes attributes;
316     unsigned long all_desktops = 0xFFFFFFFF;
317
318     /*
319      * Adjust xpos, ypos based on the alignments xalign, yalign and the sizes:
320      */
321     if (xalign < 0)
322       xpos = WidthOfScreen(DefaultScreenOfDisplay(dpy))
323           - xpos - xsize - 2 * border_width;
324     else if (xalign == 0)
325       xpos = ((WidthOfScreen(DefaultScreenOfDisplay(dpy))
326                - xsize - 2 * border_width) >> 1) + xpos;
327
328     if (yalign<0)
329       ypos = HeightOfScreen(DefaultScreenOfDisplay(dpy))
330           - ypos - ysize - 2 * border_width;
331     else if (yalign == 0)
332       ypos = ((HeightOfScreen(DefaultScreenOfDisplay(dpy))
333                - ysize - 2 * border_width) >> 1) + ypos;
334
335     /*
336      * Create the window:
337      */
338     attributes = xattributes;
339     attributes.background_pixel = gram->bgcolor;
340     
341     gram->w = w = XCreateWindow (dpy, DefaultRootWindow (dpy), xpos, ypos,
342                                  xsize, ysize, border_width, 0,
343                                  CopyFromParent, CopyFromParent,
344                                  xattributes_mask, &attributes);
345     
346     sizehints.x = xpos;
347     sizehints.y = ypos;
348     sizehints.width = xsize;
349     sizehints.height = ysize;
350     sizehints.win_gravity = x_calc_gravity(xalign, yalign);
351     sizehints.flags = USPosition | USSize | PWinGravity;
352
353     wmhints.input = False;
354     wmhints.initial_state = NormalState;
355     if (set_transient) {
356        wmhints.window_group = group_leader;
357        wmhints.flags = InputHint | StateHint | WindowGroupHint;
358
359        x_set_icccm_hints(dpy, w, title_name, icon_name, &sizehints, &wmhints,
360                          group_leader);
361     } else {
362        wmhints.flags = InputHint | StateHint;
363
364        x_set_icccm_hints(dpy, w, title_name, icon_name, &sizehints, &wmhints, 0);
365     }
366        
367     if (net_wm_window_type != None && net_wm_window_type_utility != None)
368         XChangeProperty(dpy, w, net_wm_window_type, XA_ATOM, 32, PropModeReplace,
369                         (unsigned char *) &net_wm_window_type_utility, 1);
370     if (set_all_desktops && net_wm_desktop != None)
371         XChangeProperty(dpy, w, net_wm_desktop, XA_CARDINAL, 32, PropModeReplace,
372                         (unsigned char *) &all_desktops, 1);
373
374     XSaveContext(dpy, w, desc_context, (XPointer)gram);
375
376     gram->can_die.tv_sec = 0;
377
378     XMapWindow(dpy, w);
379
380     if (beepcount)
381         XBell(dpy, 0);
382
383     xerror_happened = 0;
384     if (reverse_stack && bottom_gram) {
385        XWindowChanges winchanges;
386        
387        winchanges.sibling = bottom_gram->w;
388        winchanges.stack_mode = Below;
389        /* Metacity may use border_width even if it's not specified in
390         * the value mask, so we must initialize it.  See:
391         * http://bugzilla.gnome.org/show_bug.cgi?id=305257 */
392        winchanges.border_width = border_width;
393
394        begin_xerror_trap (dpy);
395        XReconfigureWMWindow (dpy, w, DefaultScreen (dpy),
396                              CWSibling | CWStackMode, &winchanges);
397        end_xerror_trap (dpy);
398        if (xerror_happened) {
399            /* The event didn't go.  Print an error message, and continue.  */
400            ERROR ("Error configuring window to the bottom of the stack.\n");
401        }
402     }
403     /* we always need to keep a linked list of windows */
404     add_to_bottom(gram);
405     if (xerror_happened)
406        pull_to_top(gram);
407
408     if (reset_saver)
409         XResetScreenSaver(dpy);
410
411     XFlush(dpy);
412     /* Because the flushing/syncing/etc with the error trapping can cause
413        events to be read into the Xlib queue, we need to go through the queue
414        here before exiting so that any pending events get processed.
415        */
416     x_get_input(dpy);
417 }
418
419 inline static void
420 SetFG(Display *dpy, GC gc, unsigned long foreground) {
421     XGCValues gcvals;
422
423     gcvals.foreground = foreground;
424     XChangeGC(dpy, gc, GCForeground, &gcvals);
425 }
426
427 void
428 x_gram_draw(Display *dpy, Window w, x_gram *gram, Region region)
429 {
430    int i;
431    GC gc;
432    XGCValues gcvals;
433    xblock *xb;
434 #ifdef X_HAVE_UTF8_STRING
435    XmbTextItem text;
436 #else
437    XwcTextItem text;
438 #endif
439    int startblock, endblock, startpixel = 0, endpixel = 0;
440    
441    gc = XCreateGC(dpy, w, 0, &gcvals);
442    XSetRegion(dpy, gc, region);
443  
444    if ((markgram == gram) && (STARTBLOCK != -1) && (ENDBLOCK != -1)) {
445       if (xmarkSecond() == XMARK_END_BOUND) {
446          startblock = STARTBLOCK;
447          endblock = ENDBLOCK;
448          startpixel = STARTPIXEL;
449          endpixel = ENDPIXEL;
450       } else {
451          startblock = ENDBLOCK;
452          endblock = STARTBLOCK;
453          startpixel = ENDPIXEL;
454          endpixel = STARTPIXEL;
455       }
456    } else {
457       startblock = -1;
458       endblock = -1;
459    }
460
461    for (i=0, xb = gram->blocks; i < gram->numblocks; i++, xb++) {
462       if (XRectInRegion(region, xb->x1, xb->y1, xb->x2 - xb->x1,
463                         xb->y2 - xb->y1) != RectangleOut) {
464          if (i == startblock) {
465             if (i == endblock) {
466                 SetFG(dpy, gc, gram->bgcolor);
467                 XFillRectangle(dpy, w, gc, xb->x1, xb->y1, startpixel,
468                                xb->y2 - xb->y1);
469                 SetFG(dpy, gc, xb->fgcolor);
470                 XFillRectangle(dpy, w, gc, xb->x1 + startpixel, xb->y1,
471                                endpixel - startpixel, xb->y2 - xb->y1);
472                 SetFG(dpy, gc, gram->bgcolor);
473                 XFillRectangle(dpy, w, gc, xb->x1 + endpixel, xb->y1,
474                                xb->x2 - xb->x1 - endpixel, xb->y2 - xb->y1);
475             } else {
476                 SetFG(dpy, gc, gram->bgcolor);
477                 XFillRectangle(dpy, w, gc, xb->x1, xb->y1, startpixel,
478                               xb->y2 - xb->y1);
479                 SetFG(dpy, gc, xb->fgcolor);
480                 XFillRectangle(dpy, w, gc, xb->x1 + startpixel, xb->y1,
481                                xb->x2 - xb->x1 - startpixel,xb->y2 - xb->y1);
482             }
483          } else if (i == endblock) {
484              SetFG(dpy, gc, xb->fgcolor);
485              XFillRectangle(dpy, w, gc, xb->x1, xb->y1, endpixel,
486                             xb->y2 - xb->y1);
487              SetFG(dpy, gc, gram->bgcolor);
488              XFillRectangle(dpy, w, gc, xb->x1 + endpixel, xb->y1,
489                             xb->x2 - xb->x1 - endpixel, xb->y2 - xb->y1);
490          } else {
491              if (startblock < i && i < endblock) {
492                  SetFG(dpy, gc, xb->fgcolor);
493              } else {
494                  SetFG(dpy, gc, gram->bgcolor);
495              }
496              XFillRectangle(dpy, w, gc, xb->x1, xb->y1, xb->x2 - xb->x1,
497                             xb->y2 - xb->y1);
498          }
499       }
500    }
501
502    gcvals.function = GXxor;
503    XChangeGC(dpy, gc, GCFunction, &gcvals);
504
505    for (i=0, xb = gram->blocks; i < gram->numblocks; i++, xb++) {
506       if (XRectInRegion(region, xb->x1, xb->y1, xb->x2 - xb->x1,
507                         xb->y2 - xb->y1) != RectangleOut) {
508           SetFG(dpy, gc, gram->bgcolor ^ xb->fgcolor);
509 #ifdef X_HAVE_UTF8_STRING
510           text.chars = xb->wstr;
511 #else
512           text.chars = (XChar2b *)xb->wstr;
513 #endif
514           text.nchars = xb->wlen;
515           text.delta = 0;
516           text.font_set = xb->font;
517 #ifdef X_HAVE_UTF8_STRING
518           Xutf8DrawText(dpy, w, gc, xb->x, xb->y, &text, 1);
519 #else
520           XwcDrawText(dpy, w, gc, xb->x, xb->y, &text, 1);
521 #endif
522      }
523    }
524
525    XFreeGC(dpy, gc);
526 }
527
528 void
529 x_gram_expose(Display *dpy,
530               Window w,
531               x_gram *gram,
532               XExposeEvent *event)
533 {
534    static Region region;
535    static int partregion;
536    XRectangle rect;
537
538    rect.x = (short) event->x;
539    rect.y = (short) event->y;
540    rect.width = (unsigned short) event->width;
541    rect.height = (unsigned short) event->height;
542
543 #ifdef MARK_DEBUG
544    printf("----- xeventExpose:\nx=%d y=%d w=%d h=%d\n-----",
545           event->x,event->y,event->width,event->height);
546 #endif
547
548    if (! partregion) {
549       region=XCreateRegion();
550       partregion = 1;
551    }
552
553    if (rect.width && rect.height) XUnionRectWithRegion(&rect,region,region);
554
555    if (event->count == 0) {
556       x_gram_draw(dpy,w,gram,region);
557       partregion = 0;
558       XDestroyRegion(region);
559    }
560 }
561
562 #endif /* X_DISPLAY_MISSING */