summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSamuel Fadel <samuel@nihil.ws>2022-12-04 12:32:13 +0100
committerSamuel Fadel <samuel@nihil.ws>2022-12-04 12:32:13 +0100
commitf5cb5e5cf8a3b410a4afda52f925e84432ca2ac8 (patch)
treecb9ea8a97dfd345baa82b5bccf3804707de892d3
parentda3523b11bcb174dac02fbfabfa58ed6b78a8622 (diff)
Key grabbing, handling outputs, init clients.
* main.scm: Added key press handler * wm.scm: Added key press handler * schewm.c: Added key grabbing, output management, client initialization (initial grab)
-rw-r--r--main.scm7
-rw-r--r--schewm.c603
-rw-r--r--wm.scm14
3 files changed, 563 insertions, 61 deletions
diff --git a/main.scm b/main.scm
index f8ac169..e0647ab 100644
--- a/main.scm
+++ b/main.scm
@@ -11,11 +11,12 @@
(make-config 2 0))
(when (wm-init)
- (wm-set-configure-notify-handler!
- (lambda (x y w h)
- (display (+ x y w h))
+ (wm-set-key-press-handler!
+ (lambda (mod keysym)
+ (display (list mod keysym))
(display "\n")))
(display config)
(display "\n")
(wm-run)
(wm-quit))
+
diff --git a/schewm.c b/schewm.c
index a515e1b..dd90982 100644
--- a/schewm.c
+++ b/schewm.c
@@ -20,8 +20,8 @@ const uint16_t MOVE_GLYPH = 52;
const uint16_t RESIZE_GLYPH = 120;
// Key modifiers
-const uint16_t META = XCB_MOD_MASK_4;
-const uint16_t SHIFT = XCB_MOD_MASK_SHIFT;
+const uint16_t MOD_META = XCB_MOD_MASK_4;
+const uint16_t MOD_SHIFT = XCB_MOD_MASK_SHIFT;
struct Point {
int16_t x, y;
@@ -58,12 +58,12 @@ min(int16_t a, int16_t b) {
return a < b ? a : b;
}
-uint16_t
+uint32_t
rect_intersect_area(const struct Rect *r1, const struct Rect *r2) {
- int16_t x1 = max(r1->x, r2->x);
- int16_t y1 = max(r1->y, r2->y);
- int16_t x2 = min(r1->x + r1->width, r2->x + r2->width);
- int16_t y2 = min(r1->y + r1->height, r2->y + r2->height);
+ int32_t x1 = max(r1->x, r2->x);
+ int32_t y1 = max(r1->y, r2->y);
+ int32_t x2 = min(r1->x + r1->width, r2->x + r2->width);
+ int32_t y2 = min(r1->y + r1->height, r2->y + r2->height);
return (x1 > x2 || y1 > y2) ? 0 : (x2 - x1) * (y2 - y1);
}
@@ -112,17 +112,15 @@ client_new(xcb_window_t id) {
return client;
}
-void
-client_free(struct Client *client) {
- // NOTE: We ignore monitor, next, and prev on purpose. The client
- // does not own that memory.
- free(client);
-}
-
+/*
+ * Adds a new client to the ring, adjusting the necessary next/prev
+ * pointers.
+ */
struct Client *
client_ring_add(struct Client *ring, struct Client *client) {
if (ring == NULL || ring->next == NULL || ring->prev == NULL) {
ring = client;
+ client->next = client;
}
client->prev = ring;
@@ -132,13 +130,18 @@ client_ring_add(struct Client *ring, struct Client *client) {
return client;
}
+/*
+ * Removes a client from the ring. We do not manage memory here, so
+ * the caller must free the memory of the client struct if that's
+ * needed.
+ */
struct Client *
client_ring_erase(struct Client *ring, struct Client *client) {
if (ring == NULL) {
return NULL;
}
if (ring == client && client->next == client) {
- client_free(ring);
+ // client_free(ring);
return NULL;
}
@@ -146,7 +149,7 @@ client_ring_erase(struct Client *ring, struct Client *client) {
client->prev->next = client->next;
if (ring == client) {
ring = client->next;
- client_free(client);
+ // client_free(client);
}
return ring;
}
@@ -204,7 +207,7 @@ client_list_free(struct ClientList *list, bool deep) {
while (list != NULL) {
next = list->next;
if (deep) {
- client_free(list->client);
+ free(list->client);
}
free(list);
list = next;
@@ -326,6 +329,48 @@ struct Monitor {
struct Monitor *prev, *next;
};
+/*
+ * Add a new monitor to the ring, keeping it circular and adjusting
+ * the necessary next/prev pointers.
+ */
+struct Monitor *
+monitor_ring_add(struct Monitor *ring, struct Monitor *monitor) {
+ if (ring == NULL || ring->next == NULL || ring->prev == NULL) {
+ ring = monitor;
+ monitor->next = monitor;
+ }
+
+ monitor->prev = ring;
+ monitor->next = ring->next;
+ ring->next->prev = monitor;
+ ring->next = monitor;
+ return monitor;
+}
+
+/*
+ * Same as for clients, we also don't manage memory here, just the
+ * structure of the ring. The caller should free the monitor struct if
+ * needed.
+ */
+struct Monitor *
+monitor_ring_erase(struct Monitor *ring, struct Monitor *monitor) {
+ if (ring == NULL) {
+ return NULL;
+ }
+ if (ring == monitor && monitor->next == monitor) {
+ // free(ring);
+ return NULL;
+ }
+
+ monitor->next->prev = monitor->prev;
+ monitor->prev->next = monitor->next;
+ if (ring == monitor) {
+ ring = monitor->next;
+ // free(monitor);
+ }
+ return ring;
+}
+
void
monitor_rect(const struct Monitor *monitor, struct Rect *rect) {
rect->x = monitor->x;
@@ -363,7 +408,7 @@ static struct {
xcb_screen_t *screen;
bool has_randr;
int randr_base, screen_num;
- struct Monitor monitors;
+ struct Monitor *monitors;
} dpy;
/*
@@ -488,7 +533,7 @@ void wm_pack_right();
void wm_pack_top();
void wm_pack_bottom();
void wm_set_workspace(uint32_t workspace);
-void wm_set_client_workspace(uint32_t workspace);
+void wm_set_focused_client_workspace(uint32_t workspace);
void wm_client_monitor_prev();
void wm_client_monitor_next();
@@ -550,6 +595,7 @@ wm_erase_client(xcb_window_t id) {
// Workspace being NULL is a bug!
workspace->ring = client_ring_erase(workspace->ring, client);
clients_map_erase(wm.clients, client);
+ free(client);
if (!wm.focus) {
dpy_set_focus(XCB_NONE);
}
@@ -560,8 +606,151 @@ wm_has_error() {
return dpy.has_error || xcb_connection_has_error(dpy.conn) > 0;
}
-static void
+struct Monitor *
+dpy_find_monitor(xcb_randr_output_t id) {
+ if (dpy.monitors == NULL) {
+ return NULL;
+ }
+
+ struct Monitor *monitor = dpy.monitors;
+ for (;;) {
+ if (monitor->id == id) {
+ return monitor;
+ }
+ monitor = monitor->next;
+ if (monitor == dpy.monitors) {
+ break;
+ }
+ }
+ return NULL;
+}
+
+static bool
+dpy_update_output(xcb_randr_output_t output,
+ xcb_timestamp_t timestamp,
+ xcb_randr_get_output_info_reply_t *o_reply) {
+ xcb_randr_get_crtc_info_cookie_t c_cookie =
+ xcb_randr_get_crtc_info(dpy.conn, o_reply->crtc, timestamp);
+ xcb_randr_get_crtc_info_reply_t *c_reply =
+ xcb_randr_get_crtc_info_reply(dpy.conn, c_cookie, NULL);
+ if (!c_reply) {
+ return false;
+ }
+
+ bool any_change = false;
+ struct Monitor *monitor = dpy_find_monitor(output);
+ if (monitor != NULL) {
+ // Monitor already known, update info
+ if (c_reply->x != monitor->x) {
+ monitor->x = c_reply->x;
+ any_change = true;
+ }
+ if (c_reply->y != monitor->y) {
+ monitor->y = c_reply->y;
+ any_change = true;
+ }
+ if (c_reply->width != monitor->width) {
+ monitor->width = c_reply->width;
+ any_change = true;
+ }
+ if (c_reply->height != monitor->height) {
+ monitor->height = c_reply->height;
+ any_change = true;
+ }
+ goto dpy_update_output_end;
+ }
+
+ // Check if we have a clone of an existing monitor, since we did
+ // not find a monitor with same id
+ if (dpy.monitors != NULL) {
+ monitor = dpy.monitors;
+ for (;;) {
+ if (monitor->x == c_reply->x
+ && monitor->y == c_reply->y
+ && monitor->width == c_reply->width
+ && monitor->height == c_reply->height) {
+ // Found a clone, can be ignored
+ goto dpy_update_output_end;
+ }
+ monitor = monitor->next;
+ if (monitor == dpy.monitors) {
+ break;
+ }
+ }
+ }
+
+ // It is a new monitor
+ // XXX: should we care about name length?
+ // int name_len = min(16, xcb_randr_get_output_info_name_length(output_info_reply.get()));
+ const char *name = (const char *) xcb_randr_get_output_info_name(o_reply);
+ monitor = calloc(1, sizeof(struct Monitor));
+ if (monitor == NULL) {
+ goto dpy_update_output_end;
+ }
+ monitor->id = output;
+ monitor->name = name;
+ monitor->x = c_reply->x;
+ monitor->y = c_reply->y;
+ monitor->width = c_reply->width;
+ monitor->height = c_reply->height;
+ dpy.monitors = monitor_ring_add(dpy.monitors, monitor);
+ any_change = true;
+
+dpy_update_output_end:
+ free(c_reply);
+ return any_change;
+}
+
+/*
+ * Attempts to update monitor info and returns whether there were any
+ * changes.
+ */
+static bool
dpy_update_outputs() {
+ if (!dpy.has_randr) {
+ return false;
+ }
+
+ xcb_randr_get_screen_resources_current_cookie_t s_cookie =
+ xcb_randr_get_screen_resources_current(dpy.conn, dpy.screen->root);
+ xcb_randr_get_screen_resources_current_reply_t *s_reply =
+ xcb_randr_get_screen_resources_current_reply(dpy.conn, s_cookie, NULL);
+ if (!s_reply) {
+ return false;
+ }
+
+ bool any_change = false;
+ xcb_timestamp_t timestamp = s_reply->config_timestamp;
+ int length = xcb_randr_get_screen_resources_current_outputs_length(s_reply);
+ xcb_randr_output_t *outputs = xcb_randr_get_screen_resources_current_outputs(s_reply);
+ for (int i = 0; i < length; i++) {
+ xcb_randr_get_output_info_cookie_t o_cookie =
+ xcb_randr_get_output_info(dpy.conn, outputs[i], timestamp);
+ xcb_randr_get_output_info_reply_t *o_reply =
+ xcb_randr_get_output_info_reply(dpy.conn, o_cookie, NULL);
+ if (!o_reply) {
+ continue;
+ }
+
+ if (o_reply->crtc == XCB_NONE) {
+ // Output no longer active, delete if we had any info about it
+ struct Monitor *monitor = dpy_find_monitor(outputs[i]);
+ if (monitor == NULL) {
+ continue;
+ }
+
+ dpy.monitors = monitor_ring_erase(dpy.monitors, monitor);
+ free(monitor);
+ any_change = true;
+ } else {
+ // New monitor or existing one changed its info
+ any_change = any_change || dpy_update_output(outputs[i], timestamp, o_reply);
+ }
+
+ free(o_reply);
+ }
+ free(s_reply);
+ return any_change;
}
static void
@@ -651,6 +840,7 @@ dpy_init(const char *wm_name) {
dpy.has_error = false;
dpy.has_randr = false;
dpy.randr_base = -1;
+ dpy.monitors = NULL;
dpy.conn = xcb_connect(NULL, &dpy.screen_num);
if (wm_has_error()) {
@@ -707,7 +897,8 @@ dpy_init(const char *wm_name) {
net_atoms);
// Setup RandR
- const struct xcb_query_extension_reply_t *reply = xcb_get_extension_data(dpy.conn, &xcb_randr_id);
+ const struct xcb_query_extension_reply_t *reply =
+ xcb_get_extension_data(dpy.conn, &xcb_randr_id);
if (!reply || !reply->present) {
dpy.has_randr = false;
} else {
@@ -791,6 +982,81 @@ dpy_init(const char *wm_name) {
return !dpy.has_error;
}
+static bool
+dpy_grab_keycode(uint16_t mod, xcb_keycode_t keycode) {
+ xcb_void_cookie_t cookie = xcb_grab_key_checked(
+ dpy.conn,
+ true,
+ dpy.screen->root,
+ mod,
+ keycode,
+ XCB_GRAB_MODE_ASYNC,
+ XCB_GRAB_MODE_ASYNC);
+ xcb_generic_error_t *error = xcb_request_check(dpy.conn, cookie);
+ return !error;
+}
+
+static void
+dpy_grab_keycode_with_lock_mods(uint16_t mod, xcb_keycode_t keycode) {
+ dpy_grab_keycode(mod, keycode);
+ if (mod == XCB_MOD_MASK_ANY) {
+ return;
+ }
+
+ if (dpy.num_lock != 0) {
+ dpy_grab_keycode(mod | dpy.num_lock, keycode);
+ }
+ if (dpy.caps_lock != 0) {
+ dpy_grab_keycode(mod | dpy.caps_lock, keycode);
+ }
+ if (dpy.scroll_lock != 0) {
+ dpy_grab_keycode(mod | dpy.scroll_lock, keycode);
+ }
+ if (dpy.num_lock != 0 && dpy.caps_lock != 0) {
+ dpy_grab_keycode(mod | dpy.num_lock | dpy.caps_lock, keycode);
+ }
+ if (dpy.caps_lock != 0 && dpy.scroll_lock != 0) {
+ dpy_grab_keycode(mod | dpy.caps_lock | dpy.scroll_lock, keycode);
+ }
+ if (dpy.num_lock != 0 && dpy.scroll_lock != 0) {
+ dpy_grab_keycode(mod | dpy.num_lock | dpy.scroll_lock, keycode);
+ }
+ if (dpy.num_lock != 0 && dpy.caps_lock != 0 && dpy.scroll_lock != 0) {
+ dpy_grab_keycode(mod | dpy.num_lock | dpy.caps_lock | dpy.scroll_lock, keycode);
+ }
+}
+
+static void
+dpy_grab_key(uint16_t mod, xcb_keysym_t keysym) {
+ const xcb_setup_t *setup = xcb_get_setup(dpy.conn);
+ if (!setup) {
+ return;
+ }
+
+ /*
+ * We go through every keycode in the setup, looking for the ones
+ * matching our keysym request.
+ */
+ unsigned int kc;
+ for (kc = setup->min_keycode; kc <= setup->max_keycode; kc++) {
+ for (unsigned int col = 0; col < 4 /* KEYSYMS_PER_KEYCODE */; col++) {
+ xcb_keysym_t ks = xcb_key_symbols_get_keysym(dpy.keysyms, kc, col);
+ if (ks == keysym) {
+ dpy_grab_keycode_with_lock_mods(mod, kc);
+ }
+ }
+ }
+}
+
+static void
+dpy_set_workspace(uint32_t workspace) {
+ xcb_ewmh_set_current_desktop(dpy.ewmh, dpy.screen_num, workspace);
+}
+
+static void
+dpy_set_num_workspaces() {
+ xcb_ewmh_set_number_of_desktops(dpy.ewmh, dpy.screen_num, wm.num_workspaces);
+}
static void
dpy_map_window(xcb_window_t window) {
@@ -812,14 +1078,13 @@ dpy_raise_window(xcb_window_t window) {
xcb_configure_window(dpy.conn, window, XCB_CONFIG_WINDOW_STACK_MODE, &mode);
}
-static void
-dpy_set_workspace(uint32_t workspace) {
- xcb_ewmh_set_current_desktop(dpy.ewmh, dpy.screen_num, workspace);
-}
-
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);
+ xcb_configure_window(
+ dpy.conn,
+ window,
+ XCB_CONFIG_WINDOW_BORDER_WIDTH,
+ &cfg.inner_border_width);
}
void
@@ -827,6 +1092,66 @@ dpy_set_window_workspace(xcb_window_t window, uint32_t workspace) {
xcb_ewmh_set_wm_desktop(dpy.ewmh, window, workspace);
}
+uint32_t
+dpy_get_window_workspace(xcb_window_t window) {
+ xcb_get_property_cookie_t cookie = xcb_ewmh_get_wm_desktop(dpy.ewmh, window);
+ uint32_t workspace = NULL_WORKSPACE;
+ // We don't check the return of the call below because, if anything goes
+ // wrong, we'd just assume o3::NULL_WORKSPACE anyway
+ xcb_ewmh_get_wm_desktop_reply(dpy.ewmh, cookie, &workspace, NULL);
+ return workspace;
+}
+
+void
+dpy_update_window_geometry(const struct Client *client) {
+ xcb_window_t window = client->id;
+ if (dpy.screen->root == window || window == XCB_NONE) {
+ return;
+ }
+
+ uint32_t data[] = {
+ (uint32_t) client->x,
+ (uint32_t) client->y,
+ (uint32_t) client->width,
+ (uint32_t) client->height,
+ };
+ uint16_t mask = XCB_CONFIG_WINDOW_X
+ | XCB_CONFIG_WINDOW_Y
+ | XCB_CONFIG_WINDOW_WIDTH
+ | XCB_CONFIG_WINDOW_HEIGHT;
+ xcb_configure_window(dpy.conn, window, mask, data);
+}
+
+void
+dpy_update_window_position(const struct Client *client) {
+ xcb_window_t window = client->id;
+ if (dpy.screen->root == window || window == XCB_NONE) {
+ return;
+ }
+
+ uint32_t data[] = {
+ (uint32_t) client->x,
+ (uint32_t) client->y,
+ };
+ uint16_t mask = XCB_CONFIG_WINDOW_X | XCB_CONFIG_WINDOW_Y;
+ xcb_configure_window(dpy.conn, window, mask, data);
+}
+
+void
+dpy_update_window_size(const struct Client *client) {
+ xcb_window_t window = client->id;
+ if (dpy.screen->root == window || window == XCB_NONE) {
+ return;
+ }
+
+ uint32_t data[] = {
+ (uint32_t) client->width,
+ (uint32_t) client->height,
+ };
+ uint16_t mask = XCB_CONFIG_WINDOW_WIDTH | XCB_CONFIG_WINDOW_HEIGHT;
+ xcb_configure_window(dpy.conn, window, mask, data);
+}
+
#define RECT_INIT(x, y, w, h) { \
(int16_t) (x), \
(int16_t) (y), \
@@ -963,6 +1288,148 @@ void
dpy_grab_buttons(struct Client *client) {
}
+bool
+dpy_fetch_geometry(struct Client *client) {
+ xcb_get_geometry_cookie_t cookie = xcb_get_geometry(dpy.conn, client->id);
+ xcb_get_geometry_reply_t *reply =
+ xcb_get_geometry_reply(dpy.conn, cookie, NULL);
+ if (!reply) {
+ return false;
+ }
+
+ client->x = reply->x;
+ client->y = reply->y;
+ client->width = reply->width;
+ client->height = reply->height;
+ free(reply);
+ return true;
+}
+
+bool
+dpy_fetch_hints(struct Client *client) {
+ xcb_size_hints_t hints;
+ xcb_get_property_cookie_t cookie =
+ xcb_icccm_get_wm_normal_hints_unchecked(dpy.conn, client->id);
+ uint8_t reply = xcb_icccm_get_wm_normal_hints_reply(dpy.conn, cookie, &hints, NULL);
+ if (!reply) {
+ return false;
+ }
+
+ if (hints.flags & XCB_ICCCM_SIZE_HINT_US_POSITION) {
+ client->user_coord = true;
+ client->x = hints.x;
+ client->y = hints.y;
+ }
+ if (hints.flags & XCB_ICCCM_SIZE_HINT_P_MIN_SIZE) {
+ client->min_width = hints.min_width;
+ client->min_height = hints.min_height;
+ }
+ if (hints.flags & XCB_ICCCM_SIZE_HINT_P_MAX_SIZE) {
+ client->max_width = hints.max_width;
+ client->max_height = hints.max_height;
+ }
+ if (hints.flags & XCB_ICCCM_SIZE_HINT_BASE_SIZE) {
+ client->base_width = hints.base_width;
+ client->base_height = hints.base_height;
+ }
+ return true;
+}
+
+bool
+dpy_fetch_cursor_position(struct Point *p) {
+ xcb_query_pointer_cookie_t cookie =
+ xcb_query_pointer(dpy.conn, dpy.screen->root);
+ xcb_query_pointer_reply_t *reply =
+ xcb_query_pointer_reply(dpy.conn, cookie, NULL);
+ if (!reply) {
+ return false;
+ }
+
+ p->x = reply->win_x;
+ p->y = reply->win_y;
+ free(reply);
+ return true;
+}
+
+struct Client *
+dpy_make_client(xcb_window_t window) {
+ xcb_ewmh_get_atoms_reply_t window_type;
+ xcb_get_property_cookie_t cookie =
+ xcb_ewmh_get_wm_window_type(dpy.ewmh, window);
+ uint8_t reply = xcb_ewmh_get_wm_window_type_reply(
+ dpy.ewmh,
+ cookie,
+ &window_type,
+ NULL);
+ if (reply) {
+ for (uint32_t i = 0; i < window_type.atoms_len; i++) {
+ xcb_atom_t atom = window_type.atoms[i];
+ if (atom == dpy.ewmh->_NET_WM_WINDOW_TYPE_TOOLBAR
+ || atom == dpy.ewmh->_NET_WM_WINDOW_TYPE_DESKTOP
+ || atom == dpy.ewmh->_NET_WM_WINDOW_TYPE_DOCK) {
+ // Ignore those windows
+ xcb_ewmh_get_atoms_reply_wipe(&window_type);
+ return NULL;
+ }
+ }
+ xcb_ewmh_get_atoms_reply_wipe(&window_type);
+ }
+
+ uint32_t event_mask[] = { XCB_EVENT_MASK_ENTER_WINDOW, XCB_NONE };
+ xcb_change_window_attributes(dpy.conn, window, XCB_CW_EVENT_MASK, event_mask);
+ xcb_change_window_attributes(dpy.conn, window, XCB_CW_BACK_PIXEL, &cfg.colors.empty);
+ xcb_change_save_set(dpy.conn, XCB_SET_MODE_INSERT, window);
+
+ struct Client *client = client_new(window);
+ if (client) {
+ dpy_fetch_geometry(client);
+ // Assume a reasonable max_width/max_height, then fetch hints
+ client->max_width = dpy.screen->width_in_pixels;
+ client->max_height = dpy.screen->height_in_pixels;
+ dpy_fetch_hints(client);
+ }
+ return client;
+}
+
+/*
+ * Finds the monitor which contains most of the client's rect.
+ */
+struct Monitor *
+find_monitor(const struct Client *client) {
+ if (dpy.monitors == NULL) {
+ return NULL;
+ }
+ if (dpy.monitors->next == dpy.monitors) {
+ // We only have one monitor
+ return dpy.monitors;
+ }
+
+ struct Rect c_rect;
+ c_rect.x = client->x;
+ c_rect.y = client->y;
+ c_rect.width = client->width;
+ c_rect.height = client->height;
+
+ uint32_t max_area = 0;
+ struct Monitor *iter = dpy.monitors;
+ struct Monitor *monitor = iter;
+ for (;;) {
+ struct Rect mon_rect;
+ monitor_rect(iter, &mon_rect);
+ uint32_t area = rect_intersect_area(&c_rect, &mon_rect);
+ if (area > max_area) {
+ max_area = area;
+ monitor = iter;
+ }
+ iter = iter->next;
+ if (iter == dpy.monitors) {
+ // We reached the first monitor in the ring again
+ break;
+ }
+ }
+ return monitor;
+}
+
void
wm_set_focus(struct Client *client) {
if (wm.focus != NULL) {
@@ -1030,8 +1497,12 @@ wm_set_workspace(uint32_t workspace) {
}
}
+void
+wm_set_client_workspace(struct Client *client, uint32_t workspace) {
+}
+
/*
- * Event handlers which take a generic event and know what to cast it into.
+ * event handlers which take a generic event and know what to cast it into.
*/
static void ev_configure_request(xcb_generic_event_t *);
static void ev_destroy_notify(xcb_generic_event_t *);
@@ -1045,9 +1516,44 @@ static void ev_circulate_request(xcb_generic_event_t *);
static void ev_button_press(xcb_generic_event_t *);
static void ev_client_message(xcb_generic_event_t *);
-static bool
-setup_client(xcb_window_t window) {
- return false;
+static struct Client *
+manage_client_maybe_reposition(xcb_window_t window, bool set_position) {
+ struct Client *client = dpy_make_client(window);
+ if (!client) {
+ return NULL;
+ }
+ // TODO: check if unkillable
+ uint32_t workspace = dpy_get_window_workspace(client->id);
+ if (workspace == ALL_WORKSPACES) {
+ fprintf(stderr, "client was supposed to be sticky;"
+ " mapping to current workspace\n");
+ workspace = wm.cur_workspace;
+ } else if (workspace == NULL_WORKSPACE || workspace >= wm.num_workspaces) {
+ workspace = wm.cur_workspace;
+ }
+ wm_set_client_workspace(client, workspace);
+ if (set_position && !client->user_coord) {
+ // No predefined position, map at cursor position if possible
+ struct Point p;
+ if (!dpy_fetch_cursor_position(&p)) {
+ p.x = p.y = 0;
+ }
+ client->x = p.x - client->width / 2;
+ client->y = p.y - client->height / 2;
+ dpy_update_window_position(client);
+ }
+ client->monitor = find_monitor(client);
+ wm_fit_client(client);
+ clients_map_add(wm.clients, client);
+ if (client->state == WS_NORMAL) {
+ dpy_draw_unfocused_borders(client);
+ }
+ return client;
+}
+
+static struct Client *
+manage_client(xcb_window_t window) {
+ return manage_client_maybe_reposition(window, false);
}
bool
@@ -1057,24 +1563,22 @@ wm_init() {
}
wm.is_running = true;
- wm.cur_workspace = 0;
- wm.num_workspaces = 10;
- wm.workspaces = calloc(wm.num_workspaces, sizeof(struct Workspace));
wm.focus = NULL;
wm.clients = clients_map_new(128);
- // TODO
- // wm.workspaces = INIT;
- // dpy_set_num_workspaces();
+ wm.cur_workspace = 0;
+ wm.num_workspaces = 10;
+ wm.workspaces = calloc(wm.num_workspaces, sizeof(struct Workspace));
+ dpy_set_num_workspaces();
// Look into all existing windows and set them up
struct WindowsQueryReply reply;
windows_query(dpy.screen->root, &reply);
for (int i = 0; i < reply.length; i++) {
xcb_window_t window = reply.data[i];
- if (!setup_client(window)) {
+ if (!manage_client(window)) {
/*
- * Could not be setup, just map it on screen because we
+ * Could not be managed, just map it on screen because we
* probably should not own it.
*/
dpy_map_window(window);
@@ -1093,6 +1597,7 @@ wm_init() {
// update_client_list();
wm_set_workspace(wm.cur_workspace);
// grab_keys();
+ dpy_grab_key(XCB_MOD_MASK_ANY, XK_q);
dpy_flush();
memset(wm.events, 0, sizeof(wm.events));
@@ -1148,15 +1653,16 @@ wm_destroy() {
dpy_destroy();
}
-static int
+static uint8_t
ev_type(const xcb_generic_event_t *ev) {
- return ev->response_type & 0x80;
+ return ev->response_type & ~0x80;
}
void
wm_run() {
while (wm.is_running) {
xcb_generic_event_t *ev = xcb_wait_for_event(dpy.conn);
+ fprintf(stderr, "ev: %u\n", ev_type(ev));
generic_event_handler_t handler = wm.events[ev_type(ev)];
if (handler) {
handler(ev);
@@ -1167,7 +1673,7 @@ wm_run() {
wm_destroy();
}
-static uint16_t mod_key = META;
+static uint16_t mod_key = MOD_META;
void
wm_set_mod_key(uint16_t mod) {
@@ -1177,7 +1683,7 @@ wm_set_mod_key(uint16_t mod) {
uint16_t
wm_get_mod_key(bool with_shift) {
if (with_shift) {
- return mod_key | SHIFT;
+ return mod_key | MOD_SHIFT;
}
return mod_key;
}
@@ -1204,7 +1710,6 @@ wm_set_configure_request_handler(configure_request_handler_t handler) {
static void
ev_configure_request(xcb_generic_event_t *generic_ev) {
- fprintf(stderr, "configure_request\n");
if (configure_request_handler) {
xcb_configure_request_event_t *ev = (xcb_configure_request_event_t *) generic_ev;
configure_request_handler();
@@ -1222,7 +1727,6 @@ wm_set_destroy_notify_handler(destroy_notify_handler_t handler) {
static void
ev_destroy_notify(xcb_generic_event_t *generic_ev) {
- fprintf(stderr, "destroy_notify\n");
if (destroy_notify_handler) {
xcb_destroy_notify_event_t *ev = (xcb_destroy_notify_event_t *) generic_ev;
destroy_notify_handler();
@@ -1240,7 +1744,6 @@ wm_set_enter_notify_handler(enter_notify_handler_t handler) {
static void
ev_enter_notify(xcb_generic_event_t *generic_ev) {
- fprintf(stderr, "enter_notify\n");
if (enter_notify_handler) {
xcb_enter_notify_event_t *ev = (xcb_enter_notify_event_t *) generic_ev;
enter_notify_handler();
@@ -1248,7 +1751,7 @@ ev_enter_notify(xcb_generic_event_t *generic_ev) {
}
// XCB_KEY_PRESS
-typedef void (*key_press_handler_t)();
+typedef void (*key_press_handler_t)(uint16_t, uint8_t);
static key_press_handler_t key_press_handler = NULL;
void
@@ -1258,10 +1761,9 @@ wm_set_key_press_handler(key_press_handler_t handler) {
static void
ev_key_press(xcb_generic_event_t *generic_ev) {
- fprintf(stderr, "key_press\n");
if (key_press_handler) {
xcb_key_press_event_t *ev = (xcb_key_press_event_t *) generic_ev;
- key_press_handler();
+ key_press_handler(ev->state, ev->detail);
}
}
@@ -1276,7 +1778,6 @@ wm_set_map_request_handler(map_request_handler_t handler) {
static void
ev_map_request(xcb_generic_event_t *generic_ev) {
- fprintf(stderr, "map_resquest\n");
if (map_request_handler) {
xcb_map_request_event_t *ev = (xcb_map_request_event_t *) generic_ev;
map_request_handler();
@@ -1294,7 +1795,6 @@ wm_set_unmap_notify_handler(unmap_notify_handler_t handler) {
static void
ev_unmap_notify(xcb_generic_event_t *generic_ev) {
- fprintf(stderr, "unmap_notify\n");
if (unmap_notify_handler) {
xcb_unmap_notify_event_t *ev = (xcb_unmap_notify_event_t *) generic_ev;
unmap_notify_handler();
@@ -1312,7 +1812,6 @@ wm_set_mapping_notify_handler(mapping_notify_handler_t handler) {
static void
ev_mapping_notify(xcb_generic_event_t *generic_ev) {
- fprintf(stderr, "mapping_notify\n");
if (mapping_notify_handler) {
xcb_mapping_notify_event_t *ev = (xcb_mapping_notify_event_t *) generic_ev;
mapping_notify_handler();
diff --git a/wm.scm b/wm.scm
index 06d1581..85a23f4 100644
--- a/wm.scm
+++ b/wm.scm
@@ -1,7 +1,7 @@
(define-module (wm)
#:use-module (system foreign)
#:export (wm-init
- wm-set-configure-notify-handler!
+ wm-set-key-press-handler!
wm-run
wm-quit))
@@ -28,14 +28,16 @@
(define wm-run
(schewm-func void "wm_run" '()))
-(define c/wm-set-configure-notify-handler
+(define c/wm-set-key-press-handler
(schewm-func void
- "wm_set_configure_notify_handler"
+ "wm_set_key_press_handler"
(list '*)))
-(define (wm-set-configure-notify-handler! handler)
- (c/wm-set-configure-notify-handler
+(define (wm-set-key-press-handler! handler)
+ ;; mod (state, uint16)
+ ;; keysym (detail, uint8)
+ (c/wm-set-key-press-handler
(procedure->pointer void
handler
- (list int16 int16 uint32 uint32))))
+ (list uint16 uint8))))