import
This commit is contained in:
13
Makefile
Normal file
13
Makefile
Normal 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
|
||||||
18
README.md
18
README.md
@@ -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
824
cerberus.c
Normal 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
136
cerberus.h
Normal 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
19
config.h
Normal 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
36
proto.h
Normal 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[]);
|
||||||
Reference in New Issue
Block a user