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