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