/* This file is part of the Project Athena Zephyr Notification System. * It is one of the source files comprising zwgc, the Zephyr WindowGram * client. * * Created by: Marc Horowitz * * $Id$ * * Copyright (c) 1989 by the Massachusetts Institute of Technology. * For copying and distribution information, see the file * "mit-copyright.h". */ #include #if (!defined(lint) && !defined(SABER)) static const char rcsid_X_gram_c[] = "$Id$"; #endif #include #ifndef X_DISPLAY_MISSING #include #include "X_gram.h" #include "xmark.h" #include #include #include #include "zwgc.h" #include "X_driver.h" #include "X_fonts.h" #include "error.h" #include "new_string.h" #include "xrevstack.h" #include "xerror.h" #include "xselect.h" #ifdef CMU_ZWGCPLUS #include "plus.h" #endif extern XContext desc_context; extern char *app_instance; /* * */ int internal_border_width = 2; unsigned long default_fgcolor; unsigned long default_bgcolor; unsigned long default_bordercolor; long ttl = 0; static int reset_saver; static int border_width = 1; static int cursor_code = XC_sailboat; static int set_transient; static int enable_delete; static char *title_name,*icon_name; static Cursor cursor; static Window group_leader; /* In order to have transient windows, * I need a top-level window to always exist */ static XClassHint classhint; static XSetWindowAttributes xattributes; static unsigned long xattributes_mask; static int set_all_desktops = True; static Atom net_wm_desktop = None; static Atom net_wm_window_type = None; static Atom net_wm_window_type_utility = None; /* ICCCM note: * * the following properties must be set on all top-level windows: * * WM_NAME XStoreName(dpy,w,name); * WM_ICON_NAME XSetIconName(dpy,w,name); * WM_NORMAL_HINTS XSetNormalHints(dpy,w,sizehints); * WM_HINTS XSetWMHints(dpy,w,wmhints); * WM_CLASS XSetClassHint(dpy,w,classhint); * * and for individual zgrams: * * WM_TRANSIENT_FOR XSetTransientForHint(dpy,w,main_window); * WM_PROTOCOLS XSetWMProtocols(dpy,w,protocols,cnt); */ /* set all properties defined in ICCCM. If main_window == 0, * per-zgram initialization is not done. */ /*ARGSUSED*/ static void x_set_icccm_hints(Display *dpy, Window w, char *name, char *wm_icon_name, XSizeHints *psizehints, XWMHints *pwmhints, Window main_window) { XStoreName(dpy,w,name); XSetIconName(dpy,w,wm_icon_name); XSetWMNormalHints(dpy,w,psizehints); XSetWMHints(dpy,w,pwmhints); XSetClassHint(dpy,w,&classhint); /* in order for some wm's to iconify, the window shouldn't be transient. e.g. Motif wm */ if (main_window != None) { if (set_transient) XSetTransientForHint(dpy,w,main_window); } if (enable_delete) XSetWMProtocols(dpy,w,&XA_WM_DELETE_WINDOW,1); } void x_gram_init(Display *dpy) { char *temp; XSizeHints sizehints; XWMHints wmhints; unsigned long rv,tc; default_fgcolor = BlackPixelOfScreen(DefaultScreenOfDisplay(dpy)); default_bgcolor = WhitePixelOfScreen(DefaultScreenOfDisplay(dpy)); rv = get_bool_resource("reverseVideo", "ReverseVideo", 0); if (rv) { tc = default_fgcolor; default_fgcolor = default_bgcolor; default_bgcolor = tc; } temp = get_string_resource("foreground", "Foreground"); if (temp) default_fgcolor = x_string_to_color(temp, default_fgcolor); temp = get_string_resource("background", "Background"); if (temp) default_bgcolor = x_string_to_color(temp, default_bgcolor); default_bordercolor = default_fgcolor; temp = get_string_resource("borderColor", "BorderColor"); if (temp) default_bordercolor = x_string_to_color(temp, default_bordercolor); temp = get_string_resource("minTimeToLive", "MinTimeToLive"); if (temp && atoi(temp)>=0) ttl = atoi(temp); #ifdef CMU_ZWGCPLUS if (ttl == 0) { temp = get_string_resource("lifespan", "LifeSpan"); if (temp && atoi(temp)>=0) ttl = atoi(temp); } get_full_names = get_bool_resource("getFullNames", "GetFullNames", 0); #endif reverse_stack = get_bool_resource("reverseStack", "ReverseStack", 0); reset_saver = get_bool_resource("resetSaver", "ResetSaver", 1); /* The default here should be 1, but mwm sucks */ set_transient = get_bool_resource("transient", "Transient", 0); enable_delete = get_bool_resource("enableDelete", "EnableDelete", 1); temp = get_string_resource("borderWidth", "BorderWidth"); /* <<<>>> */ if (temp && atoi(temp)>=0) border_width = atoi(temp); temp = get_string_resource("internalBorder", "InternalBorder"); /* <<<>>> */ if (temp && atoi(temp)>=0) internal_border_width = atoi(temp); temp = get_string_resource("cursorCode", "CursorCode"); /* <<<>>> */ if (temp && atoi(temp)) cursor_code = atoi(temp); cursor = XCreateFontCursor(dpy, cursor_code); if (!cursor) cursor = XCreateFontCursor(dpy, XC_sailboat); temp = get_string_resource("pointerColor", "Foreground"); if (temp) { char *temp2; XColor cursor_fore, cursor_back; /* XXX need to do our own parsing here, since the RecolorCursor routine requires an XColor, not an unsigned long (pixel) */ if (!(temp2 = get_string_resource("background","Background"))) { if (default_bgcolor == WhitePixelOfScreen(DefaultScreenOfDisplay(dpy))) temp2 = "white"; else temp2 = "black"; } if (XParseColor(dpy, DefaultColormapOfScreen(DefaultScreenOfDisplay(dpy)), temp, &cursor_fore) && XParseColor(dpy, DefaultColormapOfScreen(DefaultScreenOfDisplay(dpy)), temp2, &cursor_back)) { XRecolorCursor(dpy, cursor, &cursor_fore, &cursor_back); } } if (!(title_name=get_string_resource("title","Title"))) if (!(title_name=get_string_resource("name","Name"))) title_name=app_instance; if (!(icon_name=get_string_resource("iconName","IconName"))) if (!(icon_name=get_string_resource("name","Name"))) icon_name=app_instance; if (!(temp=get_string_resource("name","Name"))) if (!(temp=(char *) getenv("RESOURCE_NAME"))) temp=app_instance; classhint.res_name=string_Copy(temp); classhint.res_class="Zwgc"; if (set_transient) { group_leader=XCreateSimpleWindow(dpy,DefaultRootWindow(dpy),0,0,100,100, 0,default_bordercolor,default_bgcolor); sizehints.x = 0; sizehints.y = 0; sizehints.width = 100; sizehints.height = 100; sizehints.flags = PPosition | PSize; wmhints.input = False; wmhints.initial_state = DontCareState; wmhints.flags = InputHint | StateHint; x_set_icccm_hints(dpy,group_leader,"ZwgcGroup","ZwgcGroup",&sizehints, &wmhints,0); } xattributes.border_pixel = default_bordercolor; xattributes.cursor = cursor; xattributes.event_mask = (ExposureMask|ButtonReleaseMask|ButtonPressMask |LeaveWindowMask|Button1MotionMask #ifdef CMU_ZWGCPLUS |KeyPressMask #endif |Button3MotionMask|StructureNotifyMask); xattributes_mask = (CWBackPixel|CWBorderPixel|CWEventMask|CWCursor); set_all_desktops = get_bool_resource("allDesktops", "AllDesktops", True); net_wm_desktop = XInternAtom(dpy, "_NET_WM_DESKTOP", False); net_wm_window_type = XInternAtom(dpy, "_NET_WM_WINDOW_TYPE", False); net_wm_window_type_utility = XInternAtom(dpy, "_NET_WM_WINDOW_TYPE_UTILITY", False); temp = get_string_resource ("backingStore", "BackingStore"); if (!temp) return; xattributes_mask |= CWBackingStore; if (!strcasecmp (temp, "notuseful")) xattributes.backing_store = NotUseful; else if (!strcasecmp (temp, "whenmapped")) xattributes.backing_store = WhenMapped; else if (!strcasecmp (temp, "always")) xattributes.backing_store = Always; else if (!strcasecmp (temp, "default")) xattributes_mask &= ~CWBackingStore; else { switch (get_bool_resource ("backingStore", "BackingStore", -1)) { case 0: xattributes.backing_store = NotUseful; break; case 1: xattributes.backing_store = WhenMapped; break; case -1: fprintf (stderr, "zwgc: Cannot interpret backing-store resource value `%s'.\n", temp); xattributes_mask &= ~CWBackingStore; break; } } } static int x_calc_gravity(int xalign, int yalign) { if (yalign > 0) { /* North */ return (xalign > 0) ? NorthWestGravity : (xalign == 0) ? NorthGravity : NorthEastGravity; } else if (yalign == 0) { /* Center */ return (xalign > 0) ? WestGravity : (xalign == 0) ? CenterGravity : EastGravity; } else { /* South */ return (xalign > 0) ? SouthWestGravity : (xalign == 0) ? SouthGravity : SouthEastGravity; } } void x_gram_create(Display *dpy, x_gram *gram, int xalign, int yalign, int xpos, int ypos, int xsize, int ysize, int beepcount) { Window w; XSizeHints sizehints; XWMHints wmhints; XSetWindowAttributes attributes; unsigned long all_desktops = 0xFFFFFFFF; /* * Adjust xpos, ypos based on the alignments xalign, yalign and the sizes: */ if (xalign < 0) xpos = WidthOfScreen(DefaultScreenOfDisplay(dpy)) - xpos - xsize - 2 * border_width; else if (xalign == 0) xpos = ((WidthOfScreen(DefaultScreenOfDisplay(dpy)) - xsize - 2 * border_width) >> 1) + xpos; if (yalign<0) ypos = HeightOfScreen(DefaultScreenOfDisplay(dpy)) - ypos - ysize - 2 * border_width; else if (yalign == 0) ypos = ((HeightOfScreen(DefaultScreenOfDisplay(dpy)) - ysize - 2 * border_width) >> 1) + ypos; /* * Create the window: */ attributes = xattributes; attributes.background_pixel = gram->bgcolor; gram->w = w = XCreateWindow (dpy, DefaultRootWindow (dpy), xpos, ypos, xsize, ysize, border_width, 0, CopyFromParent, CopyFromParent, xattributes_mask, &attributes); sizehints.x = xpos; sizehints.y = ypos; sizehints.width = xsize; sizehints.height = ysize; sizehints.win_gravity = x_calc_gravity(xalign, yalign); sizehints.flags = USPosition | USSize | PWinGravity; wmhints.input = False; wmhints.initial_state = NormalState; if (set_transient) { wmhints.window_group = group_leader; wmhints.flags = InputHint | StateHint | WindowGroupHint; x_set_icccm_hints(dpy, w, title_name, icon_name, &sizehints, &wmhints, group_leader); } else { wmhints.flags = InputHint | StateHint; x_set_icccm_hints(dpy, w, title_name, icon_name, &sizehints, &wmhints, 0); } if (net_wm_window_type != None && net_wm_window_type_utility != None) XChangeProperty(dpy, w, net_wm_window_type, XA_ATOM, 32, PropModeReplace, (unsigned char *) &net_wm_window_type_utility, 1); if (set_all_desktops && net_wm_desktop != None) XChangeProperty(dpy, w, net_wm_desktop, XA_CARDINAL, 32, PropModeReplace, (unsigned char *) &all_desktops, 1); XSaveContext(dpy, w, desc_context, (XPointer)gram); gram->can_die.tv_sec = 0; XMapWindow(dpy, w); if (beepcount) XBell(dpy, 0); xerror_happened = 0; if (reverse_stack && bottom_gram) { XWindowChanges winchanges; winchanges.sibling = bottom_gram->w; winchanges.stack_mode = Below; /* Metacity may use border_width even if it's not specified in * the value mask, so we must initialize it. See: * http://bugzilla.gnome.org/show_bug.cgi?id=305257 */ winchanges.border_width = border_width; begin_xerror_trap (dpy); XReconfigureWMWindow (dpy, w, DefaultScreen (dpy), CWSibling | CWStackMode, &winchanges); end_xerror_trap (dpy); if (xerror_happened) { /* The event didn't go. Print an error message, and continue. */ ERROR ("Error configuring window to the bottom of the stack.\n"); } } /* we always need to keep a linked list of windows */ add_to_bottom(gram); if (xerror_happened) pull_to_top(gram); if (reset_saver) XResetScreenSaver(dpy); XFlush(dpy); /* Because the flushing/syncing/etc with the error trapping can cause events to be read into the Xlib queue, we need to go through the queue here before exiting so that any pending events get processed. */ x_get_input(dpy); } inline static void SetFG(Display *dpy, GC gc, unsigned long foreground) { XGCValues gcvals; gcvals.foreground = foreground; XChangeGC(dpy, gc, GCForeground, &gcvals); } static void x_gram_draw(Display *dpy, Window w, x_gram *gram, Region region) { int i; GC gc; XGCValues gcvals; xblock *xb; #ifdef X_HAVE_UTF8_STRING XmbTextItem text; #else XwcTextItem text; #endif int startblock, endblock, startpixel = 0, endpixel = 0; gc = XCreateGC(dpy, w, 0, &gcvals); XSetRegion(dpy, gc, region); if ((markgram == gram) && (STARTBLOCK != -1) && (ENDBLOCK != -1)) { if (xmarkSecond() == XMARK_END_BOUND) { startblock = STARTBLOCK; endblock = ENDBLOCK; startpixel = STARTPIXEL; endpixel = ENDPIXEL; } else { startblock = ENDBLOCK; endblock = STARTBLOCK; startpixel = ENDPIXEL; endpixel = STARTPIXEL; } } else { startblock = -1; endblock = -1; } for (i=0, xb = gram->blocks; i < gram->numblocks; i++, xb++) { if (XRectInRegion(region, xb->x1, xb->y1, xb->x2 - xb->x1, xb->y2 - xb->y1) != RectangleOut) { if (i == startblock) { if (i == endblock) { SetFG(dpy, gc, gram->bgcolor); XFillRectangle(dpy, w, gc, xb->x1, xb->y1, startpixel, xb->y2 - xb->y1); SetFG(dpy, gc, xb->fgcolor); XFillRectangle(dpy, w, gc, xb->x1 + startpixel, xb->y1, endpixel - startpixel, xb->y2 - xb->y1); SetFG(dpy, gc, gram->bgcolor); XFillRectangle(dpy, w, gc, xb->x1 + endpixel, xb->y1, xb->x2 - xb->x1 - endpixel, xb->y2 - xb->y1); } else { SetFG(dpy, gc, gram->bgcolor); XFillRectangle(dpy, w, gc, xb->x1, xb->y1, startpixel, xb->y2 - xb->y1); SetFG(dpy, gc, xb->fgcolor); XFillRectangle(dpy, w, gc, xb->x1 + startpixel, xb->y1, xb->x2 - xb->x1 - startpixel,xb->y2 - xb->y1); } } else if (i == endblock) { SetFG(dpy, gc, xb->fgcolor); XFillRectangle(dpy, w, gc, xb->x1, xb->y1, endpixel, xb->y2 - xb->y1); SetFG(dpy, gc, gram->bgcolor); XFillRectangle(dpy, w, gc, xb->x1 + endpixel, xb->y1, xb->x2 - xb->x1 - endpixel, xb->y2 - xb->y1); } else { if (startblock < i && i < endblock) { SetFG(dpy, gc, xb->fgcolor); } else { SetFG(dpy, gc, gram->bgcolor); } XFillRectangle(dpy, w, gc, xb->x1, xb->y1, xb->x2 - xb->x1, xb->y2 - xb->y1); } } } gcvals.function = GXxor; XChangeGC(dpy, gc, GCFunction, &gcvals); for (i=0, xb = gram->blocks; i < gram->numblocks; i++, xb++) { if (XRectInRegion(region, xb->x1, xb->y1, xb->x2 - xb->x1, xb->y2 - xb->y1) != RectangleOut) { SetFG(dpy, gc, gram->bgcolor ^ xb->fgcolor); #ifdef X_HAVE_UTF8_STRING text.chars = xb->wstr; #else text.chars = (wchar_t *)xb->wstr; #endif text.nchars = xb->wlen; text.delta = 0; text.font_set = xb->font; #ifdef X_HAVE_UTF8_STRING Xutf8DrawText(dpy, w, gc, xb->x, xb->y, &text, 1); #else XwcDrawText(dpy, w, gc, xb->x, xb->y, &text, 1); #endif } } XFreeGC(dpy, gc); } void x_gram_expose(Display *dpy, Window w, x_gram *gram, XExposeEvent *event) { static Region region; static int partregion; XRectangle rect; rect.x = (short) event->x; rect.y = (short) event->y; rect.width = (unsigned short) event->width; rect.height = (unsigned short) event->height; #ifdef MARK_DEBUG printf("----- xeventExpose:\nx=%d y=%d w=%d h=%d\n-----", event->x,event->y,event->width,event->height); #endif if (! partregion) { region=XCreateRegion(); partregion = 1; } if (rect.width && rect.height) XUnionRectWithRegion(&rect,region,region); if (event->count == 0) { x_gram_draw(dpy,w,gram,region); partregion = 0; XDestroyRegion(region); } } #endif /* X_DISPLAY_MISSING */