diff --git a/src/client.c b/src/client.c index fe712d6..bc8ab1d 100644 --- a/src/client.c +++ b/src/client.c @@ -8,6 +8,7 @@ #include #include "atoms-extern.h" +#include "monitor.h" #include "types.h" #include "utils.h" #include "wm.h" @@ -56,17 +57,58 @@ task_in_tag_t *client_get_task_in_tag(client_t *client, tag_t *tag) { return nullptr; } +static void client_add_to_tag(client_t *client, tag_t *tag) { + if (client_get_task_in_tag(client, tag)) return; + + task_in_tag_t *task = p_new(task_in_tag_t, 1); + task->client = client; + task->next = tag->task_list; + tag->task_list = task; +} + +static void client_remove_from_tag(client_t *client, tag_t *tag) { + for (task_in_tag_t **task = &tag->task_list; *task; task = &(*task)->next) { + if ((*task)->client != client) continue; + p_delete(task); + *task = (*task)->next; + } +} + +static inline void client_wipe(client_t *client) { + p_delete(&client->class); + p_delete(&client->instance); + p_delete(&client->name); + p_delete(&client->icon_name); + p_delete(&client->net_name); + p_delete(&client->net_icon_name); + p_delete(&client->role); + p_delete(&client); +} + +static inline void client_tags_apply(client_t *client) { + for (tag_t *tag = client->monitor->tag_list; tag; tag = tag->next) { + if (client->tags & tag->mask) { + client_add_to_tag(client, tag); + } else { + client_remove_from_tag(client, tag); + } + } + + if (client->tags == 0) client_wipe(client); +} + +static inline void client_update_names(client_t *c) { + xwindow_get_text_property(c->window, WM_NAME, &c->name); + xwindow_get_text_property(c->window, WM_ICON_NAME, &c->icon_name); + xwindow_get_text_property(c->window, _NET_WM_NAME, &c->net_name); + xwindow_get_text_property(c->window, _NET_WM_ICON_NAME, &c->net_icon_name); +} + void client_manage(xcb_window_t window, xcb_get_geometry_reply_t *geometry_reply) { client_t *c = p_new(client_t, 1); c->window = window; - c->name = xwindow_get_text_property(c->window, WM_NAME); - c->icon_name = xwindow_get_text_property(c->window, WM_ICON_NAME); - c->net_name = xwindow_get_text_property(c->window, _NET_WM_NAME); - c->net_icon_name = xwindow_get_text_property(c->window, _NET_WM_ICON_NAME); - c->role = xwindow_get_text_property(c->window, WM_WINDOW_ROLE); - client_attach_list(c); client_attach_stack(c); @@ -79,6 +121,7 @@ void client_manage(xcb_window_t window, c->instance = strdup(prop.instance_name); xcb_icccm_get_wm_class_reply_wipe(&prop); } + xwindow_get_text_property(c->window, WM_WINDOW_ROLE, &c->role); } { @@ -96,8 +139,11 @@ void client_manage(xcb_window_t window, c->monitor = wm.current_monitor; c->tags = c->monitor->selected_tag->mask; } + + client_tags_apply(c); } + monitor_arrange(c->monitor); { const xcb_params_cw_t params = { .event_mask = XCB_EVENT_MASK_ENTER_WINDOW | XCB_EVENT_MASK_FOCUS_CHANGE | @@ -109,9 +155,19 @@ void client_manage(xcb_window_t window, xcb_map_window(wm.xcb_conn, window); } + client_update_names(c); + monitor_draw_bar(c->monitor); xcb_flush(wm.xcb_conn); } +char **client_get_task_title(client_t *client) { + if (client->net_icon_name) return &client->net_icon_name; + if (client->net_name) return &client->net_name; + if (client->icon_name) return &client->icon_name; + if (client->name) return &client->name; + return nullptr; +} + bool client_need_layout(client_t *client) { if (client->floating || client->fullscreen || client->maximize || client->minimize) { diff --git a/src/client.h b/src/client.h index df3e8aa..73e37ee 100644 --- a/src/client.h +++ b/src/client.h @@ -9,6 +9,7 @@ client_t *client_get_by_window(xcb_window_t window); task_in_tag_t *client_get_task_in_tag(client_t *client, tag_t *tag); void client_manage(xcb_window_t window, xcb_get_geometry_reply_t *geometry_reply); +char **client_get_task_title(client_t *client); bool client_need_layout(client_t *client); void client_send_to_tag(client_t *client, uint32_t tag_mask); diff --git a/src/event.c b/src/event.c index 9c1094d..67647b8 100644 --- a/src/event.c +++ b/src/event.c @@ -2,7 +2,7 @@ #include #include -#include +#include #include #include #include @@ -10,12 +10,15 @@ #include #include "action.h" +#include "atoms-extern.h" #include "client.h" #include "default_config.h" +#include "monitor.h" #include "types.h" #include "utils.h" #include "wm.h" #include "xkb.h" +#include "xwindow.h" static void button_press(xcb_button_press_event_t *ev) { click_area_t click_area = click_none; @@ -116,6 +119,26 @@ static void map_request(xcb_map_request_event_t *ev) { p_delete(&wa_reply); } +static void property_notify(xcb_property_notify_event_t *ev) { + client_t *client = client_get_by_window(ev->window); + if (client == nullptr) return; + + if (ev->atom == WM_NAME) { + xwindow_get_text_property(ev->window, ev->atom, &client->name); + monitor_draw_bar(client->monitor); + } else if (ev->atom == WM_ICON_NAME) { + if (client->icon_name) p_delete(&client->icon_name); + xwindow_get_text_property(ev->window, ev->atom, &client->icon_name); + monitor_draw_bar(client->monitor); + } else if (ev->atom == _NET_WM_NAME) { + xwindow_get_text_property(ev->window, ev->atom, &client->net_name); + monitor_draw_bar(client->monitor); + } else if (ev->atom == _NET_WM_ICON_NAME) { + xwindow_get_text_property(ev->window, ev->atom, &client->net_icon_name); + monitor_draw_bar(client->monitor); + } +} + static void handle_xcb_event(xcb_generic_event_t *event) { uint8_t event_type = XCB_EVENT_RESPONSE_TYPE(event); const char *label = xcb_event_get_label(event_type); @@ -133,6 +156,7 @@ static void handle_xcb_event(xcb_generic_event_t *event) { EVENT(XCB_KEY_RELEASE, key_press); EVENT(XCB_CONFIGURE_REQUEST, configure_request); EVENT(XCB_MAP_REQUEST, map_request); + EVENT(XCB_PROPERTY_NOTIFY, property_notify); #undef EVENT } diff --git a/src/monitor.c b/src/monitor.c index 15e7b6c..df99280 100644 --- a/src/monitor.c +++ b/src/monitor.c @@ -197,6 +197,34 @@ static void monitor_draw_layout_symbol(monitor_t *monitor) { } } +static void monitor_draw_tasks(monitor_t *monitor) { + task_in_tag_t *task_list = monitor->selected_tag->task_list; + if (!task_list) return; + + uint16_t task_count = 0; + for (task_in_tag_t *task = task_list; task; task = task->next) ++task_count; + if (task_count == 0) return; + + int16_t x = monitor->layout_symbol_extent.end; + uint16_t task_width = (monitor->geometry.width - x) / task_count; + for (task_in_tag_t *task = task_list; task; task = task->next) { + task->bar_extent.start = x; + task->bar_extent.end = x + task_width; + x += task_width; + + char **title = client_get_task_title(task->client); + if (title == nullptr || *title == nullptr) continue; + color_t *color = &wm.color_set.active_tag_color; + area_t area = { + .x = task->bar_extent.start, + .y = monitor->geometry.y, + .width = task_width, + .height = wm.bar_height, + }; + draw_text(monitor->bar_cr, *title, color, area, false); + } +} + void monitor_draw_bar(monitor_t *monitor) { cairo_t *cr = monitor->bar_cr; uint16_t height = wm.bar_height; @@ -210,6 +238,7 @@ void monitor_draw_bar(monitor_t *monitor) { monitor_draw_tags(monitor); monitor_draw_layout_symbol(monitor); + monitor_draw_tasks(monitor); } void monitor_arrange(monitor_t *monitor) {