/* wampager.c Copyright © 2003 Andreas Larsson, David Reveman. This file is part of Waimea. Waimea 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, or (at your option) any later version. Waimea 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 Waimea; see the file COPYING. If not, write to the Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include #include #include #include #include #include #include #include #include "background.xpm" #include "foreground.xpm" #include "grid.xpm" #define WADOCK_WIDTH 64 #define WADOCK_HEIGHT 64 #define WADOCK_X_OFFSET 5 #define WADOCK_Y_OFFSET 5 Display *d; Window win; Window win2; GC gc; Visual *visual = NULL; int depth = 0; Window rootWin; char *program_name; Pixmap maskPixmap; Pixmap backPixmap; Pixmap frontPixmap; Pixmap gridPixmap; Pixmap pagerPixmap; Atom net_desktop_viewport; Atom net_desktop_geometry; Atom net_current_desktop; int snap_button = 1; int exact_button = 3; unsigned int nrOfDesktopsX = 0; unsigned int nrOfDesktopsY = 0; unsigned int screenSizeX; unsigned int screenSizeY; unsigned int virtualDesktopSizeX = 0; unsigned int virtualDesktopSizeY = 0; unsigned int desktopPosX = 0; unsigned int desktopPosY = 0; void usage(void) { printf("Usage: %s [-snap BUTTON] [-exact BUTTON]\n", program_name); } void createWindow(char *name, int width, int height) { XClassHint *classHints; XWMHints *wmHints; XTextProperty pname; XGCValues gcv; /* Create Windows */ win = XCreateSimpleWindow(d, rootWin, 0, 0, width, height, 0, 0, 0); win2 = XCreateSimpleWindow(d, rootWin, 0, 0, 1, 1, 0, 0, 0); /* Set ClassHint */ if (!(classHints = XAllocClassHint())){ fprintf(stderr, "%s: can't allocate memory for class hints\n", program_name); exit(1); } classHints->res_class = "DockApp"; classHints->res_name = name; XSetClassHint(d, win2, classHints); XFree(classHints); /* Set WMHints */ if (!(wmHints = XAllocWMHints())){ fprintf(stderr, "%s: can't allocate memory for wm hints\n", program_name); exit(1); } wmHints->flags = IconWindowHint|WindowGroupHint; wmHints->flags |= StateHint; wmHints->initial_state = WithdrawnState; wmHints->window_group = win2; wmHints->icon_window = win; XSetWMHints(d, win2, wmHints); if (XStringListToTextProperty(&name, 1, &pname) == 0) { fprintf(stderr, "%s: can't allocate window name\n", program_name); exit(1); } XSetWMName(d, win, &pname); depth = DefaultDepth(d, DefaultScreen(d)); visual = DefaultVisual(d, DefaultScreen(d)); gc = DefaultGC(d, DefaultScreen(d)); gcv.graphics_exposures = False; XChangeGC(d, gc, GCGraphicsExposures, &gcv); XFlush(d); } void getVirtualSize() { CARD32 *data; int real_format; Atom real_type; unsigned long items_read, items_left; int _d; unsigned int _ud; Window _wd; XGetGeometry(d, rootWin, &_wd, &_d, &_d, &screenSizeX, &screenSizeY, &_ud, &_ud); if (XGetWindowProperty(d, rootWin, net_desktop_geometry, 0L, 2L, False, XA_CARDINAL, &real_type, &real_format, &items_read, &items_left, (unsigned char **) &data) == Success && items_read >= 2) { virtualDesktopSizeX = data[0]; virtualDesktopSizeY = data[1]; free(data); } nrOfDesktopsX = virtualDesktopSizeX/screenSizeX; nrOfDesktopsY = virtualDesktopSizeY/screenSizeY; } void pixmapFromData(char **data, Pixmap *pixmap, Pixmap *mask) { XpmAttributes xpmAttr; xpmAttr.valuemask = XpmCloseness; xpmAttr.closeness = 40000; if (XpmCreatePixmapFromData(d, win, data, pixmap, mask, &xpmAttr) == -1) { fprintf(stderr, "%s: error reading pixmap\n", program_name); exit(-1); } } void setPixmap(Pixmap pixmap) { XSetWindowBackgroundPixmap(d, win, pixmap); XClearWindow(d, win); XFlush(d); } int setupPixmaps() { pixmapFromData(background_xpm, &backPixmap, &maskPixmap); pixmapFromData(foreground_xpm, &frontPixmap, None); pixmapFromData(grid_xpm, &gridPixmap, None); pagerPixmap = XCreatePixmap(d, win, WADOCK_WIDTH, WADOCK_HEIGHT, depth); return 1; } int updatePager() { int i; XCopyArea(d, backPixmap, pagerPixmap, gc, 0, 0, WADOCK_WIDTH, WADOCK_HEIGHT, 0, 0); if (virtualDesktopSizeX) { XCopyArea(d, frontPixmap, pagerPixmap, gc, (1.0 * desktopPosX * (WADOCK_WIDTH - WADOCK_X_OFFSET * 2)) / (screenSizeX * nrOfDesktopsX) + WADOCK_X_OFFSET, (1.0 * desktopPosY * (WADOCK_HEIGHT - WADOCK_Y_OFFSET * 2)) / (screenSizeY * nrOfDesktopsY) + WADOCK_Y_OFFSET, (1.0 * WADOCK_WIDTH - WADOCK_X_OFFSET * 2) / nrOfDesktopsX + 1, (1.0 * WADOCK_HEIGHT - WADOCK_Y_OFFSET * 2) / nrOfDesktopsY + 1, (1.0 * desktopPosX * (WADOCK_WIDTH - WADOCK_X_OFFSET * 2)) / (screenSizeX * nrOfDesktopsX) + WADOCK_X_OFFSET, (1.0 * desktopPosY * (WADOCK_HEIGHT - WADOCK_Y_OFFSET * 2)) / (screenSizeY * nrOfDesktopsY) + WADOCK_Y_OFFSET); for (i = 1; i < nrOfDesktopsX; i++) XCopyArea(d, gridPixmap, pagerPixmap, gc, (1.0 * WADOCK_WIDTH - WADOCK_X_OFFSET * 2) / nrOfDesktopsX * i + WADOCK_X_OFFSET, 0, 1, WADOCK_HEIGHT, (1.0 * WADOCK_WIDTH - WADOCK_X_OFFSET * 2) / nrOfDesktopsX * i + WADOCK_X_OFFSET, 0); for (i = 1; i < nrOfDesktopsY; i++) XCopyArea(d, gridPixmap, pagerPixmap, gc, 0, (1.0 * WADOCK_HEIGHT - WADOCK_Y_OFFSET * 2) / nrOfDesktopsY * i + WADOCK_Y_OFFSET, WADOCK_WIDTH, 1, 0, (1.0 * WADOCK_HEIGHT - WADOCK_Y_OFFSET * 2) / nrOfDesktopsY * i + WADOCK_Y_OFFSET); } setPixmap(pagerPixmap); return 1; } void updateDesktopPos() { CARD32 *data; int real_format; Atom real_type; unsigned long items_read, items_left; int desktop = 0; if (XGetWindowProperty(d, rootWin, net_current_desktop, 0L, 1L, False, XA_CARDINAL, &real_type, &real_format, &items_read, &items_left, (unsigned char **) &data) == Success && items_read) { desktop = data[0]; XFree(data); } if (XGetWindowProperty(d, rootWin ,net_desktop_viewport, 0L, 8192L, False, XA_CARDINAL, &real_type, &real_format, &items_read, &items_left, (unsigned char **) &data) == Success && items_read >= 2) { if (items_read >= (desktop * 2 + 2)) { desktopPosX = data[desktop * 2]; desktopPosY = data[desktop * 2 + 1]; } XFree(data); } } void setupAtoms(void) { net_desktop_viewport = XInternAtom(d, "_NET_DESKTOP_VIEWPORT", False); net_desktop_geometry = XInternAtom(d, "_NET_DESKTOP_GEOMETRY", False); net_current_desktop = XInternAtom(d, "_NET_CURRENT_DESKTOP", False); } void moveDesktopPos(int x, int y) { XEvent sevent; sevent.type = ClientMessage; sevent.xclient.message_type = net_desktop_viewport; sevent.xclient.display = d; sevent.xclient.window = rootWin; sevent.xclient.format = 32; sevent.xclient.data.l[0] = x; sevent.xclient.data.l[1] = y; XSendEvent(d, rootWin, False, PropertyChangeMask, &sevent); } void setDesktopExakt(int x, int y) { int newX, newY; if ((x > WADOCK_X_OFFSET) && (x < WADOCK_WIDTH-WADOCK_X_OFFSET) && (y > WADOCK_Y_OFFSET) && (x < WADOCK_HEIGHT-WADOCK_Y_OFFSET)) { newX = ((1.0 * x + ((WADOCK_WIDTH - WADOCK_X_OFFSET) / nrOfDesktopsX) - WADOCK_X_OFFSET) / ((WADOCK_WIDTH - WADOCK_X_OFFSET * 2) / nrOfDesktopsX) - 1) * screenSizeX - screenSizeX / 2; newY = ((1.0 * y + ((WADOCK_HEIGHT-WADOCK_Y_OFFSET) / nrOfDesktopsY) - WADOCK_Y_OFFSET) / ((WADOCK_HEIGHT - WADOCK_Y_OFFSET * 2) / nrOfDesktopsY) - 1) * screenSizeY - screenSizeY / 2; if (newX < 0) newX = 0; if (newY < 0) newY = 0; if (newX > virtualDesktopSizeX - screenSizeX) newX = virtualDesktopSizeX - screenSizeX; if (newY > virtualDesktopSizeY - screenSizeY) newY = virtualDesktopSizeY - screenSizeY; moveDesktopPos(newX, newY); } } void setDesktop(int x, int y) { int newX, newY; if ((x > WADOCK_X_OFFSET) && (x < WADOCK_WIDTH-WADOCK_X_OFFSET) && (y > WADOCK_Y_OFFSET) && (x < WADOCK_HEIGHT-WADOCK_Y_OFFSET)) { newX = (x + ((WADOCK_WIDTH - WADOCK_X_OFFSET) / nrOfDesktopsX) - WADOCK_X_OFFSET) / ((WADOCK_WIDTH - WADOCK_X_OFFSET * 2) / nrOfDesktopsX) - 1; newY = (y + ((WADOCK_HEIGHT - WADOCK_Y_OFFSET) / nrOfDesktopsY) - WADOCK_Y_OFFSET) / ((WADOCK_HEIGHT - WADOCK_Y_OFFSET * 2) / nrOfDesktopsY) - 1; newX *= screenSizeX; newY *= screenSizeY; moveDesktopPos(newX, newY); } } int main(int argc, char **argv) { XEvent event; int i, snap_drag = 0, exact_drag = 0; program_name = argv[0]; for (i = 1; i < argc; i++) { if (! strcmp(argv[i], "--version")) { fprintf(stderr, "%s %s\n", PACKAGE, VERSION); exit(0); } else if (! strcmp(argv[i], "--help")) { usage(); exit(0); } else if (! strcmp(argv[i], "-exact")) { if (i + 1 < argc) exact_button = atoi(argv[i++ + 1]); else { fprintf(stderr, "%s: option '-exact' equires an argument\n", program_name); usage(); exit(1); } } else if (! strcmp(argv[i], "-snap")) { if (i + 1 < argc) snap_button = atoi(argv[i++ + 1]); else { fprintf(stderr, "%s: option '-snap' equires an argument\n", program_name); usage(); exit(1); } } else { fprintf(stderr, "%s: unrecognized option '%s'\n", program_name, argv[i]); usage(); exit(1); } } if (! (d = XOpenDisplay(NULL))) { fprintf(stderr, "%s: unable to open display\n", program_name); } rootWin = DefaultRootWindow(d); setupAtoms(); createWindow(program_name, WADOCK_WIDTH, WADOCK_HEIGHT); setupPixmaps(); XShapeCombineMask(d, win, ShapeBounding, 0, 0, maskPixmap, ShapeSet); getVirtualSize(); updateDesktopPos(); updatePager(); XMapWindow(d, win2); XSelectInput(d, rootWin, PropertyChangeMask); XSelectInput(d, win, ButtonPressMask | ButtonReleaseMask | PointerMotionMask); for (;;) { XNextEvent(d, &event); switch (event.type) { case PropertyNotify: if (event.xproperty.state == PropertyDelete) { if (event.xproperty.atom == net_desktop_viewport) virtualDesktopSizeX = 0; } else { if (event.xproperty.atom == net_desktop_viewport) { updateDesktopPos(); updatePager(); } else if (event.xproperty.atom == net_desktop_geometry) { getVirtualSize(); updateDesktopPos(); updatePager(); } } break; case Expose: break; case DestroyNotify: XCloseDisplay(d); exit(0); break; case ButtonPress: if ((event.xbutton.button == snap_button) && (virtualDesktopSizeX)) { snap_drag = 1; setDesktop(event.xbutton.x, event.xbutton.y); } else if ((event.xbutton.button == exact_button) && (virtualDesktopSizeX)) { exact_drag = 1; setDesktopExakt(event.xbutton.x, event.xbutton.y); } break; case ButtonRelease: if ((event.xbutton.button == snap_button) && (virtualDesktopSizeX)) { snap_drag = 0; setDesktop(event.xbutton.x, event.xbutton.y); } if ((event.xbutton.button == exact_button) && (virtualDesktopSizeX)) { exact_drag = 0; } break; case MotionNotify: if ((snap_drag || exact_drag) && (virtualDesktopSizeX)) setDesktopExakt(event.xbutton.x, event.xbutton.y); break; } } XCloseDisplay(d); return 0; }