Mercurial > audlegacy-plugins
diff src/aosd/ghosd.c @ 569:d401f87f89f7 trunk
[svn] - added Audacious OSD, yet-another-written-from-scratch plugin to display OSD, based on Ghosd library; currently untied from configure, to compile it you have to run make in its directory; will be added to configure after some testing
author | giacomo |
---|---|
date | Mon, 29 Jan 2007 06:40:04 -0800 |
parents | |
children | 6584e697e6da |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/aosd/ghosd.c Mon Jan 29 06:40:04 2007 -0800 @@ -0,0 +1,248 @@ +/* ghosd -- OSD with fake transparency, cairo, and pango. + * Copyright (C) 2006 Evan Martin <martine@danga.com> + */ + +#include "config.h" + +#include <stdio.h> +#include <stdlib.h> +#include <cairo/cairo-xlib-xrender.h> +#include <X11/Xatom.h> + +#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); + /* I think it's ok to free it here because XCreatePixmap(3X11) says: "the X + * server frees the pixmap storage when there are no references to it". + */ + 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) { + if (ghosd->transparent) { + if (ghosd->background) + XFreePixmap(ghosd->dpy, ghosd->background); + ghosd->background = take_snapshot(ghosd); + } + + ghosd_render(ghosd); + + XMapWindow(ghosd->dpy, ghosd->win); +} + +void +ghosd_hide(Ghosd *ghosd) { + XUnmapWindow(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 *user_data, void (*user_data_d)(void*)) { + ghosd->render.func = render_func; + ghosd->render.data = user_data; + ghosd->render.data_destroy = user_data_d; +} + +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; + + 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; +} + +void +ghosd_destroy(Ghosd* ghosd) { + if (ghosd->background) + XFreePixmap(ghosd->dpy, ghosd->background); + XDestroyWindow(ghosd->dpy, ghosd->win); +} + +int +ghosd_get_socket(Ghosd *ghosd) { + return ConnectionNumber(ghosd->dpy); +} + +/* vim: set ts=2 sw=2 et cino=(0 : */