From f82d0b7a87627d6256b552b05295f2ed2bae9719 Mon Sep 17 00:00:00 2001 From: Zedhugh Chen Date: Fri, 5 Jun 2026 22:53:22 +0800 Subject: [PATCH] =?UTF-8?q?refactor:=20=E7=A7=BB=E9=99=A4=E6=96=B0?= =?UTF-8?q?=E6=9E=B6=E6=9E=84=E7=94=A8=E4=B8=8D=E5=88=B0=E7=9A=84=E8=80=81?= =?UTF-8?q?=E6=A8=A1=E5=9D=97?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/.clang-format | 16 - src/action.c | 158 -------- src/action.h | 67 ---- src/atoms-list.txt | 34 -- src/atoms.c | 39 -- src/atoms.h | 5 - src/audio.c | 182 --------- src/audio.h | 19 - src/backend/.clang-format | 1 - src/backtrace.c | 37 -- src/backtrace.h | 5 - src/bar/.clang-format | 1 - src/base.h | 17 - src/base/.clang-format | 1 - src/buffer.c | 37 -- src/buffer.h | 78 ---- src/client.c | 706 ---------------------------------- src/client.h | 76 ---- src/color.c | 87 ----- src/color.h | 22 -- src/config.c | 81 ---- src/config.h | 57 --- src/config/.clang-format | 1 - src/core/.clang-format | 1 - src/default_config.h | 197 ---------- src/event.c | 387 ------------------- src/event.h | 3 - src/image.c | 171 --------- src/image.h | 36 -- src/layout.c | 93 ----- src/layout.h | 6 - src/layouts/.clang-format | 1 - src/monitor.c | 454 ---------------------- src/monitor.h | 17 - src/rect.c | 156 -------- src/rect.h | 42 --- src/renderer.c | 90 ----- src/renderer.h | 41 -- src/status.c | 253 ------------- src/status.h | 53 --- src/text.c | 183 --------- src/text.h | 16 - src/tray.c | 598 ----------------------------- src/tray.h | 21 -- src/types.h | 114 ------ src/utils.h | 8 - src/wallpaper.c | 313 --------------- src/wallpaper.h | 20 - src/wm.c | 775 -------------------------------------- src/wm.h | 76 ---- src/xcursor.c | 148 -------- src/xcursor.h | 17 - src/xkb.c | 216 ----------- src/xkb.h | 7 - src/xres.c | 52 --- src/xres.h | 8 - src/xwindow.c | 288 -------------- src/xwindow.h | 44 --- 58 files changed, 6632 deletions(-) delete mode 100644 src/.clang-format delete mode 100644 src/action.c delete mode 100644 src/action.h delete mode 100644 src/atoms-list.txt delete mode 100644 src/atoms.c delete mode 100644 src/atoms.h delete mode 100644 src/audio.c delete mode 100644 src/audio.h delete mode 120000 src/backend/.clang-format delete mode 100644 src/backtrace.c delete mode 100644 src/backtrace.h delete mode 120000 src/bar/.clang-format delete mode 100644 src/base.h delete mode 120000 src/base/.clang-format delete mode 100644 src/buffer.c delete mode 100644 src/buffer.h delete mode 100644 src/client.c delete mode 100644 src/client.h delete mode 100644 src/color.c delete mode 100644 src/color.h delete mode 100644 src/config.c delete mode 100644 src/config.h delete mode 120000 src/config/.clang-format delete mode 120000 src/core/.clang-format delete mode 100644 src/default_config.h delete mode 100644 src/event.c delete mode 100644 src/event.h delete mode 100644 src/image.c delete mode 100644 src/image.h delete mode 100644 src/layout.c delete mode 100644 src/layout.h delete mode 120000 src/layouts/.clang-format delete mode 100644 src/monitor.c delete mode 100644 src/monitor.h delete mode 100644 src/rect.c delete mode 100644 src/rect.h delete mode 100644 src/renderer.c delete mode 100644 src/renderer.h delete mode 100644 src/status.c delete mode 100644 src/status.h delete mode 100644 src/text.c delete mode 100644 src/text.h delete mode 100644 src/tray.c delete mode 100644 src/tray.h delete mode 100644 src/types.h delete mode 100644 src/utils.h delete mode 100644 src/wallpaper.c delete mode 100644 src/wallpaper.h delete mode 100644 src/wm.c delete mode 100644 src/wm.h delete mode 100644 src/xcursor.c delete mode 100644 src/xcursor.h delete mode 100644 src/xkb.c delete mode 100644 src/xkb.h delete mode 100644 src/xres.c delete mode 100644 src/xres.h delete mode 100644 src/xwindow.c delete mode 100644 src/xwindow.h diff --git a/src/.clang-format b/src/.clang-format deleted file mode 100644 index 586880b..0000000 --- a/src/.clang-format +++ /dev/null @@ -1,16 +0,0 @@ -# -*- mode: yaml; -*- - ---- -BasedOnStyle: Google -IndentWidth: 2 -ConstructorInitializerIndentWidth: 2 -ContinuationIndentWidth: 2 ---- -Language: C -DerivePointerAlignment: false -PointerAlignment: Right ---- -Language: Cpp -DerivePointerAlignment: false -PointerAlignment: Right ---- diff --git a/src/action.c b/src/action.c deleted file mode 100644 index aa6cbf3..0000000 --- a/src/action.c +++ /dev/null @@ -1,158 +0,0 @@ -#include "action.h" - -#include -#include - -#include "audio.h" -#include "client.h" -#include "monitor.h" -#include "types.h" -#include "wm.h" -#include "xcursor.h" - -void focus_client_in_same_tag(const user_action_arg_t *arg) { - bool next = arg->b; - tag_t *tag = wm.current_monitor->selected_tag; - - task_in_tag_t *task = nullptr; - if (next) { - task = client_get_next_task_in_tag(wm.client_focused, tag); - } else { - task = client_get_previous_task_in_tag(wm.client_focused, tag); - } - - if (!task) return; - - client_stack_raise(task->client); - client_focus(task->client); -} - -void select_tag_of_current_monitor(const user_action_arg_t *arg) { - monitor_select_tag(wm.current_monitor, arg->ui); -} - -void send_client_to_tag(const user_action_arg_t *arg) { - if (wm.client_focused) client_send_to_tag(wm.client_focused, arg->ui); -} - -void send_client_to_next_monitor(const user_action_arg_t *arg) { - if (!wm.client_focused) return; - - monitor_t *next_monitor = wm_get_next_monitor(wm.client_focused->monitor); - if (wm.client_focused->monitor == next_monitor) return; - - client_send_to_monitor(wm.client_focused, next_monitor); -} - -void focus_next_monitor(const user_action_arg_t *arg) { - monitor_t *next_monitor = wm_get_next_monitor(wm.current_monitor); - if (next_monitor == wm.current_monitor) return; - - wm_set_current_monitor(next_monitor, true); - if (wm.current_monitor->selected_tag->task_list) { - monitor_deal_focus(wm.current_monitor); - } -} - -void spawn(const user_action_arg_t *arg) { - const char *cmd = (const char *)arg->ptr; - if (cmd == nullptr || strlen(cmd) == 0) return; - - g_spawn_command_line_async((const char *)arg->ptr, nullptr); -} - -void quit(const user_action_arg_t *arg) { - const bool restart = arg->b; - if (restart) { - wm_restart(); - } else { - wm_quit(); - } -} - -void raise_or_run(const user_action_arg_t *arg) { - const char *class = ((const char **)arg->ptr)[0]; - client_t *client = client_get_next_by_class(wm.client_focused, class); - - if (client) { - bool monitor_changed = client->monitor != wm.current_monitor; - bool tag_changed = client->tags != client->monitor->selected_tag->mask; - if (monitor_changed || tag_changed) { - point_t point = monitor_changed - ? monitor_get_restore_cursor_point(client->monitor) - : xcursor_query_pointer_position(); - wm_ignore_enter_notify_at_point(point); - } - wm_set_current_monitor(client->monitor, true); - monitor_select_tag(client->monitor, client->tags); - client_stack_raise(client); - client_focus(client); - return; - } - - const char *command = ((const char **)arg->ptr)[1]; - user_action_arg_t arguments = {.ptr = command}; - spawn(&arguments); -} - -void toggle_mute(const user_action_arg_t *arg) { - if (wm.status->pulse_context) toggle_pulse_mute(wm.status->pulse_context); -} - -void change_volume(const user_action_arg_t *arg) { - if (wm.status->pulse_context) { - change_pulse_volume(wm.status->pulse_context, arg->i); - } -} - -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); -} - -void toggle_client_fullscreen(const user_action_arg_t *arg) { - if (!wm.client_focused) return; - - client_set_fullscreen(wm.client_focused, !wm.client_focused->fullscreen); -} - -void toggle_client_maximize(const user_action_arg_t *arg) { - if (!wm.client_focused) return; - - client_set_maximize(wm.client_focused, !wm.client_focused->maximize); -} - -void toggle_client_minimize(const user_action_arg_t *arg) { - client_t *client = wm.client_focused; - if (arg->ptr) client = (client_t *)arg->ptr; - if (!client) return; - - client_set_minimize(client, !client->minimize); -} - -void kill_client(const user_action_arg_t *arg) { - if (wm.client_focused) { - client_kill(wm.client_focused); - } -} - -/** - * @brief 根据 client 的当前状态切换 client 下一状态 - * @description 如果当前 client 被遮挡了或者最小化了,则显示出来,否则最小化 - */ -void change_client_state_dwim(const user_action_arg_t *arg) { - client_t *client = (client_t *)arg->ptr; - if (!client) return; - - if (client != wm.client_stack_list && - client_get_obscured_state(client, wm.client_stack_list) != - obscured_none) { - client_stack_raise(client); - client_focus(client); - return; - } - - client_set_minimize(client, !client->minimize); - if (!client->minimize) client_stack_raise(client); -} diff --git a/src/action.h b/src/action.h deleted file mode 100644 index 7003ca7..0000000 --- a/src/action.h +++ /dev/null @@ -1,67 +0,0 @@ -#pragma once - -#include -#include - -typedef union user_action_arg_t { - bool b; - int32_t i; - uint32_t ui; - const void *ptr; -} user_action_arg_t; - -typedef enum click_area_t { - click_none, - click_tag, - click_client_name, -} click_area_t; - -typedef enum modifier_t { - modifier_none = 0, - modifier_shift = XCB_MOD_MASK_SHIFT, - modifier_control = XCB_MOD_MASK_CONTROL, - modifier_alt = XCB_MOD_MASK_1, - modifier_super = XCB_MOD_MASK_4, - modifier_any = XCB_MOD_MASK_ANY, -} modifier_t; - -typedef uint16_t modifier_value_t; - -typedef enum button_index_t { - button_any = XCB_BUTTON_INDEX_ANY, - button_left = XCB_BUTTON_INDEX_1, - button_right = XCB_BUTTON_INDEX_2, - button_middle = XCB_BUTTON_INDEX_3, -} button_index_t; - -typedef struct button_t { - click_area_t click_area : 16; - modifier_value_t modifiers; - button_index_t button; - void (*func)(const user_action_arg_t *arg); - const user_action_arg_t arg; -} button_t; - -typedef struct keyboard_t { - modifier_value_t modifiers; - xcb_keysym_t keysym; - void (*func)(const user_action_arg_t *arg); - const user_action_arg_t arg; -} keyboard_t; - -void focus_client_in_same_tag(const user_action_arg_t *arg); -void select_tag_of_current_monitor(const user_action_arg_t *arg); -void send_client_to_tag(const user_action_arg_t *arg); -void send_client_to_next_monitor(const user_action_arg_t *arg); -void focus_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_mute(const user_action_arg_t *arg); -void change_volume(const user_action_arg_t *arg); -void toggle_client_floating(const user_action_arg_t *arg); -void toggle_client_fullscreen(const user_action_arg_t *arg); -void toggle_client_maximize(const user_action_arg_t *arg); -void toggle_client_minimize(const user_action_arg_t *arg); -void kill_client(const user_action_arg_t *arg); -void change_client_state_dwim(const user_action_arg_t *arg); diff --git a/src/atoms-list.txt b/src/atoms-list.txt deleted file mode 100644 index d7a5ea1..0000000 --- a/src/atoms-list.txt +++ /dev/null @@ -1,34 +0,0 @@ -_XKB_RULES_NAMES - -_XROOTPMAP_ID -ESETROOT_PMAP_ID - -WM_CHANGE_STATE -WM_DELETE_WINDOW -WM_PROTOCOLS -WM_TAKE_FOCUS -WM_NAME -WM_WINDOW_ROLE - -UTF8_STRING -COMPOUND_TEXT -MANAGER -_XEMBED -_XEMBED_INFO - -_NET_ACTIVE_WINDOW -_NET_CLIENT_LIST -_NET_SYSTEM_TRAY_COLORS -_NET_SYSTEM_TRAY_OPCODE -_NET_SYSTEM_TRAY_ORIENTATION -_NET_WM_DESKTOP -_NET_WM_NAME -_NET_WM_STATE -_NET_WM_STATE_FULLSCREEN -_NET_WM_STATE_MAXIMIZED_HORZ -_NET_WM_STATE_MAXIMIZED_VERT -_NET_WM_STATE_SKIP_TASKBAR -_NET_WM_WINDOW_TYPE -_NET_WM_WINDOW_TYPE_DIALOG -_NET_SUPPORTED -_NET_SUPPORTING_WM_CHECK diff --git a/src/atoms.c b/src/atoms.c deleted file mode 100644 index a7e50e3..0000000 --- a/src/atoms.c +++ /dev/null @@ -1,39 +0,0 @@ -#include "atoms.h" - -#include -#include -#include - -#include "atoms-intern.h" -#include "utils.h" -#include "wm.h" - -void atoms_init(xcb_connection_t *conn) { - xcb_intern_atom_cookie_t cookies[countof(ATOM_LIST)]; - for (int i = 0; i < countof(ATOM_LIST); i++) { - atom_item_t atom = ATOM_LIST[i]; - cookies[i] = xcb_intern_atom_unchecked(conn, false, atom.len, atom.name); - } - - atom_item_t *atom = nullptr; - uint32_t supported_length = 0; - xcb_atom_t supported_list[countof(ATOM_LIST)] = {XCB_ATOM_NONE}; - - xcb_intern_atom_reply_t *reply = nullptr; - for (int i = 0; i < countof(ATOM_LIST); i++) { - reply = xcb_intern_atom_reply(conn, cookies[i], nullptr); - if (reply) { - atom = &ATOM_LIST[i]; - *atom->atom = reply->atom; - p_delete(&reply); - - if (strstr(atom->name, "_NET_") == atom->name) { - supported_list[supported_length++] = *atom->atom; - } - } - } - - xcb_change_property(wm.xcb_conn, XCB_PROP_MODE_REPLACE, wm.screen->root, - _NET_SUPPORTED, XCB_ATOM_ATOM, 32, supported_length, - supported_list); -} diff --git a/src/atoms.h b/src/atoms.h deleted file mode 100644 index cd92758..0000000 --- a/src/atoms.h +++ /dev/null @@ -1,5 +0,0 @@ -#pragma once - -#include - -void atoms_init(xcb_connection_t *conn); diff --git a/src/audio.c b/src/audio.c deleted file mode 100644 index 7bb91bc..0000000 --- a/src/audio.c +++ /dev/null @@ -1,182 +0,0 @@ -#include "audio.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "app.h" -#include "utils.h" - -struct pulse_context_t { - GMainContext *g_context; - pulse_notify_t callback; - pa_glib_mainloop *loop; - pa_context *context; - pa_cvolume volume; - pulse_t pulse; - uint32_t index; - bool inited; -}; - -static void clean_pulse_context(pulse_context_t *context, bool full_cleanup); -static void init_pulse_context(pulse_context_t *context, bool create_loop); -static void state_callback(pa_context *context, void *userdata); -static void subscribe_callback(pa_context *context, - pa_subscription_event_type_t t, uint32_t index, - void *userdata); -static void server_info_callback(pa_context *context, - const pa_server_info *info, void *userdata); -static void sink_info_callback(pa_context *context, const pa_sink_info *info, - int eol, void *userdata); -static void parse_sink(const pa_sink_info *info, pulse_t *pulse); - -pulse_context_t *init_pulse(GMainContext *context, pulse_notify_t callback) { - pulse_context_t *ctx = p_new(pulse_context_t, 1); - ctx->g_context = context; - ctx->callback = callback; - init_pulse_context(ctx, true); - - return ctx; -} - -void change_pulse_volume(pulse_context_t *context, int step) { - if (!context->inited || !step || step > 100 || step < -100) return; - - pa_cvolume vol = context->volume; - if (step > 0) { - pa_cvolume_inc(&vol, PA_VOLUME_NORM * step / 100); - if (pa_cvolume_max(&vol) > PA_VOLUME_NORM) { - pa_cvolume_set(&vol, vol.channels, PA_VOLUME_NORM); - } - } else { - pa_cvolume_dec(&vol, PA_VOLUME_NORM * (-step) / 100); - if (pa_cvolume_min(&vol) < PA_VOLUME_MUTED) { - pa_cvolume_set(&vol, vol.channels, PA_VOLUME_MUTED); - } - } - pa_context *ctx = context->context; - uint32_t index = context->index; - pa_context_set_sink_volume_by_index(ctx, index, &vol, nullptr, nullptr); -} - -void toggle_pulse_mute(pulse_context_t *context) { - if (!context->inited) return; - - pa_context *ctx = context->context; - uint32_t index = context->index; - bool mute = !context->pulse.mute; - pa_context_set_sink_mute_by_index(ctx, index, mute, nullptr, nullptr); -} - -void clean_pulse(pulse_context_t *context) { - clean_pulse_context(context, true); - p_delete(&context); -} - -void clean_pulse_context(pulse_context_t *context, bool full_cleanup) { - memset(&context->pulse, 0, sizeof(context->pulse)); - if (context->callback) context->callback(&context->pulse); - if (context->context) { - pa_context_set_state_callback(context->context, nullptr, nullptr); - pa_context_set_subscribe_callback(context->context, nullptr, nullptr); - pa_context_disconnect(context->context); - pa_context_unref(context->context); - context->context = nullptr; - } - context->inited = false; - if (full_cleanup && context->loop) { - pa_glib_mainloop_free(context->loop); - context->loop = nullptr; - } -} - -void init_pulse_context(pulse_context_t *context, bool create_loop) { - if (create_loop) { - if (context->loop) pa_glib_mainloop_free(context->loop); - context->loop = pa_glib_mainloop_new(context->g_context); - } - pa_mainloop_api *api = pa_glib_mainloop_get_api(context->loop); - context->context = pa_context_new(api, APP_NAME "-audio-status"); - pa_context_connect(context->context, nullptr, PA_CONTEXT_NOFAIL, nullptr); - pa_context_set_state_callback(context->context, state_callback, context); -} - -void state_callback(pa_context *context, void *userdata) { - pulse_context_t *ctx = userdata; - - pa_context_state_t state = pa_context_get_state(context); - if (PA_CONTEXT_IS_GOOD(state)) { - pa_context_set_subscribe_callback(context, subscribe_callback, ctx); - pa_context_subscribe(context, PA_SUBSCRIPTION_MASK_SINK, nullptr, nullptr); - pa_context_get_server_info(context, server_info_callback, ctx); - } else if (state == PA_CONTEXT_FAILED) { - clean_pulse_context(ctx, false); - init_pulse_context(ctx, false); - } -} - -void subscribe_callback(pa_context *context, pa_subscription_event_type_t t, - uint32_t index, void *userdata) { - auto facility = t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK; - if (facility != PA_SUBSCRIPTION_EVENT_SINK) return; - - pa_subscription_event_type_t event = t & PA_SUBSCRIPTION_EVENT_TYPE_MASK; - if (event == PA_SUBSCRIPTION_EVENT_CHANGE) { - pa_context_get_server_info(context, server_info_callback, userdata); - } -} - -void server_info_callback(pa_context *context, const pa_server_info *info, - void *userdata) { - pa_context_get_sink_info_by_name(context, info->default_sink_name, - sink_info_callback, userdata); -} - -void sink_info_callback(pa_context *context, const pa_sink_info *info, int eol, - void *userdata) { - if (eol != 0 || !info) return; - - pulse_context_t *ctx = userdata; - ctx->inited = true; - ctx->volume = info->volume; - ctx->index = info->index; - parse_sink(info, &ctx->pulse); - if (ctx->callback) ctx->callback(&ctx->pulse); -} - -static inline int volume_to_percent(pa_volume_t volume) { - return (int)round(((double)volume) * 100 / PA_VOLUME_NORM); -} - -void parse_sink(const pa_sink_info *info, pulse_t *pulse) { - size_t size = sizeof(pulse->device_name); - pulse->mute = info->mute; - pulse->volume_percent = volume_to_percent(pa_cvolume_avg(&info->volume)); - strncpy(pulse->device_name, info->description, size); - - const char *key = nullptr; - void *state = nullptr; - while ((key = pa_proplist_iterate(info->proplist, &state)) != nullptr) { - if (strcmp(key, "api.alsa.path") != 0 && - strcmp(key, "device.description") != 0) { - continue; - } - - const char *value = pa_proplist_gets(info->proplist, key); - size_t len = strnlen(value, size); - if (len && (len < strnlen(pulse->device_name, size))) { - strncpy(pulse->device_name, value, size); - pulse->device_name[size - 1] = '\0'; - } - } -} diff --git a/src/audio.h b/src/audio.h deleted file mode 100644 index 18f5bf8..0000000 --- a/src/audio.h +++ /dev/null @@ -1,19 +0,0 @@ -#pragma once - -#include -#include - -typedef struct pulse_context_t pulse_context_t; - -typedef struct pulse_t { - int volume_percent; - bool mute; - char device_name[255]; -} pulse_t; - -typedef void (*pulse_notify_t)(pulse_t *pulse); - -pulse_context_t *init_pulse(GMainContext *context, pulse_notify_t callback); -void change_pulse_volume(pulse_context_t *context, int step); -void toggle_pulse_mute(pulse_context_t *context); -void clean_pulse(pulse_context_t *context); diff --git a/src/backend/.clang-format b/src/backend/.clang-format deleted file mode 120000 index 2d11237..0000000 --- a/src/backend/.clang-format +++ /dev/null @@ -1 +0,0 @@ -../../.clang-format \ No newline at end of file diff --git a/src/backtrace.c b/src/backtrace.c deleted file mode 100644 index 4fbbe65..0000000 --- a/src/backtrace.c +++ /dev/null @@ -1,37 +0,0 @@ -#include "backtrace.h" - -#include "app.h" -#include "buffer.h" - -#ifdef HAS_EXECINFO -#include -#endif - -#define MAX_STACK_SIZE 32 - -/** - * Get a backtrace. - * @param buffer The buffer to fill with backtrace. - */ -void backtrace_get(buffer_t *buffer) { - buffer_init(buffer); - -#ifdef HAS_EXECINFO - void *stack[MAX_STACK_SIZE]; - char **bt; - int stack_size; - - stack_size = backtrace(stack, countof(stack)); - bt = backtrace_symbols(stack, stack_size); - - if (bt) { - for (int i = 0; i < stack_size; i++) { - if (i > 0) buffer_addsl(buffer, "\n"); - buffer_adds(buffer, bt[i]); - } - p_delete(&bt); - } else -#endif - - buffer_addsl(buffer, "Cannot get backtrace symbols."); -} diff --git a/src/backtrace.h b/src/backtrace.h deleted file mode 100644 index 0b5947b..0000000 --- a/src/backtrace.h +++ /dev/null @@ -1,5 +0,0 @@ -#pragma once - -#include "buffer.h" - -void backtrace_get(buffer_t *buffer); diff --git a/src/bar/.clang-format b/src/bar/.clang-format deleted file mode 120000 index 2d11237..0000000 --- a/src/bar/.clang-format +++ /dev/null @@ -1 +0,0 @@ -../../.clang-format \ No newline at end of file diff --git a/src/base.h b/src/base.h deleted file mode 100644 index df4c294..0000000 --- a/src/base.h +++ /dev/null @@ -1,17 +0,0 @@ -#pragma once - -#include - -typedef struct area_t { - int16_t x, y; - uint16_t width, height; -} area_t; - -typedef struct extent_in_bar_t { - int16_t start; - int16_t end; -} extent_in_bar_t; - -typedef struct point_t { - int16_t x, y; -} point_t; diff --git a/src/base/.clang-format b/src/base/.clang-format deleted file mode 120000 index 2d11237..0000000 --- a/src/base/.clang-format +++ /dev/null @@ -1 +0,0 @@ -../../.clang-format \ No newline at end of file diff --git a/src/buffer.c b/src/buffer.c deleted file mode 100644 index a0be1ce..0000000 --- a/src/buffer.c +++ /dev/null @@ -1,37 +0,0 @@ -#include "buffer.h" - -#include -#include -#include - -#include "utils.h" - -char buffer_slop[1]; - -void buffer_ensure(buffer_t *buf, int newlen) { - if (newlen < 0) exit(EX_SOFTWARE); - - if (newlen < buf->size) return; - - if (newlen < buf->offs + buf->size && buf->offs > buf->size / 4) { - /* Data fits in the current area, shift it left */ - memmove(buf->s - buf->offs, buf->s, buf->len + 1); - buf->s -= buf->offs; - buf->size += buf->offs; - buf->offs = 0; - return; - } - - buf->size = p_alloc_nr(buf->size + buf->offs); - if (buf->size < newlen + 1) buf->size = newlen + 1; - if (buf->alloced && !buf->offs) - p_realloc(&buf->s, buf->size); - else { - char *new_area = xmalloc(buf->size); - memcpy(new_area, buf->s, buf->len + 1); - if (buf->alloced) free(buf->s - buf->offs); - buf->alloced = true; - buf->s = new_area; - buf->offs = 0; - } -} diff --git a/src/buffer.h b/src/buffer.h deleted file mode 100644 index fbbe9f9..0000000 --- a/src/buffer.h +++ /dev/null @@ -1,78 +0,0 @@ -#pragma once - -#include -#include - -#include "utils.h" - -typedef struct buffer_t { - char *s; - int len, size; - unsigned alloced : 1; - unsigned offs : 31; -} buffer_t; - -extern char buffer_slop[1]; - -#define BUFFER_INIT (buffer_t){.s = buffer_slop, .size = 1} - -/** - * Initialize a buffer. - * @param buf A buffer pointer. - * @return The same buffer pointer. - */ -static inline buffer_t *buffer_init(buffer_t *buf) { - *buf = BUFFER_INIT; - return buf; -} - -void buffer_ensure(buffer_t *buf, int len); - -/** - * Add data in the buffer. - * @param buf Buffer where to add. - * @param pos Position where to add. - * @param len Length. - * @param data Data to add. - * @param dlen Data length. - */ -static inline void buffer_splice(buffer_t *buf, int pos, int len, - const void *data, int dlen) { - assert(pos >= 0 && len >= 0 && dlen >= 0); - - if (unlikely(pos > buf->len)) pos = buf->len; - if (unlikely(len > buf->len - pos)) len = buf->len - pos; - if (pos == 0 && len + buf->offs >= dlen) { - buf->offs += len - dlen; - buf->s += len - dlen; - buf->size -= len - dlen; - buf->len -= len - dlen; - } else if (len != dlen) { - buffer_ensure(buf, buf->len + dlen - len); - memmove(buf->s + pos + dlen, buf->s + pos + len, buf->len - pos - len); - buf->len += dlen - len; - buf->s[buf->len] = '\0'; - } - memcpy(buf->s + pos, data, dlen); -} - -/** - * Add data at the end of buffer. - * @param buf Buffer where to add. - * @param data Data to add. - * @param len Data length. - */ -static inline void buffer_add(buffer_t *buf, const void *data, int len) { - buffer_splice(buf, buf->len, 0, data, len); -} - -#define buffer_addsl(buf, data) buffer_add(buf, data, sizeof(data) - 1); - -/** - * Add a string to the and of a buffer. - * @param buf The buffer where to add. - * @param s The string to add. - */ -static inline void buffer_adds(buffer_t *buf, const char *s) { - buffer_splice(buf, buf->len, 0, s, a_strlen(s)); -} diff --git a/src/client.c b/src/client.c deleted file mode 100644 index ff637cb..0000000 --- a/src/client.c +++ /dev/null @@ -1,706 +0,0 @@ -#include "client.h" - -#include -#include -#include -#include -#include -#include - -#include "atoms-extern.h" -#include "base.h" -#include "monitor.h" -#include "rect.h" -#include "types.h" -#include "utils.h" -#include "wm.h" -#include "xwindow.h" - -client_t *client_get_by_window(xcb_window_t window) { - for (client_t *c = wm.client_list; c; c = c->next) { - if (c->window == window) return c; - } - - return nullptr; -} - -client_t *client_get_next_by_class(client_t *current, const char *class) { - client_t *start = current ? current->next : wm.client_list; - for (client_t *c = start; c; c = c->next) { - if (strcmp(class, c->class) == 0) return c; - } - - for (client_t *c = wm.client_list; c && c != start; c = c->next) { - if (strcmp(class, c->class) == 0) return c; - } - - return nullptr; -} - -static void client_attach_list(client_t *client) { - client->next = wm.client_list; - wm.client_list = client; -} - -static void client_detach_list(client_t *client) { - client_t **tc = nullptr; - for (tc = &wm.client_list; *tc && *tc != client; tc = &(*tc)->next); - *tc = client->next; -} - -static void client_attach_stack(client_t *client) { - client->stack_next = wm.client_stack_list; - wm.client_stack_list = client; -} - -static void client_detach_stack(client_t *client) { - client_t **tc = &wm.client_stack_list; - for (; *tc && *tc != client; tc = &(*tc)->stack_next); - *tc = client->stack_next; - - if (client == wm.client_focused) { - client_t *t = wm.client_stack_list; - for (; t && !client_is_visible(t); t = t->stack_next); - wm.client_focused = t; - } -} - -task_in_tag_t *client_get_task_in_tag(client_t *client, tag_t *tag) { - for (task_in_tag_t *task = tag->task_list; task; task = task->next) { - if (task->client == client) return task; - } - return nullptr; -} - -task_in_tag_t *client_get_next_task_in_tag(client_t *client, tag_t *tag) { - if (!tag->task_list) return nullptr; - - task_in_tag_t *task = tag->task_list; - for (; task && task->client != client; task = task->next); - return task && task->next ? task->next : tag->task_list; -} - -task_in_tag_t *client_get_previous_task_in_tag(client_t *client, tag_t *tag) { - if (!tag->task_list) return nullptr; - - task_in_tag_t *task = tag->task_list; - for (; task && task->next && task->next->client != client; task = task->next); - return task ? task : tag->task_list; -} - -static void client_add_to_tag(client_t *client, tag_t *tag) { - if (client_get_task_in_tag(client, tag)) return; - - xwindow_set_wm_desktop(client->window, tag->index); - - task_in_tag_t *task = p_new(task_in_tag_t, 1); - task->client = client; - task->next = tag->task_list; - tag->task_list = task; - task->geometry = client->geometry; -} - -static void client_remove_from_tag(client_t *client, tag_t *tag) { - task_in_tag_t **task = &tag->task_list; - while (*task) { - if ((*task)->client == client) { - task_in_tag_t *t = *task; - *task = (*task)->next; - p_delete(&t); - continue; - } - - 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->net_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, _NET_WM_NAME, &c->net_name); -} - -static inline void client_init_geometry(client_t *c) { - if (c->maximize || c->fullscreen || c->minimize) 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); - } - if (c->geometry.y + client_height(c) > workarea.y + workarea.height) { - c->geometry.y = workarea.y + workarea.height - client_height(c); - } - c->geometry.x = MAX(c->geometry.x, workarea.x); - c->geometry.y = MAX(c->geometry.y, workarea.y); - - client_move_to(c, c->geometry.x, c->geometry.y); - client_resize(c, c->geometry.width, c->geometry.height); - client_change_border_color(c, &wm.color_set.active_border_color); - client_change_border_width(c, wm.border_width); -} - -static void client_init_tag_by_wm_desktop(client_t *client, uint32_t tag_mask) { - uint32_t tag_index = 0; - if (!xwindow_get_wm_desktop(client->window, &tag_index)) { - goto tag_fallback; - } - - for (monitor_t *m = wm.monitor_list; m; m = m->next) { - for (tag_t *tag = m->tag_list; tag; tag = tag->next) { - if (tag->index == tag_index) { - client->monitor = m; - client->tags = tag->mask; - return; - } - } - } - -tag_fallback: - client->tags = tag_mask; -} - -static void set_window_event_mask(xcb_window_t window, bool clean) { - xcb_cw_t change_mask = XCB_CW_EVENT_MASK; - uint32_t init_event_mask = - XCB_EVENT_MASK_ENTER_WINDOW | XCB_EVENT_MASK_FOCUS_CHANGE | - XCB_EVENT_MASK_PROPERTY_CHANGE | XCB_EVENT_MASK_STRUCTURE_NOTIFY | - XCB_EVENT_MASK_SUBSTRUCTURE_NOTIFY; - uint32_t event_mask = clean ? XCB_EVENT_MASK_NO_EVENT : init_event_mask; - xcb_params_cw_t params = {.event_mask = event_mask}; - xcb_aux_change_window_attributes(wm.xcb_conn, window, change_mask, ¶ms); -} - -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->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; - - c->minimize = xwindow_get_state(window) == XCB_ICCCM_WM_STATE_ICONIC; - - set_window_event_mask(window, false); - - client_update_names(c); - xwindow_get_text_property(c->window, WM_WINDOW_ROLE, &c->role); - client_update_wm_hints(c); - - { - xcb_icccm_get_wm_class_reply_t prop; - xcb_get_property_cookie_t cookie = - xcb_icccm_get_wm_class_unchecked(wm.xcb_conn, c->window); - if (xcb_icccm_get_wm_class_reply(wm.xcb_conn, cookie, &prop, nullptr)) { - c->class = strdup(prop.class_name); - c->instance = strdup(prop.instance_name); - xcb_icccm_get_wm_class_reply_wipe(&prop); - } - } - - { - xcb_get_property_cookie_t cookie = - xcb_icccm_get_wm_transient_for_unchecked(wm.xcb_conn, window); - xcb_icccm_get_wm_transient_for_reply(wm.xcb_conn, cookie, - &c->transient_for_window, nullptr); - - client_t *transient_for_client = nullptr; - if (c->transient_for_window && (transient_for_client = client_get_by_window( - c->transient_for_window))) { - c->monitor = transient_for_client->monitor; - client_init_tag_by_wm_desktop(c, transient_for_client->tags); - } else { - c->monitor = wm.current_monitor; - client_init_tag_by_wm_desktop(c, c->monitor->selected_tag->mask); - client_apply_rules(c, wm.rules, wm.rules_count); - } - - client_init_geometry(c); - client_tags_apply(c); - } - - client_attach_list(c); - client_attach_stack(c); - client_update_window_type(c); - client_update_size_hints(c); - - xcb_change_property(wm.xcb_conn, XCB_PROP_MODE_APPEND, wm.screen->root, - _NET_CLIENT_LIST, XCB_ATOM_WINDOW, 32, 1, &window); - - if (c->minimize) { - xwindow_set_state(window, XCB_ICCCM_WM_STATE_ICONIC); - } else { - xwindow_set_state(window, XCB_ICCCM_WM_STATE_NORMAL); - xcb_map_window(wm.xcb_conn, window); - } - - monitor_arrange(c->monitor); - monitor_draw_bar(c->monitor); - - wm_restack_clients(); - if (c->tags & c->monitor->selected_tag->mask) client_focus(c); - - xcb_flush(wm.xcb_conn); -} - -char **client_get_task_title(client_t *client) { - if (client->net_name) return &client->net_name; - if (client->name) return &client->name; - if (client->instance) return &client->instance; - if (client->class) return &client->class; - return nullptr; -} - -bool client_need_layout(client_t *client) { - if (client->floating || client->fullscreen || client->maximize || - client->minimize || client->size_freeze) { - return false; - } - return true; -} - -void client_send_to_tag(client_t *client, uint32_t tag_mask) { - if (client->tags == tag_mask) return; - client->tags = tag_mask; - client_tags_apply(client); - monitor_arrange(client->monitor); - monitor_draw_bar(client->monitor); - monitor_deal_focus(client->monitor); - xcb_flush(wm.xcb_conn); -} - -void client_send_to_monitor(client_t *client, monitor_t *monitor) { - if (client->monitor == monitor) return; - - for (tag_t *tag = client->monitor->tag_list; tag; tag = tag->next) { - client_remove_from_tag(client, tag); - } - - monitor_t *m = client->monitor; - - client->monitor = monitor; - client->tags = monitor->selected_tag->mask; - client_add_to_tag(client, monitor->selected_tag); - - wm_set_current_monitor(monitor, true); - monitor_arrange(m); - monitor_arrange(monitor); - monitor_draw_bar(m); - monitor_draw_bar(monitor); - - xcb_flush(wm.xcb_conn); -} - -void client_move_to(client_t *client, int16_t x, int16_t y) { - client->geometry.x = x; - client->geometry.y = y; - uint16_t mask = XCB_CONFIG_WINDOW_X | XCB_CONFIG_WINDOW_Y; - const xcb_params_configure_window_t params = {.x = x, .y = y}; - xcb_aux_configure_window(wm.xcb_conn, client->window, mask, ¶ms); - logger("== client 0x%x move to x: %d, y: %d\n", client->window, x, y); -} - -void client_resize(client_t *client, uint16_t width, uint16_t height) { - client->geometry.width = width; - client->geometry.height = height; - uint16_t mask = XCB_CONFIG_WINDOW_WIDTH | XCB_CONFIG_WINDOW_HEIGHT; - const xcb_params_configure_window_t params = { - .width = width, - .height = height, - }; - xcb_aux_configure_window(wm.xcb_conn, client->window, mask, ¶ms); - logger("== client 0x%x resize to width: %d, height: %d\n", client->window, - width, height); -} - -void client_change_border_color(client_t *client, color_t *color) { - uint16_t mask = XCB_CW_BORDER_PIXEL; - const xcb_params_cw_t params = {.border_pixel = color->argb}; - xcb_aux_change_window_attributes(wm.xcb_conn, client->window, mask, ¶ms); - client->border_color = color; - logger("== client 0x%x border color: RGBA(#%08x), ARGB(#%08x)\n", - client->window, color->rgba, color->argb); -} - -void client_change_border_width(client_t *client, uint16_t border_width) { - if (client->border_width == border_width) return; - uint16_t mask = XCB_CONFIG_WINDOW_BORDER_WIDTH; - const xcb_params_configure_window_t params = {.border_width = border_width}; - xcb_aux_configure_window(wm.xcb_conn, client->window, mask, ¶ms); - client->border_width = border_width; - logger("== client 0x%x border width: %u\n", client->window, border_width); -} - -void client_update_window_type(client_t *client) { - xcb_window_t window = client->window; - xcb_get_property_cookie_t state_cookie = xcb_get_property_unchecked( - wm.xcb_conn, false, window, _NET_WM_STATE, XCB_ATOM_ATOM, 0, 1); - xcb_get_property_cookie_t window_type_cookie = xcb_get_property_unchecked( - wm.xcb_conn, false, window, _NET_WM_WINDOW_TYPE, XCB_ATOM_ATOM, 0, 1); - xcb_get_property_reply_t *state_reply = - xcb_get_property_reply(wm.xcb_conn, state_cookie, nullptr); - xcb_get_property_reply_t *window_type_reply = - xcb_get_property_reply(wm.xcb_conn, window_type_cookie, nullptr); - - if (state_reply) { - xcb_atom_t state = *(xcb_atom_t *)xcb_get_property_value(state_reply); - client->skip_taskbar = state == _NET_WM_STATE_SKIP_TASKBAR; - if (state == _NET_WM_STATE_FULLSCREEN) { - client_set_fullscreen(client, true); - } - p_delete(&state_reply); - } - - if (window_type_reply) { - xcb_atom_t window_type = - *(xcb_atom_t *)xcb_get_property_value(window_type_reply); - if (window_type == _NET_WM_WINDOW_TYPE_DIALOG) { - client_set_floating(client, true); - } - p_delete(&window_type_reply); - } -} - -void client_update_wm_hints(client_t *client) { - xcb_get_property_cookie_t cookie = - xcb_icccm_get_wm_hints(wm.xcb_conn, client->window); - xcb_icccm_wm_hints_t hints; - if (xcb_icccm_get_wm_hints_reply(wm.xcb_conn, cookie, &hints, nullptr)) { - if (client == wm.client_focused && - (hints.flags & XCB_ICCCM_WM_HINT_X_URGENCY)) { - hints.flags &= ~XCB_ICCCM_WM_HINT_X_URGENCY; - xcb_icccm_set_wm_hints(wm.xcb_conn, client->window, &hints); - } else { - client->urgent = hints.flags & XCB_ICCCM_WM_HINT_X_URGENCY; - } - } -} - -void client_update_size_hints(client_t *client) { - xcb_connection_t *conn = wm.xcb_conn; - xcb_size_hints_t hints; - xcb_get_property_cookie_t cookie = - xcb_icccm_get_wm_normal_hints_unchecked(conn, client->window); - if (!xcb_icccm_get_wm_normal_hints_reply(conn, cookie, &hints, nullptr)) { - return; - } - - int32_t min_width = 0, min_height = 0, max_width = 0, max_height = 0; - if (hints.flags & XCB_ICCCM_SIZE_HINT_P_MIN_SIZE) { - min_width = hints.min_width; - min_height = hints.min_height; - } - if (hints.flags & XCB_ICCCM_SIZE_HINT_P_MAX_SIZE) { - max_width = hints.max_width; - max_height = hints.max_height; - } - if (min_width == max_width && min_height == max_height) { - client->size_freeze = true; - client_set_floating(client, true); - } -} - -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); -} - -void client_set_fullscreen(client_t *client, bool fullscreen) { - if (client->fullscreen == fullscreen) return; - - client->fullscreen = fullscreen; - if (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); - client->old_border_width = client->border_width; - if (client->floating) client->old_geometry = client->geometry; - client_change_border_width(client, 0); - client_apply_geometry(client, client->monitor->geometry); - client_stack_raise(client); - } else { - xcb_change_property(wm.xcb_conn, XCB_PROP_MODE_REPLACE, client->window, - _NET_WM_STATE, XCB_ATOM_ATOM, 32, 0, 0); - client_change_border_width(client, client->old_border_width); - client_apply_geometry(client, client->old_geometry); - } - monitor_arrange(client->monitor); - client_focus(client); -} - -void client_set_maximize(client_t *client, bool maximize) { - if (client->maximize == maximize) return; - - client->maximize = maximize; - if (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); - client->old_border_width = client->border_width; - if (client->floating) client->old_geometry = client->geometry; - client_change_border_width(client, 0); - client_apply_geometry(client, client->monitor->workarea); - client_stack_raise(client); - } else { - xcb_change_property(wm.xcb_conn, XCB_PROP_MODE_REPLACE, client->window, - _NET_WM_STATE, XCB_ATOM_ATOM, 32, 0, 0); - client_change_border_width(client, client->old_border_width); - client_apply_geometry(client, client->old_geometry); - } - monitor_arrange(client->monitor); - client_focus(client); -} - -void client_set_minimize(client_t *client, bool minimize) { - if (client->minimize == minimize) return; - - client->minimize = minimize; - if (minimize) { - xwindow_set_state(client->window, XCB_ICCCM_WM_STATE_ICONIC); - - xcb_grab_server(wm.xcb_conn); - set_window_event_mask(client->window, true); - xcb_unmap_window(wm.xcb_conn, client->window); - set_window_event_mask(client->window, false); - xcb_ungrab_server(wm.xcb_conn); - } else { - xwindow_set_state(client->window, XCB_ICCCM_WM_STATE_NORMAL); - xcb_map_window(wm.xcb_conn, client->window); - } - monitor_arrange(client->monitor); - client_focus(client); -} - -static void update_client_list(void) { - xcb_connection_t *conn = wm.xcb_conn; - xcb_window_t root = wm.screen->root; - xcb_atom_t atom = _NET_CLIENT_LIST; - uint8_t mode = XCB_PROP_MODE_PREPEND; - xcb_atom_t type = XCB_ATOM_WINDOW; - - xcb_delete_property(conn, root, atom); - for (client_t *c = wm.client_list; c; c = c->next) { - xcb_change_property(conn, mode, root, atom, type, 32, 1, &c->window); - } -} - -void client_kill(client_t *client) { - if (!xwindow_send_event(client->window, WM_DELETE_WINDOW)) { - xwindow_kill_window(client->window); - } -} - -void client_unmanage(client_t *client, bool destroyed) { - monitor_t *m = client->monitor; - - client_detach_list(client); - client_detach_stack(client); - - for (tag_t *tag = m->tag_list; tag; tag = tag->next) { - if (tag->mask & client->tags) client_remove_from_tag(client, tag); - } - - if (!destroyed) { - set_window_event_mask(client->window, true); - client_change_border_width(client, client->old_border_width); - xwindow_set_state(client->window, XCB_ICCCM_WM_STATE_WITHDRAWN); - xcb_flush(wm.xcb_conn); - } - - client_wipe(client); - - update_client_list(); - - wm_restack_clients(); - monitor_deal_focus(m); - monitor_arrange(m); - monitor_draw_bar(m); - xcb_flush(wm.xcb_conn); -} - -void client_apply_geometry(client_t *client, area_t geometry) { - 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); -} - -/** - * @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 (y + height + client->border_width * 2 > workarea.y + workarea.height) { - y = workarea.y + workarea.height - height - client->border_width * 2; - } - - area_t rect = {.x = x, .y = y, .width = width, .height = height}; - 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_set_current_monitor(m, true); - 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); -} - -void client_stack_raise(client_t *client) { - if (wm.client_stack_list == client) return; - - client_detach_stack(client); - client_attach_stack(client); - wm_restack_clients(); -} - -bool client_is_visible(client_t *client) { - return client->tags & client->monitor->selected_tag->mask; -} - -static inline rect_t client_to_rect(client_t *client) { - return (rect_t){ - .x1 = client->geometry.x, - .y1 = client->geometry.y, - .x2 = client->geometry.x + (int32_t)client_width(client), - .y2 = client->geometry.y + (int32_t)client_height(client), - }; -} - -obscured_t client_get_obscured_state(client_t *client, client_t *client_stack) { - if (!client || !client_stack) return obscured_none; - - rect_t source = client_to_rect(client); - size_t clip_count = 0; - size_t clip_capacity = 0; - rect_t *clips = nullptr; - - bool has_overlap = false; - for (client_t *c = client_stack; c; c = c->stack_next) { - if (c == client) break; - if (c->minimize || !client_is_visible(c)) continue; - - rect_t clip = client_to_rect(c); - if (!rect_intersection(source, clip, nullptr)) continue; - - has_overlap = true; - if (clip_count == clip_capacity) { - clip_capacity = clip_capacity ? clip_capacity * 2 : 4; - p_realloc(&clips, clip_capacity); - } - clips[clip_count++] = clip; - } - - if (!has_overlap) { - p_delete(&clips); - return obscured_none; - } - - rect_t *remaining = nullptr; - size_t remaining_count = - rect_subtract_many(source, clips, clip_count, &remaining); - - p_delete(&clips); - p_delete(&remaining); - return remaining_count == 0 ? obscured_fully : obscured_partially; -} diff --git a/src/client.h b/src/client.h deleted file mode 100644 index b2c8acf..0000000 --- a/src/client.h +++ /dev/null @@ -1,76 +0,0 @@ -#pragma once - -#include -#include - -#include "color.h" -#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); -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); -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); -void client_send_to_monitor(client_t *client, monitor_t *monitor); - -void client_move_to(client_t *client, int16_t x, int16_t y); -void client_resize(client_t *client, uint16_t width, uint16_t height); -void client_change_border_color(client_t *client, color_t *color); -void client_change_border_width(client_t *client, uint16_t border_width); - -void client_update_window_type(client_t *client); -void client_update_wm_hints(client_t *client); -void client_update_size_hints(client_t *client); - -void client_set_floating(client_t *client, bool floating); -void client_set_fullscreen(client_t *client, bool fullscreen); -void client_set_maximize(client_t *client, bool maximize); -void client_set_minimize(client_t *client, bool minimize); -void client_set_sticky(client_t *client, bool sticky); -void client_set_urgent(client_t *client, bool urgent); - -void client_set_class_instance(client_t *client, const char *class, - const char *instance); - -void client_set_name(client_t *client, char *name); -void client_set_icon_name(client_t *client, char *icon_name); -void client_set_net_name(client_t *client, char *name); -void client_set_net_icon_name(client_t *client, char *icon_name); - -void client_set_role(client_t *client, char *role); - -void client_set_transient_for_window(client_t *client, - xcb_window_t transient_for_window); -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, bool destroyed); -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); - -bool client_is_visible(client_t *client); - -typedef enum obscured_t { - obscured_none, - obscured_partially, - obscured_fully, -} obscured_t; -obscured_t client_get_obscured_state(client_t *client, client_t *client_stack); - -static inline uint16_t client_width(client_t *c) { - return c->geometry.width + c->border_width * 2; -} -static inline uint16_t client_height(client_t *c) { - return c->geometry.height + c->border_width * 2; -} diff --git a/src/color.c b/src/color.c deleted file mode 100644 index f09a1a3..0000000 --- a/src/color.c +++ /dev/null @@ -1,87 +0,0 @@ -#include "color.h" - -#include "utils.h" - -/** - * @brief 将 16 进制的颜色配置转换成 uint32 格式的 rgba数据 - * - * @param hex 16 进制的颜色表示,支持的格式有 - * RGB/RGBA/RRGGBB/RRGGBBAA/#RGB/#RGBA/#RRGGBB/#RRGGBBAA - * @param rgba 返回数据的指针,解析结果会放到这个指针指向的内存中 - * @return 如果 hex 是一个能正常解析的颜色,返回 true ,否则返回 false - */ -static bool hex_color_to_rgba(const char *hex, uint32_t *rgba); -/** - * @brief 将 rgba 通道顺序的颜色转换成 argb 顺序 - * @description xcb 库用到的颜色需要是 argb 通道顺序 - */ -static uint32_t rgba_to_argb(uint32_t rgba); -/** - * @brief 提取颜色中的各个颜色通道数值 - * @param rgba uint32 表示的颜色 - * @param red red channel, range 0~1 - * @param green green channel, range 0~1 - * @param blue blue channel, range 0~1 - * @param alpha alpha channel, range 0~1 - */ -static void extract_color_channel(const uint32_t rgba, double *red, - double *green, double *blue, double *alpha); - -void color_parse(const char *hex, color_t *const color) { - bool success = hex_color_to_rgba(hex, &color->rgba); - if (!success) fatal("invalid hex color: %s", hex); - - color->argb = rgba_to_argb(color->rgba); - extract_color_channel(color->rgba, &color->red, &color->green, &color->blue, - &color->alpha); -} - -bool hex_color_to_rgba(const char *hex, uint32_t *rgba) { - if (hex[0] == '#') hex++; - - size_t len = strlen(hex); - char extended[9] = {0}; - uint32_t color = 0x00000000; - - if (len == 3 || len == 4) { - extended[0] = extended[1] = hex[0]; - extended[2] = extended[3] = hex[1]; - extended[4] = extended[5] = hex[2]; - if (len == 3) { - extended[6] = extended[7] = 'F'; - } else { - extended[6] = extended[7] = hex[3]; - } - } else if (len == 6) { - strncpy(extended, hex, len); - extended[6] = extended[7] = 'F'; - } else if (len == 8) { - strncpy(extended, hex, len); - } else { - return false; - } - - color = strtoul(extended, nullptr, 16) & 0xffffffff; - *rgba = color; - return true; -} - -#define EXTRACE_COLOR_BIT(C, R) (((C) >> (R)) & 0xff) -uint32_t rgba_to_argb(uint32_t rgba) { - uint32_t r = EXTRACE_COLOR_BIT(rgba, 24); - uint32_t g = EXTRACE_COLOR_BIT(rgba, 16); - uint32_t b = EXTRACE_COLOR_BIT(rgba, 8); - uint32_t a = EXTRACE_COLOR_BIT(rgba, 0); - - uint32_t argb = (a << 24) | (r << 16) | (g << 8) | b; - return argb; -} - -#define COLOR_SPLIT(C, R) (EXTRACE_COLOR_BIT((C), (R)) / (double)0xff) -void extract_color_channel(const uint32_t rgba, double *red, double *green, - double *blue, double *alpha) { - *red = COLOR_SPLIT(rgba, 24); - *green = COLOR_SPLIT(rgba, 16); - *blue = COLOR_SPLIT(rgba, 8); - *alpha = COLOR_SPLIT(rgba, 0); -} diff --git a/src/color.h b/src/color.h deleted file mode 100644 index ec7f0d7..0000000 --- a/src/color.h +++ /dev/null @@ -1,22 +0,0 @@ -#pragma once - -#include - -typedef struct color_t { - uint32_t rgba; /* use for human reading */ - uint32_t argb; /* use for xcb */ - - /* channels of color, use for cairo */ - double red; /* red channel */ - double green; /* green channel */ - double blue; /* blue channel */ - double alpha; /* alpha channel */ -} color_t; - -/** - * @brief parsing color represented in hexadecimal format - * @param hex color represented in hexadecimal format, the supported format - have: RGB/RGBA/RRGGBB/RRGGBBAA/#RGB/#RGBA/#RRGGBB/#RRGGBBAA - * @param color return the parsed color - */ -void color_parse(const char *hex, color_t *const color); diff --git a/src/config.c b/src/config.c deleted file mode 100644 index 84ada86..0000000 --- a/src/config.c +++ /dev/null @@ -1,81 +0,0 @@ -#include "config.h" - -#include - -#include "default_config.h" -#include "utils.h" - -void config_status_set_gap(config_status_t *status_config, uint32_t gap, - uint32_t item_gap) { - if (!status_config) fatal("no valid status config"); - status_config->status_gap = gap; - status_config->status_item_gap = item_gap; -} - -void config_status_add_item(config_status_t **status_config, - config_status_item_t status_item) { - const auto item_count = (*status_config)->status_count + 1; - const auto new_size = - sizeof(config_status_t) + item_count * sizeof(config_status_item_t); - xrealloc((void **)status_config, (ssize_t)new_size); - (*status_config)->list[item_count - 1] = status_item; - (*status_config)->status_count = item_count; -} - -void config_status_filter_item(config_status_t **status_config, - status_item_filter filter) { - if (!status_config || !*status_config) fatal("no valid status config"); - if (!filter) return; - - config_status_t *config = *status_config; - size_t write_idx = 0; - size_t item_count = config->status_count; - - for (size_t read_idx = 0; read_idx < item_count; read_idx++) { - config_status_item_t *item = &config->list[read_idx]; - if (!filter(item)) continue; - if (write_idx != read_idx) { - config->list[write_idx] = *item; - } - write_idx++; - } - - if (write_idx == item_count) return; - - config->status_count = write_idx; - size_t new_size = - sizeof(config_status_t) + write_idx * sizeof(config_status_item_t); - xrealloc((void **)status_config, (ssize_t)new_size); -} - -static config_status_t *config_create_default_status(void) { - size_t item_count = countof(status_list); - size_t size = sizeof(config_status_t) + item_count * sizeof(status_list[0]); - config_status_t *status = xmalloc((ssize_t)size); - - status->status_gap = status_config.status_gap; - status->status_item_gap = status_config.status_item_gap; - status->status_count = item_count; - memcpy(status->list, status_list, item_count * sizeof(status_list[0])); - - return status; -} - -static config_t runtime_config = {}; - -const config_t *init_config(void) { - if (!runtime_config.status) { - runtime_config.status = config_create_default_status(); - } - if (!runtime_config.status_renderer) { - runtime_config.status_renderer = renderer_render_status; - } - return &runtime_config; -} - -void config_set_custom_status_renderer(config_t *config, - status_renderer_t status_renderer) { - if (!config) fatal("no valid config"); - config->status_renderer = - status_renderer ? status_renderer : renderer_render_status; -} diff --git a/src/config.h b/src/config.h deleted file mode 100644 index 658ec8e..0000000 --- a/src/config.h +++ /dev/null @@ -1,57 +0,0 @@ -#pragma once - -#include -#include - -#include "renderer.h" -#include "status.h" - -typedef struct config_status_item_t { - status_type_t type; - icon_type_t icon_type; - const char *icon; - const char *color; -} config_status_item_t; - -typedef struct config_status_t { - uint32_t status_gap; - uint32_t status_item_gap; - size_t status_count; - config_status_item_t list[]; -} config_status_t; - -void config_status_set_gap(config_status_t *status_config, uint32_t gap, - uint32_t item_gap); - -void config_status_add_item(config_status_t **status_config, - config_status_item_t status_item); - -typedef bool (*status_item_filter)(config_status_item_t *item); -/** - * @brief 按过滤条件原地筛选 status 项并保持原有顺序 - * - * 当 filter(item) 返回 true 时保留该项,返回 false 时删除该项。 - * 筛选后会压缩 list、更新 status_count,并收缩配置对象内存。 - * - * @param status_config 配置指针地址(函数内部会重分配内存) - * @param filter 过滤函数 - */ -void config_status_filter_item(config_status_t **status_config, - status_item_filter filter); - -typedef void (*status_renderer_t)(config_status_t *config, size_t config_count, - status_t *status, renderer_status_t *output); - -typedef struct config_t { - config_status_t *status; - status_renderer_t status_renderer; -} config_t; - -/** - * @brief 初始化配置对象 - * @return 返回静态变量指针,不需要释放 - */ -const config_t *init_config(void); - -void config_set_custom_status_renderer(config_t *config, - status_renderer_t status_renderer); diff --git a/src/config/.clang-format b/src/config/.clang-format deleted file mode 120000 index 2d11237..0000000 --- a/src/config/.clang-format +++ /dev/null @@ -1 +0,0 @@ -../../.clang-format \ No newline at end of file diff --git a/src/core/.clang-format b/src/core/.clang-format deleted file mode 120000 index 2d11237..0000000 --- a/src/core/.clang-format +++ /dev/null @@ -1 +0,0 @@ -../../.clang-format \ No newline at end of file diff --git a/src/default_config.h b/src/default_config.h deleted file mode 100644 index 21d27e4..0000000 --- a/src/default_config.h +++ /dev/null @@ -1,197 +0,0 @@ -#pragma once - -#include -#include -#include - -#include "action.h" -#include "config.h" -#include "layout.h" -#include "renderer.h" -#include "status.h" -#include "types.h" - -static const char *const font_family = "monospace"; -static const int font_size = 10; -static const int default_dpi = 144; - -static const char *const tags[] = { - "1", "2", "3", "4", "5", "6", "7", "8", "9", nullptr, -}; -static const uint16_t border_width = 1; -static const uint16_t bar_y_padding = 1; -static const uint16_t tag_x_padding = 10; - -static const char *const bar_bg = "#222222"; -static const char *const tag_bg = "#222222"; -static const char *const active_tag_bg = "#005577"; -static const char *const tag_color = "#bbbbbb"; -static const char *const active_tag_color = "#eeeeee"; -static const char *const border_color = "#444444"; -static const char *const active_border_color = "#005577"; - -static const config_status_item_t status_list[] = { - { - .type = status_net_down, - .icon_type = icon_type_image, - .icon = "/home/zedhugh/develop/zswm/src/resources/icons/" - "corner-left-down-line.png", - .color = "#87af5f", - }, - { - .type = status_net_up, - .icon_type = icon_type_image, - .icon = - "/home/zedhugh/develop/zswm/src/resources/icons/corner-right-up-line.png", - .color = "#e54c62", - }, - { - .type = status_audio, - .icon_type = icon_type_image, - .icon = "/home/zedhugh/develop/zswm/src/resources/icons/volume-up-fill.png", - .color = "#7493d2", - }, - { - .type = status_memory, - .icon_type = icon_type_image, - .icon = "/home/zedhugh/develop/zswm/src/resources/icons/ram-line.png", - .color = "#e0da37", - }, - { - .type = status_cpu, - .icon_type = icon_type_image, - .icon = "/home/zedhugh/develop/zswm/src/resources/icons/cpu-line.png", - .color = "#e33a6e", - }, - { - .type = status_time, - .icon_type = icon_type_image, - .icon = "/home/zedhugh/develop/zswm/src/resources/icons/time-line.png", - .color = "#7788af", - }, -}; -static const config_status_t status_config = { - .status_gap = 10, - .status_item_gap = 5, -}; - -static const config_t config = { - .status = (config_status_t *)&status_config, -}; - -static const button_t button_list[] = { - {click_tag, modifier_none, button_left, select_tag_of_current_monitor, {0}}, - { - click_client_name, - modifier_none, - button_left, - change_client_state_dwim, - {0}, - }, -}; - -#define TAGKEYS(KEY, TAG) \ - {modifier_super, KEY, select_tag_of_current_monitor, {.ui = TAG}}, { \ - modifier_super | modifier_shift, KEY, send_client_to_tag, {.ui = TAG}, \ - } - -static const char launcher[] = - "rofi -show combi -modes combi -combi-modes window,drun,run,ssh,windowcd"; -static const char terminal[] = "xterm"; -static const char terminal_class[] = "XTerm"; -static const char editor[] = "emacsclient -a '' -r -n"; -static const char editor_class[] = "Emacs"; -static const char browser[] = "firefox-bin"; -static const char browser_class[] = "firefox"; -static const char chrome[] = "google-chrome-stable"; -static const char chrome_class[] = "Google-chrome"; -static const keyboard_t key_list[] = { - {modifier_super, XK_r, spawn, {.ptr = launcher}}, - {modifier_super, XK_Return, spawn, {.ptr = terminal}}, - { - modifier_control | modifier_alt, - XK_r, - raise_or_run, - {.ptr = (const char *[]){terminal_class, terminal}}, - }, - { - modifier_super, - XK_e, - raise_or_run, - {.ptr = (const char *[]){editor_class, editor}}, - }, - { - modifier_super, - XK_q, - raise_or_run, - {.ptr = (const char *[]){browser_class, browser}}, - }, - { - modifier_super, - XK_a, - raise_or_run, - {.ptr = (const char *[]){chrome_class, chrome}}, - }, - {modifier_super | modifier_shift, XK_q, quit, {.b = false}}, - {modifier_super | modifier_control, XK_r, quit, {.b = true}}, - {modifier_super | modifier_shift, - XK_j, - focus_client_in_same_tag, - {.b = true}}, - {modifier_super | modifier_shift, - XK_k, - focus_client_in_same_tag, - {.b = false}}, - - TAGKEYS(XK_1, 1 << 0), - TAGKEYS(XK_2, 1 << 1), - TAGKEYS(XK_3, 1 << 2), - TAGKEYS(XK_4, 1 << 3), - TAGKEYS(XK_5, 1 << 4), - TAGKEYS(XK_6, 1 << 5), - TAGKEYS(XK_7, 1 << 6), - TAGKEYS(XK_8, 1 << 7), - TAGKEYS(XK_9, 1 << 8), - - {modifier_super, XK_o, send_client_to_next_monitor, {0}}, - - {modifier_super | modifier_control, XK_j, focus_next_monitor, {0}}, - {modifier_super | modifier_control, XK_space, toggle_client_floating, {0}}, - {modifier_super, XK_f, toggle_client_fullscreen, {0}}, - {modifier_super, XK_m, toggle_client_maximize, {0}}, - {modifier_super | modifier_shift, XK_c, kill_client, {0}}, - - {modifier_super, XK_Up, change_volume, {.i = 1}}, - {modifier_super, XK_Down, change_volume, {.i = -1}}, - {modifier_super | modifier_shift, XK_m, toggle_mute, {0}}, - {modifier_none, XF86XK_AudioRaiseVolume, change_volume, {.i = 1}}, - {modifier_none, XF86XK_AudioLowerVolume, change_volume, {.i = -1}}, - {modifier_none, XF86XK_AudioMute, toggle_mute, {0}}, - -}; - -static const layout_t layout_list[] = { - {.symbol = "[]=", .arrange = tile}, - {.symbol = "[M]", .arrange = monocle}, - {.symbol = "><=", .arrange = nullptr}, -}; - -static const char *const autostart_list[] = { - "gentoo-pipewire-launcher restart", - "picom -b --backend xrender", - "fcitx5 -d", - nullptr, -}; - -static const rule_t rules[] = { - {.role = "dialog", .tag_index = -1, .floating = true}, - {.class = "firefox", .role = "About", .tag_index = 1, .floating = true}, - {.class = "firefox", .role = "browser", .tag_index = 1, .maximize = true}, - {.class = "Google-chrome", .tag_index = 1, .maximize = true}, - {.class = "mpv", .tag_index = 11, .switch_to_tag = true, .fullscreen = true}, - {.class = "Emacs", .tag_index = 10, .switch_to_tag = true, .maximize = true}, -}; - -static const char *const wallpapers[] = {"~/bg/*", "~/Downloads/bg/*"}; -/* static const char *const wallpapers[] = {}; */ -static const uint32_t wallpaper_interval = 0; diff --git a/src/event.c b/src/event.c deleted file mode 100644 index cc0f4c7..0000000 --- a/src/event.c +++ /dev/null @@ -1,387 +0,0 @@ -#include "event.h" - -#include -#include -#include -#include -#include -#include -#include -#include - -#include "action.h" -#include "atoms-extern.h" -#include "base.h" -#include "client.h" -#include "default_config.h" -#include "monitor.h" -#include "tray.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; - user_action_arg_t arg = {0}; - - monitor_t *monitor = nullptr; - for (monitor_t *m = wm.monitor_list; m; m = m->next) { - if (ev->event == m->bar_window) { - monitor = m; - break; - } - } - - if (!monitor) return; - - wm_set_current_monitor(monitor, false); - if (ev->event_x >= monitor->tag_extent.start && - ev->event_x <= monitor->tag_extent.end) { - click_area = click_tag; - - for (tag_t *tag = monitor->tag_list; tag; tag = tag->next) { - if (ev->event_x >= tag->bar_extent.start && - ev->event_x <= tag->bar_extent.end) { - arg.ui = tag->mask; - break; - } - } - } - - if (click_area == click_none) { - for (task_in_tag_t *task = monitor->selected_tag->task_list; task; - task = task->next) { - if (ev->event_x >= task->bar_extent.start && - ev->event_x <= task->bar_extent.end) { - click_area = click_client_name; - arg.ptr = task->client; - break; - } - } - } - - if (click_area == click_none) return; - - for (int i = 0; i < countof(button_list); i++) { - if (click_area == button_list[i].click_area && - button_list[i].button == ev->detail && - button_list[i].modifiers == ev->state) { - button_list[i].func(&arg); - } - } -} - -static void configure_request(xcb_configure_request_event_t *ev) { - if (tray_handle_configure_request(ev)) return; - - client_t *client = client_get_by_window(ev->window); - if (client) { - if (ev->value_mask & XCB_CONFIG_WINDOW_BORDER_WIDTH) { - client_change_border_width(client, ev->border_width); - } else { - tag_t *tag = client->monitor->selected_tag; - task_in_tag_t *task = client_get_task_in_tag(client, tag); - if (!task || !(client->floating || !tag->layout->arrange)) return; - - if (ev->value_mask & XCB_CONFIG_WINDOW_X) task->geometry.x = ev->x; - if (ev->value_mask & XCB_CONFIG_WINDOW_Y) task->geometry.y = ev->y; - if (ev->value_mask & XCB_CONFIG_WINDOW_WIDTH) - task->geometry.width = ev->width; - if (ev->value_mask & XCB_CONFIG_WINDOW_HEIGHT) - task->geometry.height = ev->height; - client_apply_geometry(client, task->geometry); - } - } else { - uint16_t value_mask = ev->value_mask; - xcb_configure_window_value_list_t value_list = { - .x = ev->x, - .y = ev->y, - .width = ev->width, - .height = ev->height, - .border_width = ev->border_width, - .sibling = ev->sibling, - .stack_mode = ev->stack_mode, - }; - xcb_configure_window_aux(wm.xcb_conn, ev->window, value_mask, &value_list); - xcb_flush(wm.xcb_conn); - } -} - -static void key_press(xcb_key_press_event_t *ev) { - bool is_press = XCB_EVENT_RESPONSE_TYPE(ev) == XCB_KEY_PRESS; - xcb_keysym_t keysym = xcb_key_press_lookup_keysym(wm.key_symbols, ev, 0); - char key_name[16]; - if (xkb_keysym_get_name(keysym, key_name, sizeof(key_name)) != -1) { - logger("key %s: %s\n", is_press ? "press" : "release", key_name); - } - - if (!is_press) return; - - for (int i = 0; i < countof(key_list); i++) { - keyboard_t key = key_list[i]; - if (keysym == key.keysym && ev->state == key.modifiers && key.func) { - /* 查询光标所在屏幕并将其设置为当前屏幕 */ - point_t point = {.x = ev->root_x, .y = ev->root_y}; - monitor_t *monitor = wm_get_monitor_by_point(point); - wm_set_current_monitor(monitor, false); - - key.func(&key.arg); - return; - } - } -} - -static void map_request(xcb_map_request_event_t *ev) { - if (tray_handle_map_request(ev)) return; - - xcb_get_window_attributes_reply_t *wa_reply = - xwindow_get_attributes_reply(ev->window); - - if (!wa_reply) return; - if (wa_reply->override_redirect) { - p_delete(&wa_reply); - return; - } - - client_t *c = client_get_by_window(ev->window); - if (!c) { - xcb_get_geometry_reply_t *geo_reply = - xwindow_get_geometry_reply(ev->window); - if (!geo_reply) { - p_delete(&wa_reply); - return; - } - - client_manage(ev->window, geo_reply); - p_delete(&geo_reply); - } - - p_delete(&wa_reply); -} - -typedef enum _NET_WM_STATE_ACTION : uint32_t { - _NET_WM_STATE_ADD = 1, - _NET_WM_STATE_REMOVE = 0, - _NET_WM_STATE_TOGGLE = 2, -} _NET_WM_STATE_ACTION; - -static inline bool get_bool_property(bool init_property, - _NET_WM_STATE_ACTION action) { - switch (action) { - case _NET_WM_STATE_ADD: - return true; - case _NET_WM_STATE_REMOVE: - return false; - case _NET_WM_STATE_TOGGLE: - return !init_property; - default: - fatal( - "Invalid _NET_WM_STATE_ACTION: %u\n" - "Only supported:" - "_NET_WM_STATE_ADD: %u" - "_NET_WM_STATE_REMOVE: %u" - "_NET_WM_STATE_TOGGLE: %u", - action, _NET_WM_STATE_ADD, _NET_WM_STATE_REMOVE, _NET_WM_STATE_TOGGLE); - } -} - -static void client_message(xcb_client_message_event_t *ev) { - if (tray_handle_client_message(ev)) return; - - client_t *c = client_get_by_window(ev->window); - if (c == nullptr) return; - - if (ev->type == _NET_ACTIVE_WINDOW) { - for (tag_t *t = c->monitor->tag_list; t; t = t->next) { - if ((t->mask & c->tags) == 0) continue; - - logger("== _NET_ACTIVE_WINDOW: %s[%u]\n", c->class, ev->data.data32[0]); - - switch (ev->data.data32[0]) { - case 2: /* 来自 pager */ - wm_set_current_monitor(c->monitor, true); - 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) { - /** - * _NET_WM_STATE 协议数据格式为 - * data.data32 = { - * action, // 操作类型(添加/移除/切换) - * property1, // 第一个状态原子(如_NET_WM_STATE_FULLSCREEN) - * property2, // 第二个状态原子(可选,通常为0) - * 0, // 预留字段(固定为0) - * 0 // 预留字段(固定为0) - * }; - * action 值可能为 - * _NET_WM_STATE_ADD(1)​​:添加全屏状态,窗口进入全屏模式 - * _NET_WM_STATE_REMOVE(0):移除全屏状态,窗口退出全屏模式 - * _NET_WM_STATE_TOGGLE(2):切换全屏状态(若当前全屏则退出,反之进入) - */ - _NET_WM_STATE_ACTION action = ev->data.data32[0]; - - if (ev->data.data32[1] == _NET_WM_STATE_FULLSCREEN || - ev->data.data32[2] == _NET_WM_STATE_FULLSCREEN) { - client_set_fullscreen(c, get_bool_property(c->fullscreen, action)); - } else if (ev->data.data32[1] == _NET_WM_STATE_MAXIMIZED_HORZ || - ev->data.data32[1] == _NET_WM_STATE_MAXIMIZED_VERT || - ev->data.data32[2] == _NET_WM_STATE_MAXIMIZED_HORZ || - ev->data.data32[2] == _NET_WM_STATE_MAXIMIZED_VERT) { - client_set_maximize(c, get_bool_property(c->maximize, action)); - } - } else if (ev->type == WM_CHANGE_STATE) { - switch (ev->data.data32[0]) { - case XCB_ICCCM_WM_STATE_ICONIC: - client_set_minimize(c, true); - break; - case XCB_ICCCM_WM_STATE_NORMAL: - client_set_minimize(c, false); - break; - } - } -} - -static void property_notify(xcb_property_notify_event_t *ev) { - if (tray_handle_property_notify(ev)) return; - - 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); - xcb_flush(wm.xcb_conn); - } else if (ev->atom == _NET_WM_NAME) { - xwindow_get_text_property(ev->window, ev->atom, &client->net_name); - monitor_draw_bar(client->monitor); - xcb_flush(wm.xcb_conn); - } else if (ev->atom == XCB_ATOM_WM_HINTS) { - client_update_wm_hints(client); - monitor_draw_bar(client->monitor); - xcb_flush(wm.xcb_conn); - } -} - -static void destroy_notify(xcb_destroy_notify_event_t *ev) { - if (tray_handle_destroy_notify(ev)) return; - - client_t *client = client_get_by_window(ev->window); - if (client) client_unmanage(client, true); -} - -static void unmap_notify(xcb_unmap_notify_event_t *ev) { - if (tray_handle_unmap_notify(ev)) return; - - client_t *client = client_get_by_window(ev->window); - if (!client || client->minimize) return; - - if (XCB_EVENT_SENT(ev)) { - xwindow_set_state(ev->window, XCB_ICCCM_WM_STATE_WITHDRAWN); - } else { - client_unmanage(client, false); - } -} - -static void map_notify(xcb_map_notify_event_t *ev) { - /* 锁屏重新进入桌面后绘制 bar 内容以免 bar 不显示内容 */ - for (monitor_t *m = wm.monitor_list; m; m = m->next) { - if (ev->window == m->bar_window) { - monitor_draw_bar(m); - xcb_flush(wm.xcb_conn); - return; - } - } -} - -static void expose(xcb_expose_event_t *ev) { - if (ev->count > 0) return; - - monitor_t *monitor = wm_get_monitor_by_window(ev->window); - monitor_draw_bar(monitor); - xcb_flush(wm.xcb_conn); -} - -static void enter_notify(xcb_enter_notify_event_t *ev) { - if (wm_should_ignore_enter_notify(ev)) return; - - client_t *client = client_get_by_window(ev->event); - if (!client || wm.client_focused == client) return; - - client_focus(client); - wm_set_current_monitor(client->monitor, false); -} - -static void selection_clear(xcb_selection_clear_event_t *ev) { - tray_handle_selection_clear(ev); -} - -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); - logger("event type: %u[%s], xkb_event: %u\n", event_type, label, - wm.event_base_xkb); - - switch (event_type) { -#define EVENT(type, callback) \ - case type: \ - callback((void *)event); \ - return - - EVENT(XCB_BUTTON_PRESS, button_press); - EVENT(XCB_KEY_PRESS, key_press); - EVENT(XCB_KEY_RELEASE, key_press); - EVENT(XCB_CONFIGURE_REQUEST, configure_request); - EVENT(XCB_MAP_REQUEST, map_request); - EVENT(XCB_MAP_NOTIFY, map_notify); - EVENT(XCB_EXPOSE, expose); - EVENT(XCB_CLIENT_MESSAGE, client_message); - EVENT(XCB_PROPERTY_NOTIFY, property_notify); - EVENT(XCB_UNMAP_NOTIFY, unmap_notify); - EVENT(XCB_DESTROY_NOTIFY, destroy_notify); - EVENT(XCB_ENTER_NOTIFY, enter_notify); - EVENT(XCB_SELECTION_CLEAR, selection_clear); - -#undef EVENT - } - - /* 处理 XKB 事件 */ - if (wm.event_base_xkb != 0 && event_type == wm.event_base_xkb) { - xkb_handle_event(event); - } -} - -static gboolean xcb_event_loop(GIOChannel *channel, GIOCondition condition, - gpointer user_data) { - if (condition & (G_IO_HUP | G_IO_ERR)) { - wm_quit(); - return false; - } - - xcb_generic_event_t *event = nullptr; - while ((event = xcb_poll_for_event(wm.xcb_conn))) { - handle_xcb_event(event); - p_delete(&event); - } - - return true; -} - -void setup_event_loop(void) { - int fd = xcb_get_file_descriptor(wm.xcb_conn); - GIOChannel *channel = g_io_channel_unix_new(fd); - g_io_channel_set_encoding(channel, nullptr, nullptr); - GIOCondition cond = G_IO_IN | G_IO_HUP | G_IO_ERR; - g_io_add_watch(channel, cond, xcb_event_loop, nullptr); - g_io_channel_unref(channel); -} diff --git a/src/event.h b/src/event.h deleted file mode 100644 index ebe8685..0000000 --- a/src/event.h +++ /dev/null @@ -1,3 +0,0 @@ -#pragma once - -void setup_event_loop(void); diff --git a/src/image.c b/src/image.c deleted file mode 100644 index cee14bd..0000000 --- a/src/image.c +++ /dev/null @@ -1,171 +0,0 @@ -#include "image.h" - -#include -#include -#include -#include - -#include "utils.h" - -typedef struct image_cache_entry_t { - char *path; - cairo_surface_t *surface; - int width; - int height; -} image_cache_entry_t; - -static image_cache_entry_t *image_cache = nullptr; -static size_t image_cache_count = 0; -static cairo_user_data_key_t image_surface_data_key; - -static inline uint32_t premultiply_argb(uint32_t pixel) { - uint8_t a = (pixel >> 24) & 0xff; - uint8_t r = (pixel >> 16) & 0xff; - uint8_t g = (pixel >> 8) & 0xff; - uint8_t b = pixel & 0xff; - r = (uint8_t)((r * a + 127) / 255); - g = (uint8_t)((g * a + 127) / 255); - b = (uint8_t)((b * a + 127) / 255); - return ((uint32_t)a << 24) | ((uint32_t)r << 16) | ((uint32_t)g << 8) | - (uint32_t)b; -} - -static bool image_load_surface(const char *path, cairo_surface_t **surface, - int *width, int *height) { - if (!path || !*path || !surface || !width || !height) return false; - - Imlib_Image image = imlib_load_image(path); - if (!image) return false; - - imlib_context_set_image(image); - int img_width = imlib_image_get_width(); - int img_height = imlib_image_get_height(); - if (img_width <= 0 || img_height <= 0) { - imlib_free_image(); - return false; - } - - DATA32 *src = imlib_image_get_data_for_reading_only(); - if (!src) { - imlib_free_image(); - return false; - } - - size_t pixel_count = (size_t)img_width * (size_t)img_height; - uint32_t *dst = xmalloc((ssize_t)(pixel_count * sizeof(uint32_t))); - for (size_t i = 0; i < pixel_count; i++) { - dst[i] = premultiply_argb(src[i]); - } - - cairo_surface_t *img_surface = cairo_image_surface_create_for_data( - (unsigned char *)dst, CAIRO_FORMAT_ARGB32, img_width, img_height, - img_width * (int)sizeof(uint32_t)); - cairo_status_t status = cairo_surface_status(img_surface); - if (status != CAIRO_STATUS_SUCCESS) { - cairo_surface_destroy(img_surface); - p_delete(&dst); - imlib_free_image(); - return false; - } - - cairo_surface_set_user_data(img_surface, &image_surface_data_key, dst, free); - *surface = img_surface; - *width = img_width; - *height = img_height; - - imlib_free_image(); - return true; -} - -static image_cache_entry_t *image_cache_find(const char *path) { - if (!path || !*path) return nullptr; - for (size_t i = 0; i < image_cache_count; i++) { - image_cache_entry_t *entry = &image_cache[i]; - if (!entry->path) continue; - if (strcmp(entry->path, path) == 0) return entry; - } - return nullptr; -} - -static image_cache_entry_t *image_cache_add(const char *path) { - if (!path || !*path) return nullptr; - - size_t new_count = image_cache_count + 1; - xrealloc((void **)&image_cache, - (ssize_t)(new_count * sizeof(image_cache_entry_t))); - image_cache_entry_t *entry = &image_cache[image_cache_count]; - entry->path = strdup(path); - entry->surface = nullptr; - entry->width = 0; - entry->height = 0; - image_cache_count = new_count; - return entry; -} - -static image_cache_entry_t *image_cache_get(const char *path) { - image_cache_entry_t *entry = image_cache_find(path); - if (entry && entry->surface) return entry; - if (!entry) entry = image_cache_add(path); - if (!entry) return nullptr; - - if (entry->surface) { - cairo_surface_destroy(entry->surface); - entry->surface = nullptr; - } - - if (!image_load_surface(path, &entry->surface, &entry->width, - &entry->height)) { - return nullptr; - } - return entry; -} - -bool image_get_scaled_size(const char *path, int32_t target_height, int *width, - int *height) { - if (!width || !height || target_height <= 0) return false; - image_cache_entry_t *entry = image_cache_get(path); - if (!entry || !entry->surface || entry->width <= 0 || entry->height <= 0) { - return false; - } - - *height = target_height; - *width = (entry->width * target_height + entry->height / 2) / entry->height; - if (*width <= 0) *width = 1; - return true; -} - -bool image_draw(cairo_t *cr, const char *path, int32_t x, int32_t y, - int32_t width, int32_t height) { - if (!cr || width <= 0 || height <= 0) return false; - image_cache_entry_t *entry = image_cache_get(path); - if (!entry || !entry->surface || entry->width <= 0 || entry->height <= 0) { - return false; - } - - cairo_save(cr); - cairo_translate(cr, (double)x, (double)y); - cairo_scale(cr, (double)width / (double)entry->width, - (double)height / (double)entry->height); - cairo_set_source_surface(cr, entry->surface, 0, 0); - cairo_paint(cr); - cairo_restore(cr); - return true; -} - -void image_cache_clean(void) { - if (!image_cache) return; - - for (size_t i = 0; i < image_cache_count; i++) { - image_cache_entry_t *entry = &image_cache[i]; - if (entry->surface) { - cairo_surface_destroy(entry->surface); - entry->surface = nullptr; - } - p_delete(&entry->path); - entry->width = 0; - entry->height = 0; - } - - p_delete(&image_cache); - image_cache_count = 0; -} diff --git a/src/image.h b/src/image.h deleted file mode 100644 index ee56d44..0000000 --- a/src/image.h +++ /dev/null @@ -1,36 +0,0 @@ -#pragma once - -#include -#include -#include - -/** - * @brief 获取图片按目标高度缩放后的尺寸 - * - * @param path 图片路径 - * @param target_height 目标高度(像素) - * @param width 返回缩放后的宽度 - * @param height 返回缩放后的高度 - * @return 加载并计算成功返回 true - */ -bool image_get_scaled_size(const char *path, int32_t target_height, int *width, - int *height); - -/** - * @brief 在 cairo 上下文中绘制图片 - * - * @param cr cairo 上下文 - * @param path 图片路径 - * @param x 绘制起始 x - * @param y 绘制起始 y - * @param width 绘制宽度 - * @param height 绘制高度 - * @return 绘制成功返回 true - */ -bool image_draw(cairo_t *cr, const char *path, int32_t x, int32_t y, - int32_t width, int32_t height); - -/** - * @brief 释放图片缓存 - */ -void image_cache_clean(void); diff --git a/src/layout.c b/src/layout.c deleted file mode 100644 index 1e09aed..0000000 --- a/src/layout.c +++ /dev/null @@ -1,93 +0,0 @@ -#include "layout.h" - -#include -#include - -#include "base.h" -#include "client.h" -#include "types.h" -#include "utils.h" - -void monocle(tag_t *tag) { - logger("== monocle tag: %s start ==\n", tag->name); - for (task_in_tag_t *task = tag->task_list; task; task = task->next) { - if (!client_need_layout(task->client)) continue; - - uint16_t border_width = task->client->border_width; - area_t workarea = task->client->monitor->workarea; - task->geometry.x = workarea.x; - task->geometry.y = workarea.y; - task->geometry.width = workarea.width - border_width * 2; - task->geometry.height = workarea.height - border_width * 2; - logger( - "== window 0x%x: x: %d, y: %d, width: %u, height: %u, border width: %u\n", - task->client->window, task->geometry.x, task->geometry.y, - task->geometry.width, task->geometry.height, border_width); - } - logger("== monocle tag: %s end ==\n", tag->name); -} - -void tile(tag_t *tag) { - logger("== tile tag: %s start ==\n", tag->name); - uint16_t amount = 0; - for (task_in_tag_t *task = tag->task_list; task; task = task->next) { - if (client_need_layout(task->client)) ++amount; - } - if (amount == 0) goto end; - - uint16_t columns = (uint32_t)floor(sqrt(amount)); - if (columns * (columns + 1) <= amount) ++columns; - - uint32_t rows_in_other_cols = amount / columns; - uint32_t rows_in_main_col; - while ((rows_in_main_col = amount - (columns - 1) * rows_in_other_cols) > - rows_in_other_cols) { - ++rows_in_other_cols; - } - - area_t *workarea = &tag->task_list->client->monitor->workarea; - - uint16_t width_avg = workarea->width / columns; - uint16_t width_for_main_col = workarea->width - (columns - 1) * width_avg; - uint16_t width_for_other_cols = width_avg; - - uint16_t i = 0, row = 0, col = 0, row_count, width, height; - int16_t x = workarea->x, y = workarea->y; - for (task_in_tag_t *task = tag->task_list; task; task = task->next) { - if (!client_need_layout(task->client)) continue; - - if (i < rows_in_main_col) { - row = i; - width = width_for_main_col; - row_count = rows_in_main_col; - } else { - width = width_for_other_cols; - row_count = rows_in_other_cols; - if (((i - rows_in_main_col) % row_count) == 0) { - col++; - row = 0; - x += col == 1 ? width_for_main_col : width_for_other_cols; - y = workarea->y; - } - } - - uint16_t h_avg = workarea->height / row_count; - uint16_t h_main = workarea->height - h_avg * (row_count - 1); - height = row == 0 ? h_main : h_avg; - task->geometry.x = x; - task->geometry.y = y; - task->geometry.width = width - task->client->border_width * 2; - task->geometry.height = height - task->client->border_width * 2; - y += height; - - ++i; - - logger( - "== window 0x%x: x: %d, y: %d, width: %u, height: %u, border width: %u\n", - task->client->window, task->geometry.x, task->geometry.y, - task->geometry.width, task->geometry.height, task->client->border_width); - } - -end: - logger("== tile tag: %s end\n", tag->name); -} diff --git a/src/layout.h b/src/layout.h deleted file mode 100644 index f2b7189..0000000 --- a/src/layout.h +++ /dev/null @@ -1,6 +0,0 @@ -#pragma once - -#include "types.h" - -void monocle(tag_t *tag); -void tile(tag_t *tag); diff --git a/src/layouts/.clang-format b/src/layouts/.clang-format deleted file mode 120000 index 2d11237..0000000 --- a/src/layouts/.clang-format +++ /dev/null @@ -1 +0,0 @@ -../../.clang-format \ No newline at end of file diff --git a/src/monitor.c b/src/monitor.c deleted file mode 100644 index ebf72e0..0000000 --- a/src/monitor.c +++ /dev/null @@ -1,454 +0,0 @@ -#include "monitor.h" - -#include -#include -#include -#include -#include -#include -#include - -#include "app.h" -#include "base.h" -#include "client.h" -#include "color.h" -#include "config.h" -#include "image.h" -#include "renderer.h" -#include "status.h" -#include "text.h" -#include "tray.h" -#include "types.h" -#include "utils.h" -#include "wm.h" -#include "xcursor.h" -#include "xwindow.h" - -uint32_t monitor_initialize_tag(monitor_t *monitor, const char **tags, - uint32_t tag_index_start_at) { - uint32_t tag_count = 0; - int i = 0; - const char *tag_name = nullptr; - tag_t *prev_tag = nullptr; - for (tag_name = tags[i]; tag_name; tag_name = tags[++i]) { - tag_t *tag = p_new(tag_t, 1); - tag->name = strdup(tag_name); - tag->mask = 1u << i; - tag->index = tag_index_start_at + tag_count; - - if (wm.layout_count > 0 && wm.layout_list) { - tag->layout = &wm.layout_list[0]; - } - - if (prev_tag) { - prev_tag->next = tag; - } else { - monitor->tag_list = tag; - } - - prev_tag = tag; - tag_count++; - } - - monitor->selected_tag = monitor->tag_list; - return tag_count; -} - -void monitor_deal_focus(monitor_t *monitor) { - if (!monitor->selected_tag->task_list) { - client_focus(nullptr); - return; - } - - client_t *client = nullptr; - for (client_t *c = wm.client_stack_list; c; c = c->stack_next) { - if (client_get_task_in_tag(c, monitor->selected_tag)) { - client = c; - break; - } - } - client_focus(client); -} - -void monitor_select_tag(monitor_t *monitor, uint32_t tag_mask) { - if (monitor->selected_tag->mask == tag_mask) return; - - for (task_in_tag_t *task = monitor->selected_tag->task_list; task; - task = task->next) { - 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->geometry; - } - } - - for (tag_t *tag = monitor->tag_list; tag; tag = tag->next) { - if (tag->mask == tag_mask) { - monitor->selected_tag = tag; - monitor_arrange(monitor); - monitor_draw_bar(monitor); - break; - } - } - - monitor_deal_focus(monitor); - xcb_flush(wm.xcb_conn); -} - -static void tag_clean(tag_t *tag) { - tag_t *next_tag = nullptr; - for (tag_t *t = tag; t; t = next_tag) { - p_delete(&t->name); - - next_tag = t->next; - p_delete(&t); - } -} - -void monitor_clean(monitor_t *monitor) { - monitor_t *next_monitor = nullptr; - for (monitor_t *m = monitor; m; m = next_monitor) { - p_delete(&m->name); - tag_clean(m->tag_list); - - m->selected_tag = nullptr; - m->tag_list = nullptr; - - cairo_destroy(m->bar_cr); - m->bar_cr = nullptr; - - next_monitor = m->next; - p_delete(&m); - } -} - -void monitor_init_bar(monitor_t *monitor) { - static xcb_colormap_t colormap = XCB_NONE; - visual_t *visual = xwindow_get_xcb_visual(true); - xcb_visualid_t visual_id = visual->visual->visual_id; - xcb_connection_t *conn = wm.xcb_conn; - if (colormap == XCB_NONE) { - colormap = xcb_generate_id(conn); - uint8_t alloc = XCB_COLORMAP_ALLOC_NONE; - xcb_create_colormap(conn, alloc, colormap, wm.screen->root, visual_id); - } - - xcb_window_t window = xcb_generate_id(conn); - uint32_t value_mask = XCB_CW_OVERRIDE_REDIRECT | XCB_CW_BACK_PIXEL | - XCB_CW_BORDER_PIXEL | XCB_CW_EVENT_MASK | - XCB_CW_CURSOR | XCB_CW_COLORMAP; - const xcb_create_window_value_list_t value_list = { - .override_redirect = true, - .background_pixel = wm.color_set.bar_bg.argb, - .border_pixel = 0, - .event_mask = XCB_EVENT_MASK_BUTTON_PRESS | XCB_EVENT_MASK_EXPOSURE, - .cursor = xcursor_get_xcb_cursor(cursor_normal), - .colormap = colormap, - }; - xcb_void_cookie_t cookie; - cookie = xcb_create_window_aux_checked( - conn, visual->depth, window, wm.screen->root, monitor->geometry.x, - monitor->geometry.y, monitor->geometry.width, wm.bar_height, 0, - XCB_WINDOW_CLASS_INPUT_OUTPUT, visual_id, value_mask, &value_list); - if (xcb_request_check(conn, cookie)) fatal("cannot create bar window"); - - cookie = xcb_map_window(conn, window); - if (xcb_request_check(conn, cookie)) fatal("cannot map bar window:"); - - xwindow_set_class_instance(window); - xwindow_set_name_static(window, APP_NAME "_bar"); - - xcb_aux_sync(conn); - - uint16_t width = monitor->geometry.width; - monitor->workarea.x = monitor->geometry.x; - monitor->workarea.y = monitor->geometry.y + wm.bar_height; - monitor->workarea.width = width; - monitor->workarea.height = monitor->geometry.height - wm.bar_height; - monitor->bar_window = window; - cairo_surface_t *surface = cairo_xcb_surface_create( - wm.xcb_conn, window, visual->visual, width, wm.bar_height); - cairo_t *cr = cairo_create(surface); - cairo_surface_destroy(surface); - p_delete(&visual); - - cairo_status_t status = cairo_status(cr); - if (status != CAIRO_STATUS_SUCCESS) { - fatal("cannot create cairo context: %s", cairo_status_to_string(status)); - } - monitor->bar_cr = cr; -} - -static void monitor_draw_tags(monitor_t *monitor) { - int16_t x = 0; - for (tag_t *tag = monitor->tag_list; tag; tag = tag->next) { - bool selected = monitor->selected_tag == tag; - bool has_client = tag->task_list; - color_t *bg = nullptr; - color_t *color = nullptr; - if (selected) { - bg = &wm.color_set.active_tag_bg; - color = &wm.color_set.active_tag_color; - } else { - bg = &wm.color_set.tag_bg; - color = &wm.color_set.tag_color; - } - int width = 0, height = 0; - text_get_size(tag->name, &width, &height); - width += 2 * wm.padding.tag_x; - tag->bar_extent.start = x; - tag->bar_extent.end = x + width; - - area_t tag_rect = {.x = x, .y = 0, .width = width, .height = wm.bar_height}; - if (bg->rgba != wm.color_set.bar_bg.rgba) { - draw_background(monitor->bar_cr, bg, tag_rect); - } - if (has_client) { - area_t rect = {.x = x, .y = 0, .width = 4, .height = 4}; - draw_rect(monitor->bar_cr, rect, selected, color, 1); - } - area_t text_rect = tag_rect; - tag_rect.y = (int16_t)((int)wm.bar_height - height) / 2; - tag_rect.height = (int16_t)height; - draw_text(monitor->bar_cr, tag->name, color, text_rect, true); - x += width; - } - - monitor->tag_extent.start = 0; - monitor->tag_extent.end = x; -} - -static void monitor_draw_layout_symbol(monitor_t *monitor) { - const layout_t *layout = monitor->selected_tag->layout; - if (layout && layout->symbol) { - color_t *color = &wm.color_set.tag_color; - int width = 0; - text_get_size(layout->symbol, &width, nullptr); - area_t area = { - .x = monitor->tag_extent.end, - .y = monitor->geometry.y, - .width = width + 2 * wm.padding.tag_x, - .height = wm.bar_height, - }; - monitor->layout_symbol_extent.start = area.x; - monitor->layout_symbol_extent.end = area.x + area.width; - draw_text(monitor->bar_cr, layout->symbol, color, area, true); - } -} - -static void monitor_draw_status(monitor_t *monitor, status_t *status, - int16_t right_edge, int16_t left_edge) { - static renderer_status_t *rendered_status = nullptr; - static size_t rendered_status_capacity = 0; - - monitor->status_extent.end = right_edge; - monitor->status_extent.start = right_edge; - int32_t end = left_edge; - - if (status == nullptr || !wm.config || !wm.config->status) return; - - config_status_t *status_config = wm.config->status; - size_t item_count = status_config->status_count; - if (item_count == 0) return; - - if (rendered_status_capacity < item_count) { - size_t size = - sizeof(renderer_status_t) + item_count * sizeof(renderer_status_item_t); - xrealloc((void **)&rendered_status, (ssize_t)size); - rendered_status_capacity = item_count; - } - - status_renderer_t renderer = wm.config->status_renderer; - if (!renderer) renderer = renderer_render_status; - - renderer(status_config, item_count, status, rendered_status); - item_count = MIN(item_count, rendered_status->item_count); - - int32_t start = monitor->status_extent.start; - int32_t icon_target_height = - (int32_t)wm.bar_height - (int32_t)wm.padding.bar_y * 2; - if (icon_target_height <= 0) icon_target_height = wm.bar_height; - - for (size_t ri = item_count; ri > 0; ri--) { - size_t i = ri - 1; - renderer_status_item_t *item = &rendered_status->item_list[i]; - bool has_text = item->text[0] != '\0'; - int text_width = 0; - if (has_text) text_get_size(item->text, &text_width, nullptr); - - bool has_text_icon = false; - int icon_text_width = 0; - if (item->icon_type == icon_type_text && item->icon_text && - item->icon_text[0]) { - has_text_icon = true; - text_get_size(item->icon_text, &icon_text_width, nullptr); - } - - bool has_image_icon = false; - int icon_width = 0; - int icon_height = 0; - if (item->icon_type == icon_type_image && item->icon_path && - item->icon_path[0]) { - has_image_icon = image_get_scaled_size( - item->icon_path, icon_target_height, &icon_width, &icon_height); - } - bool has_icon = has_text_icon || has_image_icon; - - int width = 0; - if (has_text_icon) width += icon_text_width; - if (has_image_icon) width += icon_width; - if (has_text) width += text_width; - if (has_icon && has_text) width += (int)rendered_status->item_gap; - if (!width) continue; - - start -= width; - if (start <= end) return; - - color_t *color = item->color ? item->color : &wm.color_set.tag_color; - int32_t draw_x = start; - if (has_text_icon && icon_text_width > 0) { - area_t icon_rect = { - .x = (int16_t)draw_x, - .y = 0, - .width = (uint16_t)icon_text_width, - .height = wm.bar_height, - }; - draw_text(monitor->bar_cr, item->icon_text, color, icon_rect, true); - draw_x += icon_text_width; - } else if (has_image_icon) { - int32_t icon_y = ((int32_t)wm.bar_height - icon_height) / 2; - image_draw(monitor->bar_cr, item->icon_path, draw_x, icon_y, icon_width, - icon_height); - draw_x += icon_width; - } - if (has_icon && has_text) draw_x += (int32_t)rendered_status->item_gap; - - if (has_text && text_width > 0) { - area_t rect = { - .x = (int16_t)draw_x, - .y = 0, - .width = (uint16_t)text_width, - .height = wm.bar_height, - }; - draw_text(monitor->bar_cr, item->text, color, rect, true); - } - monitor->status_extent.start = (int16_t)start; - - if (ri > 1) { - start -= (int32_t)rendered_status->gap; - if (start < end) return; - } - } -} - -static void monitor_draw_tasks(monitor_t *monitor, int16_t right_edge) { - 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) { - if (task->client->skip_taskbar) continue; - ++task_count; - }; - if (task_count == 0) return; - - int16_t x = monitor->layout_symbol_extent.end; - int32_t available_width = (int32_t)right_edge - x; - if (available_width <= 0) return; - - uint16_t task_width = (uint16_t)(available_width / task_count); - if (task_width < wm.font_size) return; - - for (task_in_tag_t *task = task_list; task; task = task->next) { - if (task->client->skip_taskbar) continue; - - 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; - uint16_t tray_width = tray_get_width(monitor); - int16_t status_right = (int16_t)monitor->geometry.width - tray_width; - if (status_right < 0) status_right = 0; - area_t bar_area = { - .x = 0, - .y = 0, - .width = monitor->geometry.width, - .height = height, - }; - draw_background(cr, &wm.color_set.bar_bg, bar_area); - - monitor_draw_tags(monitor); - monitor_draw_layout_symbol(monitor); - monitor_draw_status(monitor, wm.status, status_right, - monitor->layout_symbol_extent.end); - monitor_draw_tasks(monitor, monitor->status_extent.start); - tray_place(monitor, monitor->geometry.width); -} - -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_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 (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_move_to(c, task->geometry.x, task->geometry.y); - } - } else { - int16_t x = -client_width(c); - int16_t y = -client_height(c); - client_move_to(c, x, y); - } - } - xcb_flush(wm.xcb_conn); -} - -void monitor_save_cursor_point(monitor_t *monitor) { - monitor->cursor_position = xcursor_query_pointer_position(); - monitor->position_inited = true; -} - -point_t monitor_get_restore_cursor_point(monitor_t *monitor) { - if (!monitor->position_inited) { - point_t point = xcursor_query_pointer_position(); - monitor_t *m = wm_get_monitor_by_point(point); - monitor->cursor_position.x = point.x - m->geometry.x + monitor->geometry.x; - monitor->cursor_position.y = point.y - m->geometry.y + monitor->geometry.y; - monitor->position_inited = true; - } - - return monitor->cursor_position; -} - -void monitor_restore_cursor_point(monitor_t *monitor) { - xcursor_set_pointer_position(monitor_get_restore_cursor_point(monitor)); -} diff --git a/src/monitor.h b/src/monitor.h deleted file mode 100644 index acd99d4..0000000 --- a/src/monitor.h +++ /dev/null @@ -1,17 +0,0 @@ -#pragma once - -#include - -#include "types.h" - -uint32_t monitor_initialize_tag(monitor_t *monitor, const char **tags, - uint32_t tag_index_start_at); -void monitor_deal_focus(monitor_t *monitor); -void monitor_select_tag(monitor_t *monitor, uint32_t tag_mask); -void monitor_clean(monitor_t *monitor); -void monitor_init_bar(monitor_t *monitor); -void monitor_draw_bar(monitor_t *monitor); -void monitor_arrange(monitor_t *monitor); -void monitor_save_cursor_point(monitor_t *monitor); -point_t monitor_get_restore_cursor_point(monitor_t *monitor); -void monitor_restore_cursor_point(monitor_t *monitor); diff --git a/src/rect.c b/src/rect.c deleted file mode 100644 index 8475ec1..0000000 --- a/src/rect.c +++ /dev/null @@ -1,156 +0,0 @@ -#include "rect.h" - -#include "base/macros.h" -#include "base/memory.h" - -static inline bool rect_valid(rect_t rect) { - return rect.x1 < rect.x2 && rect.y1 < rect.y2; -} - -/* 将一个合法矩形追加到动态矩形数组中。 */ -static inline void rect_list_append(rect_t **list, size_t *length, - size_t *capacity, rect_t rect) { - if (!rect_valid(rect)) return; - - if (*length == *capacity) { - *capacity = *capacity ? *capacity * 2 : 4; - p_realloc(list, *capacity); - } - (*list)[(*length)++] = rect; -} - -bool rect_intersection(rect_t a, rect_t b, rect_t *intersection) { - if (!rect_valid(a) || !rect_valid(b)) return false; - - rect_t overlap = { - .x1 = MAX(a.x1, b.x1), - .y1 = MAX(a.y1, b.y1), - .x2 = MIN(a.x2, b.x2), - .y2 = MIN(a.y2, b.y2), - }; - if (!rect_valid(overlap)) return false; - - if (intersection) *intersection = overlap; - return true; -} - -size_t rect_subtract(rect_t source, rect_t clip, rect_t remaining[4]) { - if (!rect_valid(source)) return 0; - - rect_t overlap; - if (!rect_intersection(source, clip, &overlap)) { - if (remaining) remaining[0] = source; - return 1; - } - - size_t count = 0; - /* - * source: [sx1,sx2) x [sy1,sy2) - * overlap: [ox1,ox2) x [oy1,oy2) = source 与 clip 的交集 - * - * y=sy1 ┌───────────────────────────────────────────┐ - * │ TOP │ - * │ UL UR │ - * y=oy1 ├───────────────┬───────────────┬───────────┤ - * │ LEFT │ OVERLAP │ RIGHT │ - * y=oy2 ├───────────────┴───────────────┴───────────┤ - * │ LL LR │ - * │ BOTTOM │ - * y=sy2 └───────────────────────────────────────────┘ - * x=sx1 x=ox1 x=ox2 x=sx2 - * - * 角块归属: - * UL、UR 属于 TOP - * LL、LR 属于 BOTTOM - * LEFT/RIGHT 只覆盖 y ∈ [oy1, oy2),因此不会包含四个角块。 - */ - rect_t parts[] = { - /* top */ - { - .x1 = source.x1, - .y1 = source.y1, - .x2 = source.x2, - .y2 = overlap.y1, - }, - /* bottom */ - { - .x1 = source.x1, - .y1 = overlap.y2, - .x2 = source.x2, - .y2 = source.y2, - }, - /* left */ - { - .x1 = source.x1, - .y1 = overlap.y1, - .x2 = overlap.x1, - .y2 = overlap.y2, - }, - /* right */ - { - .x1 = overlap.x2, - .y1 = overlap.y1, - .x2 = source.x2, - .y2 = overlap.y2, - }, - }; - - for (size_t i = 0; i < countof(parts); i++) { - if (!rect_valid(parts[i])) continue; - if (remaining) remaining[count] = parts[i]; - count++; - } - - return count; -} - -size_t rect_subtract_many(rect_t source, const rect_t clips[], - size_t clip_count, rect_t **remaining) { - /* - * 实现说明: - * 初始剩余集合为 {source},然后依次处理每个 clip: - * 1) 用 clip 去减当前集合中的每个碎片 - * 2) 将产生的新碎片合并到 next 集合 - * 3) 用 next 替换 current,进入下一轮 - * 最终 current 即 source - clips[0] - clips[1] - ... 的结果。 - */ - if (remaining) *remaining = nullptr; - if (!rect_valid(source)) return 0; - - size_t current_length = 1; - size_t current_capacity = 1; - rect_t *current = p_new(rect_t, current_capacity); - current[0] = source; - - if (clips) { - for (size_t i = 0; i < clip_count && current_length > 0; i++) { - rect_t clip = clips[i]; - if (!rect_valid(clip)) continue; - - size_t next_length = 0; - size_t next_capacity = current_length ? current_length : 1; - rect_t *next = p_new(rect_t, next_capacity); - - for (size_t j = 0; j < current_length; j++) { - rect_t fragments[4]; - size_t fragment_count = rect_subtract(current[j], clip, fragments); - for (size_t k = 0; k < fragment_count; k++) { - rect_list_append(&next, &next_length, &next_capacity, fragments[k]); - } - } - - p_delete(¤t); - current = next; - current_length = next_length; - current_capacity = next_capacity; - } - } - - if (remaining) { - *remaining = current; - } else { - p_delete(¤t); - } - - return current_length; -} diff --git a/src/rect.h b/src/rect.h deleted file mode 100644 index 0c9f001..0000000 --- a/src/rect.h +++ /dev/null @@ -1,42 +0,0 @@ -#pragma once - -#include -#include - -typedef struct rect_t { - int32_t x1; - int32_t y1; - int32_t x2; - int32_t y2; -} rect_t; - -bool rect_intersection(rect_t a, rect_t b, rect_t *intersection); -size_t rect_subtract(rect_t source, rect_t clip, rect_t remaining[4]); -/** - * @brief 从 source 中按顺序减去 clips 中的多个矩形 - * @param source 源矩形 - * @param clips 要减去的矩形数组,可为 nullptr - * @param clip_count clips 的长度 - * @param remaining 输出剩余矩形数组;若不为 - * nullptr,则由函数分配内存并由调用方释放 - * @return 剩余矩形数量 - * - * @code{.c} - * rect_t source = {.x1 = 0, .y1 = 0, .x2 = 100, .y2 = 80}; - * rect_t clips[] = { - * {.x1 = 10, .y1 = 10, .x2 = 40, .y2 = 40}, - * {.x1 = 30, .y1 = 20, .x2 = 70, .y2 = 60}, - * }; - * - * rect_t *remaining = nullptr; - * size_t count = rect_subtract_many(source, clips, 2, &remaining); - * - * for (size_t i = 0; i < count; i++) { - * // use remaining[i] - * } - * - * free(remaining); - * @endcode - */ -size_t rect_subtract_many(rect_t source, const rect_t clips[], - size_t clip_count, rect_t **remaining); diff --git a/src/renderer.c b/src/renderer.c deleted file mode 100644 index b8e8b94..0000000 --- a/src/renderer.c +++ /dev/null @@ -1,90 +0,0 @@ -#include "renderer.h" - -#include -#include - -#include "color.h" -#include "config.h" -#include "status.h" -#include "utils.h" - -static void status_text_from_data(status_type_t type, const status_t *status, - char *text, size_t text_size) { - if (!text || text_size == 0) return; - text[0] = '\0'; - if (!status) return; - - switch (type) { - case status_net_down: - snprintf(text, text_size, "%s", status->net_speed.down); - break; - case status_net_up: - snprintf(text, text_size, "%s", status->net_speed.up); - break; - case status_audio: - if (status->pulse) { - snprintf(text, text_size, "%d%%%s%s%s", status->pulse->volume_percent, - status->pulse->mute ? "M" : "", - strlen(status->pulse->device_name) ? "|" : "", - status->pulse->device_name); - } else { - snprintf(text, text_size, "0%%"); - } - break; - case status_memory: - snprintf(text, text_size, "%s(%.1f%%)", status->mem_usage.mem_used_text, - status->mem_usage.mem_percent); - break; - case status_cpu: - snprintf(text, text_size, "%.1lf", status->cpu_usage_percent); - break; - case status_time: - snprintf(text, text_size, "%s", status->time); - break; - default: - break; - } -} - -void renderer_render_status(config_status_t *config, size_t config_count, - status_t *status, renderer_status_t *output) { - static color_t *color_cache = nullptr; - static size_t color_cache_count = 0; - - if (!config || !output) return; - - size_t item_count = config->status_count; - if (config_count && config_count < item_count) item_count = config_count; - - output->gap = config->status_gap; - output->item_gap = config->status_item_gap; - output->item_count = item_count; - - if (item_count == 0) return; - - if (color_cache_count < item_count) { - xrealloc((void **)&color_cache, (ssize_t)(sizeof(color_t) * item_count)); - color_cache_count = item_count; - } - - for (size_t i = 0; i < item_count; i++) { - const config_status_item_t *config_item = &config->list[i]; - renderer_status_item_t *output_item = &output->item_list[i]; - memset(output_item, 0, sizeof(*output_item)); - - output_item->icon_type = config_item->icon_type; - if (config_item->icon_type == icon_type_text) { - output_item->icon_text = config_item->icon; - } else if (config_item->icon_type == icon_type_image) { - output_item->icon_path = config_item->icon; - } - - status_text_from_data(config_item->type, status, output_item->text, - sizeof(output_item->text)); - - if (config_item->color && *config_item->color) { - color_parse(config_item->color, &color_cache[i]); - output_item->color = &color_cache[i]; - } - } -} diff --git a/src/renderer.h b/src/renderer.h deleted file mode 100644 index c68a9ce..0000000 --- a/src/renderer.h +++ /dev/null @@ -1,41 +0,0 @@ -#pragma once - -#include -#include - -#include "color.h" - -typedef struct config_status_t config_status_t; -typedef struct status_t status_t; - -typedef enum icon_type_t { - icon_type_none, - icon_type_text, - icon_type_image, -} icon_type_t; - -typedef struct renderer_status_item_t { - icon_type_t icon_type; - char text[124]; - const char *icon_text; - const char *icon_path; - color_t *color; -} renderer_status_item_t; - -typedef struct renderer_status_t { - uint32_t gap; - uint32_t item_gap; - size_t item_count; - renderer_status_item_t item_list[]; -} renderer_status_t; - -/** - * @brief 根据 status 配置和实时状态生成可绘制的 status 数据 - * - * @param config status 配置 - * @param config_count 最大输出项数,传 0 表示使用 config->status_count - * @param status 实时状态数据 - * @param output 渲染输出缓冲 - */ -void renderer_render_status(config_status_t *config, size_t config_count, - status_t *status, renderer_status_t *output); diff --git a/src/status.c b/src/status.c deleted file mode 100644 index f1af97a..0000000 --- a/src/status.c +++ /dev/null @@ -1,253 +0,0 @@ -#include "status.h" - -#include -#include -#include -#include -#include -#include -#include - -#include "audio.h" - -static constexpr char CPU_FILE[] = "/proc/stat"; -static constexpr char MEM_FILE[] = "/proc/meminfo"; -static constexpr char NET_FILE[] = "/proc/net/dev"; - -static constexpr uint32_t INTERVAL = 1; -static constexpr char TIME_FORMAT[] = "%A %m-%d %H:%M:%S"; - -static status_t status; -static guint timer = 0; -static status_changed_notify listener = nullptr; - -typedef struct cpu_stat_t { - uint64_t user; - uint64_t nice; - uint64_t system; - uint64_t idle; - uint64_t iowait; - uint64_t irq; - uint64_t softirq; - uint64_t steal; - uint64_t guest; - uint64_t guest_nice; -} cpu_stat_t; - -static double calc_cpu_usage(cpu_stat_t *curr, cpu_stat_t *prev) { - uint64_t curr_idle = curr->idle + curr->iowait; - uint64_t curr_total = curr->user + curr->nice + curr->system + curr->idle + - curr->iowait + curr->irq + curr->softirq + curr->steal + - curr->guest + curr->guest_nice; - - uint64_t prev_idle = prev->idle + prev->iowait; - uint64_t prev_total = prev->user + prev->nice + prev->system + prev->idle + - prev->iowait + prev->irq + prev->softirq + prev->steal + - prev->guest + prev->guest_nice; - - uint64_t total = curr_total - prev_total; - uint64_t idle = curr_idle - prev_idle; - uint64_t active = total - idle; - if (total == 0) return 0.0; - - double usage = ((double)active) / total * 100; - return usage; -} - -static bool get_cpu_usage(double *usage, GError *error) { - static cpu_stat_t prev_cpu_stat = {0}; - static bool inited = false; - - gchar *contents = nullptr; - gsize length = 0; - - if (!g_file_get_contents(CPU_FILE, &contents, &length, &error)) return false; - - gchar **lines = g_strsplit(contents, "\n", 0); - g_free(contents); - - char cpu_line[256]; - for (gchar **line = lines; *line != nullptr; ++line) { - if (strncmp(*line, "cpu ", 4) == 0) { - strncpy(cpu_line, *line, sizeof(cpu_line)); - break; - } - } - - cpu_stat_t stat = {0}; - sscanf(cpu_line, "cpu %lu %lu %lu %lu %lu %lu %lu %lu %lu %lu", &stat.user, - &stat.nice, &stat.system, &stat.idle, &stat.iowait, &stat.irq, - &stat.softirq, &stat.steal, &stat.guest, &stat.guest_nice); - - if (!inited) { - prev_cpu_stat = stat; - inited = true; - } - - double percent = calc_cpu_usage(&stat, &prev_cpu_stat); - prev_cpu_stat = stat; - *usage = percent; - - g_strfreev(lines); - return true; -} - -static void bytes_to_readable_size(uint64_t bytes, char *str) { - static char *uints[] = {"B", "K", "M", "G", "T"}; - - double size = (double)bytes; - int i = 0; - while (size > 1024.0) { - size /= 1024.0; - ++i; - } - if (i == 0) { - sprintf(str, "%ld%s", bytes, uints[i]); - } else { - sprintf(str, "%.1lf%s", size, uints[i]); - } -} - -static void calc_mem_usage(memory_usage_t *usage) { - usage->mem_used = usage->mem_total - usage->mem_free - usage->buffers - - usage->cached - usage->s_reclaimable; - usage->swap_used = usage->swap_total - usage->swap_free; - - usage->mem_percent = - ((float)usage->mem_used) / ((float)usage->mem_total) * 100; - if (usage->swap_total == 0) { - usage->swap_percent = 0; - } else { - usage->swap_percent = - ((float)usage->swap_used) / ((float)usage->swap_total) * 100; - } - - bytes_to_readable_size(usage->mem_used * 1024, usage->mem_used_text); - bytes_to_readable_size(usage->swap_used * 1024, usage->swap_used_text); -} - -static bool get_mem_usage(memory_usage_t *usage, GError *error) { - gchar *contents = nullptr; - gsize length = 0; - - if (!g_file_get_contents(MEM_FILE, &contents, &length, &error)) return false; - - gchar **lines = g_strsplit(contents, "\n", 0); - g_free(contents); - - for (gchar **line = lines; *line != nullptr; ++line) { - char name[32]; - uint64_t kb = 0; - sscanf(*line, "%[^:]: %lu", name, &kb); - if (g_str_equal(name, "MemTotal")) { - usage->mem_total = kb; - } else if (g_str_equal(name, "MemFree")) { - usage->mem_free = kb; - } else if (g_str_equal(name, "Buffers")) { - usage->buffers = kb; - } else if (g_str_equal(name, "Cached")) { - usage->cached = kb; - } else if (g_str_equal(name, "SwapTotal")) { - usage->swap_total = kb; - } else if (g_str_equal(name, "SwapFree")) { - usage->swap_free = kb; - } else if (g_str_equal(name, "SReclaimable")) { - usage->s_reclaimable = kb; - } - } - calc_mem_usage(usage); - g_strfreev(lines); - return true; -} - -typedef struct net_stat_t { - uint64_t rx_bytes; - uint64_t tx_bytes; -} net_stat_t; - -static void calc_speed(const uint32_t interval, const net_stat_t *current, - const net_stat_t *previous, net_speed_t *speed) { - speed->rx_bytes = current->rx_bytes - previous->rx_bytes; - speed->tx_bytes = current->tx_bytes - previous->tx_bytes; - bytes_to_readable_size(speed->rx_bytes / interval, speed->down); - bytes_to_readable_size(speed->tx_bytes / interval, speed->up); -} - -static bool get_net_speed(const uint32_t interval, net_speed_t *speed, - GError *error) { - static net_stat_t prev = {0}; - static bool inited = false; - - gchar *contents = nullptr; - gsize length = 0; - - if (!g_file_get_contents(NET_FILE, &contents, &length, &error)) return false; - - gchar **lines = g_strsplit(contents, "\n", 0); - g_free(contents); - - net_stat_t stat = {0}; - for (gchar **line = lines; *line != nullptr; ++line) { - if (strchr(*line, ':') == nullptr) continue; - - char name[16]; - uint64_t rx_bytes, tx_bytes; - sscanf(*line, " %[^:]: %lu %*u %*u %*u %*u %*u %*u %*u %lu", name, - &rx_bytes, &tx_bytes); - if (g_str_equal(name, "lo")) continue; - - stat.rx_bytes += rx_bytes; - stat.tx_bytes += tx_bytes; - } - - if (!inited) { - inited = true; - prev = stat; - } - - calc_speed(interval, &stat, &prev, speed); - prev = stat; - - g_strfreev(lines); - return true; -} - -static void get_date_time(char *time_text, size_t length) { - time_t current_time = time(nullptr); - struct tm *time_info = localtime(¤t_time); - strftime(time_text, length, TIME_FORMAT, time_info); -} - -static inline void notify_status_change(void) { - if (listener) listener(&status); -} - -static gboolean update_status(gpointer data) { - GError *error = nullptr; - get_net_speed(INTERVAL, &status.net_speed, error); - get_mem_usage(&status.mem_usage, error); - get_cpu_usage(&status.cpu_usage_percent, error); - get_date_time(status.time, sizeof(status.time)); - notify_status_change(); - - return true; -} - -void notify_pulse_change(pulse_t *pulse) { - status.pulse = pulse; - notify_status_change(); -} - -void init_status(GMainContext *context, status_changed_notify callback) { - listener = callback; - status.pulse_context = init_pulse(context, notify_pulse_change); - update_status(nullptr); - timer = g_timeout_add_seconds(INTERVAL, update_status, nullptr); -} - -void clean_status(void) { - clean_pulse(status.pulse_context); - g_source_remove(timer); - status.pulse_context = nullptr; - listener = nullptr; -} diff --git a/src/status.h b/src/status.h deleted file mode 100644 index d87242d..0000000 --- a/src/status.h +++ /dev/null @@ -1,53 +0,0 @@ -#pragma once - -#include -#include - -#include "audio.h" - -typedef struct memory_usage_t { - char mem_used_text[32]; - char swap_used_text[32]; - - float mem_percent; - float swap_percent; - uint64_t mem_used; - uint64_t swap_used; - - uint64_t mem_total; - uint64_t mem_free; - uint64_t buffers; - uint64_t cached; - uint64_t swap_total; - uint64_t swap_free; - uint64_t s_reclaimable; -} memory_usage_t; - -typedef struct net_speed_t { - uint64_t rx_bytes; - uint64_t tx_bytes; - char up[64]; - char down[64]; -} net_speed_t; - -typedef enum status_type_t { - status_net_down, - status_net_up, - status_audio, - status_memory, - status_cpu, - status_time, -} status_type_t; - -typedef struct status_t { - net_speed_t net_speed; - memory_usage_t mem_usage; - double cpu_usage_percent; - char time[64]; - pulse_t *pulse; - pulse_context_t *pulse_context; -} status_t; - -typedef void (*status_changed_notify)(status_t *status); -void init_status(GMainContext *context, status_changed_notify callback); -void clean_status(void); diff --git a/src/text.c b/src/text.c deleted file mode 100644 index 685f87b..0000000 --- a/src/text.c +++ /dev/null @@ -1,183 +0,0 @@ -#include "text.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "base.h" -#include "color.h" -#include "utils.h" - -static PangoContext *context = nullptr; -static PangoLayout *layout = nullptr; -static PangoAttrList *attr_list = nullptr; - -/** - * 初始化绘制文字的 pango 环境 - * @param family 多个字体用逗号分割 - * @param size 字体大小 - * @param dpi - * @return layout 高度,可用于确定 bar 高度 - */ -int text_init_pango_layout(const char *family, uint32_t size, uint32_t dpi) { - logger("family: %s, size: %u, dpi: %u\n", family, size, dpi); - - static const char *layout_family = nullptr; - static uint32_t layout_size = 0; - static uint32_t layout_dpi = 0; - static int text_height = 0; - - if (layout_family != nullptr && strcmp(layout_family, family) == 0 && - layout_size == size && layout_dpi == dpi) { - logger("text pango layout config cached\n"); - return text_height; - } - - text_clean_pango_layout(); - - layout_family = family; - layout_size = size; - layout_dpi = dpi; - - PangoFontMap *fontmap = pango_cairo_font_map_new(); - context = pango_font_map_create_context(fontmap); - pango_cairo_context_set_resolution(context, (double)dpi); - - layout = pango_layout_new(context); - - PangoFontDescription *desc = pango_font_description_from_string(family); - pango_font_description_set_size(desc, size * PANGO_SCALE); - PangoLanguage *lang = pango_context_get_language(context); - PangoFontMetrics *metrics = pango_context_get_metrics(context, desc, lang); - - PangoAttribute *attr = pango_attr_font_desc_new(desc); - attr_list = pango_attr_list_new(); - pango_attr_list_insert(attr_list, attr); - pango_layout_set_attributes(layout, attr_list); - - pango_layout_set_wrap(layout, PANGO_WRAP_NONE); - pango_layout_set_ellipsize(layout, PANGO_ELLIPSIZE_END); - - int height = pango_font_metrics_get_height(metrics); - int ascent = pango_font_metrics_get_ascent(metrics); - int descent = pango_font_metrics_get_descent(metrics); - text_height = - MAX(PANGO_PIXELS_CEIL(height), PANGO_PIXELS_CEIL(ascent + descent)); - - pango_font_metrics_unref(metrics); - pango_font_description_free(desc); - g_object_unref(fontmap); - - PangoRectangle ink_rect, logical_rect; - pango_layout_get_pixel_extents(layout, &ink_rect, &logical_rect); - - text_height = MAX(text_height, MAX(ink_rect.height, logical_rect.height)); - - return text_height; -} - -void text_clean_pango_layout(void) { - if (attr_list) { - pango_attr_list_unref(attr_list); - attr_list = nullptr; - } - - if (layout) { - g_object_unref(layout); - layout = nullptr; - } - - if (context) { - g_object_unref(context); - context = nullptr; - } -} - -static void get_layout_size(int *width, int *height) { - PangoRectangle logical_rect; - pango_layout_get_pixel_extents(layout, nullptr, &logical_rect); - if (width) *width = logical_rect.width; - if (height) *height = logical_rect.height; -} - -void text_get_size(const char *text, int *width, int *height) { - pango_layout_set_width(layout, -1); - pango_layout_set_text(layout, text, -1); - get_layout_size(width, height); -} - -static void text_layout_prepare(bool align_center) { - if (layout == nullptr) { - layout = pango_layout_new(context); - pango_layout_set_attributes(layout, attr_list); - pango_layout_set_wrap(layout, PANGO_WRAP_NONE); - pango_layout_set_ellipsize(layout, PANGO_ELLIPSIZE_END); - } - - PangoAlignment align = align_center ? PANGO_ALIGN_CENTER : PANGO_ALIGN_LEFT; - pango_layout_set_alignment(layout, align); -} - -void draw_text(cairo_t *cr, const char *text, color_t *color, area_t area, - bool align_center) { - text_layout_prepare(align_center); - pango_layout_set_width(layout, (int)area.width * PANGO_SCALE); - pango_layout_set_text(layout, text, -1); - cairo_set_source_rgba(cr, (double)color->red, (double)color->green, - (double)color->blue, (double)color->alpha); - int height = 0; - get_layout_size(nullptr, &height); - - double offset_y = ((double)area.height - (double)height) / 2; - pango_cairo_update_layout(cr, layout); - cairo_move_to(cr, (double)area.x, offset_y + (double)area.y); - pango_cairo_show_layout(cr, layout); -} - -void draw_rect(cairo_t *cr, area_t area, bool fill, color_t *color, - uint16_t line_width) { - cairo_set_line_width(cr, (double)line_width); - double x = (double)area.x; - double y = (double)area.y; - double width = (double)area.width; - double height = (double)area.height; - if (fill) { - cairo_rectangle(cr, x, y, width, height); - cairo_set_source_rgba(cr, (double)color->red, (double)color->green, - (double)color->blue, (double)color->alpha); - cairo_fill(cr); - } else { - double half_line_width = (double)line_width / 2; - x += half_line_width; - y += half_line_width; - width -= half_line_width; - height -= half_line_width; - - cairo_rectangle(cr, x, y, width, height); - cairo_set_source_rgba(cr, (double)color->red, (double)color->green, - (double)color->blue, (double)color->alpha); - cairo_stroke(cr); - } -} - -void draw_background(cairo_t *cr, color_t *color, area_t area) { - double x = (double)area.x; - double y = (double)area.y; - double width = (double)area.width; - double height = (double)area.height; - cairo_move_to(cr, x, y); - cairo_set_source_rgba(cr, (double)color->red, (double)color->green, - (double)color->blue, (double)color->alpha); - cairo_rectangle(cr, x, y, width, height); - cairo_fill(cr); -} diff --git a/src/text.h b/src/text.h deleted file mode 100644 index 4be7238..0000000 --- a/src/text.h +++ /dev/null @@ -1,16 +0,0 @@ -#pragma once - -#include -#include - -#include "base.h" -#include "color.h" - -int text_init_pango_layout(const char *family, uint32_t size, uint32_t dpi); -void text_clean_pango_layout(void); -void text_get_size(const char *text, int *width, int *height); -void draw_text(cairo_t *cr, const char *text, color_t *color, area_t area, - bool align_center); -void draw_rect(cairo_t *cr, area_t area, bool fill, color_t *color, - uint16_t line_width); -void draw_background(cairo_t *cr, color_t *color, area_t area); diff --git a/src/tray.c b/src/tray.c deleted file mode 100644 index b909fbf..0000000 --- a/src/tray.c +++ /dev/null @@ -1,598 +0,0 @@ -#include "tray.h" - -#include -#include -#include -#include -#include -#include -#include -#include - -#include "atoms-extern.h" -#include "base.h" -#include "monitor.h" -#include "utils.h" -#include "wm.h" -#include "xwindow.h" - -typedef enum tray_opcode_t { - TRAY_REQUEST_DOCK = 0, - TRAY_BEGIN_MESSAGE = 1, - TRAY_CANCEL_MESSAGE = 2, -} tray_opcode_t; - -typedef enum tray_orientation_t { - TRAY_ORIENTATION_HORIZONTAL = 0, -} tray_orientation_t; - -typedef enum xembed_message_t { - XEMBED_EMBEDDED_NOTIFY = 0, -} xembed_message_t; - -enum { - XEMBED_VERSION = 0, -}; - -typedef struct tray_icon_t { - xcb_window_t window; - uint16_t natural_width; - uint16_t natural_height; - uint8_t ignore_unmaps; - bool mapped; -} tray_icon_t; - -typedef struct tray_t { - bool initialized; - xcb_atom_t selection_atom; - xcb_window_t window; - monitor_t *host_monitor; - - tray_icon_t *icons; - size_t icon_count; - size_t icon_capacity; - - uint16_t spacing; - uint16_t side_padding; -} tray_t; - -static tray_t tray = { - .selection_atom = XCB_ATOM_NONE, - .window = XCB_WINDOW_NONE, -}; - -static xcb_atom_t tray_intern_atom(const char *name) { - if (!name || !name[0]) return XCB_ATOM_NONE; - - xcb_intern_atom_cookie_t cookie = - xcb_intern_atom(wm.xcb_conn, false, (uint16_t)strlen(name), name); - xcb_intern_atom_reply_t *reply = - xcb_intern_atom_reply(wm.xcb_conn, cookie, nullptr); - if (!reply) return XCB_ATOM_NONE; - - xcb_atom_t atom = reply->atom; - p_delete(&reply); - return atom; -} - -static tray_icon_t *tray_get_icon(xcb_window_t window) { - for (size_t i = 0; i < tray.icon_count; i++) { - tray_icon_t *icon = &tray.icons[i]; - if (icon->window == window) return icon; - } - - return nullptr; -} - -static size_t tray_find_icon_index(xcb_window_t window) { - for (size_t i = 0; i < tray.icon_count; i++) { - if (tray.icons[i].window == window) return i; - } - - return SIZE_MAX; -} - -static uint16_t tray_target_icon_height(void) { - return MAX((uint16_t)1, wm.bar_height); -} - -static uint32_t tray_color_component_u16(double channel) { - if (channel < 0.0) channel = 0.0; - if (channel > 1.0) channel = 1.0; - - uint32_t value = (uint32_t)(channel * 65535.0 + 0.5); - return MIN(value, 65535u); -} - -static void tray_get_icon_layout(const tray_icon_t *icon, uint16_t *width, - uint16_t *height) { - uint32_t target_height = tray_target_icon_height(); - uint32_t natural_width = icon->natural_width; - uint32_t natural_height = icon->natural_height; - - if (natural_width == 0) natural_width = target_height; - if (natural_height == 0) natural_height = target_height; - if (natural_height == 0) natural_height = 1; - - uint32_t scaled_width = - (natural_width * target_height + natural_height / 2) / natural_height; - if (scaled_width == 0) scaled_width = 1; - - if (width) *width = (uint16_t)MIN((uint32_t)UINT16_MAX, scaled_width); - if (height) *height = (uint16_t)MIN((uint32_t)UINT16_MAX, target_height); -} - -static uint16_t tray_compute_width(void) { - if (!tray.initialized || tray.host_monitor == nullptr || - tray.icon_count == 0) { - return 0; - } - - uint32_t width = (uint32_t)tray.side_padding * 2; - for (size_t i = 0; i < tray.icon_count; i++) { - uint16_t icon_width = 0; - tray_get_icon_layout(&tray.icons[i], &icon_width, nullptr); - width += icon_width; - } - if (tray.icon_count > 1) { - width += (uint32_t)(tray.icon_count - 1) * tray.spacing; - } - - return (uint16_t)MIN((uint32_t)UINT16_MAX, width); -} - -static void tray_send_xembed_message(xcb_window_t window, uint32_t message, - uint32_t detail, uint32_t data1, - uint32_t data2) { - xcb_client_message_event_t ev; - p_clear(&ev, 1); - ev.response_type = XCB_CLIENT_MESSAGE; - ev.format = 32; - ev.window = window; - ev.type = _XEMBED; - ev.data.data32[0] = XCB_CURRENT_TIME; - ev.data.data32[1] = message; - ev.data.data32[2] = detail; - ev.data.data32[3] = data1; - ev.data.data32[4] = data2; - - xcb_send_event(wm.xcb_conn, false, window, XCB_EVENT_MASK_NO_EVENT, - (char *)&ev); -} - -static void tray_select_icon_events(xcb_window_t window) { - xcb_params_cw_t params = { - .back_pixel = wm.color_set.bar_bg.argb, - .border_pixel = 0, - .event_mask = - XCB_EVENT_MASK_PROPERTY_CHANGE | XCB_EVENT_MASK_STRUCTURE_NOTIFY, - }; - uint32_t mask = XCB_CW_BACK_PIXEL | XCB_CW_BORDER_PIXEL | XCB_CW_EVENT_MASK; - xcb_aux_change_window_attributes(wm.xcb_conn, window, mask, ¶ms); - xcb_clear_area(wm.xcb_conn, false, window, 0, 0, 0, 0); -} - -static void tray_release_icon_window(tray_icon_t *icon) { - if (!icon || icon->window == XCB_WINDOW_NONE) return; - - xcb_change_save_set(wm.xcb_conn, XCB_SET_MODE_DELETE, icon->window); - xcb_params_cw_t params = { - .event_mask = XCB_EVENT_MASK_NO_EVENT, - .border_pixel = 0, - }; - xcb_aux_change_window_attributes(wm.xcb_conn, icon->window, - XCB_CW_EVENT_MASK | XCB_CW_BORDER_PIXEL, - ¶ms); - xcb_reparent_window(wm.xcb_conn, icon->window, wm.screen->root, 0, 0); -} - -static void tray_layout_icons(int16_t right_edge) { - if (!tray.initialized || tray.window == XCB_WINDOW_NONE || - tray.host_monitor == nullptr) { - return; - } - - uint16_t tray_width = tray_compute_width(); - if (tray_width == 0) { - xcb_unmap_window(wm.xcb_conn, tray.window); - return; - } - - int32_t tray_x = (int32_t)right_edge - tray_width; - if (tray_x < 0) tray_x = 0; - - xcb_configure_window_value_list_t tray_values = { - .x = (int16_t)tray_x, - .y = 0, - .width = tray_width, - .height = wm.bar_height, - }; - uint16_t tray_mask = XCB_CONFIG_WINDOW_X | XCB_CONFIG_WINDOW_Y | - XCB_CONFIG_WINDOW_WIDTH | XCB_CONFIG_WINDOW_HEIGHT; - xcb_configure_window_aux(wm.xcb_conn, tray.window, tray_mask, &tray_values); - xcb_map_window(wm.xcb_conn, tray.window); - - int32_t x = tray.side_padding; - for (size_t i = 0; i < tray.icon_count; i++) { - tray_icon_t *icon = &tray.icons[i]; - uint16_t icon_width = 0; - uint16_t icon_height = 0; - tray_get_icon_layout(icon, &icon_width, &icon_height); - - int32_t y = ((int32_t)wm.bar_height - icon_height) / 2; - if (y < 0) y = 0; - - xcb_configure_window_value_list_t icon_values = { - .x = (int16_t)x, - .y = (int16_t)y, - .width = icon_width, - .height = icon_height, - .border_width = 0, - }; - uint16_t icon_mask = XCB_CONFIG_WINDOW_X | XCB_CONFIG_WINDOW_Y | - XCB_CONFIG_WINDOW_WIDTH | XCB_CONFIG_WINDOW_HEIGHT | - XCB_CONFIG_WINDOW_BORDER_WIDTH; - xcb_configure_window_aux(wm.xcb_conn, icon->window, icon_mask, - &icon_values); - xcb_map_window(wm.xcb_conn, icon->window); - icon->mapped = true; - - x += icon_width + tray.spacing; - } -} - -static void tray_request_redraw(void) { - if (!tray.initialized || tray.host_monitor == nullptr) return; - - monitor_draw_bar(tray.host_monitor); - xcb_flush(wm.xcb_conn); -} - -static void tray_remove_icon_by_index(size_t index, bool release_window) { - if (index >= tray.icon_count) return; - - tray_icon_t icon = tray.icons[index]; - if (release_window) tray_release_icon_window(&icon); - - if (index + 1 < tray.icon_count) { - memmove(&tray.icons[index], &tray.icons[index + 1], - sizeof(*tray.icons) * (tray.icon_count - index - 1)); - } - tray.icon_count--; - - tray_request_redraw(); -} - -static void tray_add_icon(xcb_window_t window) { - if (!tray.initialized || window == XCB_WINDOW_NONE || - tray_get_icon(window) != nullptr) { - return; - } - - if (tray.icon_count == tray.icon_capacity) { - tray.icon_capacity = (size_t)p_alloc_nr(tray.icon_capacity); - p_realloc(&tray.icons, tray.icon_capacity); - } - - xcb_get_geometry_reply_t *geometry = xwindow_get_geometry_reply(window); - if (!geometry) return; - - tray_icon_t *icon = &tray.icons[tray.icon_count++]; - *icon = (tray_icon_t){ - .window = window, - .natural_width = geometry->width, - .natural_height = geometry->height, - .ignore_unmaps = 1, - .mapped = true, - }; - p_delete(&geometry); - - xcb_change_save_set(wm.xcb_conn, XCB_SET_MODE_INSERT, window); - xcb_reparent_window(wm.xcb_conn, window, tray.window, 0, 0); - tray_select_icon_events(window); - tray_send_xembed_message(window, XEMBED_EMBEDDED_NOTIFY, 0, tray.window, - XEMBED_VERSION); - xcb_map_window(wm.xcb_conn, window); - - tray_request_redraw(); -} - -static bool tray_create_window(void) { - static xcb_colormap_t colormap = XCB_NONE; - if (tray.host_monitor == nullptr || - tray.host_monitor->bar_window == XCB_NONE) { - return false; - } - - visual_t *visual = xwindow_get_xcb_visual(false); - if (visual == nullptr) return false; - - xcb_visualid_t visual_id = visual->visual->visual_id; - if (colormap == XCB_NONE) { - colormap = xcb_generate_id(wm.xcb_conn); - xcb_create_colormap(wm.xcb_conn, XCB_COLORMAP_ALLOC_NONE, colormap, - wm.screen->root, visual_id); - } - - xcb_window_t window = xcb_generate_id(wm.xcb_conn); - uint32_t value_mask = XCB_CW_BACK_PIXEL | XCB_CW_BORDER_PIXEL | - XCB_CW_EVENT_MASK | XCB_CW_COLORMAP; - const xcb_create_window_value_list_t value_list = { - .background_pixel = wm.color_set.bar_bg.argb, - .border_pixel = 0, - .event_mask = - XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT | XCB_EVENT_MASK_SUBSTRUCTURE_NOTIFY, - .colormap = colormap, - }; - - xcb_void_cookie_t cookie = xcb_create_window_aux_checked( - wm.xcb_conn, visual->depth, window, tray.host_monitor->bar_window, 0, 0, 1, - wm.bar_height, 0, XCB_WINDOW_CLASS_INPUT_OUTPUT, visual_id, value_mask, - &value_list); - p_delete(&visual); - if (xcb_request_check(wm.xcb_conn, cookie)) { - logger("cannot create system tray window\n"); - return false; - } - - tray.window = window; - xwindow_set_class_instance(window); -#define NAME APP_NAME "_tray" - xwindow_set_name_static(window, NAME); - xcb_change_property(wm.xcb_conn, XCB_PROP_MODE_REPLACE, window, _NET_WM_NAME, - UTF8_STRING, 8, sizeof(NAME) - 1, NAME); -#undef NAME - - return true; -} - -static bool tray_take_selection_owner(void) { - xcb_get_selection_owner_cookie_t owner_cookie = - xcb_get_selection_owner(wm.xcb_conn, tray.selection_atom); - xcb_get_selection_owner_reply_t *owner_reply = - xcb_get_selection_owner_reply(wm.xcb_conn, owner_cookie, nullptr); - if (owner_reply && owner_reply->owner != XCB_WINDOW_NONE) { - logger("system tray selection is already owned by window %u\n", - owner_reply->owner); - p_delete(&owner_reply); - return false; - } - p_delete(&owner_reply); - - xcb_set_selection_owner(wm.xcb_conn, tray.window, tray.selection_atom, - XCB_CURRENT_TIME); - - owner_cookie = xcb_get_selection_owner(wm.xcb_conn, tray.selection_atom); - owner_reply = - xcb_get_selection_owner_reply(wm.xcb_conn, owner_cookie, nullptr); - bool success = owner_reply && owner_reply->owner == tray.window; - p_delete(&owner_reply); - - if (!success) logger("cannot acquire system tray selection\n"); - return success; -} - -static void tray_set_properties(void) { - uint32_t orientation = TRAY_ORIENTATION_HORIZONTAL; - xcb_change_property(wm.xcb_conn, XCB_PROP_MODE_REPLACE, tray.window, - _NET_SYSTEM_TRAY_ORIENTATION, XCB_ATOM_CARDINAL, 32, 1, - &orientation); - - const color_t *fg = &wm.color_set.tag_color; - uint32_t colors[12] = { - tray_color_component_u16(fg->red), tray_color_component_u16(fg->green), - tray_color_component_u16(fg->blue), tray_color_component_u16(fg->red), - tray_color_component_u16(fg->green), tray_color_component_u16(fg->blue), - tray_color_component_u16(fg->red), tray_color_component_u16(fg->green), - tray_color_component_u16(fg->blue), tray_color_component_u16(fg->red), - tray_color_component_u16(fg->green), tray_color_component_u16(fg->blue), - }; - xcb_change_property(wm.xcb_conn, XCB_PROP_MODE_REPLACE, tray.window, - _NET_SYSTEM_TRAY_COLORS, XCB_ATOM_CARDINAL, 32, - countof(colors), colors); -} - -static void tray_announce_manager(void) { - xcb_client_message_event_t ev; - p_clear(&ev, 1); - ev.response_type = XCB_CLIENT_MESSAGE; - ev.window = wm.screen->root; - ev.format = 32; - ev.type = MANAGER; - ev.data.data32[0] = XCB_CURRENT_TIME; - ev.data.data32[1] = tray.selection_atom; - ev.data.data32[2] = tray.window; - - xcb_send_event(wm.xcb_conn, false, wm.screen->root, - XCB_EVENT_MASK_STRUCTURE_NOTIFY, (char *)&ev); -} - -void tray_init(void) { - if (tray.initialized) return; - - tray.host_monitor = wm.monitor_list; - if (tray.host_monitor == nullptr) return; - - tray.side_padding = 0; - tray.spacing = 0; - - char selection_name[64]; - snprintf(selection_name, sizeof(selection_name), "_NET_SYSTEM_TRAY_S%d", - wm.default_screen); - tray.selection_atom = tray_intern_atom(selection_name); - if (tray.selection_atom == XCB_ATOM_NONE) { - logger("cannot intern tray selection atom %s\n", selection_name); - return; - } - - if (!tray_create_window()) return; - if (!tray_take_selection_owner()) { - tray_cleanup(); - return; - } - - tray.initialized = true; - tray_set_properties(); - tray_announce_manager(); - tray_layout_icons((int16_t)tray.host_monitor->geometry.width); - xcb_flush(wm.xcb_conn); -} - -void tray_cleanup(void) { - for (size_t i = 0; i < tray.icon_count; i++) { - tray_release_icon_window(&tray.icons[i]); - } - p_delete(&tray.icons); - tray.icon_count = 0; - tray.icon_capacity = 0; - - if (tray.selection_atom != XCB_ATOM_NONE) { - xcb_set_selection_owner(wm.xcb_conn, XCB_WINDOW_NONE, tray.selection_atom, - XCB_CURRENT_TIME); - } - - if (tray.window != XCB_WINDOW_NONE) { - xcb_destroy_window(wm.xcb_conn, tray.window); - } - - tray = (tray_t){ - .selection_atom = XCB_ATOM_NONE, - .window = XCB_WINDOW_NONE, - }; -} - -bool tray_handle_client_message(xcb_client_message_event_t *ev) { - if (!tray.initialized) return false; - - if (ev->type != _NET_SYSTEM_TRAY_OPCODE) return false; - if (ev->window != tray.window && ev->window != wm.screen->root) return false; - - switch (ev->data.data32[1]) { - case TRAY_REQUEST_DOCK: - tray_add_icon((xcb_window_t)ev->data.data32[2]); - break; - case TRAY_BEGIN_MESSAGE: - case TRAY_CANCEL_MESSAGE: - default: - break; - } - - xcb_flush(wm.xcb_conn); - return true; -} - -bool tray_handle_configure_request(xcb_configure_request_event_t *ev) { - if (!tray.initialized) return false; - - if (ev->window == tray.window) { - tray_layout_icons((int16_t)tray.host_monitor->geometry.width); - xcb_flush(wm.xcb_conn); - return true; - } - - tray_icon_t *icon = tray_get_icon(ev->window); - if (!icon) return false; - - if ((ev->value_mask & XCB_CONFIG_WINDOW_WIDTH) && ev->width > 0) { - icon->natural_width = ev->width; - } - if ((ev->value_mask & XCB_CONFIG_WINDOW_HEIGHT) && ev->height > 0) { - icon->natural_height = ev->height; - } - - tray_request_redraw(); - return true; -} - -bool tray_handle_map_request(xcb_map_request_event_t *ev) { - if (!tray.initialized) return false; - - if (ev->window == tray.window) { - tray_layout_icons((int16_t)tray.host_monitor->geometry.width); - xcb_flush(wm.xcb_conn); - return true; - } - - tray_icon_t *icon = tray_get_icon(ev->window); - if (!icon && ev->parent != tray.window) return false; - - if (icon) icon->mapped = true; - xcb_map_window(wm.xcb_conn, ev->window); - tray_layout_icons((int16_t)tray.host_monitor->geometry.width); - xcb_flush(wm.xcb_conn); - return true; -} - -bool tray_handle_destroy_notify(xcb_destroy_notify_event_t *ev) { - if (!tray.initialized) return false; - - if (ev->window == tray.window) { - tray.window = XCB_WINDOW_NONE; - tray.initialized = false; - return true; - } - - size_t index = tray_find_icon_index(ev->window); - if (index == SIZE_MAX) return false; - - tray_remove_icon_by_index(index, false); - return true; -} - -bool tray_handle_unmap_notify(xcb_unmap_notify_event_t *ev) { - if (!tray.initialized) return false; - - tray_icon_t *icon = tray_get_icon(ev->window); - if (!icon) return ev->window == tray.window; - - if (icon->ignore_unmaps > 0) { - icon->ignore_unmaps--; - return true; - } - - size_t index = tray_find_icon_index(ev->window); - if (index != SIZE_MAX) tray_remove_icon_by_index(index, true); - - return true; -} - -bool tray_handle_property_notify(xcb_property_notify_event_t *ev) { - if (!tray.initialized) return false; - return tray_is_window(ev->window); -} - -bool tray_handle_selection_clear(xcb_selection_clear_event_t *ev) { - if (!tray.initialized) return false; - if (ev->owner != tray.window) return false; - if (ev->selection != tray.selection_atom) return false; - - monitor_t *host_monitor = tray.host_monitor; - logger("system tray selection lost\n"); - tray_cleanup(); - - if (host_monitor != nullptr) monitor_draw_bar(host_monitor); - xcb_flush(wm.xcb_conn); - return true; -} - -uint16_t tray_get_width(monitor_t *monitor) { - if (!tray.initialized || monitor == nullptr || monitor != tray.host_monitor) { - return 0; - } - - return tray_compute_width(); -} - -void tray_place(monitor_t *monitor, int16_t right_edge) { - if (!tray.initialized || monitor == nullptr || monitor != tray.host_monitor) { - return; - } - - tray_layout_icons(right_edge); -} - -bool tray_is_window(xcb_window_t window) { - if (window == XCB_WINDOW_NONE) return false; - if (tray.window == window) return true; - return tray_get_icon(window) != nullptr; -} diff --git a/src/tray.h b/src/tray.h deleted file mode 100644 index 546193f..0000000 --- a/src/tray.h +++ /dev/null @@ -1,21 +0,0 @@ -#pragma once - -#include -#include - -#include "types.h" - -void tray_init(void); -void tray_cleanup(void); - -bool tray_handle_client_message(xcb_client_message_event_t *ev); -bool tray_handle_configure_request(xcb_configure_request_event_t *ev); -bool tray_handle_map_request(xcb_map_request_event_t *ev); -bool tray_handle_destroy_notify(xcb_destroy_notify_event_t *ev); -bool tray_handle_unmap_notify(xcb_unmap_notify_event_t *ev); -bool tray_handle_property_notify(xcb_property_notify_event_t *ev); -bool tray_handle_selection_clear(xcb_selection_clear_event_t *ev); - -uint16_t tray_get_width(monitor_t *monitor); -void tray_place(monitor_t *monitor, int16_t right_edge); -bool tray_is_window(xcb_window_t window); diff --git a/src/types.h b/src/types.h deleted file mode 100644 index 6c2aff1..0000000 --- a/src/types.h +++ /dev/null @@ -1,114 +0,0 @@ -#pragma once - -#include -#include -#include -#include -#include - -#include "base.h" -#include "color.h" - -typedef struct client_t client_t; -typedef struct monitor_t monitor_t; -typedef struct tag_t tag_t; -typedef struct task_in_tag_t task_in_tag_t; - -typedef struct layout_t { - char *symbol; - void (*arrange)(tag_t *tag); -} layout_t; - -struct client_t { - client_t *stack_next; - client_t *next; - - monitor_t *monitor; - uint32_t tags; - - area_t old_geometry; - area_t geometry; - uint16_t border_width; - uint16_t old_border_width; - color_t *border_color; - - bool floating; - bool fullscreen; - bool maximize; - bool minimize; - bool sticky; - bool urgent; - bool skip_taskbar; - bool size_freeze; /* 尺寸冻结窗口一定是浮动窗口 */ - - char *class, *instance; - char *name, *net_name; - char *role; - - xcb_window_t transient_for_window; - xcb_window_t leader_window; - xcb_window_t frame_window; - xcb_window_t window; -}; - -struct monitor_t { - monitor_t *next; - area_t geometry; - area_t workarea; - tag_t *tag_list; - tag_t *selected_tag; - - char *name; - cairo_t *bar_cr; - - extent_in_bar_t tag_extent; - extent_in_bar_t layout_symbol_extent; - extent_in_bar_t status_extent; - - xcb_window_t bar_window; - - point_t cursor_position; - bool position_inited; -}; - -struct tag_t { - uint32_t index; /* index in all tags */ - uint32_t mask; - extent_in_bar_t bar_extent; - tag_t *next; - char *name; - const layout_t *layout; - task_in_tag_t *task_list; -}; - -struct task_in_tag_t { - client_t *client; - task_in_tag_t *next; - area_t geometry; - extent_in_bar_t bar_extent; -}; - -typedef struct color_set_t { - color_t bar_bg; - color_t tag_bg; - color_t active_tag_bg; - color_t tag_color; - color_t active_tag_color; - color_t border_color; - color_t active_border_color; -} color_set_t; - -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/utils.h b/src/utils.h deleted file mode 100644 index 1b01031..0000000 --- a/src/utils.h +++ /dev/null @@ -1,8 +0,0 @@ -#pragma once - -/* Compatibility umbrella header. Prefer including headers from src/base in new - * code. */ - -#include "base/log.h" -#include "base/macros.h" -#include "base/memory.h" diff --git a/src/wallpaper.c b/src/wallpaper.c deleted file mode 100644 index 2452287..0000000 --- a/src/wallpaper.c +++ /dev/null @@ -1,313 +0,0 @@ -#include "wallpaper.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "atoms-extern.h" -#include "base.h" -#include "types.h" -#include "utils.h" -#include "xwindow.h" - -struct wallpaper_context_t { - uint32_t interval; - uint32_t image_count; - char **image_path_list; - - xcb_screen_t *screen; - xcb_connection_t *conn; - - area_t *monitor_geometries; - uint32_t monitor_count; - - xcb_pixmap_t pixmap; - cairo_t *cr; - - uint32_t index; - - guint timer; -}; - -static inline bool file_exist(const char *path) { - if (!path || !*path) return false; - return access(path, R_OK) == 0; -} - -static char **parse_wallpaper_paths(char **images, uint32_t image_count) { - GStrvBuilder *builder = g_strv_builder_new(); - for (uint32_t i = 0; i < image_count; i++) { - wordexp_t p; - if (wordexp(images[i], &p, 0)) continue; - - for (size_t i = 0; i < p.we_wordc; i++) { - size_t len = strlen(p.we_wordv[i]) + 1; - char *realpath = p_new(char, len); - strncpy(realpath, p.we_wordv[i], len); - g_strstrip(realpath); - if (file_exist(realpath)) g_strv_builder_add(builder, realpath); - - p_delete(&realpath); - } - wordfree(&p); - } - - return g_strv_builder_unref_to_strv(builder); -} - -static uint32_t *generate_wallpaper(wallpaper_context_t *context) { - int width = context->screen->width_in_pixels; - int height = context->screen->height_in_pixels; - - Imlib_Image final_image = imlib_create_image(width, height); - if (!final_image) return nullptr; - - imlib_context_set_image(final_image); - imlib_image_set_has_alpha(true); - - /* 重置像素数据 */ - imlib_image_clear(); - for (uint32_t i = 0; i < context->monitor_count; i++) { - uint32_t img_index = (context->index + i) % context->image_count; - const char *path = context->image_path_list[img_index]; - logger("== wallpaper[%u]: %s\n", img_index, path); - Imlib_Image wallpaper_img = imlib_load_image(path); - if (!wallpaper_img) continue; - - uint32_t dst_width = context->monitor_geometries[i].width; - uint32_t dst_height = context->monitor_geometries[i].height; - - /* 获取图片原始尺寸 */ - imlib_context_set_image(wallpaper_img); - uint32_t img_width = imlib_image_get_width(); - uint32_t img_height = imlib_image_get_height(); - - /* 计算缩放比例,保持原始宽高比 */ - float width_ratio = (float)dst_width / img_width; - float height_ratio = (float)dst_height / img_height; - float scale = MAX(width_ratio, height_ratio); - - /* 图片居中显示,计算原图绘制区域 */ - int src_width = dst_width / scale; - int src_height = dst_height / scale; - int src_x = (img_width - src_width) / 2; - int src_y = (img_height - src_height) / 2; - - /* 绘制图片到最终图像上 */ - imlib_context_set_image(final_image); - imlib_blend_image_onto_image(wallpaper_img, true, src_x, src_y, src_width, - src_height, context->monitor_geometries[i].x, - context->monitor_geometries[i].y, dst_width, - dst_height); - - /* 释放图片资源 */ - imlib_context_set_image(wallpaper_img); - imlib_free_image(); - } - - /* 获取最终图像数据 */ - imlib_context_set_image(final_image); - uint32_t *data = imlib_image_get_data_for_reading_only(); - - /* 复制数据到自己分配的内存中(原数据会在图像释放后无效) */ - int count = width * height; - uint32_t *result = p_new(uint32_t, count); - if (result) memcpy(result, data, count * sizeof(uint32_t)); - - /* 释放图像 */ - imlib_context_set_image(final_image); - imlib_free_image(); - - return result; -} - -double get_time() { - struct timeval tv; - gettimeofday(&tv, nullptr); - return tv.tv_sec + tv.tv_usec / 1000000.0; -} - -static void cairo_set_wallpaper(wallpaper_context_t *context, - uint8_t *wallpaper_data) { - xcb_connection_t *conn = context->conn; - xcb_window_t root = context->screen->root; - xcb_pixmap_t pixmap = context->pixmap; - uint32_t width = context->screen->width_in_pixels; - uint32_t height = context->screen->height_in_pixels; - int stride = cairo_format_stride_for_width(CAIRO_FORMAT_ARGB32, width); - cairo_surface_t *s = cairo_image_surface_create_for_data( - wallpaper_data, CAIRO_FORMAT_ARGB32, width, height, stride); - cairo_set_source_surface(context->cr, s, 0, 0); - cairo_paint(context->cr); - - xcb_change_window_attributes_value_list_t value_list = { - .background_pixmap = pixmap, - }; - xcb_change_window_attributes_aux(conn, root, XCB_CW_BACK_PIXMAP, &value_list); - - /* 即使壁纸对应的 pixmap 未变更,也强制通知合成器壁纸已更改 */ - uint8_t mode = XCB_PROP_MODE_REPLACE; - xcb_pixmap_t none = XCB_PIXMAP_NONE; - xcb_atom_t atom = _XROOTPMAP_ID; - xcb_change_property(conn, mode, root, atom, XCB_ATOM_PIXMAP, 32, 1, &none); - xcb_change_property(conn, mode, root, atom, XCB_ATOM_PIXMAP, 32, 1, &pixmap); - atom = ESETROOT_PMAP_ID; - xcb_change_property(conn, mode, root, atom, XCB_ATOM_PIXMAP, 32, 1, &none); - xcb_change_property(conn, mode, root, atom, XCB_ATOM_PIXMAP, 32, 1, &pixmap); - - xcb_clear_area(conn, 0, root, 0, 0, 0, 0); - xcb_flush(conn); - cairo_surface_destroy(s); -} - -static void set_wallpaper(wallpaper_context_t *context) { - double start = get_time(); - uint32_t *wallpaper = generate_wallpaper(context); - logger("== generate wallpaper cost: %lf, %p\n", get_time() - start, - wallpaper); - if (!wallpaper) return; - - uint8_t *data = (uint8_t *)wallpaper; - - cairo_set_wallpaper(context, data); - - p_delete(&wallpaper); - - context->index += context->monitor_count; -} - -static gboolean update_wallpaper(gpointer user_data) { - if (!user_data) return FALSE; - - wallpaper_context_t *wc = user_data; - set_wallpaper(wc); - - /* 屏幕比壁纸多则不动态切换 */ - if (wc->monitor_count >= wc->image_count) return FALSE; - - return TRUE; -} - -static bool prepare_wallpaper_surface(wallpaper_context_t *context) { - uint16_t width = context->screen->width_in_pixels; - uint16_t height = context->screen->height_in_pixels; - xcb_window_t root = context->screen->root; - xcb_connection_t *conn = context->conn; - xcb_pixmap_t pixmap = context->pixmap; - - visual_t *visual = xwindow_get_xcb_visual(false); - - if (pixmap != XCB_PIXMAP_NONE) { - xcb_kill_client(conn, pixmap); - xcb_free_pixmap(conn, pixmap); - } - - pixmap = xcb_generate_id(conn); - xcb_create_pixmap(conn, visual->depth, pixmap, root, width, height); - xcb_gcontext_t gc = xcb_generate_id(conn); - xcb_create_gc(conn, gc, pixmap, 0, nullptr); - xcb_change_window_attributes_value_list_t value_list = { - .background_pixmap = pixmap, - }; - xcb_change_window_attributes_aux(conn, root, XCB_CW_BACK_PIXMAP, &value_list); - - /* 兼容合成器,不设置若开启合成器壁纸会不显示 */ - uint8_t mode = XCB_PROP_MODE_REPLACE; - xcb_atom_t atom = _XROOTPMAP_ID; - xcb_change_property(conn, mode, root, atom, XCB_ATOM_PIXMAP, 32, 1, &pixmap); - atom = ESETROOT_PMAP_ID; - xcb_change_property(conn, mode, root, atom, XCB_ATOM_PIXMAP, 32, 1, &pixmap); - - xcb_clear_area(conn, 0, root, 0, 0, width, height); - xcb_free_gc(conn, gc); - xcb_flush(conn); - - context->pixmap = pixmap; - - if (context->cr) cairo_destroy(context->cr); - cairo_surface_t *surface = - cairo_xcb_surface_create(conn, pixmap, visual->visual, width, height); - p_delete(&visual); - if (cairo_surface_status(surface) != CAIRO_STATUS_SUCCESS) { - cairo_surface_destroy(surface); - return false; - } - - cairo_t *cr = cairo_create(surface); - if (cairo_status(cr) != CAIRO_STATUS_SUCCESS) { - cairo_destroy(cr); - cairo_surface_destroy(surface); - return false; - } - - context->cr = cr; - - cairo_surface_destroy(surface); - cairo_set_source_rgba(cr, 0, 0, 0, 1); - cairo_paint(cr); - xcb_clear_area(conn, 0, root, 0, 0, 0, 0); - xcb_flush(context->conn); - - return true; -} - -wallpaper_context_t *wallpaper_init(wallpaper_config_t config) { - if (!config.path_count || !config.paths) return nullptr; - if (!config.monitors) return nullptr; - - char **images = parse_wallpaper_paths(config.paths, config.path_count); - if (!images || !g_strv_length(images)) return nullptr; - - wallpaper_context_t *ctx = p_new(wallpaper_context_t, 1); - ctx->interval = config.interval; - ctx->image_path_list = images; - ctx->image_count = g_strv_length(images); - - ctx->conn = config.conn; - ctx->screen = config.screen; - - { - uint32_t i = 0; - monitor_t *m = nullptr; - for (m = config.monitors; m; m = m->next) ctx->monitor_count++; - - ctx->monitor_geometries = p_new(area_t, ctx->monitor_count); - for (m = config.monitors; m; m = m->next) { - ctx->monitor_geometries[i++] = m->geometry; - } - } - - if (!prepare_wallpaper_surface(ctx)) { - wallpaper_clean(ctx); - return nullptr; - } - - if (!update_wallpaper(ctx) || !ctx->interval) { - wallpaper_clean(ctx); - return nullptr; - } - - ctx->timer = g_timeout_add_seconds(ctx->interval, update_wallpaper, ctx); - - return ctx; -} - -void wallpaper_clean(wallpaper_context_t *context) { - if (!context) return; - - if (context->timer) g_source_remove(context->timer); - if (context->cr) cairo_destroy(context->cr); - p_delete(&context->monitor_geometries); - if (context->image_path_list) g_strfreev(context->image_path_list); - - p_delete(&context); -} diff --git a/src/wallpaper.h b/src/wallpaper.h deleted file mode 100644 index e0df166..0000000 --- a/src/wallpaper.h +++ /dev/null @@ -1,20 +0,0 @@ -#pragma once - -#include -#include - -#include "types.h" - -typedef struct wallpaper_context_t wallpaper_context_t; - -typedef struct wallpaper_config_t { - uint32_t interval; - uint32_t path_count; - char **paths; - xcb_screen_t *screen; - xcb_connection_t *conn; - monitor_t *monitors; -} wallpaper_config_t; - -wallpaper_context_t *wallpaper_init(wallpaper_config_t config); -void wallpaper_clean(wallpaper_context_t *context); diff --git a/src/wm.c b/src/wm.c deleted file mode 100644 index f303e30..0000000 --- a/src/wm.c +++ /dev/null @@ -1,775 +0,0 @@ -#include "wm.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "app.h" -#include "atoms-extern.h" -#include "atoms.h" -#include "backtrace.h" -#include "base.h" -#include "buffer.h" -#include "client.h" -#include "color.h" -#include "config.h" -#include "default_config.h" -#include "event.h" -#include "image.h" -#include "monitor.h" -#include "status.h" -#include "text.h" -#include "tray.h" -#include "types.h" -#include "utils.h" -#include "wallpaper.h" -#include "xcursor.h" -#include "xkb.h" -#include "xres.h" -#include "xwindow.h" - -static void wm_setup_signal(void); -static void wm_check_other_wm(void); -static void wm_check_xcb_extensions(void); -static void wm_detect_monitor(void); -static void wm_scan_clients(void); -static void wm_clean(void); -static void wm_setup(void); -static void wm_init_wallpaper(void); -static void wm_get_xres_config(void); -static void wm_init_color_set(void); -static void wm_setup_keybindings(void); -static void wm_update_status(status_t *status); -static void wm_run_autostart(const char *const commands[]); -static void run_once(const char *command); -static bool command_already_running(const char *command); -static gboolean clear_ignore_enter_notify(gpointer data); - -wm_t wm; - -/** argv used to run wm */ -static char **cmd_argv; - -static gboolean restart_on_signal(gpointer data) { - wm_restart(); - return TRUE; -} - -static gboolean exit_on_signal(gpointer data) { - g_main_loop_quit(wm.loop); - return TRUE; -} - -static void signal_fatal(int signal_number) { - buffer_t buffer; - backtrace_get(&buffer); - fatal("signal %d, dumping backtrace\n%s", signal_number, buffer.s); -} - -static guint sources[3] = {0}; - -static gboolean clear_ignore_enter_notify(gpointer data) { - wm.ignore_enter_notify = false; - wm.ignored_enter_notify_point = (point_t){0}; - wm.clear_ignore_enter_notify_source = 0; - return G_SOURCE_REMOVE; -} - -void wm_ignore_enter_notify_at_point(point_t point) { - wm.ignore_enter_notify = true; - wm.ignored_enter_notify_point = point; - if (wm.clear_ignore_enter_notify_source) { - g_source_remove(wm.clear_ignore_enter_notify_source); - } - wm.clear_ignore_enter_notify_source = - g_idle_add(clear_ignore_enter_notify, nullptr); -} - -bool wm_should_ignore_enter_notify(const xcb_enter_notify_event_t *ev) { - if (!wm.ignore_enter_notify) return false; - if (ev->root_x != wm.ignored_enter_notify_point.x || - ev->root_y != wm.ignored_enter_notify_point.y) { - return false; - } - - if (wm.clear_ignore_enter_notify_source) { - g_source_remove(wm.clear_ignore_enter_notify_source); - wm.clear_ignore_enter_notify_source = 0; - } - wm.ignore_enter_notify = false; - wm.ignored_enter_notify_point = (point_t){0}; - return true; -} - -void wm_setup_signal(void) { - sources[0] = g_unix_signal_add(SIGINT, exit_on_signal, nullptr); - sources[1] = g_unix_signal_add(SIGTERM, exit_on_signal, nullptr); - sources[2] = g_unix_signal_add(SIGHUP, restart_on_signal, nullptr); - - struct sigaction sa = {.sa_handler = signal_fatal, .sa_flags = SA_RESETHAND}; - sigemptyset(&sa.sa_mask); - sigaction(SIGABRT, &sa, 0); - sigaction(SIGBUS, &sa, 0); - sigaction(SIGFPE, &sa, 0); - sigaction(SIGILL, &sa, 0); - sigaction(SIGSEGV, &sa, 0); - signal(SIGPIPE, SIG_IGN); -} - -void wm_check_other_wm(void) { - /* connect X server */ - int default_screen; - xcb_connection_t *conn = xcb_connect(nullptr, &default_screen); - int xcb_conn_error = xcb_connection_has_error(conn); - if (xcb_conn_error) fatal("cannot open display (error %d)", xcb_conn_error); - - xcb_screen_t *screen = xcb_aux_get_screen(conn, default_screen); - xcb_window_t root = screen->root; - - /* check other window manager running */ - uint32_t mask = XCB_CW_EVENT_MASK; - const xcb_params_cw_t params = { - .event_mask = XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT, - }; - xcb_void_cookie_t cookie = - xcb_aux_change_window_attributes_checked(conn, root, mask, ¶ms); - if (xcb_request_check(conn, cookie)) { - fatal( - "another window manager is already running (cannot select " - "SubstructureRedirect)"); - } - - wm.xcb_conn = conn; - wm.default_screen = default_screen; - wm.screen = screen; -} - -void wm_check_xcb_extensions(void) { - xcb_prefetch_extension_data(wm.xcb_conn, &xcb_xfixes_id); - xcb_prefetch_extension_data(wm.xcb_conn, &xcb_xinerama_id); - xcb_prefetch_extension_data(wm.xcb_conn, &xcb_randr_id); - - const xcb_query_extension_reply_t *query; - query = xcb_get_extension_data(wm.xcb_conn, &xcb_xfixes_id); - wm.have_xfixes = query && query->present; - - query = xcb_get_extension_data(wm.xcb_conn, &xcb_xinerama_id); - wm.have_xinerama = query && query->present; - - query = xcb_get_extension_data(wm.xcb_conn, &xcb_randr_id); - wm.have_randr = query && query->present; -} - -static inline int32_t right(area_t a) { - return (int32_t)a.x + (int32_t)a.width; -} -static inline int32_t bottom(area_t a) { - return (int32_t)a.y + (int32_t)a.height; -} -static inline bool fully_contains(area_t outer, area_t inner) { - return inner.x >= outer.x && inner.y >= outer.y && - right(inner) <= right(outer) && bottom(inner) <= bottom(outer); -} -static inline void monitor_remove_duplication(monitor_t **monitor_list) { - if (!monitor_list || !*monitor_list) return; - - int32_t count = 0; - for (monitor_t *m = *monitor_list; m; m = m->next) count++; - if (count <= 1) return; - - monitor_t **nodes = p_new(monitor_t *, count); - bool *remove = p_new(bool, count); - - size_t index = 0; - for (monitor_t *m = *monitor_list; m; m = m->next) nodes[index++] = m; - - for (int32_t i = count - 1; i >= 0; i--) { - if (remove[i]) continue; - for (int32_t j = i - 1; j >= 0; j--) { - if (remove[j]) continue; - if (fully_contains(nodes[j]->geometry, nodes[i]->geometry)) { - remove[i] = true; - } else if (fully_contains(nodes[i]->geometry, nodes[j]->geometry)) { - remove[j] = true; - } - } - } - - monitor_t *head = nullptr; - monitor_t *tail = nullptr; - for (int32_t i = 0; i < count; i++) { - monitor_t *node = nodes[i]; - if (remove[i]) { - p_delete(&node->name); - p_delete(&node); - continue; - } - - if (tail) { - tail->next = node; - } else { - head = node; - } - node->next = nullptr; - tail = node; - } - - if (tail) tail->next = nullptr; - *monitor_list = head; - - p_delete(&nodes); - p_delete(&remove); -} - -static bool wm_detect_monitor_by_randr(void) { - xcb_randr_get_monitors_cookie_t monitors_cookie = - xcb_randr_get_monitors(wm.xcb_conn, wm.screen->root, 1); - xcb_randr_get_monitors_reply_t *monitors_reply = - xcb_randr_get_monitors_reply(wm.xcb_conn, monitors_cookie, nullptr); - if (monitors_reply == nullptr) { - warn("RandR get monitor failed"); - return false; - } - - monitor_t *prev_monitor = nullptr; - xcb_randr_monitor_info_iterator_t monitor_iter = - xcb_randr_get_monitors_monitors_iterator(monitors_reply); - for (; monitor_iter.rem; xcb_randr_monitor_info_next(&monitor_iter)) { - monitor_t *monitor = p_new(monitor_t, 1); - monitor->geometry.x = monitor_iter.data->x; - monitor->geometry.y = monitor_iter.data->y; - monitor->geometry.width = monitor_iter.data->width; - monitor->geometry.height = monitor_iter.data->height; - - xcb_get_atom_name_cookie_t name_cookie = - xcb_get_atom_name_unchecked(wm.xcb_conn, monitor_iter.data->name); - xcb_get_atom_name_reply_t *name_reply = - xcb_get_atom_name_reply(wm.xcb_conn, name_cookie, nullptr); - - if (name_reply) { - char *name = xcb_get_atom_name_name(name_reply); - int length = xcb_get_atom_name_name_length(name_reply); - monitor->name = memcpy(p_new(char, length + 1), name, length); - monitor->name[length] = '\0'; - p_delete(&name_reply); - } else { - monitor->name = strdup("unknown"); - } - - if (prev_monitor) { - prev_monitor->next = monitor; - } else { - wm.monitor_list = monitor; - wm.current_monitor = monitor; - } - - prev_monitor = monitor; - } - - p_delete(&monitors_reply); - - monitor_remove_duplication(&wm.monitor_list); - - return true; -} - -static bool wm_detect_monitor_by_xinerama(void) { - xcb_xinerama_is_active_cookie_t active_cookie = - xcb_xinerama_is_active(wm.xcb_conn); - xcb_xinerama_is_active_reply_t *active_reply = - xcb_xinerama_is_active_reply(wm.xcb_conn, active_cookie, nullptr); - if (!active_reply || !active_reply->state) { - p_delete(&active_reply); - return false; - } - - xcb_xinerama_query_screens_cookie_t screens_cookie = - xcb_xinerama_query_screens(wm.xcb_conn); - xcb_xinerama_query_screens_reply_t *screens_reply = - xcb_xinerama_query_screens_reply(wm.xcb_conn, screens_cookie, nullptr); - - if (screens_reply == nullptr) return false; - - int count = xcb_xinerama_query_screens_screen_info_length(screens_reply); - if (count <= 0) { - p_delete(&screens_reply); - return false; - } - - xcb_xinerama_screen_info_t *screen_info = - xcb_xinerama_query_screens_screen_info(screens_reply); - - monitor_t *prev_monitor = nullptr; - for (int i = 0; i < count; i++) { - monitor_t *monitor = p_new(monitor_t, 1); - monitor->geometry.x = screen_info[i].x_org; - monitor->geometry.y = screen_info[i].y_org; - monitor->geometry.width = screen_info[i].width; - monitor->geometry.height = screen_info[i].height; - - if (prev_monitor) { - prev_monitor->next = monitor; - } else { - wm.monitor_list = monitor; - wm.current_monitor = monitor; - } - - prev_monitor = monitor; - } - p_delete(&screens_reply); - - monitor_remove_duplication(&wm.monitor_list); - - return true; -} - -void wm_detect_monitor(void) { - if (wm.have_randr && wm_detect_monitor_by_randr()) return; - if (wm.have_xinerama && wm_detect_monitor_by_xinerama()) return; - - if (wm.screen == nullptr) fatal("cannot detect monitor info"); - - monitor_t *monitor = p_new(monitor_t, 1); - monitor->geometry.x = 0; - monitor->geometry.y = 0; - monitor->geometry.width = wm.screen->width_in_pixels; - monitor->geometry.height = wm.screen->height_in_pixels; - - wm.monitor_list = monitor; - wm.current_monitor = monitor; -} - -typedef struct window_list_t { - xcb_window_t *normal_list; - xcb_window_t *transient_list; - uint32_t normal_count; - uint32_t transient_count; -} window_list_t; -void wm_scan_clients(void) { - xcb_query_tree_cookie_t cookie = xcb_query_tree(wm.xcb_conn, wm.screen->root); - xcb_query_tree_reply_t *tree_reply = - xcb_query_tree_reply(wm.xcb_conn, cookie, nullptr); - if (!tree_reply) return; - - xcb_window_t *list = xcb_query_tree_children(tree_reply); - int len = xcb_query_tree_children_length(tree_reply); - if (!len) { - p_delete(&tree_reply); - return; - } - - xcb_get_window_attributes_reply_t *wa_reply = nullptr; - xcb_get_geometry_reply_t *geo_reply = nullptr; - for (int i = 0; i < len; i++) { - xcb_window_t window = list[i]; - wa_reply = xwindow_get_attributes_reply(window); - if (!wa_reply) continue; - - uint8_t override_redirect = wa_reply->override_redirect; - uint8_t map_state = wa_reply->map_state; - p_delete(&wa_reply); - - if (override_redirect || xwindow_get_transient_for(window)) continue; - - geo_reply = xwindow_get_geometry_reply(window); - if (!geo_reply) continue; - - if (map_state == XCB_MAP_STATE_VIEWABLE || - xwindow_get_state(window) == XCB_ICCCM_WM_STATE_ICONIC) { - client_manage(window, geo_reply); - } - p_delete(&geo_reply); - } - for (int i = 0; i < len; i++) { - xcb_window_t window = list[i]; - wa_reply = xwindow_get_attributes_reply(window); - if (!wa_reply) continue; - - uint8_t map_state = wa_reply->map_state; - p_delete(&wa_reply); - - geo_reply = xwindow_get_geometry_reply(window); - if (!geo_reply) continue; - - if (xwindow_get_transient_for(window) && - (map_state == XCB_MAP_STATE_VIEWABLE || - xwindow_get_state(window) == XCB_ICCCM_WM_STATE_ICONIC)) { - client_manage(window, geo_reply); - } - p_delete(&geo_reply); - } - p_delete(&tree_reply); -} - -void wm_clean(void) { - wallpaper_clean(wm.wallpaper); - wm.wallpaper = nullptr; - - clean_status(); - text_clean_pango_layout(); - image_cache_clean(); - tray_cleanup(); - - { - client_t *c = wm.client_stack_list; - while (c) { - client_t *next_client = c->stack_next; - client_unmanage(c, false); - c = next_client; - } - } - - monitor_clean(wm.monitor_list); - - for (int i = 0; i < countof(sources); i++) { - guint source_id = sources[i]; - if (source_id) g_source_remove(source_id); - } - if (wm.clear_ignore_enter_notify_source) { - g_source_remove(wm.clear_ignore_enter_notify_source); - wm.clear_ignore_enter_notify_source = 0; - } - wm.ignore_enter_notify = false; - wm.ignored_enter_notify_point = (point_t){0}; - - xcb_delete_property(wm.xcb_conn, wm.screen->root, _NET_ACTIVE_WINDOW); - xcb_delete_property(wm.xcb_conn, wm.screen->root, _NET_SUPPORTING_WM_CHECK); - xcb_aux_sync(wm.xcb_conn); - - xcb_destroy_window(wm.xcb_conn, wm.wm_check_window); - wm.wm_check_window = XCB_WINDOW_NONE; - - xcursor_clean(); - xkb_free(); - xcb_ungrab_key(wm.xcb_conn, XCB_GRAB_ANY, wm.screen->root, modifier_any); - xcb_key_symbols_free(wm.key_symbols); - xcb_aux_sync(wm.xcb_conn); - xcb_disconnect(wm.xcb_conn); -} - -static void wm_setup(void) { - wm.config = init_config(); - if (!wm.config) fatal("cannot init config"); - - 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(); - - { - int font_height = - text_init_pango_layout(wm.font_family, wm.font_size, wm.dpi); - wm.bar_height = (uint16_t)font_height + 2 * wm.padding.bar_y; - logger("font height: %d, bar height: %u\n", font_height, wm.bar_height); - } - - uint32_t tag_count = 0; - for (monitor_t *m = wm.monitor_list; m; m = m->next) { - tag_count += monitor_initialize_tag(m, (const char **)tags, tag_count); - monitor_init_bar(m); - monitor_draw_bar(m); - } - - uint32_t mask = XCB_CW_EVENT_MASK | XCB_CW_CURSOR | XCB_CW_BACK_PIXEL; - xcb_params_cw_t params = { - .back_pixel = wm.screen->black_pixel, - .cursor = xcursor_get_xcb_cursor(cursor_normal), - .event_mask = - XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT | XCB_EVENT_MASK_KEY_PRESS, - }; - xcb_aux_change_window_attributes(wm.xcb_conn, wm.screen->root, mask, ¶ms); - - wm_setup_keybindings(); - atoms_init(wm.xcb_conn); - tray_init(); - - { - wm.wm_check_window = xcb_generate_id(wm.xcb_conn); - xcb_create_window(wm.xcb_conn, wm.screen->root_depth, wm.wm_check_window, - wm.screen->root, -1, -1, 1, 1, 0, XCB_COPY_FROM_PARENT, - wm.screen->root_visual, XCB_NONE, nullptr); - xwindow_set_class_instance(wm.wm_check_window); -#define NAME APP_NAME "_wm_check" - xwindow_set_name_static(wm.wm_check_window, NAME); - const void *data = &wm.wm_check_window; - xcb_atom_t type = XCB_ATOM_WINDOW; - uint8_t mode = XCB_PROP_MODE_REPLACE; - xcb_change_property(wm.xcb_conn, mode, wm.wm_check_window, _NET_WM_NAME, - UTF8_STRING, 8, sizeof(NAME) - 1, NAME); - xcb_change_property(wm.xcb_conn, mode, wm.wm_check_window, - _NET_SUPPORTING_WM_CHECK, type, 32, 1, data); - xcb_change_property(wm.xcb_conn, mode, wm.screen->root, - _NET_SUPPORTING_WM_CHECK, type, 32, 1, data); -#undef NAME - } - - xkb_init(); - xcb_flush(wm.xcb_conn); -} - -static void wm_init_wallpaper(void) { - wallpaper_config_t config = { - .interval = wallpaper_interval, - .path_count = countof(wallpapers), - .paths = (char **)wallpapers, - .screen = wm.screen, - .conn = wm.xcb_conn, - .monitors = wm.monitor_list, - }; - wm.wallpaper = wallpaper_init(config); -} - -static void wm_get_xres_config(void) { - xres_init_xrm_db(); - - wm.dpi = default_dpi; - wm.font_size = font_size; - xres_get_uint32("Xft.dpi", (uint32_t *)&wm.dpi); - xres_get_uint32(APP_NAME ".font_size", (uint32_t *)&wm.font_size); - xres_get_string(APP_NAME ".font_family", &wm.font_family, font_family); - - wm.border_width = border_width; - xres_get_uint32(APP_NAME ".border_width", (uint32_t *)&wm.border_width); - - wm.padding.bar_y = bar_y_padding; - wm.padding.tag_x = tag_x_padding; -#define NAME APP_NAME ".padding." - xres_get_uint32(NAME "bar_y", (uint32_t *)&wm.padding.bar_y); - xres_get_uint32(NAME "tag_x", (uint32_t *)&wm.padding.tag_x); -#undef NAME - - xres_clean(); -} - -void wm_init_color_set(void) { - color_parse(bar_bg, &wm.color_set.bar_bg); - color_parse(tag_bg, &wm.color_set.tag_bg); - color_parse(active_tag_bg, &wm.color_set.active_tag_bg); - color_parse(tag_color, &wm.color_set.tag_color); - color_parse(active_tag_color, &wm.color_set.active_tag_color); - color_parse(border_color, &wm.color_set.border_color); - color_parse(active_border_color, &wm.color_set.active_border_color); -} - -void wm_setup_keybindings(void) { - if (wm.key_symbols) { - xcb_key_symbols_free(wm.key_symbols); - p_delete(&wm.key_symbols); - } - - wm.key_symbols = xcb_key_symbols_alloc(wm.xcb_conn); - xwindow_grab_keys(wm.screen->root, key_list, countof(key_list)); -} - -void wm_update_status(status_t *status) { - wm.status = status; - for (monitor_t *m = wm.monitor_list; m; m = m->next) monitor_draw_bar(m); - xcb_flush(wm.xcb_conn); -} - -/** - * @brief 允许自启动程序 - * @param commands 命令字符串数组(以 nullptr 结尾) - */ -void wm_run_autostart(const char *const commands[]) { - for (int i = 0; commands[i]; i++) run_once(commands[i]); -} - -void run_once(const char *command) { - if (command_already_running(command)) return; - g_spawn_command_line_async(command, nullptr); -} - -bool command_already_running(const char *command) { - char shell_cmd[1024]; - const char *user = getenv("USER"); - snprintf(shell_cmd, sizeof(shell_cmd), "pgrep -u %s -fx '%s'", user, command); - - char *output = nullptr; - char *error = nullptr; - g_spawn_command_line_sync(shell_cmd, &output, &error, nullptr, nullptr); - - bool result = - (error == nullptr || !strlen(error)) && (output && strlen(output)); - p_delete(&output); - p_delete(&error); - - return result; -} - -void wm_restart(void) { - wm.need_restart = true; - if (g_main_loop_is_running(wm.loop)) { - g_main_loop_quit(wm.loop); - } -} - -void wm_quit(void) { - wm.need_restart = false; - if (g_main_loop_is_running(wm.loop)) { - g_main_loop_quit(wm.loop); - } -} - -void wm_restack_clients(void) { - xcb_window_t sibling_window = XCB_WINDOW_NONE; - for (client_t *c = wm.client_stack_list; c; c = c->stack_next) { - uint16_t mask = XCB_CONFIG_WINDOW_STACK_MODE; - xcb_params_configure_window_t params = {}; - if (sibling_window == XCB_WINDOW_NONE) { - mask = XCB_CONFIG_WINDOW_STACK_MODE; - params.stack_mode = XCB_STACK_MODE_ABOVE; - } else { - mask = XCB_CONFIG_WINDOW_STACK_MODE | XCB_CONFIG_WINDOW_SIBLING; - params.stack_mode = XCB_STACK_MODE_BELOW; - params.sibling = sibling_window; - } - sibling_window = c->window; - xcb_aux_configure_window(wm.xcb_conn, c->window, mask, ¶ms); - } - xcb_flush(wm.xcb_conn); -} - -void wm_set_current_monitor(monitor_t *monitor, bool restore_cursor) { - if (!monitor || wm.current_monitor == monitor) return; - - point_t point = xcursor_query_pointer_position(); - monitor_t *point_monitor = wm_get_monitor_by_point(point); - if (point_monitor == wm.current_monitor) { - point_monitor->cursor_position = point; - point_monitor->position_inited = true; - } - - wm.current_monitor = monitor; - if (restore_cursor) monitor_restore_cursor_point(monitor); -} - -static inline int intersect(area_t area, monitor_t *m) { - return MAX(0, MIN(area.x + area.width, m->geometry.x + m->geometry.width) - - MAX(area.x, m->geometry.x)) * - MAX(0, MIN(area.y + area.height, m->geometry.y + m->geometry.height) - - MAX(area.y, m->geometry.y)); -} - -monitor_t *__attribute__((returns_nonnull)) wm_get_monitor_by_area( - area_t area) { - if (wm.current_monitor == nullptr) { - fatal("wm_get_monitor_by_area: current monitor is nullptr"); - } - - monitor_t *monitor = wm.current_monitor; - int temp_area, max_area = 0; - for (monitor_t *m = wm.monitor_list; m; m = m->next) { - if ((temp_area = intersect(area, m)) > max_area) { - max_area = temp_area; - monitor = m; - } - } - - return monitor; -} - -monitor_t *__attribute__((returns_nonnull)) wm_get_monitor_by_point( - point_t point) { - area_t area = {.x = point.x, .y = point.y, .width = 1, .height = 1}; - return wm_get_monitor_by_area(area); -} - -monitor_t *wm_get_monitor_by_window(xcb_window_t window) { - if (window == wm.screen->root) { - xcb_query_pointer_cookie_t cookie = xcb_query_pointer(wm.xcb_conn, window); - xcb_query_pointer_reply_t *reply = - xcb_query_pointer_reply(wm.xcb_conn, cookie, nullptr); - if (reply) { - area_t area = {reply->root_x, reply->root_y, 1, 1}; - p_delete(&reply); - return wm_get_monitor_by_area(area); - } - } - - for (monitor_t *m = wm.monitor_list; m; m = m->next) { - if (window == m->bar_window) return m; - } - - client_t *c = client_get_by_window(window); - if (c) return c->monitor; - - return wm.current_monitor; -} - -monitor_t *wm_get_next_monitor(monitor_t *monitor) { - monitor_t *next_monitor = monitor->next; - if (next_monitor == nullptr) next_monitor = wm.monitor_list; - return next_monitor; -} - -static void debug_show_monitor_list(void) { - logger("\n========================== monitors ==========================\n"); - for (monitor_t *m = wm.monitor_list; m; m = m->next) { - logger("x: %4d, y: %4d, width: %4u, height: %4u -> monitor[%s]\n", - m->geometry.x, m->geometry.y, m->geometry.width, m->geometry.height, - m->name); - logger("x: %4d, y: %4d, width: %4u, height: %4u -> workarea[%s]: %u\n", - m->workarea.x, m->workarea.y, m->workarea.width, m->workarea.height, - m->name, wm.bar_height); - logger("tag extend: [%d, %d]\n", m->tag_extent.start, m->tag_extent.end); - for (const tag_t *tag = m->tag_list; tag; tag = tag->next) { - logger("tag[%u]: %4u, \"%s\", [%d, %d]\n", tag->index, tag->mask, - tag->name, tag->bar_extent.start, tag->bar_extent.end); - } - } -} - -int main(int argc, char *argv[]) { - p_clear(&wm, 1); - cmd_argv = argv; - wm_setup_signal(); - - wm_check_other_wm(); - wm_check_xcb_extensions(); - - wm_detect_monitor(); - wm_setup(); - wm_init_wallpaper(); - setup_event_loop(); - wm_scan_clients(); - - debug_show_monitor_list(); - - wm_run_autostart(autostart_list); - - if (wm.loop == nullptr) { - wm.loop = g_main_loop_new(nullptr, FALSE); - init_status(g_main_loop_get_context(wm.loop), wm_update_status); - g_main_loop_run(wm.loop); - } - g_main_loop_unref(wm.loop); - wm.loop = nullptr; - - wm_clean(); - - if (wm.need_restart) { - execvp(cmd_argv[0], cmd_argv); - fatal("execv() failed: %s", strerror(errno)); - } - - return EXIT_SUCCESS; -} diff --git a/src/wm.h b/src/wm.h deleted file mode 100644 index 12b9bfe..0000000 --- a/src/wm.h +++ /dev/null @@ -1,76 +0,0 @@ -#pragma once - -#include -#include -#include -#include - -#include "base.h" -#include "status.h" -#include "types.h" -#include "wallpaper.h" - -typedef struct config_t config_t; - -typedef struct wm_t { - padding_t padding; - uint16_t border_width; - uint16_t bar_height; - uint16_t layout_count; - - bool need_restart; - GMainLoop *loop; - status_t *status; - const config_t *config; - - client_t *client_list; - client_t *client_stack_list; - client_t *client_focused; - monitor_t *monitor_list; - monitor_t *current_monitor; - bool ignore_enter_notify; - point_t ignored_enter_notify_point; - guint clear_ignore_enter_notify_source; - const layout_t *layout_list; - - 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; - xcb_xrm_database_t *xrm; - xcb_key_symbols_t *key_symbols; - xcb_window_t wm_check_window; - int default_screen; - bool have_xfixes; - bool have_xinerama; - bool have_randr; - bool have_xkb; - - uint8_t event_base_xkb; - bool xkb_reload_keymap; - bool xkb_update_pending; - struct xkb_context *xkb_ctx; - struct xkb_state *xkb_state; - - wallpaper_context_t *wallpaper; - - color_set_t color_set; -} wm_t; - -extern wm_t wm; - -void wm_restart(void); -void wm_quit(void); -void wm_restack_clients(void); -void wm_ignore_enter_notify_at_point(point_t point); -bool wm_should_ignore_enter_notify(const xcb_enter_notify_event_t *ev); -void wm_set_current_monitor(monitor_t *monitor, bool restore_cursor); -monitor_t *__attribute__((returns_nonnull)) wm_get_monitor_by_area(area_t area); -monitor_t *__attribute__((returns_nonnull)) wm_get_monitor_by_point( - point_t point); -monitor_t *wm_get_monitor_by_window(xcb_window_t window); -monitor_t *wm_get_next_monitor(monitor_t *monitor); diff --git a/src/xcursor.c b/src/xcursor.c deleted file mode 100644 index 81c5c65..0000000 --- a/src/xcursor.c +++ /dev/null @@ -1,148 +0,0 @@ -#include "xcursor.h" - -#include -#include -#include -#include - -#include "base.h" -#include "utils.h" -#include "wm.h" - -static const char *const xcursor_font[] = { - [XC_X_cursor] = "X_cursor", - [XC_arrow] = "arrow", - [XC_based_arrow_down] = "based_arrow_down", - [XC_based_arrow_up] = "based_arrow_up", - [XC_boat] = "boat", - [XC_bogosity] = "bogosity", - [XC_bottom_left_corner] = "bottom_left_corner", - [XC_bottom_right_corner] = "bottom_right_corner", - [XC_bottom_side] = "bottom_side", - [XC_bottom_tee] = "bottom_tee", - [XC_box_spiral] = "box_spiral", - [XC_center_ptr] = "center_ptr", - [XC_circle] = "circle", - [XC_clock] = "clock", - [XC_coffee_mug] = "coffee_mug", - [XC_cross] = "cross", - [XC_cross_reverse] = "cross_reverse", - [XC_crosshair] = "crosshair", - [XC_diamond_cross] = "diamond_cross", - [XC_dot] = "dot", - [XC_dotbox] = "dotbox", - [XC_double_arrow] = "double_arrow", - [XC_draft_large] = "draft_large", - [XC_draft_small] = "draft_small", - [XC_draped_box] = "draped_box", - [XC_exchange] = "exchange", - [XC_fleur] = "fleur", - [XC_gobbler] = "gobbler", - [XC_gumby] = "gumby", - [XC_hand1] = "hand1", - [XC_hand2] = "hand2", - [XC_heart] = "heart", - [XC_icon] = "icon", - [XC_iron_cross] = "iron_cross", - [XC_left_ptr] = "left_ptr", - [XC_left_side] = "left_side", - [XC_left_tee] = "left_tee", - [XC_leftbutton] = "leftbutton", - [XC_ll_angle] = "ll_angle", - [XC_lr_angle] = "lr_angle", - [XC_man] = "man", - [XC_middlebutton] = "middlebutton", - [XC_mouse] = "mouse", - [XC_pencil] = "pencil", - [XC_pirate] = "pirate", - [XC_plus] = "plus", - [XC_question_arrow] = "question_arrow", - [XC_right_ptr] = "right_ptr", - [XC_right_side] = "right_side", - [XC_right_tee] = "right_tee", - [XC_rightbutton] = "rightbutton", - [XC_rtl_logo] = "rtl_logo", - [XC_sailboat] = "sailboat", - [XC_sb_down_arrow] = "sb_down_arrow", - [XC_sb_h_double_arrow] = "sb_h_double_arrow", - [XC_sb_left_arrow] = "sb_left_arrow", - [XC_sb_right_arrow] = "sb_right_arrow", - [XC_sb_up_arrow] = "sb_up_arrow", - [XC_sb_v_double_arrow] = "sb_v_double_arrow", - [XC_shuttle] = "shuttle", - [XC_sizing] = "sizing", - [XC_spider] = "spider", - [XC_spraycan] = "spraycan", - [XC_star] = "star", - [XC_target] = "target", - [XC_tcross] = "tcross", - [XC_top_left_arrow] = "top_left_arrow", - [XC_top_left_corner] = "top_left_corner", - [XC_top_right_corner] = "top_right_corner", - [XC_top_side] = "top_side", - [XC_top_tee] = "top_tee", - [XC_trek] = "trek", - [XC_ul_angle] = "ul_angle", - [XC_umbrella] = "umbrella", - [XC_ur_angle] = "ur_angle", - [XC_watch] = "watch", - [XC_xterm] = "xterm", -}; - -static xcb_cursor_context_t *get_xcb_cursor_context(void) { - static xcb_cursor_context_t *ctx = nullptr; - if (ctx) return ctx; - - if (xcb_cursor_context_new(wm.xcb_conn, wm.screen, &ctx) < 0) { - return nullptr; - } - - return ctx; -} - -static const char *xcursor_font_tostr(uint16_t c) { - if (c < (uint16_t)countof(xcursor_font)) { - return xcursor_font[c]; - } - return nullptr; -} - -static xcb_cursor_t cursor_list[countof(xcursor_font)]; -static xcb_cursor_context_t *cursor_ctx = nullptr; - -xcb_cursor_t xcursor_get_xcb_cursor(cursor_t cursor) { - if (!cursor_ctx) cursor_ctx = get_xcb_cursor_context(); - if (!cursor_ctx) fatal("cannot get cursor"); - - if (!cursor_list[cursor]) { - const char *name = xcursor_font_tostr(cursor); - cursor_list[cursor] = xcb_cursor_load_cursor(cursor_ctx, name); - } - - return cursor_list[cursor]; -} - -void xcursor_clean(void) { - for (int i = 0; i < countof(xcursor_font); i++) { - xcb_cursor_t cursor = cursor_list[i]; - if (cursor) xcb_free_cursor(wm.xcb_conn, cursor); - } - if (cursor_ctx) xcb_cursor_context_free(cursor_ctx); -} - -point_t xcursor_query_pointer_position(void) { - xcb_query_pointer_cookie_t cookie = - xcb_query_pointer(wm.xcb_conn, wm.screen->root); - xcb_query_pointer_reply_t *reply = - xcb_query_pointer_reply(wm.xcb_conn, cookie, nullptr); - point_t pos = {.x = reply->root_x, .y = reply->root_y}; - p_delete(&reply); - return pos; -} - -void xcursor_set_pointer_position(point_t point) { - xcb_window_t window = XCB_WINDOW_NONE; - xcb_window_t root = wm.screen->root; - xcb_warp_pointer(wm.xcb_conn, window, root, 0, 0, 1, 1, point.x, point.y); - xcb_flush(wm.xcb_conn); -} diff --git a/src/xcursor.h b/src/xcursor.h deleted file mode 100644 index a39c238..0000000 --- a/src/xcursor.h +++ /dev/null @@ -1,17 +0,0 @@ -#pragma once - -#include -#include - -#include "base.h" - -typedef enum cursor_t { - cursor_normal = XC_left_ptr, - cursor_resize = XC_bottom_right_corner, - cursor_move = XC_fleur, -} cursor_t; - -xcb_cursor_t xcursor_get_xcb_cursor(cursor_t cursor); -void xcursor_clean(void); -point_t xcursor_query_pointer_position(void); -void xcursor_set_pointer_position(point_t point); diff --git a/src/xkb.c b/src/xkb.c deleted file mode 100644 index 0f680e6..0000000 --- a/src/xkb.c +++ /dev/null @@ -1,216 +0,0 @@ -#include "xkb.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "atoms-extern.h" -#include "default_config.h" -#include "utils.h" -#include "wm.h" -#include "xwindow.h" - -static bool xkb_fill_rule_names_from_root(struct xkb_rule_names *xkb_names) { - xcb_get_property_cookie_t cookie = xcb_get_property_unchecked( - wm.xcb_conn, false, wm.screen->root, _XKB_RULES_NAMES, - XCB_GET_PROPERTY_TYPE_ANY, 0, UINT_MAX); - xcb_get_property_reply_t *reply = - xcb_get_property_reply(wm.xcb_conn, cookie, nullptr); - if (!reply) return false; - - if (reply->value_len == 0) { - p_delete(&reply); - return false; - } - - const char *walk = xcb_get_property_value(reply); - size_t remaining = xcb_get_property_value_length(reply); - for (int i = 0; i < 5 && remaining > 0; i++) { - size_t len = strnlen(walk, remaining); - switch (i) { - case 0: - xkb_names->rules = strndup(walk, len); - break; - case 1: - xkb_names->model = strndup(walk, len); - break; - case 2: - xkb_names->layout = strndup(walk, len); - break; - case 3: - xkb_names->variant = strndup(walk, len); - break; - case 4: - xkb_names->options = strndup(walk, len); - break; - } - remaining -= len + 1; - walk = &walk[len + 1]; - } - - p_delete(&reply); - return true; -} - -static void xkb_fill_state(void) { - int32_t device_id = -1; - if (wm.have_xkb) { - device_id = xkb_x11_get_core_keyboard_device_id(wm.xcb_conn); - if (device_id == -1) warn("Failed get XKB device id"); - } - - if (device_id == -1) { - struct xkb_rule_names names = {nullptr, nullptr, nullptr, nullptr, nullptr}; - if (!xkb_fill_rule_names_from_root(&names)) { - warn( - "Could not get _XKB_RULES_NAMES from root window, falling back to " - "defaults."); - } - - struct xkb_keymap *xkb_keymap = xkb_keymap_new_from_names( - wm.xkb_ctx, &names, XKB_KEYMAP_COMPILE_NO_FLAGS); - wm.xkb_state = xkb_state_new(xkb_keymap); - - if (!wm.xkb_state) fatal("Failed create XKB state"); - - xkb_keymap_unref(xkb_keymap); - p_delete(&names.rules); - p_delete(&names.model); - p_delete(&names.layout); - p_delete(&names.variant); - p_delete(&names.options); - } else { - struct xkb_keymap *xkb_keymap = xkb_x11_keymap_new_from_device( - wm.xkb_ctx, wm.xcb_conn, device_id, XKB_KEYMAP_COMPILE_NO_FLAGS); - if (!xkb_keymap) fatal("Failed get XKB keymap from device"); - wm.xkb_state = - xkb_x11_state_new_from_device(xkb_keymap, wm.xcb_conn, device_id); - if (!wm.xkb_state) fatal("Failed get XKB state from device"); - xkb_keymap_unref(xkb_keymap); - } -} - -static void xkb_init_keymap(void) { - wm.xkb_ctx = xkb_context_new(XKB_CONTEXT_NO_FLAGS); - if (!wm.xkb_ctx) fatal("Cannot get XKB context"); - - xkb_fill_state(); -} - -#define DEVICE_SPEC XCB_XKB_ID_USE_CORE_KBD - -void xkb_init(void) { - wm.xkb_update_pending = false; - wm.xkb_reload_keymap = false; - - wm.have_xkb = xkb_x11_setup_xkb_extension( - wm.xcb_conn, XKB_X11_MIN_MAJOR_XKB_VERSION, XKB_X11_MIN_MINOR_XKB_VERSION, - XKB_X11_SETUP_XKB_EXTENSION_NO_FLAGS, nullptr, nullptr, &wm.event_base_xkb, - nullptr); - logger("xkb base event: %u\n", wm.event_base_xkb); - if (!wm.have_xkb) { - warn("XKB not found or not supported"); - xkb_init_keymap(); - return; - } - - xcb_xkb_per_client_flags_cookie_t cookie = xcb_xkb_per_client_flags( - wm.xcb_conn, DEVICE_SPEC, XCB_XKB_PER_CLIENT_FLAG_DETECTABLE_AUTO_REPEAT, - XCB_XKB_PER_CLIENT_FLAG_DETECTABLE_AUTO_REPEAT, 0, 0, 0); - xcb_discard_reply(wm.xcb_conn, cookie.sequence); - uint16_t xkb_event = XCB_XKB_EVENT_TYPE_STATE_NOTIFY | - XCB_XKB_EVENT_TYPE_MAP_NOTIFY | - XCB_XKB_EVENT_TYPE_NEW_KEYBOARD_NOTIFY; - uint16_t map_parts = - XCB_XKB_MAP_PART_KEY_TYPES | XCB_XKB_MAP_PART_KEY_SYMS | - XCB_XKB_MAP_PART_MODIFIER_MAP | XCB_XKB_MAP_PART_EXPLICIT_COMPONENTS | - XCB_XKB_MAP_PART_KEY_ACTIONS | XCB_XKB_MAP_PART_KEY_BEHAVIORS | - XCB_XKB_MAP_PART_VIRTUAL_MODS | XCB_XKB_MAP_PART_VIRTUAL_MOD_MAP; - xcb_xkb_select_events(wm.xcb_conn, DEVICE_SPEC, xkb_event, 0, xkb_event, - map_parts, map_parts, nullptr); - - xkb_init_keymap(); -} - -void xkb_free(void) { - if (wm.have_xkb) { - xcb_xkb_select_events(wm.xcb_conn, DEVICE_SPEC, 0, 0, 0, 0, 0, nullptr); - } - - xkb_state_unref(wm.xkb_state); - xkb_context_unref(wm.xkb_ctx); - wm.xkb_state = nullptr; - wm.xkb_ctx = nullptr; -} - -static void xkb_reload_keymap(void) { - assert(wm.have_xkb); - - xcb_key_symbols_free(wm.key_symbols); - wm.key_symbols = xcb_key_symbols_alloc(wm.xcb_conn); - xwindow_grab_keys(wm.screen->root, key_list, countof(key_list)); - - xkb_state_unref(wm.xkb_state); - xkb_fill_state(); -} - -static gboolean xkb_refresh(gpointer ignore) { - wm.xkb_update_pending = false; - if (wm.xkb_reload_keymap) xkb_reload_keymap(); - - wm.xkb_reload_keymap = false; - - return G_SOURCE_REMOVE; -} - -static void xkb_schedule_refresh(void) { - if (wm.xkb_update_pending) return; - - wm.xkb_update_pending = true; - g_idle_add_full(G_PRIORITY_LOW, xkb_refresh, nullptr, nullptr); -} - -void xkb_handle_event(xcb_generic_event_t *event) { - assert(wm.have_xkb); - - logger("xkb event type: %u\n", event->pad0); - - /* XKB 中没有对应所有事件的泛型类型,xcb_generic_event_t 类型中的 pad0 - * 字段对应 XKB 事件中的 xkbType 字段,故用该字段判断 XKB 事件类型 */ - switch (event->pad0) { - case XCB_XKB_NEW_KEYBOARD_NOTIFY: { - wm.xkb_reload_keymap = true; - xkb_schedule_refresh(); - break; - } - case XCB_XKB_MAP_NOTIFY: { - wm.xkb_reload_keymap = true; - xkb_schedule_refresh(); - break; - } - case XCB_XKB_STATE_NOTIFY: { - xcb_xkb_state_notify_event_t *state_notify_event = (void *)event; - xkb_state_update_mask( - wm.xkb_state, state_notify_event->baseMods, - state_notify_event->latchedMods, state_notify_event->lockedMods, - state_notify_event->baseGroup, state_notify_event->latchedGroup, - state_notify_event->lockedGroup); - - logger("changed: %u\n", - state_notify_event->changed & XCB_XKB_STATE_PART_GROUP_STATE); - if (state_notify_event->changed & XCB_XKB_STATE_PART_GROUP_STATE) { - xkb_schedule_refresh(); - } - break; - } - } -} diff --git a/src/xkb.h b/src/xkb.h deleted file mode 100644 index 0ff32f0..0000000 --- a/src/xkb.h +++ /dev/null @@ -1,7 +0,0 @@ -#pragma once - -#include - -void xkb_init(void); -void xkb_free(void); -void xkb_handle_event(xcb_generic_event_t *event); diff --git a/src/xres.c b/src/xres.c deleted file mode 100644 index d43f360..0000000 --- a/src/xres.c +++ /dev/null @@ -1,52 +0,0 @@ -#include "xres.h" - -#include -#include -#include -#include - -#include "wm.h" - -void xres_init_xrm_db(void) { - if (wm.xrm) return; - wm.xrm = xcb_xrm_database_from_default(wm.xcb_conn); -} - -void xres_clean(void) { - if (!wm.xrm) return; - - xcb_xrm_database_free(wm.xrm); - wm.xrm = nullptr; -} - -/** - * @brief 获取 Xresources 文件中配置的 long 类型配置并将其转化为 uint32_t 类型 - * @param name Xresources 文件中的配置名 - * @param value 获取到的结果存放的指针 - * @returns 配置获取是否成功,成功返回 true 失败返回 false - */ -bool xres_get_uint32(const char *name, uint32_t *value) { - if (!wm.xrm) return false; - - long temp_value = 0; - bool success = - xcb_xrm_resource_get_long(wm.xrm, name, nullptr, &temp_value) == 0; - if (success) *value = (uint32_t)temp_value; - - return success; -} - -/** - * @brief 读取 Xresources 文件中的字符串配置 - * @param name Xresources 文件中的配置名 - * @param value 存放读取到的字符串存储的指针的指针,使用完后记得使用 - * free(*value) 释放字符串对应的内存 - * @param fallback 若 Xresources 解析失败,则使用这个默认值 - */ -void xres_get_string(const char *name, char **value, const char *fallback) { - if (!wm.xrm) return; - - if (!xcb_xrm_resource_get_string(wm.xrm, name, nullptr, value)) return; - - *value = strdup(fallback); -} diff --git a/src/xres.h b/src/xres.h deleted file mode 100644 index 59ed263..0000000 --- a/src/xres.h +++ /dev/null @@ -1,8 +0,0 @@ -#pragma once - -#include - -void xres_init_xrm_db(void); -void xres_clean(void); -bool xres_get_uint32(const char *name, uint32_t *value); -void xres_get_string(const char *name, char **value, const char *fallback); diff --git a/src/xwindow.c b/src/xwindow.c deleted file mode 100644 index ed5b1ca..0000000 --- a/src/xwindow.c +++ /dev/null @@ -1,288 +0,0 @@ -#include "xwindow.h" - -#include -#include -#include -#include -#include -#include -#include - -#include "action.h" -#include "atoms-extern.h" -#include "utils.h" -#include "wm.h" -#include "xcursor.h" - -xcb_window_t xwindow_create_bar_window(area_t area, uint32_t background_argb, - cursor_t cursor) { - static xcb_colormap_t colormap = XCB_NONE; - visual_t *visual = xwindow_get_xcb_visual(true); - xcb_visualid_t visual_id = visual->visual->visual_id; - xcb_connection_t *conn = wm.xcb_conn; - if (colormap == XCB_NONE) { - colormap = xcb_generate_id(conn); - uint8_t alloc = XCB_COLORMAP_ALLOC_NONE; - xcb_create_colormap(conn, alloc, colormap, wm.screen->root, visual_id); - } - - xcb_window_t window = xcb_generate_id(conn); - uint32_t value_mask = XCB_CW_OVERRIDE_REDIRECT | XCB_CW_BACK_PIXEL | - XCB_CW_BORDER_PIXEL | XCB_CW_EVENT_MASK | - XCB_CW_CURSOR | XCB_CW_COLORMAP; - const xcb_create_window_value_list_t value_list = { - .override_redirect = true, - .background_pixel = background_argb, - .border_pixel = 0, - .event_mask = XCB_EVENT_MASK_BUTTON_PRESS | XCB_EVENT_MASK_EXPOSURE, - .cursor = xcursor_get_xcb_cursor(cursor), - .colormap = colormap, - }; - xcb_void_cookie_t cookie; - cookie = xcb_create_window_aux_checked( - conn, visual->depth, window, wm.screen->root, area.x, area.y, area.width, - area.height, 0, XCB_WINDOW_CLASS_INPUT_OUTPUT, visual_id, value_mask, - &value_list); - p_delete(&visual); - if (xcb_request_check(conn, cookie)) fatal("cannot create bar window"); - - cookie = xcb_map_window(conn, window); - if (xcb_request_check(conn, cookie)) fatal("cannot map bar window:"); - - xcb_aux_sync(conn); - - return window; -} - -static visual_t *find_alpha_visual() { - xcb_depth_iterator_t depth_iter; - depth_iter = xcb_screen_allowed_depths_iterator(wm.screen); - for (; depth_iter.rem; xcb_depth_next(&depth_iter)) { - if (depth_iter.data->depth != 32) continue; - - xcb_visualtype_iterator_t visual_iter; - visual_iter = xcb_depth_visuals_iterator(depth_iter.data); - for (; visual_iter.rem; xcb_visualtype_next(&visual_iter)) { - if (visual_iter.data->_class == XCB_VISUAL_CLASS_TRUE_COLOR) { - visual_t *r = p_new(visual_t, 1); - r->visual = visual_iter.data; - r->depth = depth_iter.data->depth; - return r; - } - } - } - - return nullptr; -} - -static visual_t *find_root_visual() { - xcb_depth_iterator_t depth_iter; - depth_iter = xcb_screen_allowed_depths_iterator(wm.screen); - xcb_visualid_t vid = wm.screen->root_visual; - for (; depth_iter.rem; xcb_depth_next(&depth_iter)) { - xcb_visualtype_iterator_t visual_iter; - visual_iter = xcb_depth_visuals_iterator(depth_iter.data); - for (; visual_iter.rem; xcb_visualtype_next(&visual_iter)) { - if (visual_iter.data->visual_id == vid) { - visual_t *r = p_new(visual_t, 1); - r->visual = visual_iter.data; - r->depth = depth_iter.data->depth; - return r; - } - } - } - - return nullptr; -} - -/** - * @brief 获取 xcb 的 visual 及其对应的 depth - * @param prefer_alpha 支持 alpha 的 visual 优先 - * @returns 返回的信息使用后记得使用 free 释放 - */ -visual_t *xwindow_get_xcb_visual(bool prefer_alpha) { - if (!prefer_alpha) return find_root_visual(); - - visual_t *visual = find_alpha_visual(); - if (visual == nullptr) visual = find_root_visual(); - - return visual; -} - -void xwindow_change_cursor(xcb_window_t window, cursor_t cursor) { - xcb_params_cw_t params = {.cursor = xcursor_get_xcb_cursor(cursor)}; - xcb_aux_change_window_attributes(wm.xcb_conn, window, XCB_CW_CURSOR, ¶ms); -} - -void xwindow_grab_keys(xcb_window_t window, const keyboard_t *keys, - int keys_length) { - xcb_connection_t *conn = wm.xcb_conn; - xcb_ungrab_key(conn, XCB_GRAB_ANY, window, modifier_any); - - xcb_grab_mode_t mode = XCB_GRAB_MODE_ASYNC; - for (int i = 0; i < keys_length; i++) { - xcb_keycode_t *keycodes = - xcb_key_symbols_get_keycode(wm.key_symbols, keys[i].keysym); - if (keycodes) { - for (xcb_keycode_t *kc = keycodes; *kc != XCB_NO_SYMBOL; kc++) { - xcb_grab_key(conn, true, window, keys[i].modifiers, *kc, mode, mode); - } - p_delete(&keycodes); - } - } -} - -bool xwindow_send_event(xcb_window_t window, xcb_atom_t atom) { - bool exist = false; - - xcb_get_property_cookie_t cookie = - xcb_icccm_get_wm_protocols(wm.xcb_conn, window, WM_PROTOCOLS); - xcb_icccm_get_wm_protocols_reply_t reply; - if (xcb_icccm_get_wm_protocols_reply(wm.xcb_conn, cookie, &reply, nullptr)) { - for (uint32_t i = 0; !exist && i < reply.atoms_len; i++) { - exist = reply.atoms[i] == atom; - } - xcb_icccm_get_wm_protocols_reply_wipe(&reply); - } - - if (exist) { - xcb_client_message_event_t ev; - p_clear(&ev, 1); - ev.response_type = XCB_CLIENT_MESSAGE; - ev.format = 32; - ev.window = window; - ev.type = WM_PROTOCOLS; - ev.data.data32[0] = atom; - ev.data.data32[1] = XCB_CURRENT_TIME; - - xcb_connection_t *conn = wm.xcb_conn; - xcb_send_event(conn, false, window, XCB_EVENT_MASK_NO_EVENT, (char *)&ev); - } - - return exist; -} - -void xwindow_focus(xcb_window_t window) { - if (window == XCB_WINDOW_NONE || window == wm.screen->root) { - xcb_set_input_focus(wm.xcb_conn, XCB_INPUT_FOCUS_POINTER_ROOT, - wm.screen->root, XCB_CURRENT_TIME); - xcb_delete_property(wm.xcb_conn, wm.screen->root, _NET_ACTIVE_WINDOW); - } else { - xcb_set_input_focus(wm.xcb_conn, XCB_INPUT_FOCUS_POINTER_ROOT, window, - XCB_CURRENT_TIME); - xcb_change_property(wm.xcb_conn, XCB_PROP_MODE_REPLACE, wm.screen->root, - _NET_ACTIVE_WINDOW, XCB_ATOM_WINDOW, 32, 1, &window); - xwindow_send_event(window, WM_TAKE_FOCUS); - } -} - -/** - * 获取目标窗口的文本属性 - * @param window 目标窗口 - * @param property 属性名 - * @param out 返回的信息使用后记得使用 free 释放 - */ -void xwindow_get_text_property(xcb_window_t window, xcb_atom_t property, - char **out) { - if (out == nullptr) return; - - xcb_get_property_cookie_t cookie = xcb_get_property_unchecked( - wm.xcb_conn, false, window, property, XCB_ATOM_ANY, 0, UINT32_MAX); - xcb_get_property_reply_t *reply = - xcb_get_property_reply(wm.xcb_conn, cookie, nullptr); - if (!reply) return; - - int length = xcb_get_property_value_length(reply); - char *value = xcb_get_property_value(reply); - - if (reply && - (reply->type == XCB_ATOM_STRING || reply->type == UTF8_STRING || - reply->type == COMPOUND_TEXT) && - reply->format == 8 && length && - (*out == nullptr || strncmp(*out, value, length) != 0)) { - if (*out) p_delete(out); - *out = p_new(char, length + 1); - memcpy(*out, value, length); - (*out)[length] = '\0'; - } - - p_delete(&reply); -} - -void xwindow_set_wm_desktop(xcb_window_t window, uint32_t desktop) { - xcb_change_property(wm.xcb_conn, XCB_PROP_MODE_REPLACE, window, - _NET_WM_DESKTOP, XCB_ATOM_CARDINAL, 32, 1, &desktop); -} - -bool xwindow_get_wm_desktop(xcb_window_t window, uint32_t *desktop) { - xcb_get_property_cookie_t cookie = xcb_get_property_unchecked( - wm.xcb_conn, false, window, _NET_WM_DESKTOP, XCB_ATOM_CARDINAL, 0, 1); - xcb_get_property_reply_t *reply = - xcb_get_property_reply(wm.xcb_conn, cookie, nullptr); - if (!reply) return false; - - bool success = reply->type == XCB_ATOM_CARDINAL && reply->format == 32; - if (success) *desktop = *(uint32_t *)xcb_get_property_value(reply); - - p_delete(&reply); - return success; -} - -void xwindow_kill_window(xcb_window_t window) { - xcb_grab_server(wm.xcb_conn); - xcb_set_close_down_mode(wm.xcb_conn, XCB_CLOSE_DOWN_DESTROY_ALL); - xcb_kill_client(wm.xcb_conn, window); - xcb_flush(wm.xcb_conn); - xcb_ungrab_server(wm.xcb_conn); -} - -xcb_window_t xwindow_get_transient_for(xcb_window_t window) { - xcb_connection_t *conn = wm.xcb_conn; - xcb_window_t t_window = XCB_WINDOW_NONE; - xcb_get_property_cookie_t cookie = - xcb_icccm_get_wm_transient_for(conn, window); - xcb_icccm_get_wm_transient_for_reply(conn, cookie, &t_window, nullptr); - return t_window; -} - -/** - * @brief 获取窗口的状态 - * @param window 目标窗口 - * @returns 成功返回 xcb_icccm_wm_state_t 枚举类型,失败返回 -1 - */ -int32_t xwindow_get_state(xcb_window_t window) { - xcb_connection_t *conn = wm.xcb_conn; - xcb_icccm_wm_hints_t hints; - xcb_get_property_cookie_t cookie = xcb_icccm_get_wm_hints(conn, window); - if (!xcb_icccm_get_wm_hints_reply(conn, cookie, &hints, nullptr)) return -1; - return hints.initial_state; -} - -void xwindow_set_state(xcb_window_t window, xcb_icccm_wm_state_t state) { - xcb_icccm_wm_hints_t hints; - xcb_icccm_wm_hints_set_none(&hints); - switch (state) { - case XCB_ICCCM_WM_STATE_WITHDRAWN: - xcb_icccm_wm_hints_set_withdrawn(&hints); - break; - case XCB_ICCCM_WM_STATE_NORMAL: - xcb_icccm_wm_hints_set_normal(&hints); - break; - case XCB_ICCCM_WM_STATE_ICONIC: - xcb_icccm_wm_hints_set_iconic(&hints); - break; - } - xcb_icccm_set_wm_hints(wm.xcb_conn, window, &hints); -} - -xcb_get_geometry_reply_t *xwindow_get_geometry_reply(xcb_window_t window) { - xcb_get_geometry_cookie_t cookie = xcb_get_geometry(wm.xcb_conn, window); - return xcb_get_geometry_reply(wm.xcb_conn, cookie, nullptr); -} - -xcb_get_window_attributes_reply_t *xwindow_get_attributes_reply( - xcb_window_t window) { - xcb_get_window_attributes_cookie_t wa_cookie = - xcb_get_window_attributes(wm.xcb_conn, window); - return xcb_get_window_attributes_reply(wm.xcb_conn, wa_cookie, nullptr); -} diff --git a/src/xwindow.h b/src/xwindow.h deleted file mode 100644 index 82a1b7e..0000000 --- a/src/xwindow.h +++ /dev/null @@ -1,44 +0,0 @@ -#pragma once - -#include -#include -#include - -#include "action.h" -#include "app.h" -#include "xcursor.h" - -typedef struct visual_t { - uint8_t depth; - xcb_visualtype_t *visual; -} visual_t; - -visual_t *xwindow_get_xcb_visual(bool prefer_alpha); -void xwindow_change_cursor(xcb_window_t window, cursor_t cursor); -void xwindow_grab_keys(xcb_window_t window, const keyboard_t *keys, - int keys_length); -bool xwindow_send_event(xcb_window_t window, xcb_atom_t atom); -void xwindow_focus(xcb_window_t window); -void xwindow_get_text_property(xcb_window_t window, xcb_atom_t property, - char **out); -void xwindow_set_wm_desktop(xcb_window_t window, uint32_t desktop); -bool xwindow_get_wm_desktop(xcb_window_t window, uint32_t *desktop); -void xwindow_kill_window(xcb_window_t window); - -xcb_window_t xwindow_get_transient_for(xcb_window_t window); -int32_t xwindow_get_state(xcb_window_t window); -void xwindow_set_state(xcb_window_t window, xcb_icccm_wm_state_t state); -xcb_get_geometry_reply_t *xwindow_get_geometry_reply(xcb_window_t window); -xcb_get_window_attributes_reply_t *xwindow_get_attributes_reply( - xcb_window_t window); - -#define xwindow_set_name_static(window, name) \ - xcb_icccm_set_wm_name(wm.xcb_conn, window, XCB_ATOM_STRING, 8, \ - sizeof(name) - 1, name) -#define xwindow_set_class_instance(window) \ - xwindow_set_class_instance_static(window, APP_NAME, APP_NAME) -#define xwindow_set_class_instance_static(window, class, instance) \ - _xwindow_set_class_instance_static(window, instance "\0" class) -#define _xwindow_set_class_instance_static(window, instance_class) \ - xcb_icccm_set_wm_class(wm.xcb_conn, window, sizeof(instance_class), \ - instance_class)