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
5 * Created by: Marc Horowitz <marc@athena.mit.edu>
9 * Copyright (c) 1989 by the Massachusetts Institute of Technology.
10 * For copying and distribution information, see the file
16 #if (!defined(lint) && !defined(SABER))
17 static const char rcsid_X_gram_c[] = "$Id$";
20 #include <zephyr/mit-copyright.h>
22 #ifndef X_DISPLAY_MISSING
24 #include <zephyr/zephyr.h>
27 #include <X11/Xutil.h>
28 #include <X11/cursorfont.h>
29 #include <X11/Xatom.h>
34 #include "new_string.h"
35 #include "xrevstack.h"
42 extern XContext desc_context;
43 extern char *app_instance;
49 int internal_border_width = 2;
51 unsigned long default_fgcolor;
52 unsigned long default_bgcolor;
53 unsigned long default_bordercolor;
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;
62 static Window group_leader; /* In order to have transient windows,
63 * I need a top-level window to always exist
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;
75 * the following properties must be set on all top-level windows:
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);
83 * and for individual zgrams:
85 * WM_TRANSIENT_FOR XSetTransientForHint(dpy,w,main_window);
86 * WM_PROTOCOLS XSetWMProtocols(dpy,w,protocols,cnt);
89 /* set all properties defined in ICCCM. If main_window == 0,
90 * per-zgram initialization is not done.
95 x_set_icccm_hints(Display *dpy,
99 XSizeHints *psizehints,
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.
110 if (main_window != None) {
112 XSetTransientForHint(dpy,w,main_window);
115 XSetWMProtocols(dpy,w,&XA_WM_DELETE_WINDOW,1);
119 x_gram_init(Display *dpy)
122 XSizeHints sizehints;
126 default_fgcolor = BlackPixelOfScreen(DefaultScreenOfDisplay(dpy));
127 default_bgcolor = WhitePixelOfScreen(DefaultScreenOfDisplay(dpy));
128 rv = get_bool_resource("reverseVideo", "ReverseVideo", 0);
130 tc = default_fgcolor;
131 default_fgcolor = default_bgcolor;
132 default_bgcolor = tc;
134 temp = get_string_resource("foreground", "Foreground");
136 default_fgcolor = x_string_to_color(temp, default_fgcolor);
137 temp = get_string_resource("background", "Background");
139 default_bgcolor = x_string_to_color(temp, default_bgcolor);
140 default_bordercolor = default_fgcolor;
141 temp = get_string_resource("borderColor", "BorderColor");
143 default_bordercolor = x_string_to_color(temp, default_bordercolor);
145 temp = get_string_resource("minTimeToLive", "MinTimeToLive");
146 if (temp && atoi(temp)>=0)
151 temp = get_string_resource("lifespan", "LifeSpan");
152 if (temp && atoi(temp)>=0)
156 get_full_names = get_bool_resource("getFullNames", "GetFullNames", 0);
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);
165 temp = get_string_resource("borderWidth", "BorderWidth");
167 if (temp && atoi(temp)>=0)
168 border_width = atoi(temp);
170 temp = get_string_resource("internalBorder", "InternalBorder");
172 if (temp && atoi(temp)>=0)
173 internal_border_width = atoi(temp);
175 temp = get_string_resource("cursorCode", "CursorCode");
177 if (temp && atoi(temp))
178 cursor_code = atoi(temp);
180 cursor = XCreateFontCursor(dpy, cursor_code);
182 cursor = XCreateFontCursor(dpy, XC_sailboat);
184 temp = get_string_resource("pointerColor", "Foreground");
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)))
197 DefaultColormapOfScreen(DefaultScreenOfDisplay(dpy)),
198 temp, &cursor_fore) &&
200 DefaultColormapOfScreen(DefaultScreenOfDisplay(dpy)),
201 temp2, &cursor_back)) {
202 XRecolorCursor(dpy, cursor, &cursor_fore, &cursor_back);
205 if (!(title_name=get_string_resource("title","Title")))
206 if (!(title_name=get_string_resource("name","Name")))
207 title_name=app_instance;
209 if (!(icon_name=get_string_resource("iconName","IconName")))
210 if (!(icon_name=get_string_resource("name","Name")))
211 icon_name=app_instance;
213 if (!(temp=get_string_resource("name","Name")))
214 if (!(temp=(char *) getenv("RESOURCE_NAME")))
216 classhint.res_name=string_Copy(temp);
217 classhint.res_class="Zwgc";
220 group_leader=XCreateSimpleWindow(dpy,DefaultRootWindow(dpy),0,0,100,100,
221 0,default_bordercolor,default_bgcolor);
224 sizehints.width = 100;
225 sizehints.height = 100;
226 sizehints.flags = PPosition | PSize;
228 wmhints.input = False;
229 wmhints.initial_state = DontCareState;
230 wmhints.flags = InputHint | StateHint;
232 x_set_icccm_hints(dpy,group_leader,"ZwgcGroup","ZwgcGroup",&sizehints,
235 xattributes.border_pixel = default_bordercolor;
236 xattributes.cursor = cursor;
237 xattributes.event_mask = (ExposureMask|ButtonReleaseMask|ButtonPressMask
238 |LeaveWindowMask|Button1MotionMask
242 |Button3MotionMask|StructureNotifyMask);
243 xattributes_mask = (CWBackPixel|CWBorderPixel|CWEventMask|CWCursor);
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",
252 temp = get_string_resource ("backingStore", "BackingStore");
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;
265 switch (get_bool_resource ("backingStore", "BackingStore", -1)) {
267 xattributes.backing_store = NotUseful;
270 xattributes.backing_store = WhenMapped;
274 "zwgc: Cannot interpret backing-store resource value `%s'.\n",
276 xattributes_mask &= ~CWBackingStore;
283 x_calc_gravity(int xalign,
286 if (yalign > 0) { /* North */
287 return (xalign > 0) ? NorthWestGravity
288 : (xalign == 0) ? NorthGravity
290 } else if (yalign == 0) { /* Center */
291 return (xalign > 0) ? WestGravity
292 : (xalign == 0) ? CenterGravity
295 return (xalign > 0) ? SouthWestGravity
296 : (xalign == 0) ? SouthGravity
302 x_gram_create(Display *dpy,
313 XSizeHints sizehints;
315 XSetWindowAttributes attributes;
316 unsigned long all_desktops = 0xFFFFFFFF;
319 * Adjust xpos, ypos based on the alignments xalign, yalign and the sizes:
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;
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;
338 attributes = xattributes;
339 attributes.background_pixel = gram->bgcolor;
341 gram->w = w = XCreateWindow (dpy, DefaultRootWindow (dpy), xpos, ypos,
342 xsize, ysize, border_width, 0,
343 CopyFromParent, CopyFromParent,
344 xattributes_mask, &attributes);
348 sizehints.width = xsize;
349 sizehints.height = ysize;
350 sizehints.win_gravity = x_calc_gravity(xalign, yalign);
351 sizehints.flags = USPosition | USSize | PWinGravity;
353 wmhints.input = False;
354 wmhints.initial_state = NormalState;
356 wmhints.window_group = group_leader;
357 wmhints.flags = InputHint | StateHint | WindowGroupHint;
359 x_set_icccm_hints(dpy, w, title_name, icon_name, &sizehints, &wmhints,
362 wmhints.flags = InputHint | StateHint;
364 x_set_icccm_hints(dpy, w, title_name, icon_name, &sizehints, &wmhints, 0);
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);
374 XSaveContext(dpy, w, desc_context, (XPointer)gram);
376 gram->can_die.tv_sec = 0;
384 if (reverse_stack && bottom_gram) {
385 XWindowChanges winchanges;
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;
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");
403 /* we always need to keep a linked list of windows */
409 XResetScreenSaver(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.
420 SetFG(Display *dpy, GC gc, unsigned long foreground) {
423 gcvals.foreground = foreground;
424 XChangeGC(dpy, gc, GCForeground, &gcvals);
428 x_gram_draw(Display *dpy, Window w, x_gram *gram, Region region)
434 #ifdef X_HAVE_UTF8_STRING
439 int startblock, endblock, startpixel = 0, endpixel = 0;
441 gc = XCreateGC(dpy, w, 0, &gcvals);
442 XSetRegion(dpy, gc, region);
444 if ((markgram == gram) && (STARTBLOCK != -1) && (ENDBLOCK != -1)) {
445 if (xmarkSecond() == XMARK_END_BOUND) {
446 startblock = STARTBLOCK;
448 startpixel = STARTPIXEL;
451 startblock = ENDBLOCK;
452 endblock = STARTBLOCK;
453 startpixel = ENDPIXEL;
454 endpixel = STARTPIXEL;
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) {
466 SetFG(dpy, gc, gram->bgcolor);
467 XFillRectangle(dpy, w, gc, xb->x1, xb->y1, startpixel,
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);
476 SetFG(dpy, gc, gram->bgcolor);
477 XFillRectangle(dpy, w, gc, xb->x1, xb->y1, startpixel,
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);
483 } else if (i == endblock) {
484 SetFG(dpy, gc, xb->fgcolor);
485 XFillRectangle(dpy, w, gc, xb->x1, xb->y1, endpixel,
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);
491 if (startblock < i && i < endblock) {
492 SetFG(dpy, gc, xb->fgcolor);
494 SetFG(dpy, gc, gram->bgcolor);
496 XFillRectangle(dpy, w, gc, xb->x1, xb->y1, xb->x2 - xb->x1,
502 gcvals.function = GXxor;
503 XChangeGC(dpy, gc, GCFunction, &gcvals);
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;
512 text.chars = (XChar2b *)xb->wstr;
514 text.nchars = xb->wlen;
516 text.font_set = xb->font;
517 #ifdef X_HAVE_UTF8_STRING
518 Xutf8DrawText(dpy, w, gc, xb->x, xb->y, &text, 1);
520 XwcDrawText(dpy, w, gc, xb->x, xb->y, &text, 1);
529 x_gram_expose(Display *dpy,
534 static Region region;
535 static int partregion;
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;
544 printf("----- xeventExpose:\nx=%d y=%d w=%d h=%d\n-----",
545 event->x,event->y,event->width,event->height);
549 region=XCreateRegion();
553 if (rect.width && rect.height) XUnionRectWithRegion(&rect,region,region);
555 if (event->count == 0) {
556 x_gram_draw(dpy,w,gram,region);
558 XDestroyRegion(region);
562 #endif /* X_DISPLAY_MISSING */