From a6c0b69fd751a900d0422c4d8d59d4d7965829db Mon Sep 17 00:00:00 2001 From: "Michael D. Lowis" Date: Wed, 11 Mar 2020 11:57:28 -0400 Subject: [PATCH] added basic window controls and focus handling --- anvil.c | 67 ++++++++++++++++++++++++++++++++++++++++--------------- anvil.h | 16 +++++++++++++ build.sh | 2 +- client.c | 68 ++++++++++++++++++++++++++++++++++++++++++++------------ mons.c | 51 ++++++++++++++++++++++++++++++++++++++++-- 5 files changed, 169 insertions(+), 35 deletions(-) diff --git a/anvil.c b/anvil.c index f0bcc56..1bc075e 100644 --- a/anvil.c +++ b/anvil.c @@ -13,19 +13,24 @@ static void check_for_wm(void) static void xbtnpress(XEvent* e) { + XButtonEvent* ev = &(e->xbutton); Client* c = client_get(e->xbutton.window); - if (c && (e->xbutton.window == c->frame)) + if (c && (ev->window == c->frame)) { - if (e->xbutton.button == Button1) { - /* TODO: handle move and resize */ -// XMapWindow(X.disp, c->frame); -// client_raise(c); - } else if (e->xbutton.button == Button2) { - /* TODO: handle close */ -// client_close(c); - } else if (e->xbutton.button == Button3) { - /* TODO: handle lowering */ -// client_lower(c); + if (ev->button == Button1) { + client_raise(c); + if (ev->y < (TITLE_HEIGHT + BORDER_WIDTH)) { + X.start_x = ev->x_root; + X.start_y = ev->y_root; + } + else + { + /* TODO: warp the pointer and resize the window*/ + } + } else if (ev->button == Button2) { + client_close(c); + } else if (ev->button == Button3) { + client_lower(c); } } } @@ -35,6 +40,20 @@ static void xbtnrelease(XEvent* e) (void)e; } +static void xbtnmotion(XEvent* e) +{ + XMotionEvent *ev = &e->xmotion; + Client* c = client_get(ev->window); + if (c && (ev->window == c->frame)) + { + /* TODO: only move window if Button1 held */ + client_move(c, ev->x_root - X.start_x, ev->y_root - X.start_y); + } + X.start_x = ev->x_root; + X.start_y = ev->y_root; +} + + static void xconfigrequest(XEvent* e) { (void)e; @@ -52,7 +71,16 @@ static void xunmapnotify(XEvent* e) static void xdestroynotify(XEvent* e) { - (void)e; + XDestroyWindowEvent* ev = &(e->xdestroywindow); + Client* c = client_get(ev->window); + if (c) + { + if (Focused == c) + { + Focused = NULL; + } + mons_delclient(c); + } } static void xclientmsg(XEvent* e) @@ -67,7 +95,14 @@ static void xpropnotify(XEvent* e) static void xenternotify(XEvent* e) { - (void)e; + XCrossingEvent* ev = &(e->xcrossing); + Client* c = client_get(ev->window); + if (c) + { + client_focus(c); +// XSetInputFocus(X.disp, c->win, RevertToPointerRoot, CurrentTime); +// client_draw(c, 0); + } } static void xexpose(XEvent* e) @@ -94,7 +129,6 @@ int main(void) /* Initialize X server*/ check( (X.disp = XOpenDisplay(0)) != NULL, "could not open display"); -puts("opened display"); X.root = DefaultRootWindow(X.disp); X.screen = DefaultScreen(X.disp); X.black = BlackPixel(X.disp, X.screen); @@ -102,15 +136,13 @@ puts("opened display"); XAllocNamedColor(X.disp, DefaultColormap(X.disp, X.screen), "DimGray", &color, &exact); X.gray = color.pixel; check_for_wm(); -puts("locked WM"); mons_init(); -puts("setup monitors"); client_initall(); -puts("found existing clients"); /* setup event handlers */ X.eventfns[ButtonPress] = xbtnpress; X.eventfns[ButtonRelease] = xbtnrelease; + X.eventfns[MotionNotify] = xbtnmotion; X.eventfns[KeyPress] = xkeypress; X.eventfns[ConfigureRequest] = xconfigrequest; X.eventfns[MapRequest] = xmaprequest; @@ -127,7 +159,6 @@ puts("found existing clients"); { XEvent ev; XNextEvent(X.disp, &ev); -printf("event: %d\n", ev.type); if (X.eventfns[ev.type]) { X.eventfns[ev.type](&ev); diff --git a/anvil.h b/anvil.h index 42c860d..3988f32 100644 --- a/anvil.h +++ b/anvil.h @@ -19,6 +19,7 @@ typedef struct { int screen; Window root; unsigned long black, white, gray; + int start_x, start_y; void (*eventfns[LASTEvent])(XEvent*); } XConf; @@ -41,7 +42,14 @@ typedef struct Monitor { Workspace* cspace; } Monitor; +typedef struct Location { + Monitor* monitor; + Workspace* workspace; + Client* client; +} Location; + #define BORDER_WIDTH 5 +#define TITLE_HEIGHT 10 /* anvil.c */ extern XConf X; @@ -50,12 +58,20 @@ extern XConf X; extern Monitor* Monitors; void mons_init(void); void mons_addclient(Client* c); +void mons_delclient(Client* c); +int mons_find(Window win, Location* loc); /* client.c */ +extern Client* Focused; void client_initall(void); Client* client_add(Window win, XWindowAttributes* attr); void client_draw(Client* c, int active); Client* client_get(Window w); +void client_raise(Client* c); +void client_lower(Client* c); +void client_move(Client* c, int xdiff, int ydiff); +void client_close(Client* c); +void client_focus(Client* c); /* error.c */ extern int (*error_default)(Display* disp, XErrorEvent* ev); diff --git a/build.sh b/build.sh index 7ce3e68..6b0dfdc 100755 --- a/build.sh +++ b/build.sh @@ -1,4 +1,4 @@ #!/bin/sh -cc -Wall -Wextra -Werror -o anvil *.c -lX11 -lXinerama +cc -g -Wall -Wextra -Werror -o anvil *.c -lX11 -lXinerama ctags * grep -n 'TODO' *.c | sed -e 's/: \+/: /' -e 's/\/\* *//g' -e 's/ *\*\/$//' -e 's/TODO: *//' \ No newline at end of file diff --git a/client.c b/client.c index 3c38e1d..fe98ed5 100644 --- a/client.c +++ b/client.c @@ -1,9 +1,6 @@ #include "anvil.h" -static int title_height(void) -{ - return 10; -} +Client* Focused = NULL; void client_initall(void) { @@ -43,22 +40,22 @@ Client* client_add(Window win, XWindowAttributes* attr) /* Reparent the window if applicable */ c->frame = XCreateSimpleWindow(X.disp, X.root, - c->x, c->y - title_height(), - c->w, c->h + title_height(), + c->x - BORDER_WIDTH, + c->y - TITLE_HEIGHT - BORDER_WIDTH, + c->w + (2 * BORDER_WIDTH), + c->h + TITLE_HEIGHT + 2*BORDER_WIDTH, 1, X.black, X.white); XSelectInput(X.disp, c->frame, ExposureMask | EnterWindowMask | - ButtonPressMask | ButtonReleaseMask | - SubstructureRedirectMask | SubstructureNotifyMask | - PointerMotionMask + ButtonPressMask | ButtonReleaseMask | ButtonMotionMask | + SubstructureRedirectMask | SubstructureNotifyMask ); - XResizeWindow(X.disp, c->win, c->w - 2 * BORDER_WIDTH, c->h - 2 * BORDER_WIDTH); XSetWindowAttributes wa; wa.event_mask = EnterWindowMask | PropertyChangeMask | FocusChangeMask; wa.win_gravity = StaticGravity; wa.do_not_propagate_mask = ButtonPressMask | ButtonReleaseMask; XChangeWindowAttributes(X.disp, c->win, CWEventMask | CWWinGravity | CWDontPropagate, &wa); - XReparentWindow(X.disp, c->win, c->frame, BORDER_WIDTH, BORDER_WIDTH + title_height()); + XReparentWindow(X.disp, c->win, c->frame, BORDER_WIDTH, BORDER_WIDTH + TITLE_HEIGHT); /* Map the window and draw the frame */ XAddToSaveSet(X.disp, c->win); @@ -91,7 +88,50 @@ void client_draw(Client* c, int active) Client* client_get(Window w) { - (void)w; - /* TODO: Actually find the window correctly... */ - return NULL; + Client* c = NULL; + Location loc = {0}; + if (mons_find(w, &loc)) + { + c = loc.client; + } + return c; +} + +void client_raise(Client* c) +{ + XRaiseWindow(X.disp, c->frame); + XRaiseWindow(X.disp, c->win); +} + +void client_lower(Client* c) +{ + XLowerWindow(X.disp, c->win); + XLowerWindow(X.disp, c->frame); } + +void client_move(Client* c, int xdiff, int ydiff) +{ + c->x += xdiff; + c->y += ydiff; + XMoveWindow(X.disp, c->frame, + c->x - BORDER_WIDTH, + c->y - TITLE_HEIGHT - BORDER_WIDTH); + XSync(X.disp, False); +} + +void client_close(Client* c) +{ + /* TODO: handle wm_delete client message here */ + XKillClient(X.disp, c->win); +} + +void client_focus(Client* c) +{ + if (Focused != NULL) + { + client_draw(Focused, 0); + } + Focused = c; + XSetInputFocus(X.disp, c->win, RevertToPointerRoot, CurrentTime); + client_draw(Focused, 1); +} \ No newline at end of file diff --git a/mons.c b/mons.c index af35b63..8a909b3 100644 --- a/mons.c +++ b/mons.c @@ -28,5 +28,52 @@ void mons_addclient(Client* c) /* for now just add it to the first monitor in the list */ c->next = Monitors->cspace->floating; Monitors->cspace->floating = c; - /* TODO: using size and position to pick the "right" monitor */ -} \ No newline at end of file + /* TODO: use size and position to pick the "right" monitor */ +} + +Client* delclient(Client* list, Client* dead) +{ + if (list == dead) + { + list = list->next; + } + else + { + list->next = delclient(list->next, dead); + } + return list; +} + +void mons_delclient(Client* c) +{ + Location loc = {0}; + if (mons_find(c->win, &loc)) + { + loc.workspace->floating = delclient(loc.workspace->floating, c); + xfree(c->name); + XDestroyWindow(X.disp, c->frame); + free(c); + } +} + +int mons_find(Window win, Location* loc) +{ + Monitor* mons = Monitors; + for (Monitor* mon = Monitors; mon; mon = mon->next) + { + for (Workspace* wspace = mons->wspaces; wspace; wspace = wspace->next) + { + for (Client* client = wspace->floating; client; client = client->next) + { + if (client->frame == win || client->win == win) + { + loc->monitor = mon; + loc->workspace = wspace; + loc->client = client; + return 1; + } + } + } + } + return 0; +} -- 2.52.0