563 lines
18 KiB
C

/* 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 <marc@athena.mit.edu>
*
* $Id$
*
* Copyright (c) 1989 by the Massachusetts Institute of Technology.
* For copying and distribution information, see the file
* "mit-copyright.h".
*/
#include <sysdep.h>
#if (!defined(lint) && !defined(SABER))
static const char rcsid_X_gram_c[] = "$Id$";
#endif
#include <zephyr/mit-copyright.h>
#ifndef X_DISPLAY_MISSING
#include <zephyr/zephyr.h>
#include "X_gram.h"
#include "xmark.h"
#include <X11/Xutil.h>
#include <X11/cursorfont.h>
#include <X11/Xatom.h>
#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 */