diff options
-rw-r--r-- | LICENSE | 21 | ||||
-rw-r--r-- | Makefile | 23 | ||||
-rw-r--r-- | README | 40 | ||||
-rw-r--r-- | config.mk | 29 | ||||
-rw-r--r-- | gridwm.1 | 16 | ||||
-rw-r--r-- | wm.c | 264 | ||||
-rw-r--r-- | wm.h | 57 |
7 files changed, 450 insertions, 0 deletions
diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..aa0a3ab --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT/X Consortium License + +(C)opyright MMVI Anselm R. Garbe <garbeam at gmail dot com> + +Permission is hereby granted, free of charge, to any person obtaining a +copy of this software and associated documentation files (the "Software"), +to deal in the Software without restriction, including without limitation +the rights to use, copy, modify, merge, publish, distribute, sublicense, +and/or sell copies of the Software, and to permit persons to whom the +Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..fff5ee1 --- /dev/null +++ b/Makefile @@ -0,0 +1,23 @@ +# gridwm - grid window manager +# (C)opyright MMVI Anselm R. Garbe + +include config.mk + +SRC = wm.c +OBJ = ${SRC:.c=.o} + +all: gridwm + @echo finished + +.c.o: + @echo CC $< + @${CC} -c ${CFLAGS} $< + +${OBJ}: wm.h + +gridwm: ${OBJ} + @echo LD $@ + @${CC} -o $@ ${OBJ} ${X11LDFLAGS} + +clean: + rm -f gridwm *.o diff --git a/README b/README new file mode 100644 index 0000000..c766f1b --- /dev/null +++ b/README @@ -0,0 +1,40 @@ +gridwm +------ + +gridwm is an automatic X11 window manager which arranges all windows in a grid. + + +Requirements +------------ +In order to build gridwm you need the Xlib header files. + + +Installation +------------ +Edit config.mk to match your local setup. gridwm is installed into +the /usr/local namespace by default. + +Afterwards enter the following command to build and install gridwm (if +necessary as root): + + make clean install + + +Running gridwm +-------------- +Add the following line to your .xinitrc to start gridwm using startx: + + exec gridwm + +In order to connect gridwm to a specific display, make sure that +the DISPLAY environment variable is set correctly, e.g.: + + DISPLAY=foo.bar:1 exec wmii + +This will start gridwm on display :1 of the host foo.bar. + + +Configuration +------------- +The configuration of gridwm is done by customizing the config.h +source file. diff --git a/config.mk b/config.mk new file mode 100644 index 0000000..8fc41d7 --- /dev/null +++ b/config.mk @@ -0,0 +1,29 @@ +# Customize to fit your system + +# paths +PREFIX = /usr/local +CONFPREFIX = ${PREFIX}/etc +MANPREFIX = ${PREFIX}/share/man + +X11INC = /usr/X11R6/include +X11LIB = /usr/X11R6/lib + +VERSION = 0.0 + +# includes and libs +LIBS = -L${PREFIX}/lib -L/usr/lib -lc +X11LIBS = -L${X11LIB} -lX11 + +# Linux/BSD +CFLAGS = -g -Wall -I. -I${PREFIX}/include -I/usr/include -I${X11INC} \ + -DVERSION=\"${VERSION}\" +LDFLAGS = -g ${LIBS} +X11LDFLAGS = ${LDFLAGS} ${X11LIBS} + +# Solaris +#CFLAGS = -fast -xtarget=ultra ${INCLUDES} -DVERSION=\"${VERSION}\" +#LIBS += -lnsl -lsocket + +AR = ar cr +CC = cc +RANLIB = ranlib diff --git a/gridwm.1 b/gridwm.1 new file mode 100644 index 0000000..02a6b6a --- /dev/null +++ b/gridwm.1 @@ -0,0 +1,16 @@ +.TH GRIDWM 1 gridwm-0.0 +.SH NAME +gridwm \- grid window manager +.SH SYNOPSIS +.B gridwm +.RB [ \-v ] +.SH DESCRIPTION +.SS Overview +.B gridwm +is an automatic window manager for X11. +.SS Options +.TP +.B \-v +prints version information to stdout, then exits. +.SH SEE ALSO +.BR gridmenu (1) diff --git a/wm.c b/wm.c new file mode 100644 index 0000000..de5324c --- /dev/null +++ b/wm.c @@ -0,0 +1,264 @@ +/* + * (C)opyright MMVI Anselm R. Garbe <garbeam at gmail dot com> + * See LICENSE file for license details. + */ + +#include <stdarg.h> +#include <stdio.h> +#include <stdlib.h> + +#include <X11/cursorfont.h> +#include <X11/Xatom.h> +#include <X11/Xproto.h> + +#include "wm.h" + +Display *dpy; +Window root; +XRectangle rect; +int screen, sel_screen; +Atom wm_atom[WMLast]; +Atom net_atom[NetLast]; +Cursor cursor[CurLast]; +unsigned int kmask, numlock_mask; +Pixmap pmap; + +enum { WM_PROTOCOL_DELWIN = 1 }; + +static Bool other_wm_running; +static int (*x_error_handler) (Display *, XErrorEvent *); +static char version[] = "gridwm - " VERSION ", (C)opyright MMVI Anselm R. Garbe\n"; + +static void +usage() +{ + fputs("usage: gridwm [-v]\n", stderr); + exit(1); +} + +void +error(char *errstr, ...) { + va_list ap; + va_start(ap, errstr); + vfprintf(stderr, errstr, ap); + va_end(ap); + exit(1); +} + +static void +scan_wins() +{ + unsigned int i, num; + Window *wins; + XWindowAttributes wa; + Window d1, d2; + + if(XQueryTree(dpy, root, &d1, &d2, &wins, &num)) { + for(i = 0; i < num; i++) { + if(!XGetWindowAttributes(dpy, wins[i], &wa)) + continue; + if(wa.override_redirect || XGetTransientForHint(dpy, wins[i], &d1)) + continue; + if(wa.map_state == IsViewable) + /*manage*/; + } + } + if(wins) + XFree(wins); +} + +static int +win_property(Window w, Atom a, Atom t, long l, unsigned char **prop) +{ + Atom real; + int format; + unsigned long res, extra; + int status; + + status = XGetWindowProperty(dpy, w, a, 0L, l, False, t, &real, &format, + &res, &extra, prop); + + if(status != Success || *prop == 0) { + return 0; + } + if(res == 0) { + free((void *) *prop); + } + return res; +} + +int +win_proto(Window w) +{ + Atom *protocols; + long res; + int protos = 0; + int i; + + res = win_property(w, wm_atom[WMProtocols], XA_ATOM, 20L, + ((unsigned char **) &protocols)); + if(res <= 0) { + return protos; + } + for(i = 0; i < res; i++) { + if(protocols[i] == wm_atom[WMDelete]) + protos |= WM_PROTOCOL_DELWIN; + } + free((char *) protocols); + return protos; +} + +/* + * There's no way to check accesses to destroyed windows, thus + * those cases are ignored (especially on UnmapNotify's). + * Other types of errors call Xlib's default error handler, which + * calls exit(). + */ +static int +error_handler(Display *dpy, XErrorEvent *error) +{ + if(error->error_code == BadWindow + || (error->request_code == X_SetInputFocus + && error->error_code == BadMatch) + || (error->request_code == X_PolyText8 + && error->error_code == BadDrawable) + || (error->request_code == X_PolyFillRectangle + && error->error_code == BadDrawable) + || (error->request_code == X_PolySegment + && error->error_code == BadDrawable) + || (error->request_code == X_ConfigureWindow + && error->error_code == BadMatch) + || (error->request_code == X_GrabKey + && error->error_code == BadAccess)) + return 0; + fprintf(stderr, "gridwm: fatal error: request code=%d, error code=%d\n", + error->request_code, error->error_code); + return x_error_handler(dpy, error); /* may call exit() */ +} + +/* + * Startup Error handler to check if another window manager + * is already running. + */ +static int +startup_error_handler(Display *dpy, XErrorEvent *error) +{ + other_wm_running = True; + return -1; +} + +static void +init_lock_keys() +{ + XModifierKeymap *modmap; + KeyCode numlock; + int i; + static int masks[] = { + ShiftMask, LockMask, ControlMask, Mod1Mask, + Mod2Mask, Mod3Mask, Mod4Mask, Mod5Mask + }; + + numlock_mask = 0; + modmap = XGetModifierMapping(dpy); + numlock = XKeysymToKeycode(dpy, XStringToKeysym("Num_Lock")); + + if(modmap && modmap->max_keypermod > 0) { + int max = (sizeof(masks) / sizeof(int)) * modmap->max_keypermod; + for(i = 0; i < max; i++) + if(numlock && (modmap->modifiermap[i] == numlock)) + numlock_mask = masks[i / modmap->max_keypermod]; + } + XFreeModifiermap(modmap); + + kmask = 255 & ~(numlock_mask | LockMask); +} + +static void +cleanup() +{ + /* + Client *c; + for(c=client; c; c=c->next) + reparent_client(c, root, c->sel->rect.x, c->sel->rect.y); + XSetInputFocus(dpy, PointerRoot, RevertToPointerRoot, CurrentTime); + */ +} + +int +main(int argc, char *argv[]) +{ + int i; + XSetWindowAttributes wa; + unsigned int mask; + Window w; + + /* command line args */ + for(i = 1; (i < argc) && (argv[i][0] == '-'); i++) { + switch (argv[i][1]) { + case 'v': + fprintf(stdout, "%s", version); + exit(0); + break; + default: + usage(); + break; + } + } + + dpy = XOpenDisplay(0); + if(!dpy) + error("gridwm: cannot connect X server\n"); + + screen = DefaultScreen(dpy); + root = RootWindow(dpy, screen); + + /* check if another WM is already running */ + other_wm_running = False; + XSetErrorHandler(startup_error_handler); + /* this causes an error if some other WM is running */ + XSelectInput(dpy, root, SubstructureRedirectMask); + XSync(dpy, False); + + if(other_wm_running) + error("gridwm: another window manager is already running\n"); + + rect.x = rect.y = 0; + rect.width = DisplayWidth(dpy, screen); + rect.height = DisplayHeight(dpy, screen); + sel_screen = XQueryPointer(dpy, root, &w, &w, &i, &i, &i, &i, &mask); + + XSetErrorHandler(0); + x_error_handler = XSetErrorHandler(error_handler); + + /* init atoms */ + wm_atom[WMState] = XInternAtom(dpy, "WM_STATE", False); + wm_atom[WMProtocols] = XInternAtom(dpy, "WM_PROTOCOLS", False); + wm_atom[WMDelete] = XInternAtom(dpy, "WM_DELETE_WINDOW", False); + net_atom[NetSupported] = XInternAtom(dpy, "_NET_SUPPORTED", False); + net_atom[NetWMName] = XInternAtom(dpy, "_NET_WM_NAME", False); + + XChangeProperty(dpy, root, net_atom[NetSupported], XA_ATOM, 32, + PropModeReplace, (unsigned char *) net_atom, NetLast); + + + /* init cursors */ + cursor[CurNormal] = XCreateFontCursor(dpy, XC_left_ptr); + cursor[CurResize] = XCreateFontCursor(dpy, XC_sizing); + cursor[CurMove] = XCreateFontCursor(dpy, XC_fleur); + + init_lock_keys(); + + pmap = XCreatePixmap(dpy, root, rect.width, rect.height, + DefaultDepth(dpy, screen)); + + wa.event_mask = SubstructureRedirectMask | EnterWindowMask | LeaveWindowMask; + wa.cursor = cursor[CurNormal]; + XChangeWindowAttributes(dpy, root, CWEventMask | CWCursor, &wa); + + scan_wins(); + + cleanup(); + XCloseDisplay(dpy); + + return 0; +} diff --git a/wm.h b/wm.h new file mode 100644 index 0000000..3885cb5 --- /dev/null +++ b/wm.h @@ -0,0 +1,57 @@ +/* + * (C)opyright MMVI Anselm R. Garbe <garbeam at gmail dot com> + * See LICENSE file for license details. + */ + +#include <X11/Xlib.h> +#include <X11/Xutil.h> + +/* WM atoms */ +enum { WMState, WMProtocols, WMDelete, WMLast }; + +/* NET atoms */ +enum { NetSupported, NetWMName, NetLast }; + +/* Cursor */ +enum { CurNormal, CurResize, CurMove, CurInput, CurLast }; + +/* Rects */ +enum { RFloat, RGrid, RLast }; + +typedef struct Client Client; +typedef struct Tag Tag; + +struct Client { + Tag *tag; + char name[256]; + int proto; + Window win; + Window trans; + Window title; + GC gc; + XSizeHints size; + XRectangle r[RLast]; + Client *next; + Client *tnext; + Client *tprev; +}; + +struct Tag { + char name[256]; + Client *clients; + Client *sel; + XRectangle r; +}; + +extern Display *dpy; +extern Window root; +extern XRectangle rect; +extern int screen, sel_screen; +extern unsigned int kmask, numlock_mask; +extern Atom wm_atom[WMLast]; +extern Atom net_atom[NetLast]; +extern Cursor cursor[CurLast]; +extern Pixmap pmap; + +/* wm.c */ +extern void error(char *errstr, ...); |