/* * Copyright (C) 2000, Matias Atria * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ /* This file contains routines I always use to make X programming easier */ #include "common.h" #ifndef X_DISPLAY_MISSING /*#define XSYNCH*/ #define DEFAULT_FONT "-*-helvetica-medium-r-*-*-*-*-*-*-*-*-*-*" #include "x11.h" #include "bitmap.h" #include "private.h" #include Display *dpy = NULL; int scrno = 0; GC normal_gc = NULL; GC image_gc = NULL; GC inv_gc = NULL; int depth = 0; Visual *visual = NULL; Colormap colormap = None; XFontStruct *default_font = NULL; int bell_percent = 100; static Window group_leader = None; static Atom protocols = None; static Atom delete = None; static XImage *image = NULL; Uint root_w = 0; Uint root_h = 0; Ulong black; Ulong white; int errorhandler(Display *d, XErrorEvent *e) { d=d, e=e; abort(); } #define NORMAL_EVENTS \ (ExposureMask|KeyPressMask|StructureNotifyMask|\ ButtonPressMask|ButtonReleaseMask) /* functions to query information about a window */ void get_screen_size(int *w, int *h) { *w = root_w; *h = root_h; } void beep_sound(void) { XBell(dpy, bell_percent); } int get_win_size(Window win, Uint *w, Uint *h) { Window junk; int junk1; XGetGeometry(dpy, win, &junk, &junk1, &junk1, w, h, (Uint*)&junk1, (Uint*)&junk1); return 0; } void free_propdata(PropData *prop) { if(prop->name) { /* this pointer was returned by XGetAtomName */ XFree(prop->name); prop->name = NULL; } prop->format = 8; prop->type = XA_STRING; prop->nitems = 0; if(prop->data) { xfree(prop->data); prop->data = NULL; } } /* * This is a general purpose function to get the data associated to a * window's property. It serves as an example of how to use * XGetWindowProperty. * * Returns -1 if there was an error, 0 on success. If all is well, it fills * the `ret' PropData structure with all the information reported about the * property. It must be freed with `free_propdata()'. */ int get_window_property(Display *dpy, Window w, Atom property, PropData *ret) { Uchar *data; int status; Atom prop_type; int prop_format; Ulong prop_nitems; Ulong bytes_left; Uchar *copy, *ptr; long size; long length; long total; /* request 1024 32bit words (we'll probably get much less than * that), and get format and type */ status = XGetWindowProperty(dpy, w, property, 0L, 1024, False, AnyPropertyType, &prop_type, &prop_format, &prop_nitems, &bytes_left, &data); if(status != Success || prop_type == None) return -1; /* this is the number of bytes we just read */ length = prop_nitems * (prop_format >> 3); /* this is the total length of the data for this property */ total = length + bytes_left; /* allocate memory to hold all the data for this property. * * Note that we allocate one extra byte and set that to 0 so strings * can be used directly. The docs for XGetWindowProperty say that * this is automatically done, but they don't mention if this extra * byte is included in the # of items returned. I assume it is not. * The worst that can happen is that we're adding a second NUL at * the end, but I better be safe than sorry. */ ptr = copy = (Uchar *)xmalloc(total + 1); size = total; if(ptr == NULL) { XFree(data); return -1; } /* copy what we have so far */ memcpy(copy, data, length); ptr = copy + length; /* we dont need this anymore */ XFree(data); while(bytes_left) { int nbytes; /* we could request all the data that is left, but * this will work too */ XGetWindowProperty(dpy, w, property, length / 4, 1024L, False, AnyPropertyType, &prop_type, &prop_format, &prop_nitems, &bytes_left, &data); /* # of bytes we read */ nbytes = prop_nitems * (prop_format >> 3); /* # of bytes so far */ length += nbytes; /* check that we don't have more data than we should */ if(length > total) { printf("I expected %ld bytes, but already got %ld, " "and there are %ld more to come.\n", total, length, bytes_left); break; } /* update our copy */ memcpy(ptr, data, nbytes); XFree(data); ptr += nbytes; } copy[length] = 0; /* fill the PropData structure */ ret->name = XGetAtomName(dpy, property); ret->format = prop_format; ret->type = prop_type; ret->nitems = length / (prop_format >> 3); ret->data = copy; return 0; } /* open a connection to a remote display */ int init_x11(void) { XGCValues xg; XKeyboardState kbd; dpy = XOpenDisplay(NULL); if(dpy == NULL) { error(_("connection to X server failed\n")); return -1; } scrno = DefaultScreen(dpy); get_win_size(RootWindow(dpy, scrno), &root_w, &root_h); depth = DefaultDepth(dpy, scrno); visual = DefaultVisual(dpy, scrno); colormap = DefaultColormap(dpy, scrno); black = BlackPixel(dpy, scrno); white = WhitePixel(dpy, scrno); /* load a default font */ default_font = XLoadQueryFont(dpy, DEFAULT_FONT); if(default_font == NULL) default_font = XLoadQueryFont(dpy, "fixed"); if(default_font == NULL) { error(_("%s: could not find display font `%s'\n"), program_name, DEFAULT_FONT); XCloseDisplay(dpy); return -1; } /* create GCs */ xg.foreground = black; xg.background = white; xg.font = default_font->fid; xg.graphics_exposures = False; normal_gc = XCreateGC(dpy, RootWindow(dpy, scrno), GCForeground|GCBackground|GCGraphicsExposures|GCFont, &xg); image_gc = XCreateGC(dpy, RootWindow(dpy, scrno), GCForeground|GCBackground|GCGraphicsExposures|GCFont, &xg); #ifdef CHEAP_X_TRICK XSetFunction(dpy, image_gc, GXand); #endif xg.foreground = white; xg.background = black; inv_gc = XCreateGC(dpy, RootWindow(dpy, scrno), GCForeground|GCBackground|GCGraphicsExposures|GCFont, &xg); image = XCreateImage(dpy, visual, 1, XYBitmap, 0, (char *)0, 0, 0, BITMAP_BITS, 0); #ifdef WORD_BIG_ENDIAN image->byte_order = image->bitmap_bit_order = MSBFirst; #else image->byte_order = image->bitmap_bit_order = LSBFirst; #endif #if !defined(NODEBUG) && defined(XSYNCH) XSynchronize(dpy, True); XSetErrorHandler(errorhandler); #endif /* get keyboard state */ XGetKeyboardControl(dpy, &kbd); bell_percent = kbd.bell_percent; if(bell_percent < -100) bell_percent = -100; if(bell_percent > 100) bell_percent = 100; return 0; } int window_closed(XClientMessageEvent *ev, Window w) { if(ev->window != w) return 0; if(ev->message_type == protocols && ev->data.l[0] == delete) return 1; return 0; } int handle_top_events(TopWindow *tw, XEvent *ev) { if(ev->xany.window != tw->win) return 0; switch(ev->type) { case ClientMessage: if(window_closed(&ev->xclient, tw->win)) { tw->active = 0; return 1; } break; case ConfigureNotify: tw->w = ev->xconfigure.width; tw->h = ev->xconfigure.height; break; case KeyPress: { if(XLookupKeysym((XKeyEvent *)ev, 0) == XK_Escape) { tw->active = 0; return 1; } break; } default: break; } return 0; } void close_top_window(TopWindow *tw) { XDestroyWindow(dpy, tw->win); if(tw->win == group_leader) group_leader = None; tw->win = None; tw->active = 0; } void expose_window(Window w, Bool putback) { XEvent ev; XMapWindow(dpy, w); while(!XCheckTypedWindowEvent(dpy, w, Expose, &ev)) ; if(putback) XPutBackEvent(dpy, &ev); } void resize_top_window(TopWindow *tw, Uint w, Uint h) { XSizeHints size_hints; size_hints.width = w; size_hints.height = h; size_hints.flags = USSize; XSetWMNormalHints(dpy, tw->win, &size_hints); XResizeWindow(dpy, tw->win, w, h); XFlush(dpy); get_win_size(tw->win, &tw->w, &tw->h); } int create_top_window(TopWindow *tw, const char *name, int w, int h) { XSetWindowAttributes xwa; if(dpy == NULL && init_x11() == -1) return -1; if(w > root_w) w = root_w; if(h > root_h) h = root_h; xwa.background_pixel = white; xwa.backing_pixel = white; xwa.bit_gravity = NorthWestGravity; xwa.backing_store = WhenMapped; xwa.colormap = colormap; tw->win = XCreateWindow(dpy, RootWindow(dpy, scrno), 0, 0, w, h, 0, depth, InputOutput, CopyFromParent, CWBackPixel|CWBackingPixel| CWBitGravity|CWBackingStore| CWColormap, &xwa); /* select `Expose' events */ XSelectInput(dpy, tw->win, NORMAL_EVENTS); XStoreName(dpy, tw->win, name); protocols = XInternAtom(dpy, "WM_PROTOCOLS", False); delete = XInternAtom(dpy, "WM_DELETE_WINDOW", False); if(delete != None) XSetWMProtocols(dpy, tw->win, &delete, 1); /* set this as group leader, if it's the first */ if(group_leader == None) group_leader = tw->win; else { XWMHints hints; hints.window_group = (XID)group_leader; hints.flags = WindowGroupHint; XSetWMHints(dpy, tw->win, &hints); } tw->active = 1; return 0; } void set_max_winsize(Window win, int w, int h) { XSizeHints size_hints; size_hints.flags = PMaxSize; size_hints.max_width = w; size_hints.max_height = h; XSetWMNormalHints(dpy, win, &size_hints); } void add_window_events(Window w, Ulong mask) { XWindowAttributes attr; XGetWindowAttributes(dpy, w, &attr); attr.your_event_mask |= mask; XSelectInput(dpy, w, attr.your_event_mask); XFlush(dpy); } void clear_window_events(Window w, Ulong mask) { XWindowAttributes attr; XGetWindowAttributes(dpy, w, &attr); attr.your_event_mask &= ~mask; XSelectInput(dpy, w, attr.your_event_mask); XFlush(dpy); } void set_window_events(Window w, Ulong mask) { XSelectInput(dpy, w, mask); } Window create_window(Window parent, Ulong c, int w, int h, int b) { XSetWindowAttributes xwa; Window win; if(parent == None) parent = RootWindow(dpy, scrno); xwa.background_pixel = c; xwa.bit_gravity = NorthWestGravity; #ifndef NODEBUG xwa.backing_store = NotUseful; #else xwa.backing_store = WhenMapped; #endif xwa.colormap = colormap; xwa.border_pixel = black; win = XCreateWindow(dpy, parent, 0, 0, w, h, b, depth, InputOutput, CopyFromParent, CWBackPixel|CWBitGravity|CWBackingStore|CWColormap, &xwa); /* select `Expose' events */ set_window_events(win, NORMAL_EVENTS); return win; } Window create_subwindow(Window parent, Ulong c, int x, int y, int w, int h, int b) { XSetWindowAttributes xwa; Window win; if(parent == None) parent = RootWindow(dpy, scrno); xwa.background_pixel = c; xwa.bit_gravity = NorthWestGravity; #ifndef NODEBUG xwa.backing_store = NotUseful; #else xwa.backing_store = WhenMapped; #endif xwa.colormap = colormap; xwa.border_pixel = black; win = XCreateWindow(dpy, parent, x, y, w, h, b, depth, InputOutput, CopyFromParent, CWBackPixel|CWBitGravity|CWBackingStore| CWColormap, &xwa); /* select `Expose' events */ set_window_events(win, NORMAL_EVENTS); return win; } Pixmap create_pixmap(Window parent, int w, int h) { Pixmap p; p = XCreatePixmap(dpy, parent, w, h, depth); XFillRectangle(dpy, p, inv_gc, 0, 0, w, h); return p; } int create_draw_area(DrawArea *area, Window in, int w, int h, int b) { XGCValues xgc; area->win = create_window(in, white, w, h, b); if(area->win == None) return -1; xgc.foreground = black; xgc.background = white; xgc.graphics_exposures = False; area->gc = XCreateGC(dpy, area->win, GCForeground|GCBackground|GCGraphicsExposures, &xgc); area->p = create_pixmap(area->win, w, h); area->w = w; area->h = h; area->hidden = 1; area->fg = black; area->bg = white; return 0; } void set_area_foreground(DrawArea *area, Ulong fg) { if(fg != area->fg) { XSetForeground(dpy, area->gc, fg); area->fg = fg; } } void set_area_background(DrawArea *area, Ulong bg) { if(bg != area->bg) { XSetBackground(dpy, area->gc, bg); area->bg = bg; } } void destroy_area(DrawArea *area) { XFreePixmap(dpy, area->p); XFreeGC(dpy, area->gc); XDestroyWindow(dpy, area->win); area->win = None; area->p = None; area->w = 0; area->h = 0; area->hidden = 1; area->gc = NULL; } void refresh_area(DrawArea *area, int x, int y, int w, int h) { if(!area->hidden) XCopyArea(dpy, area->p, area->win, area->gc, x, y, w, h, x, y); } void clear_area(DrawArea *area) { XSetForeground(dpy, area->gc, area->bg); XFillRectangle(dpy, area->p, area->gc, 0, 0, area->w, area->h); XClearArea(dpy, area->win, 0, 0, area->w, area->h, True); XSetForeground(dpy, area->gc, area->fg); } void show_area(DrawArea *area) { XEvent ev; if(!area->hidden) return; add_window_events(area->win, ExposureMask); XFlush(dpy); XMapWindow(dpy, area->win); while(!XCheckTypedWindowEvent(dpy, area->win, Expose, &ev)) ; XPutBackEvent(dpy, &ev); area->hidden = 0; } void hide_area(DrawArea *area) { XUnmapWindow(dpy, area->win); area->hidden = 1; } int resize_area(DrawArea *area, Uint w, Uint h) { Pixmap p; p = create_pixmap(area->win, w, h); if(p == None) return -1; XFreePixmap(dpy, area->p); area->p = p; XResizeWindow(dpy, area->win, w, h); area->w = w; area->h = h; return 0; } void set_clip_area(DrawArea *area, int x, int y, int w, int h) { XRectangle rect; rect.x = x; rect.y = y; rect.width = w; rect.height = h; XSetClipRectangles(dpy, area->gc, 0, 0, &rect, 1, Unsorted); } void draw_line(Drawable d, int x0, int y0, int x1, int y1) { XDrawLine(dpy, d, normal_gc, x0, y0, x1, y1); } void draw_box(Drawable d, int x, int y, int w, int h, int filled) { if(filled) XFillRectangle(dpy, d, normal_gc, x, y, w, h); else XDrawRectangle(dpy, d, normal_gc, x, y, w, h); } void attach_window(Window win, Window to) { XReparentWindow(dpy, win, to, 0, 0); } void detach_window(TopWindow *tw, Window win, int x, int y) { /* set window transient for the top window */ if(tw && win == tw->win) return; if(tw != NULL) XSetTransientForHint(dpy, win, tw->win); /* make window a child of the root */ XReparentWindow(dpy, win, RootWindow(dpy, scrno), x, y); /* and make it exit cleanly */ if(delete != None) XSetWMProtocols(dpy, win, &delete, 1); } void close_x11(void) { XFreeGC(dpy, normal_gc); XFreeGC(dpy, inv_gc); XFreeGC(dpy, image_gc); XFreeFont(dpy, default_font); XCloseDisplay(dpy); dpy = NULL; } void draw_bitmap(Drawable d, BITMAP *bm, int x, int y) { if(bm->width <= 0 || bm->height <= 0) return; /* create X image */ if(bm->data != NULL) { image->width = bm->width; image->height = bm->height; image->data = (char *)bm->data; image->bytes_per_line = bm->stride; XPutImage(dpy, d, image_gc, image, 0, 0, x, y, bm->width, bm->height); } } void put_image(Drawable d, void *image, int x, int y, int w, int h) { XPutImage(dpy, d, image_gc, (XImage *)image, 0, 0, x, y, w, h); } GC create_gc(int func, Window p, Ulong fg, Ulong bg) { XGCValues xgc; xgc.foreground = fg; xgc.background = bg; xgc.function = func; xgc.font = default_font->fid; xgc.graphics_exposures = False; return XCreateGC(dpy, p, GCFunction|GCForeground|GCBackground| GCGraphicsExposures|GCFont, &xgc); } void draw_text(DrawArea *area, GC gc, int x, int y, const char *text, int justify) { int n = strlen(text); int w, y0; w = XTextWidth(default_font, text, n); switch(justify & MX11_HJUSTIFY) { case MX11_LEFT: x = 2; break; case MX11_RIGHT: x = area->w - w - 2; if(x < 2) x = 2; break; case MX11_HCENTER: x = (area->w - w) / 2; break; } switch(justify & MX11_VJUSTIFY) { case MX11_ABOVE: y -= default_font->max_bounds.descent; if(y < 2) y = 2; break; case MX11_BELOW: y += default_font->max_bounds.ascent; if(y + default_font->max_bounds.descent + 2 > area->h) y = area->h - default_font->max_bounds.descent - 2; if(y < 2) y = 2; break; case MX11_VCENTER: y -= FONT_HEIGHT(default_font) / 2 + default_font->max_bounds.ascent; if(y < 2) y = 2; break; } if(gc == NULL) gc = area->gc; y0 = y - default_font->max_bounds.ascent; XFillRectangle(dpy, area->p, inv_gc, x, y0, w, FONT_HEIGHT(default_font)); XDrawString(dpy, area->p, gc, x, y, text, n); } Ulong get_color_byname(const char *name) { XColor xc; if(dpy == NULL) return 0UL; if(!XParseColor(dpy, colormap, name, &xc)) { warning(_("invalid color name `%s', using black\n")); return black; } if(!XAllocColor(dpy, colormap, &xc)) { warning(_("failed to allocate color `%s', using black\n")); return black; } return xc.pixel; } #endif /* X_DISPLAY_MISSING */