/* ghosd -- OSD with fake transparency, cairo, and pango. * Copyright (C) 2006 Evan Martin */ #include "config.h" #include #include #include #include #include "ghosd.h" #include "ghosd-internal.h" static Pixmap take_snapshot(Ghosd *ghosd) { Pixmap pixmap; GC gc; /* create a pixmap to hold the screenshot. */ pixmap = XCreatePixmap(ghosd->dpy, ghosd->win, ghosd->width, ghosd->height, DefaultDepth(ghosd->dpy, DefaultScreen(ghosd->dpy))); /* then copy the screen into the pixmap. */ gc = XCreateGC(ghosd->dpy, pixmap, 0, NULL); XSetSubwindowMode(ghosd->dpy, gc, IncludeInferiors); XCopyArea(ghosd->dpy, DefaultRootWindow(ghosd->dpy), pixmap, gc, ghosd->x, ghosd->y, ghosd->width, ghosd->height, 0, 0); XSetSubwindowMode(ghosd->dpy, gc, ClipByChildren); XFreeGC(ghosd->dpy, gc); return pixmap; } void ghosd_render(Ghosd *ghosd) { Pixmap pixmap; GC gc; /* make our own copy of the background pixmap as the initial surface. */ pixmap = XCreatePixmap(ghosd->dpy, ghosd->win, ghosd->width, ghosd->height, DefaultDepth(ghosd->dpy, DefaultScreen(ghosd->dpy))); gc = XCreateGC(ghosd->dpy, pixmap, 0, NULL); if (ghosd->transparent) { XCopyArea(ghosd->dpy, ghosd->background, pixmap, gc, 0, 0, ghosd->width, ghosd->height, 0, 0); } else { XFillRectangle(ghosd->dpy, pixmap, gc, 0, 0, ghosd->width, ghosd->height); } XFreeGC(ghosd->dpy, gc); /* render with cairo. */ if (ghosd->render_func) { /* create cairo surface using the pixmap. */ XRenderPictFormat *xrformat = XRenderFindVisualFormat(ghosd->dpy, DefaultVisual(ghosd->dpy, DefaultScreen(ghosd->dpy))); cairo_surface_t *surf = cairo_xlib_surface_create_with_xrender_format( ghosd->dpy, pixmap, ScreenOfDisplay(ghosd->dpy, DefaultScreen(ghosd->dpy)), xrformat, ghosd->width, ghosd->height); /* draw some stuff. */ cairo_t *cr = cairo_create(surf); ghosd->render_func(ghosd, cr, ghosd->render_data); cairo_destroy(cr); } /* point window at its new backing pixmap. */ XSetWindowBackgroundPixmap(ghosd->dpy, ghosd->win, pixmap); XFreePixmap(ghosd->dpy, pixmap); /* and tell the window to redraw with this pixmap. */ XClearWindow(ghosd->dpy, ghosd->win); } static void set_hints(Display *dpy, Window win) { /* we're almost a _NET_WM_WINDOW_TYPE_SPLASH, but we don't want * to be centered on the screen. instead, manually request the * behavior we want. */ /* turn off window decorations. * we could pull this in from a motif header, but it's easier to * use this snippet i found on a mailing list. */ Atom mwm_hints = XInternAtom(dpy, "_MOTIF_WM_HINTS", False); #define MWM_HINTS_DECORATIONS (1<<1) struct { long flags, functions, decorations, input_mode; } mwm_hints_setting = { MWM_HINTS_DECORATIONS, 0, 0, 0 }; XChangeProperty(dpy, win, mwm_hints, mwm_hints, 32, PropModeReplace, (unsigned char *)&mwm_hints_setting, 4); /* always on top, not in taskbar or pager. */ Atom win_state = XInternAtom(dpy, "_NET_WM_STATE", False); Atom win_state_setting[] = { XInternAtom(dpy, "_NET_WM_STATE_ABOVE", False), XInternAtom(dpy, "_NET_WM_STATE_SKIP_TASKBAR", False), XInternAtom(dpy, "_NET_WM_STATE_SKIP_PAGER", False) }; XChangeProperty(dpy, win, win_state, XA_ATOM, 32, PropModeReplace, (unsigned char*)&win_state_setting, 3); } static Window make_window(Display *dpy) { Window win; XSetWindowAttributes att; /* XXX I don't understand X well enough to know if these are the correct * settings. */ att.backing_store = WhenMapped; att.background_pixel = None; att.border_pixel = 0; att.background_pixmap = None; att.save_under = True; att.event_mask = ExposureMask | StructureNotifyMask; att.override_redirect = True; win = XCreateWindow(dpy, DefaultRootWindow(dpy), -1, -1, 1, 1, 0, CopyFromParent, InputOutput, CopyFromParent, CWBackingStore | CWBackPixel | CWBackPixmap | CWEventMask | CWSaveUnder | CWOverrideRedirect, &att); set_hints(dpy, win); /* XXX: XSetClassHint? */ return win; } void ghosd_show(Ghosd *ghosd) { XMapWindow(ghosd->dpy, ghosd->win); } void ghosd_set_transparent(Ghosd *ghosd, int transparent) { ghosd->transparent = (transparent != 0); } void ghosd_set_render(Ghosd *ghosd, GhosdRenderFunc render_func, void *render_data) { ghosd->render_func = render_func; ghosd->render_data = render_data; } void ghosd_set_position(Ghosd *ghosd, int x, int y, int width, int height) { const int dpy_width = DisplayWidth(ghosd->dpy, DefaultScreen(ghosd->dpy)); const int dpy_height = DisplayHeight(ghosd->dpy, DefaultScreen(ghosd->dpy)); if (x == GHOSD_COORD_CENTER) { x = (dpy_width - width) / 2; } else if (x < 0) { x = dpy_width - width + x; } if (y == GHOSD_COORD_CENTER) { y = (dpy_height - height) / 2; } else if (y < 0) { y = dpy_height - height + y; } ghosd->x = x; ghosd->y = y; ghosd->width = width; ghosd->height = height; if (ghosd->transparent) ghosd->background = take_snapshot(ghosd); XMoveResizeWindow(ghosd->dpy, ghosd->win, ghosd->x, ghosd->y, ghosd->width, ghosd->height); } #if 0 static int x_error_handler(Display *dpy, XErrorEvent* evt) { /* segfault so we can get a backtrace. */ char *x = NULL; *x = 0; return 0; } #endif Ghosd * ghosd_new(void) { Ghosd *ghosd; Display *dpy; Window win; dpy = XOpenDisplay(NULL); if (dpy == NULL) { fprintf(stderr, "Couldn't open display: (XXX FIXME)\n"); return NULL; } win = make_window(dpy); ghosd = calloc(1, sizeof(Ghosd)); ghosd->dpy = dpy; ghosd->win = win; ghosd->transparent = 1; return ghosd; } int ghosd_get_socket(Ghosd *ghosd) { return ConnectionNumber(ghosd->dpy); } /* vim: set ts=2 sw=2 et cino=(0 : */