From: Michael D. Lowis Date: Thu, 16 Dec 2021 20:24:34 +0000 (-0500) Subject: checked in current state of anvil code X-Git-Url: https://git.mdlowis.com/?a=commitdiff_plain;h=4f62a67c42e72607c64d07430429e0957b402268;p=proto%2Faos.git checked in current state of anvil code --- diff --git a/bin/winmgr/anvil.h b/bin/winmgr/anvil.h new file mode 100644 index 0000000..2e38de5 --- /dev/null +++ b/bin/winmgr/anvil.h @@ -0,0 +1,208 @@ +#ifndef ANVIL_H +#define ANVIL_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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 index 0000000..3960f8e --- /dev/null +++ b/bin/winmgr/client.c @@ -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 index 0000000..2a9b55e --- /dev/null +++ b/bin/winmgr/error.c @@ -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 index 0000000..5c21ebb --- /dev/null +++ b/bin/winmgr/keys.c @@ -0,0 +1,146 @@ +#include "anvil.h" +#include + +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 index 0000000..646912e --- /dev/null +++ b/bin/winmgr/list.c @@ -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; +} + diff --git a/bin/winmgr/main.c b/bin/winmgr/main.c index d2b68a7..c1c7cea 100644 --- a/bin/winmgr/main.c +++ b/bin/winmgr/main.c @@ -1,8 +1,20 @@ -#include +#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 index 0000000..78fa4b9 --- /dev/null +++ b/bin/winmgr/mons.c @@ -0,0 +1,529 @@ +#include "anvil.h" +#include + +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 index 0000000..fbd8973 --- /dev/null +++ b/bin/winmgr/mouse.c @@ -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 index 0000000..047efec --- /dev/null +++ b/bin/winmgr/revised.h @@ -0,0 +1,70 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +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 index 0000000..6da1d8e --- /dev/null +++ b/bin/winmgr/tile.c @@ -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 index 0000000..af4eba7 --- /dev/null +++ b/bin/winmgr/util.c @@ -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 index 0000000..d039546 --- /dev/null +++ b/bin/winmgr/winmgr.c @@ -0,0 +1,309 @@ +#include "anvil.h" +#include + +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; +} diff --git a/lib/a/gc.c b/lib/a/gc.c index 88860e3..b4ca0e4 100644 --- a/lib/a/gc.c +++ b/lib/a/gc.c @@ -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); }