]> asedeno.scripts.mit.edu Git - 1ts-debian.git/blob - zephyr/zwgc/X_gram.c
96e8fb1c508f1c1ab59a4d5063c127773e70e57d
[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
41 /*
42  *
43  */
44
45 int internal_border_width = 2;
46
47 unsigned long default_fgcolor;
48 unsigned long default_bgcolor;
49 unsigned long default_bordercolor;
50 long ttl = 500;
51 static int reset_saver;
52 static int border_width = 1;
53 static int cursor_code = XC_sailboat;
54 static int set_transient;
55 static int enable_delete;
56 static char *title_name,*icon_name;
57 static Cursor cursor;
58 static Window group_leader; /* In order to have transient windows,
59                              * I need a top-level window to always exist
60                              */
61 static XClassHint classhint;
62 static XSetWindowAttributes xattributes;
63 static unsigned long xattributes_mask;
64 static int set_all_desktops = True;
65 static Atom net_wm_desktop = None;
66 static Atom net_wm_window_type = None;
67 static Atom net_wm_window_type_utility = None;
68
69 /* ICCCM note:
70  *
71  * the following properties must be set on all top-level windows:
72  *
73  * WM_NAME                  XStoreName(dpy,w,name);
74  * WM_ICON_NAME             XSetIconName(dpy,w,name);
75  * WM_NORMAL_HINTS          XSetNormalHints(dpy,w,sizehints);
76  * WM_HINTS                 XSetWMHints(dpy,w,wmhints);
77  * WM_CLASS                 XSetClassHint(dpy,w,classhint);
78  *
79  * and for individual zgrams:
80  *
81  * WM_TRANSIENT_FOR         XSetTransientForHint(dpy,w,main_window);
82  * WM_PROTOCOLS             XSetWMProtocols(dpy,w,protocols,cnt);
83  */
84
85 /* set all properties defined in ICCCM.  If main_window == 0,
86  * per-zgram initialization is not done.
87  */
88
89 /*ARGSUSED*/
90 void
91 x_set_icccm_hints(Display *dpy,
92                   Window w,
93                   char *name,
94                   char *icon_name,
95                   XSizeHints *psizehints,
96                   XWMHints *pwmhints,
97                   Window main_window)
98 {
99    XStoreName(dpy,w,name);
100    XSetIconName(dpy,w,icon_name);
101    XSetWMNormalHints(dpy,w,psizehints);
102    XSetWMHints(dpy,w,pwmhints);
103    XSetClassHint(dpy,w,&classhint);
104    /* in order for some wm's to iconify, the window shouldn't be transient.
105       e.g. Motif wm */
106    if (main_window != None) {
107       if (set_transient)
108           XSetTransientForHint(dpy,w,main_window);
109    }
110    if (enable_delete)
111       XSetWMProtocols(dpy,w,&XA_WM_DELETE_WINDOW,1);
112 }
113
114 void
115 x_gram_init(Display *dpy)
116 {
117     char *temp;
118     XSizeHints sizehints;
119     XWMHints wmhints;
120     unsigned long rv,tc;
121
122     default_fgcolor = BlackPixelOfScreen(DefaultScreenOfDisplay(dpy));
123     default_bgcolor = WhitePixelOfScreen(DefaultScreenOfDisplay(dpy));
124     rv = get_bool_resource("reverseVideo", "ReverseVideo", 0);
125     if (rv) {
126        tc = default_fgcolor;
127        default_fgcolor = default_bgcolor;
128        default_bgcolor = tc;
129     }
130     if (temp = get_string_resource("foreground","Foreground"))
131       default_fgcolor = x_string_to_color(temp,default_fgcolor);
132     if (temp = get_string_resource("background","Background"))
133       default_bgcolor = x_string_to_color(temp,default_bgcolor);
134     default_bordercolor = default_fgcolor;
135     if (temp = get_string_resource("borderColor","BorderColor"))
136       default_bordercolor = x_string_to_color(temp,default_bordercolor);
137
138     temp = get_string_resource("minTimeToLive","MinTimeToLive");
139     if (temp && atoi(temp)>=0)
140        ttl = atoi(temp);
141
142     reverse_stack = get_bool_resource("reverseStack", "ReverseStack", 0);
143     reset_saver =  get_bool_resource("resetSaver", "ResetSaver", 1);
144     /* The default here should be 1, but mwm sucks */
145     set_transient = get_bool_resource("transient", "Transient", 0);
146     enable_delete = get_bool_resource("enableDelete", "EnableDelete", 1);
147
148     temp = get_string_resource("borderWidth", "BorderWidth");
149     /* <<<>>> */
150     if (temp && atoi(temp)>=0)
151       border_width = atoi(temp);
152
153     temp = get_string_resource("internalBorder", "InternalBorder");
154     /* <<<>>> */
155     if (temp && atoi(temp)>=0)
156       internal_border_width = atoi(temp);
157
158     temp = get_string_resource("cursorCode", "CursorCode");
159     /* <<<>>> */
160     if (temp && atoi(temp))
161       cursor_code = atoi(temp);
162
163     cursor = XCreateFontCursor(dpy, cursor_code);
164     if (!cursor)
165       cursor = XCreateFontCursor(dpy, XC_sailboat);
166
167     temp = get_string_resource("pointerColor", "Foreground");
168     if (temp) {
169         char *temp2;
170         XColor cursor_fore, cursor_back;
171         /* XXX need to do our own parsing here, since the RecolorCursor
172            routine requires an XColor, not an unsigned long (pixel) */
173         if (!(temp2 = get_string_resource("background","Background"))) {
174             if (default_bgcolor == WhitePixelOfScreen(DefaultScreenOfDisplay(dpy)))
175                 temp2 = "white";
176             else
177                 temp2 = "black";
178         }
179         if (XParseColor(dpy,
180                         DefaultColormapOfScreen(DefaultScreenOfDisplay(dpy)),
181                         temp, &cursor_fore) &&
182             XParseColor(dpy,
183                         DefaultColormapOfScreen(DefaultScreenOfDisplay(dpy)),
184                         temp2, &cursor_back)) {
185               XRecolorCursor(dpy, cursor, &cursor_fore, &cursor_back);
186           }
187     }
188     if (!(title_name=get_string_resource("title","Title")))
189       if (!(title_name=get_string_resource("name","Name")))
190         title_name=app_instance;
191
192     if (!(icon_name=get_string_resource("iconName","IconName")))
193       if (!(icon_name=get_string_resource("name","Name")))
194         icon_name=app_instance;
195
196     if (!(temp=get_string_resource("name","Name")))
197       if (!(temp=(char *) getenv("RESOURCE_NAME")))
198         temp=app_instance;
199     classhint.res_name=string_Copy(temp);
200     classhint.res_class="Zwgc";
201
202     if (set_transient) {
203        group_leader=XCreateSimpleWindow(dpy,DefaultRootWindow(dpy),0,0,100,100,
204                                         0,default_bordercolor,default_bgcolor);
205        sizehints.x = 0;
206        sizehints.y = 0;
207        sizehints.width = 100;
208        sizehints.height = 100;
209        sizehints.flags = PPosition | PSize;
210
211        wmhints.input = False;
212        wmhints.initial_state = DontCareState;
213        wmhints.flags = InputHint | StateHint;
214
215        x_set_icccm_hints(dpy,group_leader,"ZwgcGroup","ZwgcGroup",&sizehints,
216                          &wmhints,0);
217     }
218     xattributes.border_pixel = default_bordercolor;
219     xattributes.cursor = cursor;
220     xattributes.event_mask = (ExposureMask|ButtonReleaseMask|ButtonPressMask
221                               |LeaveWindowMask|Button1MotionMask
222                               |Button3MotionMask|StructureNotifyMask);
223     xattributes_mask = (CWBackPixel|CWBorderPixel|CWEventMask|CWCursor);
224
225     set_all_desktops = get_bool_resource("allDesktops", "AllDesktops", True);
226     net_wm_desktop = XInternAtom(dpy, "_NET_WM_DESKTOP", False);
227     net_wm_window_type = XInternAtom(dpy, "_NET_WM_WINDOW_TYPE", False);
228     net_wm_window_type_utility = XInternAtom(dpy,
229                                              "_NET_WM_WINDOW_TYPE_UTILITY",
230                                              False);
231
232     temp = get_string_resource ("backingStore", "BackingStore");
233     if (!temp)
234         return;
235     xattributes_mask |= CWBackingStore;
236     if (!strcasecmp (temp, "notuseful"))
237         xattributes.backing_store = NotUseful;
238     else if (!strcasecmp (temp, "whenmapped"))
239         xattributes.backing_store = WhenMapped;
240     else if (!strcasecmp (temp, "always"))
241         xattributes.backing_store = Always;
242     else if (!strcasecmp (temp, "default"))
243         xattributes_mask &= ~CWBackingStore;
244     else {
245         switch (get_bool_resource ("backingStore", "BackingStore", -1)) {
246         case 0:
247             xattributes.backing_store = NotUseful;
248             break;
249         case 1:
250             xattributes.backing_store = WhenMapped;
251             break;
252         case -1:
253             fprintf (stderr,
254                  "zwgc: Cannot interpret backing-store resource value `%s'.\n",
255                      temp);
256             xattributes_mask &= ~CWBackingStore;
257             break;
258         }
259     }
260 }
261
262 int
263 x_calc_gravity(int xalign,
264                int yalign)
265 {
266     if (yalign > 0) {                                   /* North */
267         return (xalign > 0)  ? NorthWestGravity
268              : (xalign == 0) ? NorthGravity
269              :                 NorthEastGravity;
270     } else if (yalign == 0) {                           /* Center */
271         return (xalign > 0)  ? WestGravity
272              : (xalign == 0) ? CenterGravity
273              :                 EastGravity;
274     } else {                                            /* South */
275         return (xalign > 0)  ? SouthWestGravity
276              : (xalign == 0) ? SouthGravity
277              :                 SouthEastGravity;
278     }
279 }
280
281 void
282 x_gram_create(Display *dpy,
283               x_gram *gram,
284               int xalign,
285               int yalign,
286               int xpos,
287               int ypos,
288               int xsize,
289               int ysize,
290               int beepcount)
291 {
292     Window w;
293     XSizeHints sizehints;
294     XWMHints wmhints;
295     XSetWindowAttributes attributes;
296     unsigned long all_desktops = 0xFFFFFFFF;
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
401 x_gram_draw(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
497 x_gram_expose(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