]> git.mdlowis.com Git - proto/aos.git/commitdiff
checked in current state of anvil code
authorMichael D. Lowis <mike.lowis@gentex.com>
Thu, 16 Dec 2021 20:24:34 +0000 (15:24 -0500)
committerMichael D. Lowis <mike.lowis@gentex.com>
Thu, 16 Dec 2021 20:24:34 +0000 (15:24 -0500)
13 files changed:
bin/winmgr/anvil.h [new file with mode: 0644]
bin/winmgr/client.c [new file with mode: 0644]
bin/winmgr/error.c [new file with mode: 0644]
bin/winmgr/keys.c [new file with mode: 0644]
bin/winmgr/list.c [new file with mode: 0644]
bin/winmgr/main.c
bin/winmgr/mons.c [new file with mode: 0644]
bin/winmgr/mouse.c [new file with mode: 0644]
bin/winmgr/revised.h [new file with mode: 0644]
bin/winmgr/tile.c [new file with mode: 0644]
bin/winmgr/util.c [new file with mode: 0644]
bin/winmgr/winmgr.c [new file with mode: 0644]
lib/a/gc.c

diff --git a/bin/winmgr/anvil.h b/bin/winmgr/anvil.h
new file mode 100644 (file)
index 0000000..2e38de5
--- /dev/null
@@ -0,0 +1,208 @@
+#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
diff --git a/bin/winmgr/client.c b/bin/winmgr/client.c
new file mode 100644 (file)
index 0000000..3960f8e
--- /dev/null
@@ -0,0 +1,229 @@
+#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);
+    }
+}
diff --git a/bin/winmgr/error.c b/bin/winmgr/error.c
new file mode 100644 (file)
index 0000000..2a9b55e
--- /dev/null
@@ -0,0 +1,26 @@
+#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);
+}
diff --git a/bin/winmgr/keys.c b/bin/winmgr/keys.c
new file mode 100644 (file)
index 0000000..5c21ebb
--- /dev/null
@@ -0,0 +1,146 @@
+#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;
+        }
+    }
+}
diff --git a/bin/winmgr/list.c b/bin/winmgr/list.c
new file mode 100644 (file)
index 0000000..646912e
--- /dev/null
@@ -0,0 +1,41 @@
+#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;
+}
+
index d2b68a7ee6c0b1be5e224981ab0c8e45bf7bbd19..c1c7cea3bece2593bd0ed5b9287a047572e1b4ae 100644 (file)
@@ -1,8 +1,20 @@
-#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;
 }
diff --git a/bin/winmgr/mons.c b/bin/winmgr/mons.c
new file mode 100644 (file)
index 0000000..78fa4b9
--- /dev/null
@@ -0,0 +1,529 @@
+#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;
+    }
+}
diff --git a/bin/winmgr/mouse.c b/bin/winmgr/mouse.c
new file mode 100644 (file)
index 0000000..fbd8973
--- /dev/null
@@ -0,0 +1,204 @@
+#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);
+}
+
diff --git a/bin/winmgr/revised.h b/bin/winmgr/revised.h
new file mode 100644 (file)
index 0000000..047efec
--- /dev/null
@@ -0,0 +1,70 @@
+#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;
diff --git a/bin/winmgr/tile.c b/bin/winmgr/tile.c
new file mode 100644 (file)
index 0000000..6da1d8e
--- /dev/null
@@ -0,0 +1,187 @@
+#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);
+    }
+}
diff --git a/bin/winmgr/util.c b/bin/winmgr/util.c
new file mode 100644 (file)
index 0000000..af4eba7
--- /dev/null
@@ -0,0 +1,51 @@
+#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);
+}
diff --git a/bin/winmgr/winmgr.c b/bin/winmgr/winmgr.c
new file mode 100644 (file)
index 0000000..d039546
--- /dev/null
@@ -0,0 +1,309 @@
+#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;
+}
index 88860e3807a7e71ef8bf666e074b87e32bf605f4..b4ca0e4594449db8d46ece9bfe6dd3528548a87c 100644 (file)
@@ -81,9 +81,9 @@ void gc_delref(void* p)
 
 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);
 }