From a772d0f56f763c03a6a70d9567cfed4de1c1ff43 Mon Sep 17 00:00:00 2001 From: Zedhugh Chen Date: Wed, 28 Jan 2026 23:40:15 +0800 Subject: [PATCH] =?UTF-8?q?=E6=94=AF=E6=8C=81=20client=20=E7=AA=97?= =?UTF-8?q?=E5=8F=A3=E6=B5=AE=E5=8A=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/action.c | 6 +++++ src/action.h | 1 + src/client.c | 62 ++++++++++++++++++++++++++++++++++---------- src/client.h | 5 ++-- src/default_config.h | 2 ++ src/event.c | 2 +- src/monitor.c | 6 ++++- src/types.h | 4 ++- src/utils.h | 5 ++++ 9 files changed, 75 insertions(+), 18 deletions(-) diff --git a/src/action.c b/src/action.c index bacedfc..4486b18 100644 --- a/src/action.c +++ b/src/action.c @@ -75,3 +75,9 @@ void raise_or_run(const user_action_arg_t *arg) { user_action_arg_t arguments = {.ptr = command}; spawn(&arguments); } + +void toggle_client_floating(const user_action_arg_t *arg) { + if (!wm.client_focused) return; + + client_set_floating(wm.client_focused, !wm.client_focused->floating); +} diff --git a/src/action.h b/src/action.h index 82f5e50..b8b95af 100644 --- a/src/action.h +++ b/src/action.h @@ -55,3 +55,4 @@ void send_client_to_next_monitor(const user_action_arg_t *arg); void spawn(const user_action_arg_t *arg); void quit(const user_action_arg_t *arg); void raise_or_run(const user_action_arg_t *arg); +void toggle_client_floating(const user_action_arg_t *arg); diff --git a/src/client.c b/src/client.c index c275213..32c840e 100644 --- a/src/client.c +++ b/src/client.c @@ -178,11 +178,12 @@ 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->geometry.x = geometry_reply->x; - c->geometry.y = geometry_reply->y; - c->geometry.width = geometry_reply->width; - c->geometry.height = geometry_reply->height; - c->border_width = geometry_reply->border_width; + c->old_geometry.x = geometry_reply->x; + c->old_geometry.y = geometry_reply->y; + c->old_geometry.width = geometry_reply->width; + c->old_geometry.height = geometry_reply->height; + c->geometry = c->old_geometry; + c->old_border_width = geometry_reply->border_width; { const xcb_params_cw_t params = { @@ -345,6 +346,22 @@ void client_update_wm_hints(client_t *client) { } } +void client_set_floating(client_t *client, bool floating) { + if (client->floating == floating || client->fullscreen) return; + + logger("== client set floating: %s\n", floating ? "true" : "false"); + logger("== old geometry: x -> %d, y -> %d, width -> %u, height -> %u\n", + client->old_geometry.x, client->old_geometry.y, + client->old_geometry.width, client->old_geometry.height); + client->floating = floating; + if (floating) { + client_apply_workarea_geometry(client, client->old_geometry); + client->old_geometry = client->geometry; + } + + monitor_arrange(client->monitor); +} + static void update_client_list(void) { xcb_connection_t *conn = wm.xcb_conn; xcb_window_t root = wm.screen->root; @@ -379,17 +396,36 @@ void client_unmanage(client_t *client) { xcb_flush(wm.xcb_conn); } -void client_apply_task_geometry(client_t *client, task_in_tag_t *task) { - if (!client || !task || client != task->client) return; +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); + } +} - if (task->geometry.x != client->geometry.x || - task->geometry.y != client->geometry.y) { - client_move_to(client, task->geometry.x, task->geometry.y); +/** + * @brief 调整 client 的几何信息但需让 client 任在其 monitor 的 workarea 中 + * @param client 要调整几何信息的 client + * @param geometry 目标几何信息 + */ +void client_apply_workarea_geometry(client_t *client, area_t geometry) { + area_t workarea = client->monitor->workarea; + uint16_t width = MIN(geometry.width, workarea.width); + uint16_t height = MIN(geometry.height, workarea.height); + int16_t x = MAX(geometry.x, workarea.x); + int16_t y = MAX(geometry.y, workarea.y); + if (x + width + client->border_width * 2 > workarea.x + workarea.width) { + x = workarea.x + workarea.width - width - client->border_width * 2; } - if (task->geometry.width != client->geometry.width || - task->geometry.height != client->geometry.height) { - client_resize(client, task->geometry.width, task->geometry.height); + if (y + height + client->border_width * 2 > workarea.y + workarea.height) { + y = workarea.y + workarea.width - height - client->border_width * 2; } + + area_t rect = {.x = x, .y = y, .width = width, .height = height}; + client_apply_geometry(client, rect); } void client_focus(client_t *client) { diff --git a/src/client.h b/src/client.h index c5731d1..3147737 100644 --- a/src/client.h +++ b/src/client.h @@ -6,7 +6,7 @@ #include "types.h" client_t *client_get_by_window(xcb_window_t window); -client_t *client_get_next_by_class(client_t *current,const char *class); +client_t *client_get_next_by_class(client_t *current, const char *class); task_in_tag_t *client_get_task_in_tag(client_t *client, tag_t *tag); task_in_tag_t *client_get_next_task_in_tag(client_t *client, tag_t *tag); task_in_tag_t *client_get_previous_task_in_tag(client_t *client, tag_t *tag); @@ -48,7 +48,8 @@ void client_set_leader_window(client_t *client, xcb_window_t leader_window); void client_kill(client_t *client); void client_unmanage(client_t *client); -void client_apply_task_geometry(client_t *client, task_in_tag_t *task); +void client_apply_geometry(client_t *client, area_t geometry); +void client_apply_workarea_geometry(client_t *client, area_t geometry); 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 b7ba177..ee99d83 100644 --- a/src/default_config.h +++ b/src/default_config.h @@ -86,6 +86,8 @@ static const keyboard_t key_list[] = { TAGKEYS(XK_9, 1 << 8), {modifier_super, XK_o, send_client_to_next_monitor, {0}}, + + {modifier_super | modifier_control, XK_space, toggle_client_floating, {0}}, }; static const layout_t layout_list[] = { diff --git a/src/event.c b/src/event.c index 268c13a..08fbf59 100644 --- a/src/event.c +++ b/src/event.c @@ -68,7 +68,7 @@ static void configure_request(xcb_configure_request_event_t *ev) { task->geometry.width = ev->width; if (ev->value_mask & XCB_CONFIG_WINDOW_HEIGHT) task->geometry.height = ev->height; - client_apply_task_geometry(client, task); + client_apply_geometry(client, task->geometry); } } else { uint16_t value_mask = ev->value_mask; diff --git a/src/monitor.c b/src/monitor.c index a93e3f0..8fc60f5 100644 --- a/src/monitor.c +++ b/src/monitor.c @@ -274,7 +274,11 @@ void monitor_arrange(monitor_t *monitor) { task_in_tag_t *task = client_get_task_in_tag(c, tag); if (task) { - client_apply_task_geometry(c, task); + if (client_need_layout(c)) { + client_apply_geometry(c, task->geometry); + } else { + client_apply_geometry(c, c->geometry); + } } else { int16_t x = -client_width(c); int16_t y = -client_height(c); diff --git a/src/types.h b/src/types.h index 1a5bfe8..d2b9996 100644 --- a/src/types.h +++ b/src/types.h @@ -26,9 +26,11 @@ struct client_t { monitor_t *monitor; uint32_t tags; + area_t old_geometry; area_t geometry; - color_t *border_color; uint16_t border_width; + uint16_t old_border_width; + color_t *border_color; bool floating; bool fullscreen; diff --git a/src/utils.h b/src/utils.h index f3bc569..270eeca 100644 --- a/src/utils.h +++ b/src/utils.h @@ -88,6 +88,11 @@ static inline void logger(const char *format, ...) { #define logger(format, ...) printf(format, ##__VA_ARGS__) #endif +#ifdef MIN +#undef MIN +#define MIN(a, b) (((a) < (b)) ? (a) : (b)) +#endif + #ifdef MAX #undef MAX #define MAX(a, b) (((a) > (b)) ? (a) : (b))