--- /dev/null
+#ifndef ANVIL_H
+#define ANVIL_H
+
+#include <X11/X.h>
+#include <X11/Xlib.h>
+#include <X11/keysym.h>
+#include <X11/XKBlib.h>
+#include <X11/Xatom.h>
+#include <X11/cursorfont.h>
+#include <X11/Xproto.h>
+#include <X11/Xutil.h>
+#include <X11/extensions/Xinerama.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <string.h>
+#include <assert.h>
+
+#define min(a,b) (a < b ? a : b)
+#define max(a,b) (a > b ? a : b)
+
+/* TODO: Add shortcuts to transfer windows between monitors */
+/* TODO: add floating addclient function */
+
+enum {
+ M_INIT,
+ M_IDLE,
+ M_RESIZE,
+ M_COL_RESIZE,
+ M_TILE_RESIZE,
+};
+
+enum {
+ E_NONE,
+ E_TOP,
+ E_RIGHT,
+ E_BOTTOM,
+ E_LEFT
+};
+
+typedef struct {
+ Display* disp;
+ int screen, mode, edge, start_x, start_y, last_x, last_y;
+ Window root;
+ unsigned long clr_bg, clr_bdr, clr_urgent;
+ Cursor csr_root, csr_point, csr_move;
+ XFontSet font;
+ XFontSetExtents *font_ext;
+ Visual* visual;
+ void (*eventfns[LASTEvent])(XEvent*);
+} XConf;
+
+enum {
+ F_WM_DELETE = (1 << 0),
+ F_DIALOG = (1 << 1),
+ F_FLOATING = (1 << 2),
+ F_SHADED = (1 << 3),
+};
+
+typedef struct Node {
+ struct Node* next;
+} Node;
+
+typedef struct Client {
+ struct Client* next;
+ char* name;
+ Window frame, win;
+ int flags, x, y, w, h;
+ long hint_flags, wm_flags;
+ XSizeHints hints;
+ Pixmap pixmap;
+ Visual visual;
+} Client;
+
+typedef struct Column {
+ struct Column* next;
+ int width;
+ Client* focused;
+ Client* clients;
+} Column;
+
+typedef struct Workspace {
+ struct Workspace* next;
+ Client* floating;
+ Column* columns;
+} Workspace;
+
+typedef struct Monitor {
+ struct Monitor* next;
+ int x, y, w, h, midx, midy;
+ Workspace* wspaces;
+ Workspace* cspace;
+} Monitor;
+
+typedef struct Location {
+ Monitor* monitor;
+ Workspace* workspace;
+ Column* column;
+ Client* client;
+} Location;
+
+typedef union {
+ int i;
+ char** cmd;
+} Arg;
+
+typedef struct {
+ unsigned int mod;
+ KeySym keysym;
+ void (*func)(Arg* arg);
+ Arg arg;
+} Key;
+
+#define BORDER_WIDTH 5
+#define TITLE_HEIGHT (X.font_ext->max_logical_extent.height)
+#define MIN_HEIGHT (TITLE_HEIGHT+BORDER_WIDTH)
+#define MIN_COL_FACT 0.20
+#define FONT_NAME "-*-lucida-bold-r-normal-sans-14-*-*-*-p-*-iso10646-1"
+#ifdef __APPLE__
+#define MODKEY Mod1Mask
+#else
+#define MODKEY Mod4Mask
+#endif
+#define FRAME_HEIGHT_SUM (2*BORDER_WIDTH + TITLE_HEIGHT)
+#define FRAME_WIDTH_SUM (2*BORDER_WIDTH)
+
+/* anvil.c */
+extern XConf X;
+
+/* list.c */
+int list_length(void* list);
+void* list_del(void* list, void* node);
+void* list_prev(void* list, void* curr);
+void* list_last(void* list);
+#define LIST_FOR_EACH(val,list) \
+ for (val = list; (val != NULL); val = val->next)
+#define LIST_FOR_EACH_UNTIL(val,list,cond) \
+ for (val = list; (val != NULL) && !(cond); val = val->next)
+
+/* keys.c */
+void keys_init(void);
+void keys_run(XKeyEvent* ev);
+
+/* mons.c */
+extern Monitor* Monitors;
+void mons_init(void);
+void mons_layer(Monitor* mon);
+void mons_addclient(Client* c);
+void mons_delclient(Location* loc);
+void mons_togglefloat(Location* loc);
+int mons_find(Window win, Location* loc);
+void mons_place(Client* c);
+void mons_wspace(int i);
+void mons_towspace(Client* c, int i);
+void mons_raise(Monitor* mon, Client* c);
+void mons_lower(Monitor* mon, Client* c);
+void mons_colsplit(void);
+void mons_coljoin(void);
+void mons_coladjust(Monitor* mon, Column* col, int wdiff);
+void mons_tilemove(Location* loc, int hdiff);
+void mons_activate(Window win);
+
+/* client.c */
+extern Client* Focused;
+void client_initall(void);
+Client* client_add(Window win, XWindowAttributes* attr);
+void client_draw(Client* c);
+void client_adjust(Client* c);
+void client_move(Client* c, int xdiff, int ydiff);
+void client_resize(Client* c, int xdiff, int ydiff);
+void client_close(Client* c);
+void client_focus(Client* c);
+void client_show(Client* c, int show);
+void client_readprops(Client* c);
+void client_shade(Client* c);
+void client_setshade(Client* c, int shade);
+
+/* mouse.c */
+void mouse_down(XButtonEvent* ev, Location* loc);
+void mouse_up(XButtonEvent* ev, Location* loc);
+void mouse_drag(XMotionEvent* ev, Location* loc);
+void mouse_tocorner(Client* c);
+void mouse_totitle(Client* c);
+void mouse_get(int* ptrx, int* ptry);
+
+/* tile.c */
+void monocled_add(Location* loc);
+void monocled_del(Location* loc);
+void monocled_raise(Location* loc);
+void stacked_add(Location* loc);
+void stacked_del(Location* loc);
+void stacked_set(Location* loc);
+void stacked_addheight(Location* loc, int amount);
+
+/* error.c */
+extern int (*error_default)(Display* disp, XErrorEvent* ev);
+int error_init(Display* disp, XErrorEvent* ev);
+int error_panic(Display* disp, XErrorEvent* ev);
+
+/* util.c */
+void check(intptr_t stat, char* msg);
+void die(char* str);
+void* ecalloc(size_t n, size_t sz);
+void xfree(void* p);
+Atom atom(char* str);
+void sendmsg(Window win, Atom proto, Atom type);
+
+#endif
--- /dev/null
+#include "anvil.h"
+
+Client* Focused = NULL;
+
+void client_initall(void)
+{
+ unsigned int nwins;
+ Window d1, d2, *wins = NULL;
+ XWindowAttributes attr;
+ if (XQueryTree(X.disp, X.root, &d1, &d2, &wins, &nwins))
+ {
+ for (unsigned int i = 0; i < nwins; i++)
+ {
+ if (XGetWindowAttributes(X.disp, wins[i], &attr) && !attr.override_redirect)
+ {
+ Client* c = client_add(wins[i], &attr);
+ c->flags |= F_FLOATING;
+ }
+ }
+ xfree(wins);
+ }
+}
+
+Client* client_add(Window win, XWindowAttributes* attr)
+{
+ Client* c = ecalloc(1, sizeof(Client));
+ c->win = win;
+ c->x = attr->x;
+ c->y = attr->y;
+ c->w = attr->width + FRAME_WIDTH_SUM;
+ c->h = attr->height + FRAME_HEIGHT_SUM;
+ client_readprops(c);
+
+ /* Reparent the window if applicable */
+ c->frame = XCreateSimpleWindow(X.disp, X.root, c->x, c->y, c->w, c->h, 1, X.clr_bdr, X.clr_bg);
+ XSelectInput(X.disp, c->frame,
+ ExposureMask | EnterWindowMask |
+ ButtonPressMask | ButtonReleaseMask | ButtonMotionMask |
+ SubstructureRedirectMask | SubstructureNotifyMask
+ );
+ XSetWindowAttributes wa;
+ wa.event_mask = EnterWindowMask | PropertyChangeMask | FocusChangeMask;
+ wa.do_not_propagate_mask = ButtonPressMask | ButtonReleaseMask;
+ XChangeWindowAttributes(X.disp, c->win, CWEventMask | CWDontPropagate, &wa);
+ XReparentWindow(X.disp, c->win, c->frame, BORDER_WIDTH, MIN_HEIGHT);
+
+ /* Map the window and draw the frame */
+ XAddToSaveSet(X.disp, c->win);
+ mons_addclient(c);
+ client_show(c, 1);
+ client_draw(c);
+ return c;
+}
+
+void client_draw(Client* c)
+{
+ if (c->frame)
+ {
+ XSetWindowBackground(X.disp, c->frame, ((c->wm_flags & XUrgencyHint) ? X.clr_urgent : X.clr_bg));
+ XSetWindowBorderWidth(X.disp, c->frame, 0);
+ XSetWindowBorderWidth(X.disp, c->win, 0);
+ XDefineCursor(X.disp, c->frame, X.csr_point);
+ XClearWindow(X.disp, c->frame);
+
+ /* set border color */
+ XSetForeground(X.disp, DefaultGC(X.disp, X.screen), BlackPixel(X.disp, X.screen));
+ XSetFillStyle(X.disp, DefaultGC(X.disp, X.screen), FillSolid);
+
+ /* draw outer border */
+ XDrawLine(X.disp, c->frame, DefaultGC(X.disp, X.screen), 0, 0, c->w, 0);
+ XDrawLine(X.disp, c->frame, DefaultGC(X.disp, X.screen), 0, c->h-1, c->w, c->h-1);
+ XDrawLine(X.disp, c->frame, DefaultGC(X.disp, X.screen), 0, 0, 0, c->h);
+ XDrawLine(X.disp, c->frame, DefaultGC(X.disp, X.screen), c->w-1, 0, c->w-1, c->h);
+
+ /* draw inner border */
+ if (!(c->flags & F_SHADED))
+ {
+ int bw = BORDER_WIDTH;
+ int mh = MIN_HEIGHT;
+ XDrawLine(X.disp, c->frame, DefaultGC(X.disp, X.screen),
+ bw-1, mh-1, c->w-bw, mh-1);
+ XDrawLine(X.disp, c->frame, DefaultGC(X.disp, X.screen),
+ bw-1, c->h-bw, c->w-bw, c->h-bw);
+ XDrawLine(X.disp, c->frame, DefaultGC(X.disp, X.screen),
+ bw-1, mh-1, bw-1, c->h-bw);
+ XDrawLine(X.disp, c->frame, DefaultGC(X.disp, X.screen),
+ c->w-bw, mh-1, c->w-bw, c->h-bw);
+ }
+
+ /* draw title text */
+ if (c->name) {
+ int ascent = abs(X.font_ext->max_logical_extent.y);
+ Xutf8DrawString(X.disp, c->frame, X.font,
+ DefaultGC(X.disp, X.screen),
+ BORDER_WIDTH,
+ 2 + ascent,
+ c->name, strlen(c->name));
+ }
+ }
+}
+
+void client_adjust(Client* c)
+{
+ int shaded = (c->flags & F_SHADED);
+ int floating = (c->flags & (F_DIALOG|F_FLOATING));
+ int minheight = (shaded || !floating ? MIN_HEIGHT : 3*MIN_HEIGHT);
+ if (c->w < minheight) c->w = minheight;
+ if (c->h < minheight) c->h = minheight;
+printf("XMoveResize(0x%lx, %d, %d, %d, %d)\n", c->frame, c->x, c->y, c->w, c->h);
+ XMoveResizeWindow(X.disp, c->frame, c->x, c->y, c->w, (shaded ? minheight : c->h));
+ if ( !(c->flags & F_SHADED) )
+ {
+ int child_w = c->w - FRAME_WIDTH_SUM;
+ int child_h = c->h - FRAME_HEIGHT_SUM;
+ if (child_w < 1) c->w = 1;
+ if (child_h < 1) c->h = 1;
+printf("XResize(0x%lx, %d, %d)\n", c->win, c->w, c->h);
+ XResizeWindow(X.disp, c->win, child_w, child_h);
+ }
+ mons_place(c);
+}
+
+void client_move(Client* c, int xdiff, int ydiff)
+{
+ c->x += xdiff;
+ c->y += ydiff;
+ client_adjust(c);
+}
+
+void client_resize(Client* c, int xdiff, int ydiff)
+{
+ c->w += xdiff;
+ c->h += ydiff;
+ client_adjust(c);
+}
+
+void client_close(Client* c)
+{
+ if (c->flags & F_WM_DELETE)
+ {
+ sendmsg(c->win, atom("WM_PROTOCOLS"), atom("WM_DELETE_WINDOW"));
+ }
+ else
+ {
+ XKillClient(X.disp, c->win);
+ }
+}
+
+void client_focus(Client* c)
+{
+ Client* prev = Focused;
+ Focused = c;
+ XSetInputFocus(X.disp, c->win, RevertToPointerRoot, CurrentTime);
+ client_draw(Focused);
+ if (prev)
+ {
+ client_draw(prev);
+ }
+}
+
+void client_show(Client* c, int show)
+{
+ int (*mapfn)(Display*,Window) = (show ? XMapWindow : XUnmapWindow);
+ mapfn(X.disp, c->frame);
+ mapfn(X.disp, c->win);
+}
+
+void client_readprops(Client* c)
+{
+ Atom *protos = NULL, actual_type, *wintype;
+ int format, nprotos = 0;
+ unsigned long n, extra;
+
+ /* get window title */
+ xfree(c->name);
+ c->name = NULL;
+ XGetWindowProperty(
+ X.disp, c->win, XA_WM_NAME, 0, -1, False, AnyPropertyType, &actual_type, &format, &n, &extra, (unsigned char **)&c->name);
+
+ /* check if the window is a dialog */
+ XGetWindowProperty(
+ X.disp, c->win, atom("_NET_WM_WINDOW_TYPE"), 0L, -1, False, XA_ATOM, &actual_type, &format, &n, &extra, (unsigned char **)&wintype);
+ if (wintype && *wintype == atom("_NET_WM_WINDOW_TYPE_DIALOG"))
+ {
+ c->flags |= F_DIALOG|F_FLOATING;
+ }
+ xfree(wintype);
+
+ /* get the other hints and protocols */
+ XGetWMNormalHints(X.disp, c->win, &(c->hints), &(c->hint_flags));
+ XGetWMProtocols(X.disp, c->win, &protos, &nprotos);
+ for (int i = 0; i < nprotos; i++)
+ {
+ if (protos[i] == atom("WM_DELETE_WINDOW"))
+ {
+ c->flags |= F_WM_DELETE;
+ }
+ }
+ xfree(protos);
+
+
+ XWMHints* hints = XGetWMHints(X.disp, c->win);
+ if (hints)
+ {
+ int ignored = (Focused == c ? XUrgencyHint : 0);
+ c->wm_flags = (hints->flags & ~ignored);
+ }
+ xfree(hints);
+}
+
+void client_shade(Client* c)
+{
+ client_setshade(c, ((c->flags & F_SHADED) ? 0 : 1));
+ client_adjust(c);
+}
+
+void client_setshade(Client* c, int shade)
+{
+ if (!shade && (c->flags & F_SHADED))
+ {
+ c->flags &= ~F_SHADED;
+ XMapWindow(X.disp, c->win);
+ }
+ else if (shade && !(c->flags & F_SHADED))
+ {
+ c->flags |= F_SHADED;
+ XUnmapWindow(X.disp, c->win);
+ }
+}
--- /dev/null
+#include "anvil.h"
+
+int (*error_default)(Display* disp, XErrorEvent* ev) = NULL;
+
+int error_init(Display* disp, XErrorEvent* ev)
+{
+ (void)disp, (void)ev;
+ die("another window manager is active");
+ return -1;
+}
+
+int error_panic(Display* disp, XErrorEvent* ev)
+{
+ int ignore_error = (
+ (ev->error_code == BadWindow)
+ || (ev->request_code == X_SetInputFocus && ev->error_code == BadMatch)
+ || (ev->request_code == X_ConfigureWindow && ev->error_code == BadMatch)
+ || (ev->request_code == X_ChangeWindowAttributes && ev->error_code == BadMatch)
+ );
+ if (ignore_error)
+ {
+ return 0;
+ }
+ fprintf(stderr, "anvil: fatal error: request code=%d, error code=%d\n", ev->request_code, ev->error_code);
+ return error_default(disp, ev);
+}
--- /dev/null
+#include "anvil.h"
+#include <unistd.h>
+
+static char* wmcmd[] = { "anvil", NULL };
+static char* pickexec[] = { "pickexec", NULL };
+static char* terminal[] = { "st", NULL };
+static char* locker[] = { "slock", NULL };
+static char* new_note[] = { "j", "note", NULL };
+static char* new_task[] = { "j", "task", NULL };
+static char* new_journal[] = { "j", "journal", NULL };
+static char* fetchsel[] = { "fetch", NULL, NULL };
+
+static void restart(Arg* arg)
+{
+ (void)arg;
+ if (X.disp)
+ {
+ XCloseDisplay(X.disp);
+ }
+ setsid();
+ exit(execvp(arg->cmd[0], arg->cmd));
+}
+
+static void quit(Arg* arg)
+{
+ (void)arg;
+ exit(0);
+}
+
+static void set_workspace(Arg* arg)
+{
+ mons_wspace(arg->i);
+}
+
+static void to_workspace(Arg* arg)
+{
+ mons_towspace(Focused, arg->i);
+}
+
+static void runcmd(Arg *arg)
+{
+ if (fork() == 0) {
+ if (X.disp)
+ {
+ close(ConnectionNumber(X.disp));
+ }
+ setsid();
+ exit(execvp(arg->cmd[0], arg->cmd));
+ }
+}
+
+static void killwin(Arg *arg)
+{
+ (void)arg;
+ if (Focused)
+ {
+ client_close(Focused);
+ }
+}
+
+static void coljoin(Arg *arg)
+{
+ (void)arg;
+ mons_coljoin();
+}
+
+static void colsplit(Arg *arg)
+{
+ (void)arg;
+ mons_colsplit();
+}
+
+static void togfloat(Arg *arg)
+{
+ (void)arg;
+ Location loc;
+ if (Focused && mons_find(Focused->win, &loc))
+ {
+ mons_togglefloat(&loc);
+ }
+}
+
+static Key keys[] = {
+ { MODKEY|ShiftMask, XK_r, restart, { .cmd = wmcmd } },
+ { MODKEY, XK_p, runcmd, { .cmd = pickexec } },
+ { MODKEY, XK_minus, coljoin, { 0 } },
+ { MODKEY, XK_equal, colsplit, { 0 } },
+ { MODKEY|ShiftMask, XK_q, quit, { 0 } },
+ { MODKEY|ShiftMask, XK_Return, runcmd, { .cmd = terminal } },
+ { MODKEY|ShiftMask, XK_l, runcmd, { .cmd = locker } },
+ { MODKEY|ShiftMask, XK_c, killwin, { 0 } },
+ { MODKEY|ShiftMask, XK_f, togfloat, { 0 } },
+
+ /* Jarvis-specific shortcuts */
+ { MODKEY, XK_n, runcmd, { .cmd = new_note } },
+ { MODKEY, XK_t, runcmd, { .cmd = new_task } },
+ { MODKEY, XK_j, runcmd, { .cmd = new_journal } },
+ { MODKEY, XK_f, runcmd, { .cmd = fetchsel } },
+
+ { MODKEY, XK_1, set_workspace, { .i = 0 } },
+ { MODKEY, XK_2, set_workspace, { .i = 1 } },
+ { MODKEY, XK_3, set_workspace, { .i = 2 } },
+ { MODKEY, XK_4, set_workspace, { .i = 3 } },
+ { MODKEY, XK_5, set_workspace, { .i = 4 } },
+ { MODKEY, XK_6, set_workspace, { .i = 5 } },
+ { MODKEY, XK_7, set_workspace, { .i = 6 } },
+ { MODKEY, XK_8, set_workspace, { .i = 7 } },
+ { MODKEY, XK_9, set_workspace, { .i = 8 } },
+ { MODKEY, XK_0, set_workspace, { .i = 9 } },
+
+ { MODKEY|ShiftMask, XK_1, to_workspace, { .i = 0 } },
+ { MODKEY|ShiftMask, XK_2, to_workspace, { .i = 1 } },
+ { MODKEY|ShiftMask, XK_3, to_workspace, { .i = 2 } },
+ { MODKEY|ShiftMask, XK_4, to_workspace, { .i = 3 } },
+ { MODKEY|ShiftMask, XK_5, to_workspace, { .i = 4 } },
+ { MODKEY|ShiftMask, XK_6, to_workspace, { .i = 5 } },
+ { MODKEY|ShiftMask, XK_7, to_workspace, { .i = 6 } },
+ { MODKEY|ShiftMask, XK_8, to_workspace, { .i = 7 } },
+ { MODKEY|ShiftMask, XK_9, to_workspace, { .i = 8 } },
+ { MODKEY|ShiftMask, XK_0, to_workspace, { .i = 9 } },
+};
+
+void keys_init(void)
+{
+ KeyCode code;
+ for (unsigned int i = 0; i < sizeof(keys)/sizeof(keys[0]); i++)
+ {
+ if ((code = XKeysymToKeycode(X.disp, keys[i].keysym)))
+ {
+ XGrabKey(X.disp, code, keys[i].mod, X.root, True, GrabModeAsync, GrabModeAsync);
+ }
+ }
+}
+
+void keys_run(XKeyEvent* ev)
+{
+ KeySym keysym = XkbKeycodeToKeysym(X.disp, ev->keycode, 0, 0);
+ for (unsigned int i = 0; i < sizeof(keys)/sizeof(keys[0]); i++)
+ {
+ if (keysym == keys[i].keysym && keys[i].mod == ev->state && keys[i].func)
+ {
+ keys[i].func(&(keys[i].arg));
+ break;
+ }
+ }
+}
--- /dev/null
+#include "anvil.h"
+
+int list_length(void* list)
+{
+ int length = 0;
+ for (Node* n = list; n; n = n->next, length++);
+ return length;
+}
+
+void* list_del(void* list, void* node)
+{
+ Node *l = list, *n = node;
+ if (!l)
+ {
+ return NULL;
+ }
+ else if (l == n)
+ {
+ return l->next;
+ }
+ else
+ {
+ l->next = list_del(l->next, n);
+ return l;
+ }
+}
+
+void* list_prev(void* list, void* node)
+{
+ Node *prev = list, *n = node;
+ for (; prev && prev->next != n; prev = prev->next);
+ return prev;
+}
+
+void* list_last(void* list)
+{
+ Node* tail = list;
+ for (; tail && tail->next; tail = tail->next);
+ return tail;
+}
+
-#include <liba.h>
+#include "revised.h"
+
+XConf X;
+Monitor Monitors[MAX_MONITORS] = {0};
+Client Clients[MAX_CLIENTS];
+size_t FreeClients[MAX_CLIENTS / (sizeof(size_t) * 8u)] = 0;
int main(int argc, char** argv)
{
- (void)argc;
- (void)argv;
+ printf("%d\n", MAX_CLIENTS);
+ printf("%f mb\n", (float)sizeof(Clients) / 1024.0 / 1024.0);
+ printf("%f mb\n", (float)sizeof(FreeClients) / 1024.0 / 1024.0);
+ printf("%lu + %lu = %lu (%f mb)\n",
+ sizeof(X),
+ sizeof(Monitors),
+ sizeof(X) + sizeof(Monitors),
+ ((float)sizeof(X) + (float)sizeof(Monitors)) / 1024.0 / 1024.0
+ );
return 0;
}
--- /dev/null
+#include "anvil.h"
+#include <math.h>
+
+int PtrX = 0, PtrY = 0;
+static Monitor* pickmon(void);
+static Column* pickcol(Column* cols, int relx);
+static Workspace* pickws(Monitor* mon, int wsid);
+static void client_visibility(Workspace* wspace, int show);
+static Client* client_find(Client* clients, Window win);
+static void add_client(Monitor* mon, Client* c, int ptrx);
+static void remove_client(Location* loc, Client* c);
+static void adjust_all(Column* col, int xoff);
+static void change_wspace(Monitor* mon, Workspace* wspace);
+
+Monitor* Monitors = NULL;
+
+static int min_col_width(Monitor* mon)
+{
+ return (int)(mon->w * MIN_COL_FACT);
+}
+
+void mons_init(void)
+{
+ int nmons;
+ check( XineramaIsActive(X.disp), "Xinerama extension is required");
+ XineramaScreenInfo* mons = XineramaQueryScreens(X.disp, &nmons);
+ for (int i = 0; i < nmons; i++)
+ {
+ Monitor* m = ecalloc(1, sizeof(Monitor));
+ m->x = mons[i].x_org;
+ m->y = mons[i].y_org;
+ m->w = mons[i].width;
+ m->h = mons[i].height;
+ m->midx = m->x + m->w/2;
+ m->midy = m->y + m->h/2;
+ m->next = Monitors;
+ Monitors = m;
+ for (int i = 0; i < 10; i++)
+ {
+ Column* col = ecalloc(1, sizeof(Column));
+ col->width = m->w;
+ Workspace* wspace = ecalloc(1, sizeof(Workspace));
+ wspace->columns = col;
+ wspace->next = m->wspaces;
+ m->wspaces = wspace;
+ }
+ m->cspace = m->wspaces;
+ }
+ xfree(mons);
+
+ Monitor* m = Monitors;
+ while (m)
+ {
+ printf("x: %d y: %d w: %d h: %d\n", m->x, m->y, m->w, m->h);
+ Workspace* w = m->wspaces;
+ while (w)
+ {
+ printf(" wspace %p\n", (void*)w);
+ Column* c = w->columns;
+ while (c)
+ {
+ printf(" col %p %d\n", (void*)c, c->width);
+ c = c->next;
+ }
+ w = w->next;
+ }
+ m = m->next;
+ }
+
+}
+
+void mons_layer(Monitor* mon)
+{
+ int nwins = 0;
+ Window* wins = NULL;
+ Client* c;
+ Column* col;
+ LIST_FOR_EACH(c, mon->cspace->floating)
+ {
+ wins = realloc(wins, ++nwins * sizeof(Window));
+ wins[nwins-1] = c->frame;
+ }
+ /* add in the monocled windows first */
+ LIST_FOR_EACH(col, mon->cspace->columns)
+ {
+ if (col->focused)
+ {
+ wins = realloc(wins, ++nwins * sizeof(Window));
+ wins[nwins-1] = col->focused->frame;
+ }
+ }
+ /* now lower all of the tiled windows */
+ LIST_FOR_EACH(col, mon->cspace->columns)
+ {
+ LIST_FOR_EACH(c, col->clients)
+ {
+ if (col->focused != c)
+ {
+ wins = realloc(wins, ++nwins * sizeof(Window));
+ wins[nwins-1] = c->frame;
+ }
+ }
+ }
+ if (nwins)
+ {
+ XRestackWindows(X.disp, wins, nwins);
+ }
+ xfree(wins);
+}
+
+/* adds a new client to the most appropriate monitor */
+void mons_addclient(Client* c)
+{
+ Monitor* mon = pickmon();
+ add_client(mon, c, PtrX);
+ mouse_totitle(c);
+}
+
+void mons_delclient(Location* loc)
+{
+ if (Focused == loc->client)
+ {
+ Focused = NULL;
+ }
+ remove_client(loc, loc->client);
+ xfree(loc->client->name);
+ XDestroyWindow(X.disp, loc->client->frame);
+ free(loc->client);
+}
+
+void mons_togglefloat(Location* loc)
+{
+ remove_client(loc, loc->client);
+ if (loc->client->flags & (F_FLOATING))
+ {
+ loc->client->flags &= ~F_FLOATING;
+ }
+ else
+ {
+ loc->client->flags |= F_FLOATING;
+ }
+ mons_addclient(loc->client);
+}
+
+int mons_find(Window win, Location* loc)
+{
+ Monitor* mon;
+ Workspace* wspace;
+ LIST_FOR_EACH(mon, Monitors)
+ {
+ LIST_FOR_EACH(wspace, mon->wspaces)
+ {
+ Column* column = NULL;
+ Client* client = client_find(wspace->floating, win);
+ if (!client)
+ {
+ LIST_FOR_EACH(column, wspace->columns)
+ {
+ if ( (client = client_find(column->clients, win)) )
+ {
+ break;
+ }
+ }
+ }
+ /* return the client location if we found it */
+ if (client)
+ {
+ loc->monitor = mon;
+ loc->workspace = wspace;
+ loc->column = column;
+ loc->client = client;
+ return 1;
+ }
+ }
+ }
+ return 0;
+}
+
+/* find the best monitor to own the window by calculating the overlap */
+void mons_place(Client* c)
+{
+ Location loc;
+ int cleft = (c->x - BORDER_WIDTH);
+ int cright = (c->x + c->w + 2*BORDER_WIDTH);
+ int ctop = (c->y - BORDER_WIDTH);
+ int cbot = (c->y + c->h + 2*BORDER_WIDTH + TITLE_HEIGHT);
+ if (mons_find(c->win, &loc) && !loc.column)
+ {
+ int maxarea = 0;
+ Monitor *mon = NULL, *closest = NULL;
+ LIST_FOR_EACH(mon, Monitors)
+ {
+ int left = max(cleft, mon->x);
+ int right = min(cright, mon->x + mon->w);
+ int top = max(ctop, mon->y);
+ int bot = min(cbot, mon->y + mon->h);
+ if (left < right && top < bot)
+ {
+ int area = (right - left) * (bot - top);
+ if (area > maxarea)
+ {
+ maxarea = area;
+ closest = mon;
+ }
+ }
+ }
+ /* if we changed monitors, make sure we update accordingly */
+ if (closest && loc.monitor != closest)
+ {
+ loc.workspace->floating = list_del(loc.workspace->floating, c);
+ c->next = closest->cspace->floating;
+ closest->cspace->floating = c;
+ }
+ }
+}
+
+void mons_wspace(int wsid)
+{
+ Monitor* mon = pickmon();
+ change_wspace(mon, pickws(mon, wsid));
+}
+
+void mons_towspace(Client* c, int wsid)
+{
+ Location loc = {0};
+ if (mons_find(c->win, &loc))
+ {
+ mouse_get(&PtrX, &PtrY);
+ Workspace* wspace = pickws(loc.monitor, wsid);
+ if (wspace != loc.workspace)
+ {
+ remove_client(&loc, c);
+ client_show(c, 0);
+ Workspace* prev = loc.monitor->cspace;
+ loc.monitor->cspace = wspace;
+ add_client(loc.monitor, c, PtrX);
+ loc.monitor->cspace = prev;
+ }
+ }
+}
+
+void mons_raise(Monitor* mon, Client* c)
+{
+ mon->cspace->floating = list_del(mon->cspace->floating, c);
+ c->next = mon->cspace->floating;
+ mon->cspace->floating = c;
+ mons_layer(mon);
+}
+
+void mons_lower(Monitor* mon, Client* c)
+{
+ mon->cspace->floating = list_del(mon->cspace->floating, c);
+ c->next = NULL;
+ if (mon->cspace->floating)
+ {
+ Client* curr = list_last(mon->cspace->floating);
+ curr->next = c;
+ }
+ else
+ {
+ mon->cspace->floating = c;
+ }
+ mons_layer(mon);
+}
+
+void mons_colsplit(void)
+{
+ Monitor* mon = pickmon();
+ Column* col = pickcol(mon->cspace->columns, PtrX - mon->x);
+ int next_empty = (col->next && !col->next->clients);
+ if (col->clients && !next_empty && col->width >= 2*min_col_width(mon))
+ {
+ Column* newcol = ecalloc(1, sizeof(Column));
+ newcol->width = col->width/2;
+ col->width -= newcol->width;
+ newcol->next = col->next;
+ col->next = newcol;
+ adjust_all(col, 0);
+ }
+}
+
+void mons_coljoin(void)
+{
+ Monitor* mon = pickmon();
+ Column* dest = pickcol(mon->cspace->columns, PtrX - mon->x);
+ Column* dead = dest->next;
+ if (!dead)
+ {
+ dead = dest;
+ dest = list_prev(mon->cspace->columns, dead);
+ }
+
+ if (dest && dead)
+ {
+ /* add the clients to the current col */
+ for (Client* c = dead->clients; c;)
+ {
+ Client* next = c->next;
+ if (dest->focused)
+ {
+
+ monocled_add(&(Location){mon, NULL, dest, c});
+ }
+ else
+ {
+ stacked_add(&(Location){mon, NULL, dest, c});
+ }
+ c = next;
+ }
+ dest->next = dead->next;
+ dest->width += dead->width;
+ free(dead);
+ adjust_all(dest, 0);
+ }
+}
+
+void mons_coladjust(Monitor* mon, Column* col, int wdiff)
+{
+ Column* neighbor = col->next;
+ int minwidth = min_col_width(mon);
+ if (X.edge == E_LEFT && mon->cspace->columns != col)
+ {
+ for (neighbor = mon->cspace->columns; neighbor && neighbor->next != col; neighbor = neighbor->next);
+ int minadj = -(neighbor->width - minwidth);
+ int maxadj = (col->width - minwidth);
+ wdiff = min(max(minadj, wdiff), maxadj);
+ neighbor->width += wdiff;
+ col->width -= wdiff;
+ adjust_all(col, wdiff);
+ adjust_all(neighbor, 0);
+ }
+ else if (X.edge == E_RIGHT && col->next)
+ {
+ int minadj = -(col->width - minwidth);
+ int maxadj = (neighbor->width - minwidth);
+ wdiff = min(max(minadj, wdiff), maxadj);
+ col->width += wdiff;
+ neighbor->width -= wdiff;
+ adjust_all(col, 0);
+ adjust_all(neighbor, wdiff);
+ }
+ else
+ {
+ /* invalid edge */
+ }
+}
+
+void mons_tilemove(Location* loc, int hdiff)
+{
+ Monitor* mon = pickmon();
+ Column* col = pickcol(mon->cspace->columns, PtrX - mon->x);
+ if (loc->monitor != mon || loc->column != col)
+ {
+ remove_client(loc, loc->client);
+ client_setshade(loc->client, 0);
+ loc->monitor = mon;
+ loc->column = col;
+ if (col->focused)
+ {
+ monocled_add(loc);
+ }
+ else
+ {
+ stacked_add(loc);
+ }
+ }
+ else
+ {
+ stacked_addheight(loc, hdiff);
+ }
+ mouse_totitle(loc->client);
+}
+
+void mons_activate(Window win)
+{
+ Location loc;
+ if (mons_find(win, &loc))
+ {
+ change_wspace(loc.monitor, loc.workspace);
+ if (loc.column && loc.column->focused)
+ {
+ monocled_raise(&loc);
+ mons_layer(loc.monitor);
+ }
+ else if (!loc.column)
+ {
+ mons_raise(loc.monitor, loc.client);
+ }
+ }
+}
+
+static Monitor* pickmon(void)
+{
+ mouse_get(&PtrX, &PtrY);
+ Monitor* mon;
+ LIST_FOR_EACH_UNTIL(mon, Monitors,
+ (mon->x <= PtrX && PtrX < mon->x+mon->w) &&
+ (mon->y <= PtrY && PtrY < mon->y+mon->h)
+ );
+ mon = (mon ? mon : Monitors);
+ return mon;
+}
+
+static Column* pickcol(Column* cols, int relx)
+{
+ Column* col;
+ int left = 0, right = 0;
+ printf("cols %p\n", cols);
+ LIST_FOR_EACH(col, cols)
+ {
+ printf("curr = %p\n", col);
+ left = right, right += col->width;
+ printf("%d <= %d < %d\n", left, relx, right);
+ if (left <= relx && relx < right)
+ {
+ break; /* we found the column holding the mouse */
+ }
+ }
+ printf("%p\n", (void*)col);
+// if (!col) col = cols;
+ return col;
+}
+
+static Workspace* pickws(Monitor* mon, int wsid)
+{
+ Workspace* wspace;
+ int i = 0;
+ LIST_FOR_EACH_UNTIL(wspace, mon->wspaces, (i++ == wsid));
+ return wspace;
+}
+
+static void client_visibility(Workspace* wspace, int show)
+{
+ Column* col;
+ Client* client;
+ LIST_FOR_EACH(client, wspace->floating)
+ {
+ client_show(client, show);
+ }
+ LIST_FOR_EACH(col, wspace->columns)
+ {
+ LIST_FOR_EACH(client, col->clients)
+ {
+ client_show(client, show);
+ }
+ }
+}
+
+static Client* client_find(Client* clients, Window win)
+{
+ Client* client;
+ LIST_FOR_EACH_UNTIL(client, clients,
+ (client->frame == win || client->win == win));
+ return client;
+}
+
+static void add_client(Monitor* mon, Client* c, int ptrx)
+{
+ if (X.mode == M_INIT || c->flags & (F_DIALOG|F_FLOATING))
+ {
+ c->next = mon->cspace->floating;
+ mon->cspace->floating = c;
+ c->x = mon->midx - c->w/2;
+ c->y = mon->midy - c->h/2;
+ client_adjust(c);
+ }
+ else
+ {
+ /* find first empty column, and fill that first */
+ Column* col = NULL;
+ LIST_FOR_EACH_UNTIL(col, mon->cspace->columns, col->clients == NULL);
+ if (!col)
+ {
+ /* otherwise pick the column to the right or the current column */
+ col = pickcol(mon->cspace->columns, ptrx - mon->x);
+ if (col->next)
+ {
+ col = col->next;
+ }
+ }
+ /* add in monocled or stacked mode */
+ if (col->focused)
+ {
+ monocled_add(&(Location){mon, NULL, col, c});
+ }
+ else
+ {
+ stacked_add(&(Location){mon, NULL, col, c});
+ }
+ }
+ mons_layer(mon);
+}
+
+static void remove_client(Location* loc, Client* c)
+{
+ if (!loc->column)
+ {
+ loc->workspace->floating = list_del(loc->workspace->floating, c);
+ }
+ else if (loc->column->focused)
+ {
+ monocled_del(loc);
+ }
+ else
+ {
+ stacked_del(loc);
+ }
+}
+
+static void adjust_all(Column* col, int xoff)
+{
+ Client* c;
+ LIST_FOR_EACH(c, col->clients)
+ {
+ c->x += xoff;
+ c->w = col->width;
+ client_adjust(c);
+ }
+}
+
+static void change_wspace(Monitor* mon, Workspace* wspace)
+{
+ if (mon->cspace != wspace)
+ {
+ client_visibility(mon->cspace, 0);
+ client_visibility(wspace, 1);
+ mon->cspace = wspace;
+ }
+}
--- /dev/null
+#include "anvil.h"
+
+static XEvent* Current = NULL;
+
+static inline int PRESSED(int mods, int btn)
+{
+ return ((mods & (1 << (btn + 7))) == (1 << (btn + 7)));
+}
+
+static inline int FLAGS_SET(int state, int flags)
+{
+ return ((!flags && !state) || ((state & flags) == flags));
+}
+
+typedef struct {
+ int mods;
+ int btn;
+ int type;
+ void(*func)(Location* loc);
+} MouseAct;
+
+static void resize_frame(Location* loc)
+{
+ if (Current->xbutton.y > MIN_HEIGHT)
+ {
+ X.mode = M_RESIZE;
+ mouse_tocorner(loc->client);
+ }
+}
+
+static void toggle_float(Location* loc)
+{
+ mons_togglefloat(loc);
+}
+
+static void close_client(Location* loc)
+{
+ client_close(loc->client);
+}
+
+static void shade_client(Location* loc)
+{
+ client_shade(loc->client);
+}
+
+static void lower_client(Location* loc)
+{
+ mons_lower(loc->monitor, loc->client);
+}
+
+static void raise_client(Location* loc)
+{
+ mons_raise(loc->monitor, loc->client);
+}
+
+static void stack_clients(Location* loc)
+{
+ stacked_set(loc);
+}
+
+static void rotate_client(Location* loc)
+{
+ Client *tail, *client = loc->column->clients;
+ if (client->next)
+ {
+ loc->column->clients = client->next;
+ tail = list_last(loc->column->clients);
+ client->next = NULL;
+ tail->next = client;
+ monocled_raise(loc);
+ }
+}
+
+static void reposition_tile(Location* loc)
+{
+ (void)loc;
+ X.mode = (X.edge == E_TOP ? M_TILE_RESIZE : M_COL_RESIZE);
+}
+
+static void monocle_client(Location* loc)
+{
+ monocled_raise(loc);
+}
+
+MouseAct Floating[] = {
+ { 0, Button1, ButtonPress, resize_frame },
+ { MODKEY|ShiftMask, Button2, ButtonPress, toggle_float },
+ { MODKEY, Button2, ButtonPress, close_client },
+ { 0, Button3, ButtonPress, shade_client },
+ { 0, Button4, ButtonPress, lower_client },
+ { 0, Button5, ButtonPress, raise_client }
+};
+
+MouseAct Monocled[] = {
+ { 0, Button1, ButtonPress, stack_clients },
+ { MODKEY|ShiftMask, Button2, ButtonPress, toggle_float },
+ { MODKEY, Button2, ButtonPress, close_client },
+ { 0, Button3, ButtonPress, rotate_client },
+};
+
+MouseAct Stacked[] = {
+ { 0, Button1, ButtonPress, reposition_tile },
+ { MODKEY|ShiftMask, Button2, ButtonPress, toggle_float },
+ { MODKEY, Button2, ButtonPress, close_client },
+ { 0, Button2, ButtonPress, stack_clients },
+ { 0, Button3, ButtonPress, monocle_client },
+};
+
+static void process(XButtonEvent* ev, Location* loc, MouseAct* actions, int nactions)
+{
+ for (int i = 0; i < nactions; i++)
+ {
+ MouseAct* act = &actions[i];
+ int match = (
+ (ev->type == act->type) &&
+ ((int)ev->button == act->btn) &&
+ FLAGS_SET(ev->state, act->mods)
+ );
+ if (match)
+ {
+ Current = (XEvent*)ev;
+ act->func(loc);
+ break;
+ }
+ }
+}
+
+void mouse_down(XButtonEvent* ev, Location* loc)
+{
+ if (!loc->column)
+ {
+ process(ev, loc, Floating, sizeof(Floating)/sizeof(Floating[0]));
+ }
+ else if (loc->column->focused)
+ {
+ process(ev, loc, Monocled, sizeof(Monocled)/sizeof(Monocled[0]));
+ }
+ else
+ {
+ process(ev, loc, Stacked, sizeof(Stacked)/sizeof(Stacked[0]));
+ }
+}
+
+static void float_drag(XMotionEvent* ev, Location* loc)
+{
+ if (PRESSED(ev->state, Button1))
+ {
+ if (X.mode != M_RESIZE)
+ {
+ client_move(loc->client, ev->x_root - X.last_x, ev->y_root - X.last_y);
+ }
+ else
+ {
+ client_resize(loc->client, ev->x_root - X.last_x, ev->y_root - X.last_y);
+ }
+ }
+}
+
+void mouse_up(XButtonEvent* ev, Location* loc)
+{
+ if (X.mode == M_TILE_RESIZE)
+ {
+ mons_tilemove(loc, ev->y_root - X.start_y);
+ }
+ else if (X.mode == M_COL_RESIZE)
+ {
+ mons_coladjust(loc->monitor, loc->column, ev->x_root - X.start_x);
+ }
+ else
+ {
+ /* nothing to do here */
+ }
+ X.mode = M_IDLE;
+}
+
+void mouse_drag(XMotionEvent* ev, Location* loc)
+{
+ if (!loc->column)
+ {
+ float_drag(ev, loc);
+ }
+}
+
+void mouse_tocorner(Client* c)
+{
+ int new_w = c->w - BORDER_WIDTH/2;
+ int new_h = c->h - BORDER_WIDTH/2;
+ XWarpPointer(X.disp, None, c->frame, 0, 0, 0, 0, new_w, new_h);
+ X.last_x = c->x + new_w;
+ X.last_y = c->y + new_h;
+}
+
+void mouse_totitle(Client* c)
+{
+ XWarpPointer(X.disp, None, X.root, 0, 0, 0, 0, (c->x + c->w/2), c->y + (MIN_HEIGHT/2));
+}
+
+void mouse_get(int* ptrx, int* ptry)
+{
+ Window root = 0, child = 0;
+ int winx = 0, winy = 0, mask = 0;
+ XQueryPointer(X.disp, X.root, &root, &child, ptrx, ptry, &winx, &winy, (unsigned int*)&mask);
+}
+
--- /dev/null
+#include <X11/X.h>
+#include <X11/Xlib.h>
+#include <X11/keysym.h>
+#include <X11/XKBlib.h>
+#include <X11/Xatom.h>
+#include <X11/cursorfont.h>
+#include <X11/Xproto.h>
+#include <X11/Xutil.h>
+#include <X11/extensions/Xinerama.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <string.h>
+#include <assert.h>
+
+typedef struct {
+ Display* disp;
+ int screen, mode, edge, start_x, start_y, last_x, last_y;
+ Window root;
+ unsigned long clr_bg, clr_bdr, clr_urgent;
+ Cursor csr_root, csr_point, csr_move;
+ XFontSet font;
+ XFontSetExtents *font_ext;
+ Visual* visual;
+ void (*eventfns[LASTEvent])(XEvent*);
+} XConf;
+
+#define MIN_COLUMN_FACTOR 0.10
+#define MAX_COLUMNS 10
+#define MAX_COLUMN_CLIENTS 128
+#define MAX_FLOATING_CLIENTS 128
+#define NUM_WORKSPACES 10
+#define MAX_MONITORS 4
+
+/* calculate the size of the client pool */
+#define MAX_WSPACE_CLIENTS (MAX_COLUMNS * MAX_COLUMN_CLIENTS + MAX_FLOATING_CLIENTS)
+#define MAX_MONITOR_CLIENTS (NUM_WORKSPACES * MAX_WSPACE_CLIENTS)
+#define MAX_CLIENTS (MAX_MONITORS * MAX_MONITOR_CLIENTS)
+
+typedef struct Client {
+ char* name;
+ Window frame, win;
+ int flags, x, y, w, h;
+ long hint_flags, wm_flags;
+ XSizeHints hints;
+ Pixmap pixmap;
+ Visual visual;
+} Client;
+
+typedef struct Column {
+ int width, focused;
+ Client* clients[MAX_COLUMN_CLIENTS];
+} Column;
+
+typedef struct Workspace {
+ Client* floating[MAX_FLOATING_CLIENTS];
+ Column columns[MAX_COLUMNS];
+} Workspace;
+
+typedef struct Monitor {
+ int x, y, w, h, midx, midy, cspace;
+ Workspace wspaces[NUM_WORKSPACES];
+} Monitor;
+
+typedef struct Location {
+ Monitor* monitor;
+ Workspace* workspace;
+ Column* column;
+ Client* client;
+} Location;
--- /dev/null
+#include "anvil.h"
+
+static void coldims(Monitor *mon, Column *col, int *x, int *y, int *w, int *h)
+{
+ Column* c;
+ *x = mon->x, *y = mon->y, *w = col->width, *h = mon->h;
+ LIST_FOR_EACH_UNTIL(c, mon->cspace->columns, (c == col))
+ {
+ *x += c->width;
+ }
+}
+
+void monocled_add(Location* loc)
+{
+ Client* c = loc->client;
+ coldims(loc->monitor, loc->column, &(c->x), &(c->y), &(c->w), &(c->h));
+ client_setshade(c, 0);
+ client_adjust(c);
+ c->next = loc->column->clients;
+ loc->column->clients = c;
+ loc->column->focused = c;
+}
+
+void monocled_del(Location* loc)
+{
+ Client* c = loc->client;
+ loc->column->clients = list_del(loc->column->clients, c);
+ loc->column->focused = loc->column->clients;
+ c = loc->column->clients;
+ if (c)
+ {
+ coldims(loc->monitor, loc->column, &(c->x), &(c->y), &(c->w), &(c->h));
+ client_setshade(c, 0);
+ client_adjust(c);
+ }
+}
+
+void monocled_raise(Location* loc)
+{
+ loc->column->clients = list_del(loc->column->clients, loc->client);
+ loc->client->next = loc->column->clients;
+ loc->column->clients = loc->client;
+ loc->column->focused = loc->client;
+ coldims(loc->monitor, loc->column,
+ &(loc->client->x), &(loc->client->y), &(loc->client->w), &(loc->client->h));
+ client_setshade(loc->client, 0);
+ client_adjust(loc->client);
+ mons_layer(loc->monitor);
+}
+
+void monocled_set(Location* loc)
+{
+ loc->column->clients = list_del(loc->column->clients, loc->client);
+ loc->client->next = loc->column->clients;
+ loc->column->clients = loc->client;
+ loc->column->focused = loc->client;
+ coldims(loc->monitor, loc->column,
+ &(loc->client->x), &(loc->client->y), &(loc->client->w), &(loc->client->h));
+ client_setshade(loc->client, 0);
+ client_adjust(loc->client);
+ mons_layer(loc->monitor);
+}
+
+void stacked_add(Location* loc)
+{
+ Client* c = loc->client;
+ coldims(loc->monitor, loc->column, &(c->x), &(c->y), &(c->w), &(c->h));
+ if (loc->column->clients)
+ {
+ Client *cl, *max = loc->column->clients;
+ LIST_FOR_EACH(cl, max)
+ {
+ if (cl->h > max->h)
+ {
+ max = cl;
+ }
+ }
+ if (max->h < 3*MIN_HEIGHT)
+ {
+ if (loc->column->next)
+ {
+ stacked_add(loc);
+ }
+ else
+ {
+ c->next = loc->monitor->cspace->floating;
+ loc->monitor->cspace->floating = c;
+ c->x = loc->monitor->midx - c->w/2;
+ c->y = loc->monitor->midy - c->h/2;
+ client_adjust(c);
+ }
+ }
+ else
+ {
+ c->h = max->h/2;
+ c->y = max->y + c->h;
+ c->w = max->w;
+ max->h -= max->h/2;
+ c->next = max->next;
+ max->next = c;
+ client_adjust(c);
+ client_adjust(max);
+ }
+ }
+ else
+ {
+ c->next = loc->column->clients;
+ loc->column->clients = c;
+ client_adjust(c);
+ }
+}
+
+void stacked_del(Location* loc)
+{
+ Client* c = loc->client;
+ if (loc->column->clients == c)
+ {
+ loc->column->clients = c->next;
+ if (loc->column->clients)
+ {
+ loc->column->clients->h += loc->column->clients->y - loc->monitor->y;
+ loc->column->clients->y = loc->monitor->y;
+ client_setshade(loc->column->clients, 0);
+ client_adjust(loc->column->clients);
+ }
+ }
+ else
+ {
+ Client* prev = list_prev(loc->column->clients, c);
+ prev->next = c->next;
+ prev->h += c->h;
+ client_setshade(prev, 0);
+ client_adjust(prev);
+ }
+}
+
+void stacked_set(Location* loc)
+{
+ Client *curr, *c = loc->client;
+ loc->column->focused = NULL;
+ int starty = loc->monitor->y;
+ /* stack the windows before the target */
+ LIST_FOR_EACH_UNTIL(curr, loc->column->clients, curr == c)
+ {
+ curr->y = starty;
+ curr->h = MIN_HEIGHT;
+ client_setshade(curr, 1);
+ client_adjust(curr);
+ starty += MIN_HEIGHT;
+ }
+ /* expand the target window */
+ int nclients = list_length(curr->next);
+ curr->y = starty;
+ curr->h = (loc->monitor->h - starty) - (nclients * MIN_HEIGHT);
+ client_setshade(c, 0);
+ client_adjust(curr);
+ mouse_totitle(curr);
+ starty = (curr->y + curr->h - 1);
+ /* stack the windows after the target */
+ LIST_FOR_EACH(curr, curr->next)
+ {
+ curr->y = starty;
+ curr->h = MIN_HEIGHT;
+ client_setshade(curr, 1);
+ client_adjust(curr);
+ starty += MIN_HEIGHT;
+ }
+}
+
+void stacked_addheight(Location* loc, int amount)
+{
+ Client* c = loc->client;
+ Client* prev = list_prev(loc->column->clients, c);
+ if (prev)
+ {
+ amount = (abs(amount) < BORDER_WIDTH ? min((int)(-c->h * 0.25), -2*MIN_HEIGHT) : amount);
+ int miny = (prev->y + MIN_HEIGHT);
+ int maxy = min((loc->monitor->y + loc->monitor->h) , (c->y + c->h)) - MIN_HEIGHT;
+ c->y = max(miny, min(maxy, c->y + amount));
+ prev->h = c->y - prev->y;
+ c->h = (c->next ? c->next->y : loc->monitor->y + loc->monitor->h) - c->y;
+ client_setshade(prev, (prev->h <= MIN_HEIGHT));
+ client_setshade(c, (c->h <= MIN_HEIGHT));
+ client_adjust(prev);
+ client_adjust(c);
+ }
+}
--- /dev/null
+#include "anvil.h"
+
+void check(intptr_t stat, char* msg)
+{
+ if (!stat)
+ {
+ die(msg);
+ }
+}
+
+void die(char* str)
+{
+ printf("anvil: fatal error: %s\n", str);
+ exit(1);
+}
+
+void* ecalloc(size_t n, size_t sz)
+{
+ void* p = calloc(n, sz);
+ if (!p)
+ {
+ die("out of memory");
+ }
+ return p;
+}
+
+void xfree(void* p)
+{
+ if (p)
+ {
+ XFree(p);
+ }
+}
+
+Atom atom(char* str)
+{
+ return XInternAtom(X.disp, str, False);
+}
+
+void sendmsg(Window win, Atom proto, Atom type)
+{
+ XEvent ev;
+ memset(&ev, 0, sizeof(ev));
+ ev.xclient.type = ClientMessage;
+ ev.xclient.window = win;
+ ev.xclient.message_type = proto;
+ ev.xclient.format = 32;
+ ev.xclient.data.l[0] = type;
+ ev.xclient.data.l[1] = CurrentTime;
+ XSendEvent(X.disp, win, False, 0, &ev);
+}
--- /dev/null
+#include "anvil.h"
+#include <sys/wait.h>
+
+XConf X = {0};
+
+static void check_for_wm(void)
+{
+ error_default = XSetErrorHandler(error_init);
+ XSelectInput(X.disp, X.root, SubstructureRedirectMask);
+ XSync(X.disp, False);
+ XSetErrorHandler(error_panic);
+ XSync(X.disp, False);
+}
+
+static void xbtnpress(XEvent* e)
+{
+ XButtonEvent* ev = &(e->xbutton);
+ XGrabPointer(X.disp, ev->window, False,
+ ButtonPressMask | ButtonReleaseMask | ButtonMotionMask,
+ GrabModeAsync, GrabModeAsync, None, X.csr_move, CurrentTime);
+ //printf("BTN_DN(w: 0x%lx s: %d x: %d y: %d rx: %d ry: %d)\n", ev->window, ev->state, ev->x, ev->y, ev->x_root, ev->y_root);
+ X.start_x = ev->x_root, X.start_y = ev->y_root;
+ X.last_x = ev->x_root, X.last_y = ev->y_root;
+ Location loc = {0};
+ if (mons_find(ev->window, &loc) && (loc.client->frame == ev->window))
+ {
+ Client* c = loc.client;
+ if (ev->y < MIN_HEIGHT)
+ {
+ X.edge = E_TOP;
+ }
+ else if (ev->y_root > (c->y + c->h - BORDER_WIDTH))
+ {
+ X.edge = E_BOTTOM;
+ }
+ else if (ev->x < BORDER_WIDTH)
+ {
+ X.edge = E_LEFT;
+ }
+ else if (ev->x_root > (c->x + c->w - BORDER_WIDTH))
+ {
+ X.edge = E_RIGHT;
+ }
+ else
+ {
+ X.edge = E_NONE;
+ }
+ mouse_down(ev, &loc);
+ }
+}
+
+static void xbtnrelease(XEvent* e)
+{
+ XButtonEvent* ev = &(e->xbutton);
+ //printf("BTN_UP(w: 0x%lx x: %d y: %d rx: %d ry: %d)\n", ev->window, ev->x, ev->y, ev->x_root, ev->y_root);
+ Location loc = {0};
+ if (mons_find(ev->window, &loc) && (loc.client->frame == ev->window))
+ {
+ mouse_up(ev, &loc);
+ }
+ X.mode = M_IDLE;
+ XUngrabPointer(X.disp, CurrentTime);
+}
+
+static void xbtnmotion(XEvent* e)
+{
+ /* make sure we get just the latest event */
+ XMotionEvent *ev = &e->xmotion;
+ //printf("BTN_MV(w: 0x%lx x: %d y: %d rx: %d ry: %d)\n", ev->window, ev->x, ev->y, ev->x_root, ev->y_root);
+ while (XCheckTypedWindowEvent(X.disp, ev->window, ev->type, e));
+ Location loc = {0};
+ if (mons_find(ev->window, &loc) && (loc.client->frame == ev->window))
+ {
+ mouse_drag(ev, &loc);
+ }
+ X.last_x = ev->x_root, X.last_y = ev->y_root;
+}
+
+static void xconfignotify(XEvent* e)
+{
+ XConfigureEvent* ev = &(e->xconfigure);
+ if (ev->window == X.root)
+ {
+ puts("root window resized");
+ }
+}
+
+static void xconfigrequest(XEvent* e)
+{
+ XConfigureRequestEvent* ev = &(e->xconfigurerequest);
+ printf("CONF(w: 0x%lx x: %d y: %d w: %d h: %d)\n", ev->window, ev->x, ev->y, ev->width, ev->height);
+ XWindowChanges wc;
+ wc.x = ev->x;
+ wc.y = ev->y;
+ wc.width = ev->width;
+ wc.height = ev->height;
+ wc.border_width = ev->border_width;
+ wc.sibling = ev->above;
+ wc.stack_mode = ev->detail;
+ Location loc = {0};
+ if (mons_find(ev->window, &loc))
+ {
+ /* only allow floating clients to resize on their own */
+ if ((loc.client->win == ev->window) && !loc.column)
+ {
+ loc.client->w = wc.width + FRAME_WIDTH_SUM;
+ loc.client->h = wc.height + FRAME_HEIGHT_SUM;
+ client_adjust(loc.client);
+ if (X.mode == M_RESIZE && Focused == loc.client)
+ {
+ mouse_tocorner(loc.client);
+ }
+ }
+ }
+ else
+ {
+ XConfigureWindow(X.disp, ev->window, ev->value_mask, &wc);
+ }
+}
+
+static void xmaprequest(XEvent* e)
+{
+ XMapRequestEvent* ev = &(e->xmaprequest);
+ //printf("MAP(w: 0x%lx)\n", ev->window);
+ XWindowAttributes attr;
+ Location loc = {0};
+ if (!mons_find(ev->window, &loc))
+ {
+ if (XGetWindowAttributes(X.disp, ev->window, &attr) && !attr.override_redirect)
+ {
+ (void)client_add(ev->window, &attr);
+ }
+ }
+}
+
+static void xunmapnotify(XEvent* e)
+{
+ XUnmapEvent* ev = &(e->xunmap);
+ //printf("UNMAP(e: 0x%lx w: 0x%lx %d)\n", ev->event, ev->window, ev->from_configure);
+ Location loc = {0};
+ if (mons_find(ev->window, &loc))// && loc.client->win == ev->window)
+ {
+ if (ev->event == X.root && ev->window == loc.client->win)
+ {
+ mons_delclient(&loc);
+ }
+ else if ( !(loc.client->flags & F_SHADED) )
+ {
+ XUnmapWindow(X.disp, loc.client->frame);
+ }
+ }
+}
+
+static void xdestroynotify(XEvent* e)
+{
+ XDestroyWindowEvent* ev = &(e->xdestroywindow);
+ //printf("DESTROY(w: 0x%lx)\n", ev->window);
+ Location loc = {0};
+ if (mons_find(ev->window, &loc))
+ {
+ mons_delclient(&loc);
+ }
+}
+
+static void xclientmsg(XEvent* e)
+{
+ XClientMessageEvent* ev = &(e->xclient);
+ //printf("CLIENT_MSG(w: 0x%lx a: '%s')\n", ev->window, XGetAtomName(X.disp, ev->message_type));
+ if (ev->message_type == atom("_NET_ACTIVE_WINDOW"))
+ {
+ mons_activate(ev->window);
+ }
+}
+
+static void xpropnotify(XEvent* e)
+{
+ XPropertyEvent* ev = &(e->xproperty);
+ //printf("PROP_NOTIFY(w: 0x%lx)\n", ev->window);
+ Location loc = {0};
+ if (mons_find(ev->window, &loc))
+ {
+ client_readprops(loc.client);
+ client_draw(loc.client);
+ }
+}
+
+static void xenternotify(XEvent* e)
+{
+ XCrossingEvent* ev = &(e->xcrossing);
+ Location loc = {0};
+ //printf("ENTER(w: 0x%lx s: %d m: %d d: %d)\n", ev->window, ev->state, ev->mode, ev->detail);
+ if (mons_find(ev->window, &loc))
+ {
+ client_focus(loc.client);
+ }
+}
+
+static void xexpose(XEvent* e)
+{
+ XExposeEvent* ev = &(e->xexpose);
+ if (ev->count == 0)
+ {
+ //printf("EXPOSE(w: 0x%lx)\n", ev->window);
+ Location loc = {0};
+ if (mons_find(ev->window, &loc))
+ {
+ client_draw(loc.client);
+ }
+ }
+}
+
+static void xkeypress(XEvent* e)
+{
+ XKeyEvent* ev = &(e->xkey);
+ //printf("KEY_DN(w: 0x%lx)\n", ev->window);
+ keys_run(ev);
+}
+
+static void init_cursors(void)
+{
+ XColor csr_bdr, csr_fill;
+ Colormap cmap = DefaultColormap(X.disp, X.screen);
+ X.csr_root = XCreateFontCursor(X.disp, XC_left_ptr);
+ XParseColor(X.disp, cmap, "rgb:ff/77/00", &csr_fill);
+ XParseColor(X.disp, cmap, "rgb:00/00/00", &csr_bdr);
+ X.csr_point = XCreateFontCursor(X.disp, XC_left_ptr);
+ XRecolorCursor(X.disp, X.csr_point, &csr_fill, &csr_bdr);
+ X.csr_move = XCreateFontCursor(X.disp, XC_fleur);
+ XRecolorCursor(X.disp, X.csr_move, &csr_fill, &csr_bdr);
+ XDefineCursor(X.disp, X.root, X.csr_root);
+}
+
+static void init_font(void)
+{
+ /* load the font */
+ char **miss, *def;
+ int nmiss;
+ X.font = XCreateFontSet(X.disp, FONT_NAME, &miss, &nmiss, &def);
+ if (!X.font)
+ {
+ X.font = XCreateFontSet(X.disp, "fixed", &miss, &nmiss, &def);
+ }
+ check(X.font != NULL, "unable to create font set for title font");
+ X.font_ext = XExtentsOfFontSet(X.font);
+}
+
+void sigchld(int sig)
+{
+ (void)sig;
+ while (waitpid(-1, 0, WNOHANG) > 0);
+}
+
+int main(void)
+{
+ /* make sure we cleanup zombie processes */
+ signal(SIGCHLD, sigchld);
+
+ /* Initialize X server*/
+ check( (X.disp = XOpenDisplay(0)) != NULL,
+ "could not open display");
+ X.mode = M_INIT;
+ X.root = DefaultRootWindow(X.disp);
+ X.screen = DefaultScreen(X.disp);
+ X.clr_bg = 0x00C0C0C0;
+ X.clr_bdr = 0x00000000;
+ X.clr_urgent = 0x00FF7700;
+ XWindowAttributes wa;
+ XGetWindowAttributes(X.disp, X.root, &wa);
+ X.visual = wa.visual;
+
+ /* initialize all the things */
+ check_for_wm();
+ init_cursors();
+ init_font();
+ mons_init();
+ client_initall();
+ keys_init();
+
+ /* setup event handlers */
+ X.eventfns[ButtonPress] = xbtnpress;
+ X.eventfns[ButtonRelease] = xbtnrelease;
+ X.eventfns[MotionNotify] = xbtnmotion;
+ X.eventfns[KeyPress] = xkeypress;
+ X.eventfns[ConfigureNotify] = xconfignotify;
+ X.eventfns[ConfigureRequest] = xconfigrequest;
+ X.eventfns[MapRequest] = xmaprequest;
+ X.eventfns[UnmapNotify] = xunmapnotify;
+ X.eventfns[DestroyNotify] = xdestroynotify;
+ X.eventfns[ClientMessage] = xclientmsg;
+ X.eventfns[PropertyNotify] = xpropnotify;
+ X.eventfns[EnterNotify] = xenternotify;
+ X.eventfns[Expose] = xexpose;
+
+ /* main event loop */
+ XSync(X.disp, False);
+ X.mode = M_IDLE;
+ for (;;)
+ {
+ XEvent ev;
+ XNextEvent(X.disp, &ev);
+ if (X.eventfns[ev.type])
+ {
+ X.eventfns[ev.type](&ev);
+ }
+ XSync(X.disp, False);
+ }
+
+ return 0;
+}
int main(int argc, char** argv)
{
- Options_Parse(argc, argv);
Stack_Bot = &(intptr_t){0};
Log.index = 0;
+ Options_Parse(argc, argv);
return usermain(argc, argv);
}