This commit is contained in:
seanpringle
2012-08-31 02:15:23 +10:00
parent 9693addc02
commit e78f92b98a
6 changed files with 1045 additions and 1 deletions

13
Makefile Normal file
View File

@@ -0,0 +1,13 @@
CFLAGS?=-Wall -O2
LDADD?=$(shell pkg-config --cflags --libs x11)
normal:
$(CC) -o cerberus cerberus.c $(CFLAGS) $(LDADD) $(LDFLAGS)
proto:
cat *.c | egrep '^(void|int|char|unsigned|Window|client)' | sed -r 's/\)/);/' > proto.h
clean:
rm -f cerberus
all: proto normal

View File

@@ -1,4 +1,20 @@
cerberus cerberus
======== ========
static tiling wm * Designed for wide screens.
* Static tiling; you get just three fixed asymmetric tiles and windows never move automatically.
* Bare minimum EWMH to support panels and (simpleswitcher)[https://github.com/seanpringle/simpleswitcher]
* A few keyboard controls for moving, focusing, cycling, closing.
* config.h for customization of borders and keys.
### The Layout
------------------------
| | |
| | |
| | |
| | |
| |-------|
| | |
| | |
------------------------

824
cerberus.c Normal file
View File

@@ -0,0 +1,824 @@
/*
MIT/X11 License
Copyright (c) 2012 Sean Plistle <sean.plistle@gmail.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.
*/
#define _GNU_SOURCE
#include "cerberus.h"
#include "proto.h"
#include "config.h"
// X error handler
int oops(Display *d, XErrorEvent *ee)
{
if (ee->error_code == BadWindow
|| (ee->request_code == X_SetInputFocus && ee->error_code == BadMatch)
|| (ee->request_code == X_ConfigureWindow && ee->error_code == BadMatch)
|| (ee->request_code == X_GrabButton && ee->error_code == BadAccess)
|| (ee->request_code == X_GrabKey && ee->error_code == BadAccess)
) return 0;
fprintf(stderr, "error: request code=%d, error code=%d\n", ee->request_code, ee->error_code);
return xerror(display, ee);
}
// get pixel value for X named color
unsigned int color_get(const char *name)
{
XColor color;
Colormap map = DefaultColormap(display, DefaultScreen(display));
return XAllocNamedColor(display, map, name, &color, &color) ? color.pixel: None;
}
// retrieve a property of any type from a window
int window_get_prop(Window w, Atom prop, Atom *type, int *items, void *buffer, int bytes)
{
Atom _type; if (!type) type = &_type;
int _items; if (!items) items = &_items;
int format; unsigned long nitems, nbytes; unsigned char *ret = NULL;
memset(buffer, 0, bytes);
if (XGetWindowProperty(display, w, prop, 0, bytes/4, False, AnyPropertyType, type,
&format, &nitems, &nbytes, &ret) == Success && ret && *type != None && format)
{
if (format == 8) memmove(buffer, ret, MIN(bytes, nitems));
if (format == 16) memmove(buffer, ret, MIN(bytes, nitems * sizeof(short)));
if (format == 32) memmove(buffer, ret, MIN(bytes, nitems * sizeof(long)));
*items = (int)nitems; XFree(ret);
return 1;
}
return 0;
}
int window_get_atom_prop(Window w, Atom atom, Atom *list, int count)
{
Atom type; int items;
return window_get_prop(w, atom, &type, &items, list, count*sizeof(Atom)) && type == XA_ATOM ? items:0;
}
void window_set_atom_prop(Window w, Atom prop, Atom *atoms, int count)
{
XChangeProperty(display, w, prop, XA_ATOM, 32, PropModeReplace, (unsigned char*)atoms, count);
}
int window_get_cardinal_prop(Window w, Atom atom, unsigned long *list, int count)
{
Atom type; int items;
return window_get_prop(w, atom, &type, &items, list, count*sizeof(unsigned long)) && type == XA_CARDINAL ? items:0;
}
void window_set_cardinal_prop(Window w, Atom prop, unsigned long *values, int count)
{
XChangeProperty(display, w, prop, XA_CARDINAL, 32, PropModeReplace, (unsigned char*)values, count);
}
// update _NET_CLIENT_LIST_STACKING
void ewmh_client_list()
{
int i; stack all, wins;
memset(&all, 0, sizeof(stack));
memset(&wins, 0, sizeof(stack));
windows_visible(&all);
for (i = all.depth-1; i > -1; i--)
{
client *c = all.clients[i];
if (c && c->manage)
{
wins.clients[wins.depth] = c;
wins.windows[wins.depth++] = c->window;
}
}
XChangeProperty(display, root, atoms[_NET_CLIENT_LIST_STACKING], XA_WINDOW, 32,
PropModeReplace, (unsigned char*)&wins.windows, wins.depth);
}
// update _NET_ACTIVE_WINDOW
void ewmh_active_window(Window w)
{
XChangeProperty(display, root, atoms[_NET_ACTIVE_WINDOW], XA_WINDOW, 32,
PropModeReplace, (unsigned char*)&w, 1);
}
// build a client struct
client* window_client(Window win)
{
if (win == None) return NULL;
client *c = calloc(1, sizeof(client));
c->window = win;
if (XGetWindowAttributes(display, c->window, &c->attr))
{
c->visible = c->attr.map_state == IsViewable ? 1:0;
XGetTransientForHint(display, c->window, &c->transient_for);
window_get_atom_prop(win, atoms[_NET_WM_WINDOW_TYPE], &c->type, 1);
c->manage = !c->attr.override_redirect
&& c->type != atoms[_NET_WM_WINDOW_TYPE_DESKTOP]
&& c->type != atoms[_NET_WM_WINDOW_TYPE_NOTIFICATION]
&& c->type != atoms[_NET_WM_WINDOW_TYPE_DOCK]
&& c->type != atoms[_NET_WM_WINDOW_TYPE_SPLASH]
? 1:0;
c->trans = c->transient_for ? 1:0;
c->spot = c->attr.x > screen_x+screen_w/2
? (c->attr.y > screen_y+screen_h/2 ? SPOT3: SPOT2)
: SPOT1;
if (c->visible)
{
XWMHints *hints = XGetWMHints(display, c->window);
if (hints)
{
memmove(&c->hints, hints, sizeof(XWMHints));
XFree(hints);
}
long sr;
XGetWMNormalHints(display, c->window, &c->size, &sr);
c->input = c->hints.flags & InputHint && c->hints.input ? 1:0;
c->urgent = c->hints.flags & XUrgencyHint ? 1: 0;
}
return c;
}
free(c);
return NULL;
}
// build a list of visible windows
void windows_visible(stack *s)
{
// check if the window list is cached
if (inplay.depth)
{
memmove(s, &inplay, sizeof(stack));
return;
}
unsigned int nwins; int i; Window w1, w2, *wins;
if (XQueryTree(display, root, &w1, &w2, &wins, &nwins) && wins)
{
for (i = nwins-1; i > -1; i--)
{
client *c = window_client(wins[i]);
if (c && c->visible && s->depth < STACK)
{
s->clients[s->depth] = c;
s->windows[s->depth++] = wins[i];
}
else
client_free(c);
}
}
if (wins) XFree(wins);
memmove(&inplay, s, sizeof(stack));
}
// send a ClientMessage (for WM_PROTOCOLS)
int window_message(Window target, Window subject, Atom atom, unsigned long protocol, unsigned long mask)
{
XEvent e; memset(&e, 0, sizeof(XEvent));
e.xclient.type = ClientMessage;
e.xclient.message_type = atom;
e.xclient.window = subject;
e.xclient.data.l[0] = protocol;
e.xclient.data.l[1] = latest;
e.xclient.send_event = True;
e.xclient.format = 32;
int r = XSendEvent(display, target, False, mask, &e) ?1:0;
XFlush(display);
return r;
}
// release a client struct
void client_free(client *c)
{
free(c);
}
// WM_PROTOCOLS
int client_protocol(client *c, Atom protocol)
{
Atom *protocols = NULL;
int i, found = 0, num_pro = 0;
if (XGetWMProtocols(display, c->window, &protocols, &num_pro))
for (i = 0; i < num_pro && !found; i++)
if (protocols[i] == protocol) found = 1;
if (found)
window_message(c->window, c->window, atoms[WM_PROTOCOLS], protocol, NoEventMask);
if (protocols) XFree(protocols);
return found;
}
// friendly close followed by stab in the back
void client_close(client *c)
{
if (!client_protocol(c, atoms[WM_DELETE_WINDOW]))
XKillClient(display, c->window);
}
// move/resize a window
void client_position(client *c, int x, int y, int w, int h)
{
if (!c) return;
w -= BORDER*2; h -= BORDER*2;
int basew = 0, baseh = 0;
int sw = w, sh = h;
if (c->size.flags & PBaseSize)
{
basew = c->size.base_width;
baseh = c->size.base_height;
}
if (c->size.flags & PMinSize)
{
w = MAX(w, c->size.min_width);
h = MAX(h, c->size.min_height);
}
if (c->size.flags & PMaxSize)
{
w = MIN(w, c->size.max_width);
h = MIN(h, c->size.max_height);
}
if (c->size.flags & PResizeInc)
{
w -= basew; h -= baseh;
w -= w % c->size.width_inc;
h -= h % c->size.height_inc;
w += basew; h += baseh;
}
if (c->size.flags & PAspect)
{
double ratio = (double) w / h;
double minr = (double) c->size.min_aspect.x / c->size.min_aspect.y;
double maxr = (double) c->size.max_aspect.x / c->size.max_aspect.y;
if (ratio < minr) h = (int)(w / minr);
else if (ratio > maxr) w = (int)(h * maxr);
}
// center if smaller than supplied size
if (w < sw) x += (sw-w)/2;
if (h < sh) y += (sh-h)/2;
// bump onto screen
x = MAX(0, MIN(x, screen_x + screen_w - w - BORDER*2));
y = MAX(0, MIN(y, screen_y + screen_h - h - BORDER*2));
XMoveResizeWindow(display, c->window, x, y, w, h);
}
// return co-ords for a screen "spot"
void spot_xywh(int spot, int *x, int *y, int *w, int *h)
{
spot = MAX(SPOT1, MIN(SPOT3, spot));
int width_3rd = screen_w/3;
int height_3rd = screen_h/3;
// default, left 2/3 of screen
*x = screen_x, *y = screen_y, *w = screen_w - width_3rd, *h = screen_h;
switch (spot)
{
// right top 2/9 of screen
case SPOT2:
*x = screen_x + screen_w - width_3rd;
*y = screen_y;
*w = width_3rd;
*h = screen_h - height_3rd;
break;
// right bottom 1/9 of screen
case SPOT3:
*x = screen_x + screen_w - width_3rd;
*y = screen_y + screen_h - height_3rd;
*w = width_3rd;
*h = height_3rd;
break;
}
}
// move a window to a screen "spot"
void client_spot(client *c, int spot)
{
if (!c) return;
int x, y, w, h;
spot = MAX(SPOT1, MIN(SPOT3, spot));
spot_xywh(spot, &x, &y, &w, &h);
c->spot = spot;
client_position(c, x, y, w, h);
}
// cycle through windows in a screen "spot"
void client_cycle(client *c)
{
spot_active(c->spot, c->window);
client_lower(c);
}
// find a window within a screen "spot" and raise/activate
Window spot_active(int spot, Window except)
{
int x, y, w, h;
spot_xywh(spot, &x, &y, &w, &h);
int i; stack wins;
memset(&wins, 0, sizeof(stack));
windows_visible(&wins);
for (i = 0; i < wins.depth; i++)
{
client *o = wins.clients[i];
if (o->window != except && o->manage
&& INTERSECT(x + w/2, y + h/2, 1, 1,
o->attr.x, o->attr.y, o->attr.width, o->attr.height))
{
client_raise(o);
client_active(o);
return o->window;
}
}
return None;
}
// build a stack of a window's transients and itself
void client_stack(client *c, stack *all, stack *raise)
{
int i; client *self = NULL;
for (i = 0; i < all->depth; i++)
{
client *o = all->clients[i];
if (o->manage && o->visible && o->transient_for == c->window)
client_stack(o, all, raise);
else
if (o->visible && o->window == c->window)
self = o;
}
if (self)
{
raise->clients[raise->depth] = self;
raise->windows[raise->depth++] = self->window;
}
}
// raise a window and all its transients
void client_raise(client *c)
{
int i; stack raise, all, family;
memset(&all, 0, sizeof(stack));
memset(&raise, 0, sizeof(stack));
memset(&family, 0, sizeof(stack));
windows_visible(&all);
for (i = 0; i < all.depth; i++)
{
client *o = all.clients[i];
// docks stay on top
if (o && o->type == atoms[_NET_WM_WINDOW_TYPE_DOCK])
{
raise.clients[raise.depth] = o;
raise.windows[raise.depth++] = o->window;
}
}
while (c->trans)
{
client *t = window_client(c->transient_for);
if (t)
{
family.clients[family.depth++] = t;
c = t;
}
}
client_stack(c, &all, &raise);
XRaiseWindow(display, raise.windows[0]);
XRestackWindows(display, raise.windows, raise.depth);
while (family.depth)
client_free(family.clients[--family.depth]);
}
// lower a window and all its transients
void client_lower(client *c)
{
stack lower, all;
memset(&all, 0, sizeof(stack));
memset(&lower, 0, sizeof(stack));
windows_visible(&all);
client_stack(c, &all, &lower);
XLowerWindow(display, lower.windows[0]);
XRestackWindows(display, lower.windows, lower.depth);
}
// focus a window
void client_active(client *c)
{
client *o;
if (!c || !c->visible) return;
Window old = current;
current = c->window;
current_spot = c->spot;
if (old && (o = window_client(old)))
{
client_review(o);
client_free(o);
}
client_protocol(c, atoms[WM_TAKE_FOCUS]);
XSetInputFocus(display, c->input ? c->window: PointerRoot, RevertToPointerRoot, CurrentTime);
client_review(c);
ewmh_active_window(c->window);
}
// client events we care about
void window_listen(Window win)
{
XSelectInput(display, win, EnterWindowMask | LeaveWindowMask | FocusChangeMask | PropertyChangeMask);
}
// set border color based on focus
void client_review(client *c)
{
XSetWindowBorder(display, c->window, color_get(c->window == current ? BORDER_FOCUS: BORDER_BLUR));
XSetWindowBorderWidth(display, c->window, BORDER);
}
// ------- event handlers --------
void create_notify(XCreateWindowEvent *e)
{
client *c = window_client(e->window);
if (c && c->manage)
window_listen(c->window);
client_free(c);
}
void configure_request(XConfigureRequestEvent *e)
{
client *c = window_client(e->window);
if (c)
{
if (c->manage && c->visible && !c->trans)
{
client_review(c);
client_spot(c, c->spot);
}
else
{
XWindowChanges wc;
if (e->value_mask & CWX) wc.x = e->x;
if (e->value_mask & CWY) wc.y = e->y;
if (e->value_mask & CWWidth) wc.width = e->width;
if (e->value_mask & CWHeight) wc.height = e->height;
if (e->value_mask & CWStackMode) wc.stack_mode = e->detail;
if (e->value_mask & CWBorderWidth) wc.border_width = BORDER;
XConfigureWindow(display, c->window, e->value_mask, &wc);
}
}
client_free(c);
}
void map_request(XMapEvent *e)
{
client *c = window_client(e->window);
if (c && c->manage)
{
client_review(c);
int x, w, y, h;
int spot = SPOT_START == SPOT_CURRENT ? current_spot: SPOT_START;
spot_xywh(spot, &x, &y, &w, &h);
if (c->trans)
{
// locate a transient's parent's spot
client *t = window_client(c->transient_for);
spot = t->spot;
client_free(t);
}
if (!c->trans && c->type != atoms[_NET_WM_WINDOW_TYPE_DIALOG])
{
// fill a spot
client_spot(c, spot);
}
else
{
// center in spot at requested size
client_position(c,
x + (w - c->attr.width)/2, y + (h - c->attr.height)/2,
c->attr.width + BORDER*2, c->attr.height + BORDER*2);
c->spot = spot;
}
}
client_free(c);
XMapWindow(display, e->window);
}
void map_notify(XMapEvent *e)
{
client *a = NULL, *c = window_client(e->window);
if (c && c->manage)
{
client_raise(c);
if (current)
a = window_client(current);
// if no current window, or new window has opened in the
// current spot, activate it
if (!a || (a && a->spot == c->spot))
client_active(c);
else
client_review(c);
client_free(a);
}
client_free(c);
ewmh_client_list();
}
void unmap_notify(XUnmapEvent *e)
{
if (e->window == current && !spot_active(current_spot, current))
{
// find something to activate
if (!spot_active(SPOT1, current)
&& !spot_active(SPOT2, current)
&& !spot_active(SPOT3, current))
current = None;
}
ewmh_client_list();
}
void key_press(XKeyEvent *e)
{
int i; XEvent ev;
while (XCheckTypedEvent(display, KeyPress, &ev));
latest = e->time;
client *c = window_client(current);
short act = ACTION_NONE;
KeySym key = XkbKeycodeToKeysym(display, e->keycode, 0, 0);
unsigned int state = e->state & ~(LockMask|NumlockMask);
for (i = 0; i < sizeof(keys)/sizeof(binding); i++)
{
if (keys[i].key == key && keys[i].mod == state)
{
act = keys[i].act;
break;
}
}
if (c && c->visible)
{
switch (act)
{
case ACTION_MOVE_SPOT1:
client_raise(c);
client_spot(c, SPOT1);
break;
case ACTION_MOVE_SPOT2:
client_raise(c);
client_spot(c, SPOT2);
break;
case ACTION_MOVE_SPOT3:
client_raise(c);
client_spot(c, SPOT3);
break;
case ACTION_CYCLE:
client_cycle(c);
break;
case ACTION_OTHER:
spot_active(c->spot, c->window);
break;
case ACTION_CLOSE:
client_close(c);
break;
}
}
switch (act)
{
case ACTION_FOCUS_SPOT1:
spot_active(SPOT1, None);
break;
case ACTION_FOCUS_SPOT2:
spot_active(SPOT2, None);
break;
case ACTION_FOCUS_SPOT3:
spot_active(SPOT3, None);
break;
}
client_free(c);
ewmh_client_list();
}
void button_press(XButtonEvent *e)
{
latest = e->time;
client *c = window_client(e->subwindow);
if (c)
{
if (c->manage)
{
client_raise(c);
client_active(c);
}
}
client_free(c);
XAllowEvents(display, ReplayPointer, CurrentTime);
ewmh_client_list();
}
void client_message(XClientMessageEvent *e)
{
client *c = window_client(e->window);
if (c && c->manage)
{
if (e->message_type == atoms[_NET_ACTIVE_WINDOW])
{
client_raise(c);
client_active(c);
}
else
if (e->message_type == atoms[_NET_CLOSE_WINDOW])
{
client_close(c);
}
}
client_free(c);
}
void focus_change(XFocusChangeEvent *e)
{
client *c = window_client(e->window);
if (c && c->manage)
client_review(c);
client_free(c);
}
int main(int argc, char *argv[])
{
int i, j;
if(!(display = XOpenDisplay(0))) return 1;
screen = DefaultScreenOfDisplay(display);
scr_id = DefaultScreen(display);
root = DefaultRootWindow(display);
xerror = XSetErrorHandler(oops);
screen_x = 0;
screen_y = 0;
screen_w = WidthOfScreen(screen);
screen_h = HeightOfScreen(screen);
XSelectInput(display, root, StructureNotifyMask | SubstructureRedirectMask | SubstructureNotifyMask);
XModifierKeymap *modmap = XGetModifierMapping(display);
for (i = 0; i < 8; i++)
for (j = 0; j < (int)modmap->max_keypermod; j++)
if (modmap->modifiermap[i*modmap->max_keypermod+j] == XKeysymToKeycode(display, XK_Num_Lock))
{ NumlockMask = (1<<i); break; }
XFreeModifiermap(modmap);
for (i = 0; i < sizeof(keys)/sizeof(binding); i++)
{
XGrabKey(display, XKeysymToKeycode(display, keys[i].key), keys[i].mod, root,
True, GrabModeAsync, GrabModeAsync);
XGrabKey(display, XKeysymToKeycode(display, keys[i].key), keys[i].mod|LockMask, root,
True, GrabModeAsync, GrabModeAsync);
XGrabKey(display, XKeysymToKeycode(display, keys[i].key), keys[i].mod|NumlockMask, root,
True, GrabModeAsync, GrabModeAsync);
XGrabKey(display, XKeysymToKeycode(display, keys[i].key), keys[i].mod|LockMask|NumlockMask, root,
True, GrabModeAsync, GrabModeAsync);
}
XGrabButton(display, Button1, AnyModifier, root, True, ButtonPressMask, GrabModeSync, GrabModeSync, None, None);
XGrabButton(display, Button3, AnyModifier, root, True, ButtonPressMask, GrabModeSync, GrabModeSync, None, None);
for (i = 0; i < ATOMS; i++) atoms[i] = XInternAtom(display, atom_names[i], False);
stack wins;
memset(&wins, 0, sizeof(stack));
windows_visible(&wins);
current = None;
current_spot = 0;
for (i = 0; i < wins.depth; i++)
{
client *c = wins.clients[i];
if (c)
{
unsigned long strut[12]; memset(strut, 0, sizeof(strut));
if (window_get_cardinal_prop(c->window, atoms[_NET_WM_STRUT_PARTIAL], strut, 12)
|| window_get_cardinal_prop(c->window, atoms[_NET_WM_STRUT], strut, 4))
{
struts[LEFT] = MAX(struts[LEFT], strut[LEFT]);
struts[RIGHT] = MAX(struts[RIGHT], strut[RIGHT]);
struts[TOP] = MAX(struts[TOP], strut[TOP]);
struts[BOTTOM] = MAX(struts[BOTTOM], strut[BOTTOM]);
}
}
if (c && c->manage)
{
window_listen(c->window);
if (!current && c->visible)
{
client_active(c);
client_raise(c);
}
if (c->visible)
client_review(c);
}
}
screen_x += struts[LEFT];
screen_y += struts[TOP];
screen_w -= struts[LEFT] + struts[RIGHT];
screen_h -= struts[TOP] + struts[BOTTOM];
for (;;)
{
while (inplay.depth)
client_free(inplay.clients[--inplay.depth]);
XEvent ev;
XNextEvent(display, &ev);
switch (ev.type)
{
case CreateNotify:
create_notify(&ev.xcreatewindow);
break;
case ConfigureRequest:
configure_request(&ev.xconfigurerequest);
break;
case MapRequest:
map_request(&ev.xmap);
break;
case MapNotify:
map_notify(&ev.xmap);
break;
case UnmapNotify:
unmap_notify(&ev.xunmap);
break;
case KeyPress:
key_press(&ev.xkey);
break;
case ButtonPress:
button_press(&ev.xbutton);
break;
case ClientMessage:
client_message(&ev.xclient);
break;
case FocusIn:
case FocusOut:
focus_change(&ev.xfocus);
break;
}
}
return 0;
}

136
cerberus.h Normal file
View File

@@ -0,0 +1,136 @@
/*
MIT/X11 License
Copyright (c) 2012 Sean Plistle <sean.plistle@gmail.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.
*/
#include <X11/X.h>
#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <X11/Xatom.h>
#include <X11/Xproto.h>
#include <X11/keysym.h>
#include <X11/XKBlib.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdarg.h>
#include <unistd.h>
#include <ctype.h>
#include <err.h>
#define MAX(a, b) ((a) > (b) ? (a) : (b))
#define MIN(a, b) ((a) < (b) ? (a) : (b))
#define NEAR(a,o,b) ((b) > (a)-(o) && (b) < (a)+(o))
#define OVERLAP(a,b,c,d) (((a)==(c) && (b)==(d)) || MIN((a)+(b), (c)+(d)) - MAX((a), (c)) > 0)
#define INTERSECT(x,y,w,h,x1,y1,w1,h1) (OVERLAP((x),(w),(x1),(w1)) && OVERLAP((y),(h),(y1),(h1)))
Display *display;
Screen *screen;
int scr_id;
Window root;
Time latest;
typedef struct {
Window window;
XWindowAttributes attr;
XWMHints hints;
XSizeHints size;
Window transient_for;
Atom type;
short spot, visible, trans, manage, input, urgent;
} client;
#define STACK 64
typedef struct {
short depth;
client *clients[STACK];
Window windows[STACK];
} stack;
short current_spot = 0;
Window current;
stack inplay;
static int (*xerror)(Display *, XErrorEvent *);
enum { LEFT, RIGHT, TOP, BOTTOM };
int struts[4] = { 0, 0, 0, 0 };
short screen_x, screen_y, screen_w, screen_h;
#define ATOM_ENUM(x) x
#define ATOM_CHAR(x) #x
#define GENERAL_ATOMS(X) \
X(_MOTIF_WM_HINTS),\
X(WM_DELETE_WINDOW),\
X(WM_TAKE_FOCUS),\
X(_NET_ACTIVE_WINDOW),\
X(_NET_CLOSE_WINDOW),\
X(_NET_WM_STRUT_PARTIAL),\
X(_NET_WM_STRUT),\
X(_NET_WM_WINDOW_TYPE),\
X(_NET_WM_WINDOW_TYPE_DESKTOP),\
X(_NET_WM_WINDOW_TYPE_DOCK),\
X(_NET_WM_WINDOW_TYPE_SPLASH),\
X(_NET_WM_WINDOW_TYPE_NOTIFICATION),\
X(_NET_WM_WINDOW_TYPE_DIALOG),\
X(_NET_CLIENT_LIST_STACKING),\
X(_NET_WM_STATE),\
X(WM_PROTOCOLS)
enum { GENERAL_ATOMS(ATOM_ENUM), ATOMS };
const char *atom_names[] = { GENERAL_ATOMS(ATOM_CHAR) };
Atom atoms[ATOMS];
unsigned int NumlockMask = 0;
enum {
ACTION_NONE,
ACTION_MOVE_SPOT1,
ACTION_MOVE_SPOT2,
ACTION_MOVE_SPOT3,
ACTION_FOCUS_SPOT1,
ACTION_FOCUS_SPOT2,
ACTION_FOCUS_SPOT3,
ACTION_CYCLE,
ACTION_CLOSE,
ACTION_OTHER,
ACTION_STATE,
ACTIONS
};
typedef struct {
unsigned int mod;
KeySym key;
short act;
} binding;
enum {
SPOT_CURRENT,
SPOT1, // large left pane
SPOT2, // medium top right pane
SPOT3 // small bottom right pane
};

19
config.h Normal file
View File

@@ -0,0 +1,19 @@
#define BORDER 2
#define BORDER_BLUR "Dark Gray"
#define BORDER_FOCUS "Royal Blue"
#define SPOT_START SPOT1
binding keys[] = {
{ .mod = Mod4Mask, .key = XK_1, .act = ACTION_FOCUS_SPOT1 },
{ .mod = Mod4Mask, .key = XK_2, .act = ACTION_FOCUS_SPOT2 },
{ .mod = Mod4Mask, .key = XK_3, .act = ACTION_FOCUS_SPOT3 },
{ .mod = ShiftMask|Mod4Mask, .key = XK_1, .act = ACTION_MOVE_SPOT1 },
{ .mod = ShiftMask|Mod4Mask, .key = XK_2, .act = ACTION_MOVE_SPOT2 },
{ .mod = ShiftMask|Mod4Mask, .key = XK_3, .act = ACTION_MOVE_SPOT3 },
{ .mod = Mod4Mask, .key = XK_Tab, .act = ACTION_OTHER },
{ .mod = Mod4Mask, .key = XK_grave, .act = ACTION_CYCLE },
{ .mod = Mod4Mask, .key = XK_Escape, .act = ACTION_CLOSE },
};

36
proto.h Normal file
View File

@@ -0,0 +1,36 @@
int oops(Display *d, XErrorEvent *ee);
unsigned int color_get(const char *name);
int window_get_prop(Window w, Atom prop, Atom *type, int *items, void *buffer, int bytes);
int window_get_atom_prop(Window w, Atom atom, Atom *list, int count);
void window_set_atom_prop(Window w, Atom prop, Atom *atoms, int count);
int window_get_cardinal_prop(Window w, Atom atom, unsigned long *list, int count);
void window_set_cardinal_prop(Window w, Atom prop, unsigned long *values, int count);
void ewmh_client_list();
void ewmh_active_window(Window w);
client* window_client(Window win);
void windows_visible(stack *s);
int window_message(Window target, Window subject, Atom atom, unsigned long protocol, unsigned long mask);
void client_free(client *c);
int client_protocol(client *c, Atom protocol);
void client_close(client *c);
void client_position(client *c, int x, int y, int w, int h);
void spot_xywh(int spot, int *x, int *y, int *w, int *h);
void client_spot(client *c, int spot);
void client_cycle(client *c);
Window spot_active(int spot, Window except);
void client_stack(client *c, stack *all, stack *raise);
void client_raise(client *c);
void client_lower(client *c);
void client_active(client *c);
void window_listen(Window win);
void client_review(client *c);
void create_notify(XCreateWindowEvent *e);
void configure_request(XConfigureRequestEvent *e);
void map_request(XMapEvent *e);
void map_notify(XMapEvent *e);
void unmap_notify(XUnmapEvent *e);
void key_press(XKeyEvent *e);
void button_press(XButtonEvent *e);
void client_message(XClientMessageEvent *e);
void focus_change(XFocusChangeEvent *e);
int main(int argc, char *argv[]);