diff --git a/src/client.c b/src/client.c index d45b24d..a4125f4 100644 --- a/src/client.c +++ b/src/client.c @@ -140,6 +140,8 @@ static inline void client_update_names(client_t *c) { } static inline void client_init_geometry(client_t *c) { + if (c->maximize || c->fullscreen) return; + area_t workarea = c->monitor->workarea; if (c->geometry.x + client_width(c) > workarea.x + workarea.width) { c->geometry.x = workarea.x + workarea.width - client_width(c); @@ -221,6 +223,7 @@ void client_manage(xcb_window_t window, } else { c->monitor = wm.current_monitor; c->tags = c->monitor->selected_tag->mask; + client_apply_rules(c, wm.rules, wm.rules_count); } client_init_geometry(c); @@ -496,13 +499,22 @@ void client_unmanage(client_t *client) { } void client_apply_geometry(client_t *client, area_t geometry) { - if (client->geometry.x != geometry.x || client->geometry.y != geometry.y) { - client_move_to(client, geometry.x, geometry.y); - } - if (client->geometry.width != geometry.width || - client->geometry.height != geometry.height) { - client_resize(client, geometry.width, geometry.height); + uint16_t mask = 0; + const xcb_params_configure_window_t params = { + .x = geometry.x, + .y = geometry.y, + .width = geometry.width, + .height = geometry.height, + }; + if (client->geometry.x != geometry.x) mask |= XCB_CONFIG_WINDOW_X; + if (client->geometry.y != geometry.y) mask |= XCB_CONFIG_WINDOW_Y; + if (client->geometry.width != geometry.width) mask |= XCB_CONFIG_WINDOW_WIDTH; + if (client->geometry.height != geometry.height) { + mask |= XCB_CONFIG_WINDOW_HEIGHT; } + + client->geometry = geometry; + xcb_aux_configure_window(wm.xcb_conn, client->window, mask, ¶ms); } /** @@ -527,6 +539,51 @@ void client_apply_workarea_geometry(client_t *client, area_t geometry) { client_apply_geometry(client, rect); } +void client_apply_rules(client_t *client, const rule_t rules[], + size_t rules_count) { + for (size_t i = 0; i < rules_count; i++) { + const rule_t *r = &rules[i]; + if (!((!r->role || (client->role && strstr(client->role, r->role))) && + (!r->class || (client->class && strstr(client->class, r->class))))) { + continue; + } + + client->fullscreen = r->fullscreen; + client->maximize = r->maximize; + client->floating = r->floating; + + for (monitor_t *m = wm.monitor_list; m; m = m->next) { + for (tag_t *t = m->tag_list; t; t = t->next) { + if (t->index != r->tag_index) continue; + + client->monitor = m; + client->tags = t->mask; + + if (r->switch_to_tag) { + wm.current_monitor = m; + m->selected_tag = t; + + logger("++ switch to tag: %u\n", t->index); + } + } + } + } + + if (client->fullscreen) { + xcb_change_property(wm.xcb_conn, XCB_PROP_MODE_REPLACE, client->window, + _NET_WM_STATE, XCB_ATOM_ATOM, 32, 1, + &_NET_WM_STATE_FULLSCREEN); + } else if (client->maximize) { + xcb_atom_t max_atoms[] = { + _NET_WM_STATE_MAXIMIZED_HORZ, + _NET_WM_STATE_MAXIMIZED_VERT, + }; + xcb_change_property(wm.xcb_conn, XCB_PROP_MODE_REPLACE, client->window, + _NET_WM_STATE, XCB_ATOM_ATOM, 32, countof(max_atoms), + max_atoms); + } +} + void client_focus(client_t *client) { wm.client_focused = client; xwindow_focus(client ? client->window : XCB_WINDOW_NONE); diff --git a/src/client.h b/src/client.h index 0223b90..c77a437 100644 --- a/src/client.h +++ b/src/client.h @@ -1,5 +1,6 @@ #pragma once +#include #include #include "color.h" @@ -52,6 +53,8 @@ void client_kill(client_t *client); void client_unmanage(client_t *client); void client_apply_geometry(client_t *client, area_t geometry); void client_apply_workarea_geometry(client_t *client, area_t geometry); +void client_apply_rules(client_t *client, const rule_t rules[], + size_t rules_count); void client_focus(client_t *client); void client_stack_raise(client_t *client); diff --git a/src/default_config.h b/src/default_config.h index a2230d7..4da8af2 100644 --- a/src/default_config.h +++ b/src/default_config.h @@ -104,3 +104,11 @@ static const char *const autostart_list[] = { "fcitx5 -d", nullptr, }; + +static const rule_t rules[] = { + {.role = "dialog", .tag_index = -1, .floating = true}, + {.class = "firefox", .tag_index = 1, .maximize = true}, + {.class = "Google-chrome", .tag_index = 1, .maximize = true}, + {.class = "mpv", .tag_index = 3, .switch_to_tag = true, .fullscreen = true}, + {.class = "Emacs", .tag_index = 10, .switch_to_tag = true, .maximize = true}, +}; diff --git a/src/event.c b/src/event.c index 45e9497..ddba3e0 100644 --- a/src/event.c +++ b/src/event.c @@ -139,10 +139,22 @@ static void client_message(xcb_client_message_event_t *ev) { for (tag_t *t = c->monitor->tag_list; t; t = t->next) { if ((t->mask & c->tags) == 0) continue; - wm.current_monitor = c->monitor; - monitor_select_tag(c->monitor, t->mask); - client_focus(c); - return; + logger("== _NET_ACTIVE_WINDOW: %s[%u]\n", c->class, ev->data.data32[0]); + + switch (ev->data.data32[0]) { + case 2: /* 来自 pager */ + wm.current_monitor = c->monitor; + monitor_select_tag(c->monitor, t->mask); + client_focus(c); + break; + case 1: /* 来自应用程序 */ + c->urgent = true; + monitor_draw_bar(c->monitor); + break; + case 0: /* 来自老版本标志 */ + default: + break; + } } } else if (ev->type == _NET_WM_STATE) { /** diff --git a/src/monitor.c b/src/monitor.c index 9d21902..c43793e 100644 --- a/src/monitor.c +++ b/src/monitor.c @@ -69,12 +69,14 @@ void monitor_select_tag(monitor_t *monitor, uint32_t tag_mask) { for (task_in_tag_t *task = monitor->selected_tag->task_list; task; task = task->next) { - if (task->client->floating || task->client->size_freeze) { + if (task->client->floating || task->client->size_freeze || + !monitor->selected_tag->layout->arrange) { task->geometry = task->client->geometry; + task->client->old_geometry = task->client->geometry; } if (task->client->fullscreen || task->client->maximize || task->client->minimize) { - task->geometry = task->client->old_geometry; + task->geometry = task->client->geometry; } } @@ -280,25 +282,24 @@ void monitor_arrange(monitor_t *monitor) { tag_t *tag = monitor->selected_tag; if (tag->layout && tag->layout->arrange) tag->layout->arrange(tag); - for (client_t *c = wm.client_list; c; c = c->next) { - if (c->monitor != monitor) continue; + for (client_t *c = wm.client_stack_list; c; c = c->stack_next) { + if (c->monitor != monitor || c->minimize) continue; task_in_tag_t *task = client_get_task_in_tag(c, tag); if (task) { - if (c->minimize) continue; - if (c->fullscreen) { + if (client_need_layout(c)) { + client_apply_geometry(c, task->geometry); + } else if (c->fullscreen) { client_apply_geometry(c, c->monitor->geometry); } else if (c->maximize) { client_apply_geometry(c, c->monitor->workarea); } else { - client_apply_geometry(c, task->geometry); + client_move_to(c, task->geometry.x, task->geometry.y); } } else { int16_t x = -client_width(c); int16_t y = -client_height(c); - if (x != c->geometry.x || y != c->geometry.y) { - client_move_to(c, x, y); - } + client_move_to(c, x, y); } } xcb_flush(wm.xcb_conn); diff --git a/src/types.h b/src/types.h index 2f09770..3938f88 100644 --- a/src/types.h +++ b/src/types.h @@ -100,3 +100,13 @@ typedef struct padding_t { uint16_t bar_y; uint16_t tag_x; } padding_t; + +typedef struct rule_t { + const char *role; + const char *class; + uint32_t tag_index; + bool switch_to_tag; + bool fullscreen; + bool maximize; + bool floating; +} rule_t; diff --git a/src/wm.c b/src/wm.c index 51880a9..6fe42e3 100644 --- a/src/wm.c +++ b/src/wm.c @@ -338,6 +338,9 @@ static void wm_setup(void) { wm.layout_list = layout_list; wm.layout_count = (uint16_t)countof(layout_list); + wm.rules = rules; + wm.rules_count = countof(rules); + wm_get_xres_config(); wm_init_color_set(); diff --git a/src/wm.h b/src/wm.h index b3a34f4..6fdaaec 100644 --- a/src/wm.h +++ b/src/wm.h @@ -27,6 +27,8 @@ typedef struct wm_t { char *font_family; uint32_t font_size; uint32_t dpi; + const rule_t *rules; + uint32_t rules_count; xcb_connection_t *xcb_conn; xcb_screen_t *screen;