/* 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 <stdio.h>
#include <stdlib.h>
#include <X11/Xlib.h>
#include <X11/Xatom.h>
#include <X11/Xmd.h>
#include <X11/xpm.h>
#include <X11/extensions/shape.h>
#include <unistd.h>
#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;
}
syntax highlighted by Code2HTML, v. 0.9.1