summaryrefslogtreecommitdiff
path: root/schewm.c
diff options
context:
space:
mode:
Diffstat (limited to 'schewm.c')
-rw-r--r--schewm.c417
1 files changed, 400 insertions, 17 deletions
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);
@@ -1113,15 +1113,6 @@ dpy_raise_window(xcb_window_t window) {
}
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);
@@ -1504,6 +1503,29 @@ dpy_update_window_list(const struct ClientsMap *clients) {
}
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) {
xcb_ewmh_connection_wipe(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);
}
}