From 99b00bcb3b2803bde4d24f9273a6b81909df5f47 Mon Sep 17 00:00:00 2001 From: Samuel Fadel Date: Sun, 11 Dec 2022 13:03:04 +0100 Subject: Implemented remaining C core functions. * main.scm: Updated function name * wm.scm: Bind remaining C core functions * schewm.c: Updated/implemented remaining core functions --- main.scm | 18 +-- schewm.c | 417 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--- wm.scm | 99 ++++++++++----- 3 files changed, 478 insertions(+), 56 deletions(-) diff --git a/main.scm b/main.scm index 92b9362..827c911 100644 --- a/main.scm +++ b/main.scm @@ -57,15 +57,15 @@ (,(make-key "8") . ,wm-set-workspace) (,(make-key "9") . ,wm-set-workspace) ;; Send client to workspace - (,(make-shift-key "1") . ,wm-set-client-workspace) - (,(make-shift-key "2") . ,wm-set-client-workspace) - (,(make-shift-key "3") . ,wm-set-client-workspace) - (,(make-shift-key "4") . ,wm-set-client-workspace) - (,(make-shift-key "5") . ,wm-set-client-workspace) - (,(make-shift-key "6") . ,wm-set-client-workspace) - (,(make-shift-key "7") . ,wm-set-client-workspace) - (,(make-shift-key "8") . ,wm-set-client-workspace) - (,(make-shift-key "9") . ,wm-set-client-workspace) + (,(make-shift-key "1") . ,wm-set-focused-client-workspace) + (,(make-shift-key "2") . ,wm-set-focused-client-workspace) + (,(make-shift-key "3") . ,wm-set-focused-client-workspace) + (,(make-shift-key "4") . ,wm-set-focused-client-workspace) + (,(make-shift-key "5") . ,wm-set-focused-client-workspace) + (,(make-shift-key "6") . ,wm-set-focused-client-workspace) + (,(make-shift-key "7") . ,wm-set-focused-client-workspace) + (,(make-shift-key "8") . ,wm-set-focused-client-workspace) + (,(make-shift-key "9") . ,wm-set-focused-client-workspace) ;; Send client to monitor (,(make-shift-key "i") . ,wm-client-monitor-prev) (,(make-shift-key "o") . ,wm-client-monitor-next))) diff --git a/schewm.c b/schewm.c index 72e6ea8..49f2e9d 100644 --- a/schewm.c +++ b/schewm.c @@ -218,6 +218,7 @@ client_restore_size(struct Client *client) { client->y = client->old_y; client->width = client->old_width; client->height = client->old_height; + client->has_old_size = false; } struct ClientList { @@ -278,7 +279,6 @@ wid_unhash(uint32_t x) { struct ClientsMap { struct ClientList **table; - // Please never modify this size_t table_size; size_t size; }; @@ -360,7 +360,7 @@ clients_map_find(const struct ClientsMap *clients, xcb_window_t id) { void clients_map_free(struct ClientsMap *clients) { for (size_t i = 0; i < clients->table_size; i++) { - client_list_free(clients->table[i], true); + client_list_free(clients->table[i], true); } free(clients->table); free(clients); @@ -1112,15 +1112,6 @@ dpy_raise_window(xcb_window_t window) { xcb_configure_window(dpy.conn, window, XCB_CONFIG_WINDOW_STACK_MODE, &mode); } -void -dpy_set_window_border_width(xcb_window_t window) { - xcb_configure_window( - dpy.conn, - window, - XCB_CONFIG_WINDOW_BORDER_WIDTH, - &cfg.inner_border_width); -} - void dpy_set_window_workspace(xcb_window_t window, uint32_t workspace) { xcb_ewmh_set_wm_desktop(dpy.ewmh, window, workspace); @@ -1186,6 +1177,15 @@ dpy_update_window_size(const struct Client *client) { xcb_configure_window(dpy.conn, window, mask, data); } +void +dpy_set_window_border_width(xcb_window_t window) { + xcb_configure_window( + dpy.conn, + window, + XCB_CONFIG_WINDOW_BORDER_WIDTH, + &cfg.inner_border_width); +} + #define RECT_INIT(x, y, w, h) { \ (int16_t) (x), \ (int16_t) (y), \ @@ -1275,11 +1275,10 @@ dpy_draw_borders(struct Client *client, bool is_focused) { client->width + cfg.inner_border_width * 2, client->height + cfg.inner_border_width * 2); - xcb_gcontext_t gc = xcb_generate_id(dpy.conn); - xcb_create_gc(dpy.conn, gc, pixmap, 0, NULL); - uint32_t color = client->is_unkillable ? cfg.colors.unkillable : cfg.colors.outer; + xcb_gcontext_t gc = xcb_generate_id(dpy.conn); + xcb_create_gc(dpy.conn, gc, pixmap, 0, NULL); xcb_change_gc(dpy.conn, gc, XCB_GC_FOREGROUND, &color); xcb_poly_fill_rectangle(dpy.conn, pixmap, gc, 5, outer_rect); @@ -1503,6 +1502,29 @@ dpy_update_window_list(const struct ClientsMap *clients) { free(windows); } +void +dpy_send_window_message(xcb_window_t window, xcb_atom_t atom) { + xcb_client_message_event_t event; + event.response_type = XCB_CLIENT_MESSAGE; + event.format = 32; + event.sequence = 0; + event.window = window; + event.type = dpy.ewmh->WM_PROTOCOLS; + event.data.data32[0] = atom; + event.data.data32[1] = XCB_CURRENT_TIME; + xcb_send_event( + dpy.conn, + false, + window, + XCB_EVENT_MASK_NO_EVENT, + (char *) &event); +} + +void +dpy_kill_window(xcb_window_t window) { + xcb_kill_client(dpy.conn, window); +} + void dpy_destroy() { if (dpy.ewmh) { @@ -1544,9 +1566,11 @@ bool wm_update_outputs(); bool wm_has_error(); bool wm_init(); void wm_quit(); -void wm_focus_close(); +void wm_grab_key_with_mod(xcb_keysym_t keysym); +void wm_grab_key_with_mod_shift(xcb_keysym_t keysym); void wm_begin_move_client(); void wm_begin_resize_client(); +void wm_focus_close(); void wm_focus_prev(); void wm_focus_next(); void wm_toggle_maximize(); @@ -1566,6 +1590,7 @@ void wm_set_workspace(uint32_t workspace); void wm_set_focused_client_workspace(uint32_t workspace); void wm_client_monitor_prev(); void wm_client_monitor_next(); +void wm_run(); struct Client * wm_find_client(xcb_window_t id) { @@ -1684,6 +1709,20 @@ wm_set_workspace(uint32_t workspace) { void wm_set_client_workspace(struct Client *client, uint32_t workspace) { + uint32_t old_workspace = client->workspace; + bool is_known = clients_map_find(wm.clients, client->id) != NULL; + if (old_workspace == workspace && is_known) { + return; + } + + // assert(workspace != NULL_WORKSPACE && workspace != ALL_WORKSPACES); + client->workspace = workspace; + dpy_set_window_workspace(client->id, workspace); + if (is_known && old_workspace != NULL_WORKSPACE) { + wm.workspaces[old_workspace].ring = client_ring_erase(wm.workspaces[old_workspace].ring, client); + dpy_unmap_window(client->id); + } + wm.workspaces[workspace].ring = client_ring_add(wm.workspaces[workspace].ring, client); } void @@ -1748,6 +1787,76 @@ wm_set_client_state(struct Client *client, enum WindowState state) { dpy_set_window_state(client->id, client->state); } +void +wm_move_client(struct Client *client, int16_t dx, int16_t dy, bool in_monitor) { + client->x += dx; + client->y += dy; + if (in_monitor) { + struct Rect mon_rect; + wm_client_monitor_size(client, true, &mon_rect); + // Complete movement by checking for magnets + if (client->x < cfg.magnet_border_width + mon_rect.x) { + client->x = mon_rect.x; + } else if (client->x + client->width + cfg.inner_border_width > + mon_rect.x + mon_rect.width - cfg.magnet_border_width) { + client->x = mon_rect.x + mon_rect.width - client->width - cfg.inner_border_width * 2; + } + if (client->y < cfg.magnet_border_width + mon_rect.y) { + client->y = mon_rect.y; + } else if (client->y + client->height + cfg.inner_border_width > + mon_rect.y + mon_rect.height - cfg.magnet_border_width) { + client->y = mon_rect.y + mon_rect.height - client->height - cfg.inner_border_width * 2; + } + } + dpy_update_window_position(client); + // dpy_draw_focused_borders(client); + dpy_flush(); +} + +void +wm_resize_client(struct Client *client, + uint16_t width_inc, + uint16_t height_inc, + bool in_monitor) { + // TODO: honor inc hints + client->width += width_inc; + client->height += height_inc; + wm_apply_size_hints(client); + + if (in_monitor) { + struct Rect mon_rect; + wm_client_monitor_size(client, true, &mon_rect); + if (client->x + client->width + cfg.inner_border_width > + mon_rect.x + mon_rect.width) { + client->width = mon_rect.width - ((client->x - mon_rect.x) + cfg.inner_border_width * 2); + } + if (client->y + client->height + cfg.inner_border_width > + mon_rect.y + mon_rect.height) { + client->height = mon_rect.height - ((client->y - mon_rect.y) + cfg.inner_border_width * 2); + } + } + dpy_update_window_size(client); + dpy_draw_focused_borders(client); + dpy_flush(); +} + +void +wm_move_resize_client(struct Client *client, + const struct Rect *rect, + bool in_monitor) { + if (in_monitor) { + } else { + client->x = rect->x; + client->y = rect->y; + client->width = rect->width; + client->height = rect->height; + } + wm_apply_size_hints(client); + dpy_update_window_geometry(client); + dpy_draw_focused_borders(client); + dpy_flush(); +} + bool wm_fit_client(struct Client *client) { if (client->state == WS_ICONIFIED) { @@ -2029,6 +2138,278 @@ wm_focus_next() { wm_focus_other(true); } +void +wm_toggle_maximize() { + if (wm.focus == NULL) { + return; + } + + if (wm.focus->state == WS_MAXIMIZED) { + wm_set_client_state(wm.focus, wm.focus->prev_state); + } else { + wm_set_client_state(wm.focus, WS_MAXIMIZED); + } +} + +static void +wm_client_ensure_state(struct Client *client, enum WindowState state) { + if (client->state != state) { + wm_set_client_state(client, state); + } +} + +void +wm_toggle_half_left() { + if (wm.focus == NULL) { + return; + } + + wm_client_ensure_state(wm.focus, WS_NORMAL); + // Start with monitor size, then reduce to appropriate target size + // for half the screen, accounting for border sizes + struct Rect rect; + wm_client_monitor_size(wm.focus, true, &rect); + rect.width = rect.width / 2 - cfg.inner_border_width * 2; + rect.height -= cfg.inner_border_width * 2; + wm_move_resize_client(wm.focus, &rect, false); + wm_raise_and_center_cursor(wm.focus); +} + +void +wm_toggle_half_right() { + if (wm.focus == NULL) { + return; + } + + wm_client_ensure_state(wm.focus, WS_NORMAL); + struct Rect rect; + wm_client_monitor_size(wm.focus, true, &rect); + rect.x += rect.width / 2; + rect.width = rect.width / 2 - cfg.inner_border_width * 2; + rect.height -= cfg.inner_border_width * 2; + wm_move_resize_client(wm.focus, &rect, false); + wm_raise_and_center_cursor(wm.focus); +} + +void +wm_toggle_half_top() { + if (wm.focus == NULL) { + return; + } + + wm_client_ensure_state(wm.focus, WS_NORMAL); + struct Rect rect; + wm_client_monitor_size(wm.focus, true, &rect); + rect.width -= cfg.inner_border_width * 2; + rect.height = rect.height / 2 - cfg.inner_border_width * 2; + wm_move_resize_client(wm.focus, &rect, false); + wm_raise_and_center_cursor(wm.focus); +} + +void +wm_toggle_half_bottom() { + if (wm.focus == NULL) { + return; + } + + wm_client_ensure_state(wm.focus, WS_NORMAL); + struct Rect rect; + wm_client_monitor_size(wm.focus, true, &rect); + rect.y += rect.height / 2; + rect.width -= cfg.inner_border_width * 2; + rect.height = rect.height / 2 - cfg.inner_border_width * 2; + wm_move_resize_client(wm.focus, &rect, false); + wm_raise_and_center_cursor(wm.focus); +} + +void +wm_toggle_top_left() { + if (wm.focus == NULL) { + return; + } + + wm_client_ensure_state(wm.focus, WS_NORMAL); + struct Rect rect; + wm_client_monitor_size(wm.focus, true, &rect); + rect.width = rect.width / 2 - cfg.inner_border_width * 2; + rect.height = rect.height / 2 - cfg.inner_border_width * 2; + wm_move_resize_client(wm.focus, &rect, false); + wm_raise_and_center_cursor(wm.focus); +} + +void +wm_toggle_top_right() { + if (wm.focus == NULL) { + return; + } + + wm_client_ensure_state(wm.focus, WS_NORMAL); + struct Rect rect; + wm_client_monitor_size(wm.focus, true, &rect); + rect.x += rect.width / 2; + rect.width = rect.width / 2 - cfg.inner_border_width * 2; + rect.height = rect.height / 2 - cfg.inner_border_width * 2; + wm_move_resize_client(wm.focus, &rect, false); + wm_raise_and_center_cursor(wm.focus); +} + +void +wm_toggle_bottom_left() { + if (wm.focus == NULL) { + return; + } + + wm_client_ensure_state(wm.focus, WS_NORMAL); + struct Rect rect; + wm_client_monitor_size(wm.focus, true, &rect); + rect.y += rect.height / 2; + rect.width = rect.width / 2 - cfg.inner_border_width * 2; + rect.height = rect.height / 2 - cfg.inner_border_width * 2; + wm_move_resize_client(wm.focus, &rect, false); + wm_raise_and_center_cursor(wm.focus); +} + +void +wm_toggle_bottom_right() { + if (wm.focus == NULL) { + return; + } + + wm_client_ensure_state(wm.focus, WS_NORMAL); + struct Rect rect; + wm_client_monitor_size(wm.focus, true, &rect); + rect.x += rect.width / 2; + rect.y += rect.height / 2; + rect.width = rect.width / 2 - cfg.inner_border_width * 2; + rect.height = rect.height / 2 - cfg.inner_border_width * 2; + wm_move_resize_client(wm.focus, &rect, false); + wm_raise_and_center_cursor(wm.focus); +} + +void +wm_pack_left() { + if (wm.focus == NULL || wm.focus->state != WS_NORMAL) { + return; + } + + struct Rect rect; + wm_client_monitor_size(wm.focus, true, &rect); + int16_t dx = rect.x - wm.focus->x; + wm_move_client(wm.focus, dx, 0, false); + wm_raise_and_center_cursor(wm.focus); +} + +void +wm_pack_right() { + if (wm.focus == NULL || wm.focus->state != WS_NORMAL) { + return; + } + + struct Rect rect; + wm_client_monitor_size(wm.focus, true, &rect); + int16_t dx = rect.width - (wm.focus->x - rect.x + wm.focus->width) - cfg.inner_border_width * 2; + wm_move_client(wm.focus, dx, 0, false); + wm_raise_and_center_cursor(wm.focus); +} + +void +wm_pack_top() { + if (wm.focus == NULL || wm.focus->state != WS_NORMAL) { + return; + } + + struct Rect rect; + wm_client_monitor_size(wm.focus, true, &rect); + int16_t dy = rect.y - wm.focus->y; + wm_move_client(wm.focus, 0, dy, false); + wm_raise_and_center_cursor(wm.focus); +} + +void +wm_pack_bottom() { + if (wm.focus == NULL || wm.focus->state != WS_NORMAL) { + return; + } + + struct Rect rect; + wm_client_monitor_size(wm.focus, true, &rect); + int16_t dy = rect.height - (wm.focus->y - rect.y + wm.focus->height) - cfg.inner_border_width * 2; + wm_move_client(wm.focus, 0, dy, false); + wm_raise_and_center_cursor(wm.focus); +} + +void +wm_set_focused_client_workspace(uint32_t workspace) { + if (wm.focus == NULL) { + return; + } + + wm_set_client_workspace(wm.focus, workspace); +} + +static void +relative_pos(const struct Client *client, + const struct Monitor *m1, + const struct Monitor *m2, + struct Point *p) { + float scale_x = ((float) (client->x - m1->x)) / m1->width; + float scale_y = ((float) (client->y - m1->y)) / m1->height; + p->x = (int16_t) (m2->x + m2->width * scale_x + 0.5f); + p->y = (int16_t) (m2->y + m2->height * scale_y + 0.5f); +} + +void +wm_client_monitor_other(struct Client *client, bool is_next) { + if (client->monitor == NULL || client->monitor->next == client->monitor) { + return; + } + + struct Monitor *other_monitor = is_next + ? client->monitor->next : client->monitor->prev; + struct Point new_pos; + relative_pos(client, client->monitor, other_monitor, &new_pos); + client->monitor = other_monitor; + if (client->state == WS_NORMAL) { + client->x = new_pos.x; + client->y = new_pos.y; + if (!wm_fit_client(client)) { + // wm_fit_client() did not update position, force it + dpy_update_window_position(client); + } + } else { + // We restore size and ensure it is located within the new + // monitor before maximizing, otherwise it will move between + // monitors again when restored. We also fool the function + // into believing we were not maximized before so that it + // properly maximizes to the new monitor size. + client_restore_size(client); + client->x = new_pos.x; + client->y = new_pos.y; + client->state = WS_NORMAL; + wm_set_client_state(client, WS_MAXIMIZED); + } + wm_raise_and_center_cursor(client); + dpy_flush(); +} + +void +wm_client_monitor_prev() { + if (wm.focus == NULL) { + return; + } + + wm_client_monitor_other(wm.focus, false); +} + +void +wm_client_monitor_next() { + if (wm.focus == NULL) { + return; + } + + wm_client_monitor_other(wm.focus, true); +} + static uint8_t ev_type(const xcb_generic_event_t *ev) { return ev->response_type & ~0x80; @@ -2111,7 +2492,7 @@ ev_enter_notify(xcb_generic_event_t *generic_ev) { } // XCB_KEY_PRESS -typedef void (*key_press_handler_t)(uint16_t, uint8_t); +typedef void (*key_press_handler_t)(uint16_t, xcb_keysym_t); static key_press_handler_t key_press_handler = NULL; void @@ -2123,7 +2504,9 @@ static void ev_key_press(xcb_generic_event_t *generic_ev) { if (key_press_handler) { xcb_key_press_event_t *ev = (xcb_key_press_event_t *) generic_ev; - key_press_handler(ev->state, ev->detail); + uint16_t mod = dpy_clean_mod(ev->state); + xcb_keysym_t keysym = dpy_keysym_from_keycode(ev->detail); + key_press_handler(mod, keysym); } } diff --git a/wm.scm b/wm.scm index bdbc6dd..3dd4797 100644 --- a/wm.scm +++ b/wm.scm @@ -26,7 +26,7 @@ wm-toggle-bottom-left wm-toggle-bottom-right wm-set-workspace - wm-set-client-workspace + wm-set-focused-client-workspace wm-client-monitor-prev wm-client-monitor-next wm-run @@ -121,41 +121,80 @@ (wm-grab-key-with-mod key)) (wm-grab-keys (cdr keybindings))))) -(define wm-focus-prev - (schewm-func void "wm_focus_prev" '())) - -(define wm-focus-next - (schewm-func void "wm_focus_next" '())) - (define c/wm-set-key-press-handler (schewm-func void "wm_set_key_press_handler" (list '*))) (define (wm-set-key-press-handler! handler) - ;; mod (state, uint16) - ;; keysym (detail, uint8) + ;; Event handler args are: + ;; - mod (`ev.state' clean from noise, uint16) + ;; - keysym (`ev.detail' converted to keysym, uint32) (c/wm-set-key-press-handler (procedure->pointer void handler - (list uint16 uint8)))) - -;; XXX: Dummy -(define (wm-focus-close) '()) -(define (wm-pack-left) '()) -(define (wm-pack-right) '()) -(define (wm-pack-top) '()) -(define (wm-pack-bottom) '()) -(define (wm-toggle-maximize) '()) -(define (wm-toggle-half-left) '()) -(define (wm-toggle-half-right) '()) -(define (wm-toggle-half-top) '()) -(define (wm-toggle-half-bottom) '()) -(define (wm-toggle-top-left) '()) -(define (wm-toggle-top-right) '()) -(define (wm-toggle-bottom-left) '()) -(define (wm-toggle-bottom-right) '()) -(define (wm-set-workspace) '()) -(define (wm-set-client-workspace) '()) -(define (wm-client-monitor-prev) '()) -(define (wm-client-monitor-next) '()) + (list uint16 uint32)))) + +(define wm-focus-prev + (schewm-func void "wm_focus_prev" '())) + +(define wm-focus-next + (schewm-func void "wm_focus_next" '())) + +(define wm-focus-close + (schewm-func void "wm_focus_close" '())) + +(define wm-pack-left + (schewm-func void "wm_pack_left" '())) + +(define wm-pack-right + (schewm-func void "wm_pack_right" '())) + +(define wm-pack-top + (schewm-func void "wm_pack_top" '())) + +(define wm-pack-bottom + (schewm-func void "wm_pack_bottom" '())) + +(define wm-toggle-maximize + (schewm-func void "wm_toggle_maximize" '())) + +(define wm-toggle-half-left + (schewm-func void "wm_toggle_half_left" '())) + +(define wm-toggle-half-right + (schewm-func void "wm_toggle_half_right" '())) + +(define wm-toggle-half-top + (schewm-func void "wm_toggle_half_top" '())) + +(define wm-toggle-half-bottom + (schewm-func void "wm_toggle_half_bottom" '())) + +(define wm-toggle-top-left + (schewm-func void "wm_toggle_top_left" '())) + +(define wm-toggle-top-right + (schewm-func void "wm_toggle_top_right" '())) + +(define wm-toggle-bottom-left + (schewm-func void "wm_toggle_bottom_left" '())) + +(define wm-toggle-bottom-right + (schewm-func void "wm_toggle_bottom_right" '())) + +(define wm-set-workspace + (schewm-func void + "wm_set_workspace" + (list uint32))) + +(define wm-set-focused-client-workspace + (schewm-func void + "wm_set_focused_client_workspace" + (list uint32))) + +(define wm-client-monitor-prev + (schewm-func void "wm_client_monitor_prev" '())) + +(define wm-client-monitor-next + (schewm-func void "wm_client_monitor_next" '())) -- cgit v1.2.3