Compare commits
29 Commits
9b77469ed2
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
c6ade0144c
|
|||
|
6d9ff81f78
|
|||
|
0626a4c512
|
|||
|
4500a5adfd
|
|||
|
9315505085
|
|||
|
bec2162890
|
|||
|
b40f499e7b
|
|||
|
9dfcf62c3c
|
|||
|
55c3fa1156
|
|||
|
ac91ebbed3
|
|||
|
6719ccca2b
|
|||
|
8cccf23a32
|
|||
|
0c361a6593
|
|||
|
645083d3a7
|
|||
|
f31b18316b
|
|||
|
88d9dfd714
|
|||
|
e58a43386d
|
|||
|
91950dc2f4
|
|||
|
1b0ed3b9b8
|
|||
|
91c417a0e3
|
|||
|
e347dabc21
|
|||
|
fc543e6f9e
|
|||
|
ad1d413c62
|
|||
|
c5e2b4c081
|
|||
|
96629c3045
|
|||
|
8d9becbf0f
|
|||
|
a0c8baa863
|
|||
|
84b07ebcf4
|
|||
|
2c49c9f179
|
3
Makefile
3
Makefile
@@ -28,13 +28,14 @@ wm: $(TARGET_NAME)
|
|||||||
DISPLAY=:3 $(TARGET)
|
DISPLAY=:3 $(TARGET)
|
||||||
|
|
||||||
debug: $(TARGET_NAME)
|
debug: $(TARGET_NAME)
|
||||||
DISPLAY=:3 valgrind $(TARGET)
|
DISPLAY=:3 valgrind --leak-check=full $(TARGET)
|
||||||
|
|
||||||
prepare:
|
prepare:
|
||||||
@cmake -S $(SRC_DIR) -B $(BUILD_DIR)
|
@cmake -S $(SRC_DIR) -B $(BUILD_DIR)
|
||||||
@cp $(BUILD_DIR)/compile_commands.json $(MAKEFILE_DIR)
|
@cp $(BUILD_DIR)/compile_commands.json $(MAKEFILE_DIR)
|
||||||
|
|
||||||
build: prepare
|
build: prepare
|
||||||
|
${RM} -f $(TARGET)
|
||||||
@cmake --build $(BUILD_DIR)
|
@cmake --build $(BUILD_DIR)
|
||||||
@cp $(BUILD_DIR)/$(TARGET_NAME) $(TARGET)
|
@cp $(BUILD_DIR)/$(TARGET_NAME) $(TARGET)
|
||||||
|
|
||||||
|
|||||||
21
TODO.org
Normal file
21
TODO.org
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
#+title: zswm 代办事项
|
||||||
|
|
||||||
|
* TODO 调整光标在每个 monitor 中最后停留位置的逻辑
|
||||||
|
因为在有些程序(如 Emacs 和 Firefox)中移动鼠标不会触发 motion_notify 事件,
|
||||||
|
因此需调整记录每个 monitor 中光标最后停留位置的逻辑,不能将 motion_notify 事件作为唯一依赖。
|
||||||
|
|
||||||
|
可在切换屏幕时主动查询当前光标,将其位置记录到切换前的 monitor 中。
|
||||||
|
|
||||||
|
* TODO 处理鼠标点击 bar_window 事件
|
||||||
|
|
||||||
|
* TODO 处理鼠标移动窗口事件
|
||||||
|
|
||||||
|
* TODO 处理鼠标调整窗口大小事件
|
||||||
|
|
||||||
|
* TODO 调整 stack 顺序和焦点逻辑,总感觉现在的情况有 bug
|
||||||
|
|
||||||
|
* TODO 调整 stack 逻辑,让 bar_window 在最底部
|
||||||
|
|
||||||
|
* TODO 添加切换布局算法功能
|
||||||
|
|
||||||
|
* TODO 添加系统托盘显示
|
||||||
@@ -12,13 +12,14 @@ set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
|
|||||||
# compile flags
|
# compile flags
|
||||||
add_compile_options(-g)
|
add_compile_options(-g)
|
||||||
add_compile_options(-Wall)
|
add_compile_options(-Wall)
|
||||||
|
# add_link_options(-fsanitize=address)
|
||||||
|
|
||||||
project(${APP_NAME}
|
project(${APP_NAME}
|
||||||
VERSION 0.0.1
|
VERSION 0.0.1
|
||||||
LANGUAGES C
|
LANGUAGES C
|
||||||
)
|
)
|
||||||
|
|
||||||
add_executable(${APP_NAME} main.c utils.c layout.c)
|
add_executable(${APP_NAME} main.c utils.c layout.c status.c)
|
||||||
|
|
||||||
# use C17
|
# use C17
|
||||||
set_property(TARGET ${APP_NAME} PROPERTY C_STANDARD 17)
|
set_property(TARGET ${APP_NAME} PROPERTY C_STANDARD 17)
|
||||||
@@ -40,10 +41,19 @@ pkg_check_modules(XCB REQUIRED
|
|||||||
)
|
)
|
||||||
pkg_check_modules(RENDERER REQUIRED cairo pango pangocairo imlib2)
|
pkg_check_modules(RENDERER REQUIRED cairo pango pangocairo imlib2)
|
||||||
pkg_check_modules(CAIRO_XCB REQUIRED cairo-xcb)
|
pkg_check_modules(CAIRO_XCB REQUIRED cairo-xcb)
|
||||||
|
pkg_check_modules(PULSE REQUIRED libpulse-mainloop-glib)
|
||||||
|
|
||||||
add_subdirectory(xcb)
|
add_subdirectory(xcb)
|
||||||
add_subdirectory(renderer)
|
add_subdirectory(renderer)
|
||||||
|
|
||||||
|
# status icons(all from https://remixicon.com/)
|
||||||
|
set(NET_UP_ICON "${SOURCE_DIR}/resources/icons/corner-right-up-line.png")
|
||||||
|
set(NET_DOWN_ICON "${SOURCE_DIR}/resources/icons/corner-left-down-line.png")
|
||||||
|
set(SPEAKER_ICON "${SOURCE_DIR}/resources/icons/volume-up-fill.png")
|
||||||
|
set(MEM_ICON "${SOURCE_DIR}/resources/icons/ram-line.png")
|
||||||
|
set(CPU_ICON "${SOURCE_DIR}/resources/icons/cpu-line.png")
|
||||||
|
set(CLOCK_ICON "${SOURCE_DIR}/resources/icons/time-line.png")
|
||||||
|
|
||||||
configure_file(
|
configure_file(
|
||||||
"${CMAKE_CURRENT_SOURCE_DIR}/app.h.in"
|
"${CMAKE_CURRENT_SOURCE_DIR}/app.h.in"
|
||||||
"${CMAKE_BINARY_DIR}/app.h"
|
"${CMAKE_BINARY_DIR}/app.h"
|
||||||
@@ -53,6 +63,7 @@ configure_file(
|
|||||||
target_include_directories(${APP_NAME} SYSTEM
|
target_include_directories(${APP_NAME} SYSTEM
|
||||||
PRIVATE ${GLIB_INCLUDE_DIRS}
|
PRIVATE ${GLIB_INCLUDE_DIRS}
|
||||||
PRIVATE ${CAIRO_XCB_INCLUDE_DIRS}
|
PRIVATE ${CAIRO_XCB_INCLUDE_DIRS}
|
||||||
|
PRIVATE ${PULSE_INCLUDE_DIRS}
|
||||||
)
|
)
|
||||||
target_include_directories(${APP_NAME}
|
target_include_directories(${APP_NAME}
|
||||||
PRIVATE "${SOURCE_DIR}/include"
|
PRIVATE "${SOURCE_DIR}/include"
|
||||||
@@ -64,6 +75,7 @@ target_link_libraries(${APP_NAME}
|
|||||||
PRIVATE m
|
PRIVATE m
|
||||||
PRIVATE ${GLIB_LIBRARIES}
|
PRIVATE ${GLIB_LIBRARIES}
|
||||||
PRIVATE ${CAIRO_XCB_LIBRARIES}
|
PRIVATE ${CAIRO_XCB_LIBRARIES}
|
||||||
|
PRIVATE ${PULSE_LIBRARIES}
|
||||||
PRIVATE ${LIB_XCB_NAME}
|
PRIVATE ${LIB_XCB_NAME}
|
||||||
PRIVATE ${LIB_RENDERER_NAME}
|
PRIVATE ${LIB_RENDERER_NAME}
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -6,3 +6,10 @@ void spawn(const user_action_arg_t *arg);
|
|||||||
void quit(const user_action_arg_t *arg);
|
void quit(const user_action_arg_t *arg);
|
||||||
void select_tag(const user_action_arg_t *arg);
|
void select_tag(const user_action_arg_t *arg);
|
||||||
void kill_client(const user_action_arg_t *arg);
|
void kill_client(const user_action_arg_t *arg);
|
||||||
|
void focus_stack(const user_action_arg_t *arg);
|
||||||
|
void focus_monitor(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 move_to_monitor(const user_action_arg_t *arg);
|
||||||
|
void move_to_tag(const user_action_arg_t *arg);
|
||||||
|
|||||||
@@ -1,3 +1,10 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#define APP_NAME "@APP_NAME@"
|
#define APP_NAME "@APP_NAME@"
|
||||||
|
|
||||||
|
#define NET_UP_ICON "@NET_UP_ICON@"
|
||||||
|
#define NET_DOWN_ICON "@NET_DOWN_ICON@"
|
||||||
|
#define SPEAKER_ICON "@SPEAKER_ICON@"
|
||||||
|
#define MEM_ICON "@MEM_ICON@"
|
||||||
|
#define CPU_ICON "@CPU_ICON@"
|
||||||
|
#define CLOCK_ICON "@CLOCK_ICON@"
|
||||||
|
|||||||
90
src/config.h
90
src/config.h
@@ -1,5 +1,6 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <X11/XF86keysym.h>
|
||||||
#include <X11/keysym.h>
|
#include <X11/keysym.h>
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
@@ -79,27 +80,77 @@ const rule_t rules[] = {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#define MODIFIER_ANY XCB_MOD_MASK_ANY
|
||||||
#define SUPER XCB_MOD_MASK_4
|
#define SUPER XCB_MOD_MASK_4
|
||||||
#define ALT XCB_MOD_MASK_1
|
#define ALT XCB_MOD_MASK_1
|
||||||
#define CTRL XCB_MOD_MASK_CONTROL
|
#define CTRL XCB_MOD_MASK_CONTROL
|
||||||
#define SHIFT XCB_MOD_MASK_SHIFT
|
#define SHIFT XCB_MOD_MASK_SHIFT
|
||||||
|
|
||||||
|
#define BUTTON_ANY XCB_BUTTON_INDEX_ANY
|
||||||
|
#define BUTTON_LEFT XCB_BUTTON_INDEX_1
|
||||||
|
#define BUTTON_RIGHT XCB_BUTTON_INDEX_2
|
||||||
|
#define BUTTON_MIDDLE XCB_BUTTON_INDEX_3
|
||||||
|
|
||||||
const char launcher[] =
|
const char launcher[] =
|
||||||
"rofi -show combi -modes window,drun,run,ssh,combi,windowcd";
|
"rofi -show combi -modes window,drun,run,ssh,combi,windowcd";
|
||||||
const keybinding_t keys[] = {
|
const keybinding_t keys[] = {
|
||||||
{SUPER, XK_p, spawn, {.ptr = launcher}},
|
{SUPER, XK_p, spawn, {.ptr = launcher}},
|
||||||
|
{SUPER, XK_Return, spawn, {.ptr = "st"}},
|
||||||
{SUPER | SHIFT, XK_q, quit, {.b = false}},
|
{SUPER | SHIFT, XK_q, quit, {.b = false}},
|
||||||
{SUPER | CTRL, XK_r, quit, {.b = true}},
|
{SUPER | CTRL, XK_r, quit, {.b = true}},
|
||||||
{SUPER | SHIFT, XK_c, kill_client, {0}},
|
{SUPER | SHIFT, XK_c, kill_client, {0}},
|
||||||
{SUPER, XK_1, select_tag, {.ui = 0}},
|
{ALT, XK_j, focus_stack, {.b = true}},
|
||||||
{SUPER, XK_2, select_tag, {.ui = 1}},
|
{ALT, XK_k, focus_stack, {.b = false}},
|
||||||
{SUPER, XK_3, select_tag, {.ui = 2}},
|
{SUPER | SHIFT, XK_j, focus_monitor, {.b = true}},
|
||||||
{SUPER, XK_4, select_tag, {.ui = 3}},
|
{SUPER | SHIFT, XK_k, focus_monitor, {.b = false}},
|
||||||
{SUPER, XK_5, select_tag, {.ui = 4}},
|
{SUPER, XK_o, move_to_monitor, {.b = true}},
|
||||||
{SUPER, XK_6, select_tag, {.ui = 5}},
|
|
||||||
{SUPER, XK_7, select_tag, {.ui = 6}},
|
{
|
||||||
{SUPER, XK_8, select_tag, {.ui = 7}},
|
SUPER,
|
||||||
{SUPER, XK_9, select_tag, {.ui = 8}},
|
XK_a,
|
||||||
|
raise_or_run,
|
||||||
|
{.ptr = (char *[]){"Google-chrome", "google-chrome-stable"}},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
SUPER,
|
||||||
|
XK_e,
|
||||||
|
raise_or_run,
|
||||||
|
{.ptr = (char *[]){"Emacs", "emacsclient -a '' -c -n"}},
|
||||||
|
},
|
||||||
|
{ALT | CTRL, XK_r, raise_or_run, {.ptr = (char *[]){"St", "st"}}},
|
||||||
|
{SUPER, XK_q, raise_or_run, {.ptr = (char *[]){"firefox", "firefox-bin"}}},
|
||||||
|
|
||||||
|
{SUPER, XK_Up, change_volume, {.i = 1}},
|
||||||
|
{SUPER, XK_Down, change_volume, {.i = -1}},
|
||||||
|
{SUPER | SHIFT, XK_m, toggle_mute, {0}},
|
||||||
|
{0, XF86XK_AudioRaiseVolume, change_volume, {.i = 1}},
|
||||||
|
{0, XF86XK_AudioLowerVolume, change_volume, {.i = -1}},
|
||||||
|
{0, XF86XK_AudioMute, toggle_mute, {0}},
|
||||||
|
|
||||||
|
{SUPER, XK_1, select_tag, {.ui = 1 << 0}},
|
||||||
|
{SUPER, XK_2, select_tag, {.ui = 1 << 1}},
|
||||||
|
{SUPER, XK_3, select_tag, {.ui = 1 << 2}},
|
||||||
|
{SUPER, XK_4, select_tag, {.ui = 1 << 3}},
|
||||||
|
{SUPER, XK_5, select_tag, {.ui = 1 << 4}},
|
||||||
|
{SUPER, XK_6, select_tag, {.ui = 1 << 5}},
|
||||||
|
{SUPER, XK_7, select_tag, {.ui = 1 << 6}},
|
||||||
|
{SUPER, XK_8, select_tag, {.ui = 1 << 7}},
|
||||||
|
{SUPER, XK_9, select_tag, {.ui = 1 << 8}},
|
||||||
|
|
||||||
|
{SUPER | SHIFT, XK_1, move_to_tag, {.ui = 1 << 0}},
|
||||||
|
{SUPER | SHIFT, XK_2, move_to_tag, {.ui = 1 << 1}},
|
||||||
|
{SUPER | SHIFT, XK_3, move_to_tag, {.ui = 1 << 2}},
|
||||||
|
{SUPER | SHIFT, XK_4, move_to_tag, {.ui = 1 << 3}},
|
||||||
|
{SUPER | SHIFT, XK_5, move_to_tag, {.ui = 1 << 4}},
|
||||||
|
{SUPER | SHIFT, XK_6, move_to_tag, {.ui = 1 << 5}},
|
||||||
|
{SUPER | SHIFT, XK_7, move_to_tag, {.ui = 1 << 6}},
|
||||||
|
{SUPER | SHIFT, XK_8, move_to_tag, {.ui = 1 << 7}},
|
||||||
|
{SUPER | SHIFT, XK_9, move_to_tag, {.ui = 1 << 8}},
|
||||||
|
};
|
||||||
|
|
||||||
|
const button_t buttons[] = {
|
||||||
|
{click_tag, 0, BUTTON_LEFT, select_tag, {0}},
|
||||||
|
{click_tag, SUPER, BUTTON_LEFT, move_to_tag, {0}},
|
||||||
};
|
};
|
||||||
|
|
||||||
const char *const bar_bg = "#222222";
|
const char *const bar_bg = "#222222";
|
||||||
@@ -108,16 +159,33 @@ const char *const active_tag_bg = "#005577";
|
|||||||
const char *const tag_color = "#bbbbbb";
|
const char *const tag_color = "#bbbbbb";
|
||||||
const char *const active_tag_color = "#eeeeee";
|
const char *const active_tag_color = "#eeeeee";
|
||||||
const char *const layout_color = "#bbbbbb";
|
const char *const layout_color = "#bbbbbb";
|
||||||
|
const char *const client_name_bg = "#222222";
|
||||||
const char *const client_name_color = "#bbbbbb";
|
const char *const client_name_color = "#bbbbbb";
|
||||||
const char *const active_client_name_color = "#005577";
|
const char *const active_client_name_bg = "#005577";
|
||||||
const char *const status_color = "#bbbbbb";
|
const char *const active_client_name_color = "#eeeeee";
|
||||||
|
const char *const status_net_recv_color = "#87af5f";
|
||||||
|
const char *const status_net_send_color = "#e54c62";
|
||||||
|
const char *const status_volume_color = "#7493d2";
|
||||||
|
const char *const status_memory_color = "#e0da37";
|
||||||
|
const char *const status_cpu_color = "#e33a6e";
|
||||||
|
const char *const status_datetime_color = "#7788af";
|
||||||
const char *const border_color = "#444444";
|
const char *const border_color = "#444444";
|
||||||
const char *const active_border_color = "#005577";
|
const char *const active_border_color = "#005577";
|
||||||
|
|
||||||
const char *const default_font_family = "sans-serif, monospace";
|
const char *const default_font_family = "sans-serif, monospace";
|
||||||
const uint32_t default_font_size = 10;
|
const uint32_t default_font_size = 10;
|
||||||
const uint32_t dpi_fallback = 96;
|
const uint32_t dpi_fallback = 96;
|
||||||
|
const uint32_t refresh_rate = 60;
|
||||||
|
|
||||||
const uint32_t bar_y_padding = 1;
|
const uint32_t bar_y_padding = 1;
|
||||||
const uint32_t tag_x_padding = 10;
|
const uint32_t tag_x_padding = 10;
|
||||||
|
const uint32_t layout_symbol_x_padding = 10;
|
||||||
|
const uint32_t client_name_x_padding = 10;
|
||||||
|
const uint32_t status_x_padding = 4;
|
||||||
|
const uint32_t status_icon_padding = 2;
|
||||||
const uint32_t border_width = 1;
|
const uint32_t border_width = 1;
|
||||||
|
|
||||||
|
const char *autostart_list[] = {
|
||||||
|
"gentoo-pipewire-launcher restart",
|
||||||
|
"picom -b --backend xrender",
|
||||||
|
};
|
||||||
|
|||||||
@@ -40,8 +40,15 @@ typedef enum notify_detail_t {
|
|||||||
notify_detail_none = 7,
|
notify_detail_none = 7,
|
||||||
} notify_detail_t;
|
} notify_detail_t;
|
||||||
|
|
||||||
|
typedef enum mapping_t {
|
||||||
|
MAPPING_MODIFIER = 0,
|
||||||
|
MAPPING_KEYBOARD = 1,
|
||||||
|
MAPPING_POINTER = 2
|
||||||
|
} mapping_t;
|
||||||
|
|
||||||
typedef struct mapping_notify_event_t {
|
typedef struct mapping_notify_event_t {
|
||||||
event_type_t type;
|
event_type_t type;
|
||||||
|
mapping_t request;
|
||||||
} mapping_notify_event_t;
|
} mapping_notify_event_t;
|
||||||
|
|
||||||
typedef struct key_press_event_t {
|
typedef struct key_press_event_t {
|
||||||
@@ -52,6 +59,10 @@ typedef struct key_press_event_t {
|
|||||||
|
|
||||||
typedef struct button_press_event_t {
|
typedef struct button_press_event_t {
|
||||||
event_type_t type;
|
event_type_t type;
|
||||||
|
xcb_window_t window;
|
||||||
|
xcb_window_t root;
|
||||||
|
int32_t x, y, root_x, root_y;
|
||||||
|
uint32_t time; /* 时间戳(单位:ms) */
|
||||||
} button_press_event_t;
|
} button_press_event_t;
|
||||||
|
|
||||||
typedef struct configure_request_event_t {
|
typedef struct configure_request_event_t {
|
||||||
@@ -100,6 +111,7 @@ typedef struct motion_notify_event_t {
|
|||||||
xcb_window_t window;
|
xcb_window_t window;
|
||||||
int32_t x;
|
int32_t x;
|
||||||
int32_t y;
|
int32_t y;
|
||||||
|
uint32_t time; /* 时间戳(单位:ms) */
|
||||||
} motion_notify_event_t;
|
} motion_notify_event_t;
|
||||||
|
|
||||||
typedef struct enter_notify_event_t {
|
typedef struct enter_notify_event_t {
|
||||||
|
|||||||
@@ -9,9 +9,13 @@ uint32_t *generate_wallpaper(const wallpaper_config_t *wallpaper_config,
|
|||||||
const monitor_t *monitors, const uint32_t index,
|
const monitor_t *monitors, const uint32_t index,
|
||||||
const uint32_t screen_width,
|
const uint32_t screen_width,
|
||||||
const uint32_t screen_height);
|
const uint32_t screen_height);
|
||||||
|
uint32_t *generate_icon_pixels(const char *image_path, uint32_t width,
|
||||||
|
uint32_t height, uint32_t padding);
|
||||||
|
|
||||||
uint32_t get_font_height(const char *font_family, const uint32_t font_size,
|
uint32_t get_font_height(const char *font_family, const uint32_t font_size,
|
||||||
const uint32_t dpi);
|
const uint32_t dpi);
|
||||||
void draw_bar(monitor_t *monitor, bool is_current_monitor,
|
void draw_bar(monitor_t *monitor, bool is_current_monitor, status_t *status,
|
||||||
color_set_t *color_set, uint32_t height, uint32_t tag_x_padding);
|
icons_t *icons, color_set_t *color_set, uint32_t height,
|
||||||
|
uint32_t tag_x_padding, uint32_t layout_x_padding,
|
||||||
|
uint32_t client_name_x_padding, uint32_t status_x_padding);
|
||||||
void clean_pango_context(void);
|
void clean_pango_context(void);
|
||||||
|
|||||||
@@ -33,6 +33,9 @@ struct monitor_t {
|
|||||||
client_t *clients_stack;
|
client_t *clients_stack;
|
||||||
client_t *selected_client;
|
client_t *selected_client;
|
||||||
|
|
||||||
|
int32_t pointer_x; /* 记录光标 x 坐标位置 */
|
||||||
|
int32_t pointer_y; /* 记录光标 y 坐标位置 */
|
||||||
|
|
||||||
xcb_window_t bar_window;
|
xcb_window_t bar_window;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -52,6 +55,8 @@ struct client_t {
|
|||||||
xcb_window_t window;
|
xcb_window_t window;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#define CLIENT_VISIBLE(C) ((C)->tags & (C)->monitor->selected_tags)
|
||||||
|
|
||||||
typedef struct wallpaper_config_t {
|
typedef struct wallpaper_config_t {
|
||||||
uint32_t count;
|
uint32_t count;
|
||||||
char **path_list;
|
char **path_list;
|
||||||
@@ -109,9 +114,89 @@ typedef struct color_set_t {
|
|||||||
color_t tag_color;
|
color_t tag_color;
|
||||||
color_t active_tag_color;
|
color_t active_tag_color;
|
||||||
color_t layout_color;
|
color_t layout_color;
|
||||||
|
color_t client_name_bg;
|
||||||
color_t client_name_color;
|
color_t client_name_color;
|
||||||
|
color_t active_client_name_bg;
|
||||||
color_t active_client_name_color;
|
color_t active_client_name_color;
|
||||||
color_t status_color;
|
color_t status_net_recv_color;
|
||||||
|
color_t status_net_send_color;
|
||||||
|
color_t status_volume_color;
|
||||||
|
color_t status_memory_color;
|
||||||
|
color_t status_cpu_color;
|
||||||
|
color_t status_datetime_color;
|
||||||
color_t border_color;
|
color_t border_color;
|
||||||
color_t active_border_color;
|
color_t active_border_color;
|
||||||
} color_set_t;
|
} color_set_t;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
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 {
|
||||||
|
uint64_t rx_bytes;
|
||||||
|
uint64_t tx_bytes;
|
||||||
|
char up[128];
|
||||||
|
char down[128];
|
||||||
|
} net_speed_t;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
uint32_t index;
|
||||||
|
int mute;
|
||||||
|
int avg_volume; /* 各通道平均音量(百分比) */
|
||||||
|
int volumes[5]; /* 各通道音量(百分比) */
|
||||||
|
char device[512]; /* 音频设备名称 */
|
||||||
|
} pulse_t;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
pulse_t pulse;
|
||||||
|
net_speed_t net_speed;
|
||||||
|
memory_usage_t mem_usage;
|
||||||
|
double cpu_usage_percent;
|
||||||
|
char time[256];
|
||||||
|
} status_t;
|
||||||
|
|
||||||
|
typedef struct icon_res_t {
|
||||||
|
/* 图像像素数据(RGBA 顺序) */
|
||||||
|
uint32_t *pixels;
|
||||||
|
/* cairo_surface_t * 类型,为避免引入 cairo 头文件使用 void * 类型代替 */
|
||||||
|
void *surface;
|
||||||
|
} icon_res_t;
|
||||||
|
typedef struct icons_t {
|
||||||
|
icon_res_t net_down;
|
||||||
|
icon_res_t net_up;
|
||||||
|
icon_res_t volume;
|
||||||
|
icon_res_t memory;
|
||||||
|
icon_res_t cpu;
|
||||||
|
icon_res_t clock;
|
||||||
|
} icons_t;
|
||||||
|
|
||||||
|
typedef enum click_area_t {
|
||||||
|
click_tag,
|
||||||
|
click_layout_symbol,
|
||||||
|
click_window_title,
|
||||||
|
click_status,
|
||||||
|
click_client_window,
|
||||||
|
click_root_win,
|
||||||
|
} click_area_t;
|
||||||
|
|
||||||
|
typedef struct button_t {
|
||||||
|
click_area_t click_area;
|
||||||
|
uint16_t modifiers;
|
||||||
|
uint8_t button;
|
||||||
|
void (*func)(const user_action_arg_t *);
|
||||||
|
const user_action_arg_t arg;
|
||||||
|
} button_t;
|
||||||
|
|||||||
@@ -10,8 +10,13 @@
|
|||||||
|
|
||||||
#define LENGTH(X) (sizeof(X) / sizeof(X)[0])
|
#define LENGTH(X) (sizeof(X) / sizeof(X)[0])
|
||||||
|
|
||||||
|
#ifndef MIN
|
||||||
#define MIN(a, b) (((a) < (b)) ? (a) : (b))
|
#define MIN(a, b) (((a) < (b)) ? (a) : (b))
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef MAX
|
||||||
#define MAX(a, b) (((a) > (b)) ? (a) : (b))
|
#define MAX(a, b) (((a) > (b)) ? (a) : (b))
|
||||||
|
#endif
|
||||||
|
|
||||||
double get_time();
|
double get_time();
|
||||||
void die(const char *format, ...);
|
void die(const char *format, ...);
|
||||||
|
|||||||
@@ -49,6 +49,7 @@ void map_window(xcb_window_t window, bool map_sub_window);
|
|||||||
char *get_window_name(xcb_window_t window);
|
char *get_window_name(xcb_window_t window);
|
||||||
void set_window_event_mask(xcb_window_t window, bool clear);
|
void set_window_event_mask(xcb_window_t window, bool clear);
|
||||||
void change_window_border_color(xcb_window_t window, uint32_t argb);
|
void change_window_border_color(xcb_window_t window, uint32_t argb);
|
||||||
|
void change_window_border_width(xcb_window_t window, uint32_t border_width);
|
||||||
void focus_window(xcb_window_t window);
|
void focus_window(xcb_window_t window);
|
||||||
/**
|
/**
|
||||||
* @brief 将 window 置于堆叠顺序的顶部
|
* @brief 将 window 置于堆叠顺序的顶部
|
||||||
@@ -83,6 +84,8 @@ bar_cairo_params_t *create_bar_window(int16_t x, int16_t y, uint16_t width,
|
|||||||
void destroy_window(xcb_window_t window);
|
void destroy_window(xcb_window_t window);
|
||||||
void configure_window(xcb_window_t window, int32_t x, int32_t y, uint32_t width,
|
void configure_window(xcb_window_t window, int32_t x, int32_t y, uint32_t width,
|
||||||
uint32_t height, uint32_t border_width);
|
uint32_t height, uint32_t border_width);
|
||||||
|
void move_window(xcb_window_t window, int32_t x, int32_t y);
|
||||||
|
void resize_window(xcb_window_t window, uint32_t width, uint32_t height);
|
||||||
|
|
||||||
typedef struct monitor_rect_t monitor_rect_t;
|
typedef struct monitor_rect_t monitor_rect_t;
|
||||||
struct monitor_rect_t {
|
struct monitor_rect_t {
|
||||||
@@ -123,13 +126,16 @@ typedef enum atom_t {
|
|||||||
atom_wm_protocols,
|
atom_wm_protocols,
|
||||||
atom_wm_delete,
|
atom_wm_delete,
|
||||||
atom_wm_take_focus,
|
atom_wm_take_focus,
|
||||||
|
atom_net_active_window,
|
||||||
atom_net_wm_name,
|
atom_net_wm_name,
|
||||||
atom_net_wm_state,
|
atom_net_wm_state,
|
||||||
atom_net_wm_fullscreen,
|
atom_net_wm_fullscreen,
|
||||||
atom__xrootpmap_id,
|
atom__xrootpmap_id,
|
||||||
atom_esetroot_pmap_id,
|
atom_esetroot_pmap_id,
|
||||||
|
atom_zswm_client_info, /* 自定义属性,用于存储所在 monitor 和 tags */
|
||||||
} atom_t;
|
} atom_t;
|
||||||
xcb_atom_t get_xcb_atom(atom_t atom);
|
xcb_atom_t get_xcb_atom(atom_t atom);
|
||||||
|
char *get_xcb_atom_name(xcb_atom_t atom);
|
||||||
bool send_event(xcb_window_t window, atom_t atom);
|
bool send_event(xcb_window_t window, atom_t atom);
|
||||||
typedef enum wm_state_t {
|
typedef enum wm_state_t {
|
||||||
wm_state_withdrawn = 0,
|
wm_state_withdrawn = 0,
|
||||||
@@ -139,10 +145,21 @@ typedef enum wm_state_t {
|
|||||||
int32_t get_window_state(xcb_window_t window);
|
int32_t get_window_state(xcb_window_t window);
|
||||||
void set_window_state(xcb_window_t window, wm_state_t state);
|
void set_window_state(xcb_window_t window, wm_state_t state);
|
||||||
void set_window_list(xcb_window_t *list, uint32_t length);
|
void set_window_list(xcb_window_t *list, uint32_t length);
|
||||||
|
typedef struct client_info_t {
|
||||||
|
uint32_t tags;
|
||||||
|
uint32_t monitor_index;
|
||||||
|
} client_tag_info_t;
|
||||||
|
bool get_window_tag(xcb_window_t window, client_tag_info_t *client_info);
|
||||||
|
void set_window_tag(xcb_window_t window, client_tag_info_t *client_info);
|
||||||
|
void remove_window_tag(xcb_window_t window);
|
||||||
|
|
||||||
bool get_pointer_position(int32_t *x, int32_t *y);
|
bool get_pointer_position(int32_t *x, int32_t *y);
|
||||||
|
void set_pointer_position(int32_t x, int32_t y);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 非阻塞检查特定事件掩码(模拟 XCheckMaskEvent)
|
* @brief 非阻塞检查特定事件掩码(模拟 XCheckMaskEvent)
|
||||||
*/
|
*/
|
||||||
bool xcb_check_mask_event(xcb_event_mask_t event_mask);
|
bool xcb_check_mask_event(xcb_event_mask_t event_mask);
|
||||||
|
|
||||||
|
void ungrab_any_button(xcb_window_t window);
|
||||||
|
void grab_button(xcb_window_t window, uint8_t button, uint16_t modifiers);
|
||||||
|
|||||||
@@ -2,8 +2,6 @@
|
|||||||
|
|
||||||
#include "types.h"
|
#include "types.h"
|
||||||
|
|
||||||
#define CLIENT_VISIBLE(C) ((C)->tags & (C)->monitor->selected_tags)
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取下一个需要被布局算法自动管理的可见 client
|
* 获取下一个需要被布局算法自动管理的可见 client
|
||||||
*/
|
*/
|
||||||
|
|||||||
531
src/main.c
531
src/main.c
@@ -13,28 +13,38 @@
|
|||||||
#include <xcb/xproto.h>
|
#include <xcb/xproto.h>
|
||||||
|
|
||||||
#include "action.h"
|
#include "action.h"
|
||||||
|
#include "app.h"
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
#include "event-adapter.h"
|
#include "event-adapter.h"
|
||||||
#include "layout.h"
|
#include "layout.h"
|
||||||
#include "renderer.h"
|
#include "renderer.h"
|
||||||
|
#include "status.h"
|
||||||
#include "types.h"
|
#include "types.h"
|
||||||
#include "utils.h"
|
#include "utils.h"
|
||||||
#include "xcb.h"
|
#include "xcb.h"
|
||||||
|
|
||||||
static void init_monitors(void);
|
static void init_monitors(void);
|
||||||
static char **get_monitor_tags(uint32_t monitor_count, uint32_t monitor_index);
|
static char **get_monitor_tags(uint32_t monitor_count, uint32_t monitor_index);
|
||||||
|
static void record_mointor_pointer_position(int32_t x, int32_t y,
|
||||||
|
uint32_t timestamp);
|
||||||
static void init_wallpaper(void);
|
static void init_wallpaper(void);
|
||||||
static gboolean update_wallpaper(gpointer user_data);
|
static gboolean update_wallpaper(gpointer user_data);
|
||||||
static void set_wallpaper_by_index(uint32_t index, wallpaper_config_t *config,
|
static void set_wallpaper_by_index(uint32_t index, wallpaper_config_t *config,
|
||||||
cairo_t *cr);
|
cairo_t *cr);
|
||||||
static wallpaper_config_t *parse_wallpaper_config(void);
|
static wallpaper_config_t *parse_wallpaper_config(void);
|
||||||
static void free_wallpaper_config(wallpaper_config_t *wallpaper_config);
|
static void free_wallpaper_config(wallpaper_config_t *wallpaper_config);
|
||||||
|
static void init_icons(void);
|
||||||
|
static void clean_icons(void);
|
||||||
static void init_color_set(void);
|
static void init_color_set(void);
|
||||||
static void parse_color(const char *hex, color_t *color);
|
static void parse_color(const char *hex, color_t *color);
|
||||||
static void init_monitor_bar(void);
|
static void init_monitor_bar(void);
|
||||||
static void get_xresource_config(void);
|
static void get_xresource_config(void);
|
||||||
|
static void update_status(status_t *status);
|
||||||
static void draw_all_monitor_bars(void);
|
static void draw_all_monitor_bars(void);
|
||||||
static void draw_monitor_bar(monitor_t *monitor);
|
static void draw_monitor_bar(monitor_t *monitor);
|
||||||
|
static void run_autostart(void);
|
||||||
|
static void run_once(const char *command);
|
||||||
|
static bool command_already_running(const char *command);
|
||||||
static void init_xcb_event_loop(void);
|
static void init_xcb_event_loop(void);
|
||||||
static void xcb_event_handler(generic_event_t *event, void *user_data);
|
static void xcb_event_handler(generic_event_t *event, void *user_data);
|
||||||
static void key_press(key_press_event_t *event);
|
static void key_press(key_press_event_t *event);
|
||||||
@@ -49,6 +59,7 @@ static void focus_in(focus_in_event_t *event);
|
|||||||
static void property_notify(property_notify_event_t *event);
|
static void property_notify(property_notify_event_t *event);
|
||||||
static void client_message(client_message_event_t *event);
|
static void client_message(client_message_event_t *event);
|
||||||
static void manage_window(xcb_window_t window);
|
static void manage_window(xcb_window_t window);
|
||||||
|
static void grab_buttons_for_client(client_t *client, bool focused);
|
||||||
static void unmanage_client(client_t *client, bool destroyed);
|
static void unmanage_client(client_t *client, bool destroyed);
|
||||||
static void update_client_list(void);
|
static void update_client_list(void);
|
||||||
static void update_client_name(client_t *client);
|
static void update_client_name(client_t *client);
|
||||||
@@ -67,22 +78,34 @@ static void set_client_fullscreen(client_t *client, bool fullscreen);
|
|||||||
static void apply_monitor_layout(monitor_t *monitor);
|
static void apply_monitor_layout(monitor_t *monitor);
|
||||||
static client_t *get_client_of_window(xcb_window_t window);
|
static client_t *get_client_of_window(xcb_window_t window);
|
||||||
static monitor_t *get_monitor_of_window(xcb_window_t window);
|
static monitor_t *get_monitor_of_window(xcb_window_t window);
|
||||||
|
static monitor_t *get_monitor_by_direction(bool forward);
|
||||||
|
static void send_client_to_monitor(client_t *client, monitor_t *monitor);
|
||||||
|
static void set_client_tag_prop(client_t *client);
|
||||||
|
static bool restore_client_tag(client_t *client);
|
||||||
|
static client_t *get_next_client_with_class(const char *class);
|
||||||
static gboolean handle_xcb_event(GIOChannel *channel, GIOCondition condition,
|
static gboolean handle_xcb_event(GIOChannel *channel, GIOCondition condition,
|
||||||
gpointer userdata);
|
gpointer userdata);
|
||||||
static void cleanup(void);
|
static void cleanup(bool will_restart);
|
||||||
|
|
||||||
static monitor_t *monitors = NULL;
|
static monitor_t *monitors = NULL;
|
||||||
static monitor_t *current_monitor = NULL;
|
static monitor_t *current_monitor = NULL;
|
||||||
static cairo_t *wallpaper_cr = NULL;
|
static cairo_t *wallpaper_cr = NULL;
|
||||||
static wallpaper_config_t *wallpaper_config = NULL;
|
static wallpaper_config_t *wallpaper_config = NULL;
|
||||||
|
static icons_t *icons = NULL;
|
||||||
static color_set_t *color_set = NULL;
|
static color_set_t *color_set = NULL;
|
||||||
|
static status_t *status = NULL;
|
||||||
static GMainLoop *loop = NULL;
|
static GMainLoop *loop = NULL;
|
||||||
static event_adapter_t *adapter = NULL;
|
static event_adapter_t *adapter = NULL;
|
||||||
static char *font_family = NULL;
|
static char *font_family = NULL;
|
||||||
static uint32_t font_size = default_font_size;
|
static uint32_t font_size = default_font_size;
|
||||||
static uint32_t dpi = dpi_fallback;
|
static uint32_t dpi = dpi_fallback;
|
||||||
|
static uint32_t fps = refresh_rate;
|
||||||
static uint32_t bar_y_pad = bar_y_padding;
|
static uint32_t bar_y_pad = bar_y_padding;
|
||||||
static uint32_t tag_x_pad = tag_x_padding;
|
static uint32_t tag_x_pad = tag_x_padding;
|
||||||
|
static uint32_t layout_symbol_x_pad = layout_symbol_x_padding;
|
||||||
|
static uint32_t client_name_x_pad = client_name_x_padding;
|
||||||
|
static uint32_t status_x_pad = status_x_padding;
|
||||||
|
static uint32_t status_icon_pad = status_icon_padding;
|
||||||
static uint32_t bar_height = 0;
|
static uint32_t bar_height = 0;
|
||||||
static uint32_t border_px = border_width;
|
static uint32_t border_px = border_width;
|
||||||
|
|
||||||
@@ -98,7 +121,7 @@ void quit(const user_action_arg_t *arg) {
|
|||||||
|
|
||||||
void select_tag(const user_action_arg_t *arg) {
|
void select_tag(const user_action_arg_t *arg) {
|
||||||
uint32_t tags_mask = get_tags_mask_of_monitor(current_monitor);
|
uint32_t tags_mask = get_tags_mask_of_monitor(current_monitor);
|
||||||
uint32_t target_tag = (1 << arg->ui) & tags_mask;
|
uint32_t target_tag = arg->ui & tags_mask;
|
||||||
if (!target_tag || current_monitor->selected_tags == target_tag) return;
|
if (!target_tag || current_monitor->selected_tags == target_tag) return;
|
||||||
|
|
||||||
current_monitor->selected_tags = target_tag;
|
current_monitor->selected_tags = target_tag;
|
||||||
@@ -115,19 +138,122 @@ void kill_client(const user_action_arg_t *arg) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void focus_stack(const user_action_arg_t *arg) {
|
||||||
|
if (!current_monitor->selected_client) return;
|
||||||
|
|
||||||
|
bool forward = arg->b;
|
||||||
|
client_t *client = NULL;
|
||||||
|
if (forward) {
|
||||||
|
for (client = current_monitor->selected_client->next;
|
||||||
|
client && !CLIENT_VISIBLE(client); client = client->next);
|
||||||
|
if (!client) {
|
||||||
|
for (client = current_monitor->clients; client && !CLIENT_VISIBLE(client);
|
||||||
|
client = client->next);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
client_t *c = NULL;
|
||||||
|
for (c = current_monitor->clients; c != current_monitor->selected_client;
|
||||||
|
c = c->next) {
|
||||||
|
if (CLIENT_VISIBLE(c)) client = c;
|
||||||
|
}
|
||||||
|
if (!client) {
|
||||||
|
for (; c; c = c->next) {
|
||||||
|
if (CLIENT_VISIBLE(c)) client = c;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (client) {
|
||||||
|
focus(client);
|
||||||
|
restack(current_monitor);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void focus_monitor(const user_action_arg_t *arg) {
|
||||||
|
if (!monitors->next) return;
|
||||||
|
|
||||||
|
bool forward = arg->b;
|
||||||
|
monitor_t *monitor = get_monitor_by_direction(forward);
|
||||||
|
if (monitor == current_monitor) return;
|
||||||
|
|
||||||
|
unfocus(current_monitor->selected_client, false);
|
||||||
|
current_monitor = monitor;
|
||||||
|
focus(NULL);
|
||||||
|
|
||||||
|
set_pointer_position(monitor->pointer_x, monitor->pointer_y);
|
||||||
|
}
|
||||||
|
|
||||||
|
void raise_or_run(const user_action_arg_t *arg) {
|
||||||
|
const char *class = ((const char **)arg->ptr)[0];
|
||||||
|
client_t *client = get_next_client_with_class(class);
|
||||||
|
if (client && client == current_monitor->selected_client) return;
|
||||||
|
|
||||||
|
user_action_arg_t argument;
|
||||||
|
if (client) {
|
||||||
|
logger("==== raise client: %s, window: 0x%x\n", client->name,
|
||||||
|
client->window);
|
||||||
|
current_monitor = client->monitor;
|
||||||
|
set_pointer_position(current_monitor->pointer_x,
|
||||||
|
current_monitor->pointer_y);
|
||||||
|
argument.ui = client->tags;
|
||||||
|
select_tag(&argument);
|
||||||
|
focus(client);
|
||||||
|
raise_window(client->window);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *command = ((const char **)arg->ptr)[1];
|
||||||
|
argument.ptr = command;
|
||||||
|
spawn(&argument);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
void toggle_mute(const user_action_arg_t *arg) { toggle_pulse_mute(); }
|
||||||
|
|
||||||
|
void change_volume(const user_action_arg_t *arg) {
|
||||||
|
change_pulse_volume(arg->i);
|
||||||
|
}
|
||||||
|
|
||||||
|
void move_to_monitor(const user_action_arg_t *arg) {
|
||||||
|
if (!current_monitor->selected_client || !monitors->next) return;
|
||||||
|
|
||||||
|
bool forward = arg->b;
|
||||||
|
monitor_t *monitor = get_monitor_by_direction(forward);
|
||||||
|
send_client_to_monitor(current_monitor->selected_client, monitor);
|
||||||
|
}
|
||||||
|
|
||||||
|
void move_to_tag(const user_action_arg_t *arg) {
|
||||||
|
if (!current_monitor || !current_monitor->selected_client) return;
|
||||||
|
|
||||||
|
uint32_t tag_mask = 0;
|
||||||
|
for (uint32_t i = 0; i < g_strv_length(current_monitor->tags); i++) {
|
||||||
|
tag_mask |= 1 << i;
|
||||||
|
}
|
||||||
|
uint32_t target_tag = arg->ui & tag_mask;
|
||||||
|
if (!target_tag) return;
|
||||||
|
|
||||||
|
client_t *client = current_monitor->selected_client;
|
||||||
|
client->tags = target_tag;
|
||||||
|
set_client_tag_prop(client);
|
||||||
|
focus(NULL);
|
||||||
|
arrange(current_monitor);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void print_monitor_info(monitor_t *monitor) {
|
||||||
|
logger("========== mon[%u]:\n", monitor->index);
|
||||||
|
logger("x: %d, y: %d, width: %u, height: %u, pointer x: %d, pointer y: %d\n",
|
||||||
|
monitor->monitor_x, monitor->monitor_y, monitor->monitor_width,
|
||||||
|
monitor->monitor_height, monitor->pointer_x, monitor->pointer_y);
|
||||||
|
logger("mon[%u] tags: ", monitor->index);
|
||||||
|
for (int i = 0; monitor->tags[i]; i++) {
|
||||||
|
logger("%s%s", i > 0 ? ", " : "", monitor->tags[i]);
|
||||||
|
}
|
||||||
|
logger("\n");
|
||||||
|
}
|
||||||
|
|
||||||
/* 调试函数,开发完成后删除 */
|
/* 调试函数,开发完成后删除 */
|
||||||
static void print_monitor_list_info(monitor_t *monitor_list) {
|
static void print_monitor_list_info(monitor_t *monitor_list) {
|
||||||
logger("========================= monitor info =========================\n");
|
logger("========================= monitor info =========================\n");
|
||||||
for (monitor_t *m = monitor_list; m; m = m->next) {
|
for (monitor_t *m = monitor_list; m; m = m->next) print_monitor_info(m);
|
||||||
logger("========== mon[%u]:\n", m->index);
|
|
||||||
logger("x: %d, y: %d, width: %u, height: %u\n", m->monitor_x, m->monitor_y,
|
|
||||||
m->monitor_width, m->monitor_height);
|
|
||||||
logger("mon[%u] tags: ", m->index);
|
|
||||||
for (int i = 0; m->tags[i]; i++) {
|
|
||||||
logger("%s%s", i > 0 ? ", " : "", m->tags[i]);
|
|
||||||
}
|
|
||||||
logger("\n");
|
|
||||||
}
|
|
||||||
logger("======================= monitor info end =======================\n");
|
logger("======================= monitor info end =======================\n");
|
||||||
}
|
}
|
||||||
static void print_color(const char *prompt, const color_t *color) {
|
static void print_color(const char *prompt, const color_t *color) {
|
||||||
@@ -141,9 +267,16 @@ static void print_color_set(color_set_t *set) {
|
|||||||
print_color("== tag color:", &set->tag_color);
|
print_color("== tag color:", &set->tag_color);
|
||||||
print_color("== active tag color:", &set->active_tag_color);
|
print_color("== active tag color:", &set->active_tag_color);
|
||||||
print_color("== layout bg:", &set->layout_color);
|
print_color("== layout bg:", &set->layout_color);
|
||||||
|
print_color("== client name bg:", &set->client_name_bg);
|
||||||
print_color("== client name color:", &set->client_name_color);
|
print_color("== client name color:", &set->client_name_color);
|
||||||
|
print_color("== active client name bg:", &set->active_client_name_bg);
|
||||||
print_color("== active client name color:", &set->active_client_name_color);
|
print_color("== active client name color:", &set->active_client_name_color);
|
||||||
print_color("== status color:", &set->status_color);
|
print_color("== status net recv color:", &set->status_net_recv_color);
|
||||||
|
print_color("== status net send color:", &set->status_net_send_color);
|
||||||
|
print_color("== status volume color:", &set->status_volume_color);
|
||||||
|
print_color("== status memory color:", &set->status_memory_color);
|
||||||
|
print_color("== status cpu color:", &set->status_cpu_color);
|
||||||
|
print_color("== status datetime color:", &set->status_datetime_color);
|
||||||
print_color("== border color:", &set->border_color);
|
print_color("== border color:", &set->border_color);
|
||||||
print_color("== active border color:", &set->active_border_color);
|
print_color("== active border color:", &set->active_border_color);
|
||||||
}
|
}
|
||||||
@@ -233,6 +366,25 @@ void init_monitors(void) {
|
|||||||
m->tags = get_monitor_tags(i, m->index);
|
m->tags = get_monitor_tags(i, m->index);
|
||||||
m->selected_tags = 0x1;
|
m->selected_tags = 0x1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int32_t x, y;
|
||||||
|
if (get_pointer_position(&x, &y)) {
|
||||||
|
monitor_t *monitor = rect_to_monitor(monitors, x, y, 1, 1);
|
||||||
|
print_monitor_info(monitor);
|
||||||
|
|
||||||
|
int32_t offset_x = x - monitor->monitor_x;
|
||||||
|
int32_t offset_y = y - monitor->monitor_y;
|
||||||
|
|
||||||
|
logger("==== init monitor pointer position ====\n");
|
||||||
|
logger("x: %d, y: %d, offset_x: %d, offset_y: %d\n", x, y, offset_x,
|
||||||
|
offset_y);
|
||||||
|
logger("== init monitor pointer position end ==\n");
|
||||||
|
|
||||||
|
for (monitor_t *m = monitors; m; m = m->next) {
|
||||||
|
m->pointer_x = m->monitor_x + offset_x;
|
||||||
|
m->pointer_y = m->monitor_y + offset_y;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
char **get_monitor_tags(uint32_t monitor_count, uint32_t monitor_index) {
|
char **get_monitor_tags(uint32_t monitor_count, uint32_t monitor_index) {
|
||||||
@@ -262,6 +414,15 @@ char **get_monitor_tags(uint32_t monitor_count, uint32_t monitor_index) {
|
|||||||
return g_strv_builder_unref_to_strv(builder);
|
return g_strv_builder_unref_to_strv(builder);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void record_mointor_pointer_position(int32_t x, int32_t y, uint32_t timestamp) {
|
||||||
|
static uint32_t last_timestamp = 0;
|
||||||
|
if ((timestamp - last_timestamp) < (1000 / fps)) return;
|
||||||
|
|
||||||
|
monitor_t *monitor = rect_to_monitor(monitors, x, y, 1, 1);
|
||||||
|
monitor->pointer_x = x;
|
||||||
|
monitor->pointer_x = y;
|
||||||
|
}
|
||||||
|
|
||||||
void init_wallpaper(void) {
|
void init_wallpaper(void) {
|
||||||
bar_cairo_params_t *p = prepare_wallpaper_surface_params();
|
bar_cairo_params_t *p = prepare_wallpaper_surface_params();
|
||||||
rect_size_t *screen_size = get_screen_size();
|
rect_size_t *screen_size = get_screen_size();
|
||||||
@@ -284,6 +445,9 @@ void init_wallpaper(void) {
|
|||||||
}
|
}
|
||||||
wallpaper_cr = cr;
|
wallpaper_cr = cr;
|
||||||
cairo_surface_destroy(surface);
|
cairo_surface_destroy(surface);
|
||||||
|
cairo_set_source_rgba(cr, 0, 0, 0, 1);
|
||||||
|
cairo_paint(cr);
|
||||||
|
refresh_root_window();
|
||||||
|
|
||||||
wallpaper_config = parse_wallpaper_config();
|
wallpaper_config = parse_wallpaper_config();
|
||||||
if (!wallpaper_config) return;
|
if (!wallpaper_config) return;
|
||||||
@@ -376,6 +540,66 @@ void free_wallpaper_config(wallpaper_config_t *wallpaper_config) {
|
|||||||
free(wallpaper_config);
|
free(wallpaper_config);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void init_icons(void) {
|
||||||
|
typedef struct {
|
||||||
|
const char *img_path;
|
||||||
|
icon_res_t *icon_res;
|
||||||
|
} icon_path_surface_map_t;
|
||||||
|
|
||||||
|
if (icons) return;
|
||||||
|
|
||||||
|
icons = ecalloc(1, sizeof(icons_t));
|
||||||
|
icon_path_surface_map_t map[] = {
|
||||||
|
{NET_DOWN_ICON, &icons->net_down}, {NET_UP_ICON, &icons->net_up},
|
||||||
|
{SPEAKER_ICON, &icons->volume}, {MEM_ICON, &icons->memory},
|
||||||
|
{CPU_ICON, &icons->cpu}, {CLOCK_ICON, &icons->clock},
|
||||||
|
};
|
||||||
|
for (int i = 0; i < LENGTH(map); i++) {
|
||||||
|
const char *path = map[i].img_path;
|
||||||
|
icon_res_t *res = map[i].icon_res;
|
||||||
|
if (path && strlen(path)) {
|
||||||
|
uint32_t *pixels =
|
||||||
|
generate_icon_pixels(path, bar_height, bar_height, status_icon_pad);
|
||||||
|
cairo_format_t format = CAIRO_FORMAT_ARGB32;
|
||||||
|
int stride = cairo_format_stride_for_width(format, bar_height);
|
||||||
|
cairo_surface_t *surface = cairo_image_surface_create_for_data(
|
||||||
|
(uint8_t *)pixels, format, bar_height, bar_height, stride);
|
||||||
|
if (cairo_surface_status(surface) == CAIRO_STATUS_SUCCESS) {
|
||||||
|
res->pixels = pixels;
|
||||||
|
res->surface = surface;
|
||||||
|
} else {
|
||||||
|
cairo_surface_destroy(surface);
|
||||||
|
free(pixels);
|
||||||
|
res->pixels = NULL;
|
||||||
|
res->surface = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void clean_icons(void) {
|
||||||
|
if (!icons) return;
|
||||||
|
|
||||||
|
icon_res_t icon_res_list[] = {
|
||||||
|
icons->net_down, icons->net_up, icons->volume,
|
||||||
|
icons->memory, icons->cpu, icons->clock,
|
||||||
|
};
|
||||||
|
for (int i = 0; i < LENGTH(icon_res_list); i++) {
|
||||||
|
icon_res_t res = icon_res_list[i];
|
||||||
|
if (res.surface) {
|
||||||
|
cairo_surface_finish(res.surface);
|
||||||
|
free(res.pixels);
|
||||||
|
cairo_surface_destroy(res.surface);
|
||||||
|
|
||||||
|
res.pixels = NULL;
|
||||||
|
res.surface = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
free(icons);
|
||||||
|
icons = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
void init_color_set(void) {
|
void init_color_set(void) {
|
||||||
color_set = ecalloc(1, sizeof(color_set_t));
|
color_set = ecalloc(1, sizeof(color_set_t));
|
||||||
char *bar_background = NULL;
|
char *bar_background = NULL;
|
||||||
@@ -384,9 +608,16 @@ void init_color_set(void) {
|
|||||||
char *tag = NULL;
|
char *tag = NULL;
|
||||||
char *active_tag = NULL;
|
char *active_tag = NULL;
|
||||||
char *layout = NULL;
|
char *layout = NULL;
|
||||||
|
char *client_name_background = NULL;
|
||||||
char *client_name = NULL;
|
char *client_name = NULL;
|
||||||
|
char *active_client_name_background = NULL;
|
||||||
char *active_client_name = NULL;
|
char *active_client_name = NULL;
|
||||||
char *status = NULL;
|
char *status_net_recv = NULL;
|
||||||
|
char *status_net_send = NULL;
|
||||||
|
char *status_volume = NULL;
|
||||||
|
char *status_memory = NULL;
|
||||||
|
char *status_cpu = NULL;
|
||||||
|
char *status_datetime = NULL;
|
||||||
char *border = NULL;
|
char *border = NULL;
|
||||||
char *active_border = NULL;
|
char *active_border = NULL;
|
||||||
|
|
||||||
@@ -397,11 +628,25 @@ void init_color_set(void) {
|
|||||||
get_xresource_string("zswm.color.tag", &tag, tag_color);
|
get_xresource_string("zswm.color.tag", &tag, tag_color);
|
||||||
get_xresource_string("zswm.color.active_tag", &active_tag, active_tag_color);
|
get_xresource_string("zswm.color.active_tag", &active_tag, active_tag_color);
|
||||||
get_xresource_string("zswm.color.layout", &layout, layout_color);
|
get_xresource_string("zswm.color.layout", &layout, layout_color);
|
||||||
get_xresource_string("zswm.color.client_name", &client_name,
|
get_xresource_string("zswm.color.client_name_bg", &client_name_background,
|
||||||
|
client_name_bg);
|
||||||
|
get_xresource_string("zswm.color.client_name_color", &client_name,
|
||||||
client_name_color);
|
client_name_color);
|
||||||
get_xresource_string("zswm.color.active_client_name", &active_client_name,
|
get_xresource_string("zswm.color.active_client_name_bg",
|
||||||
active_client_name_color);
|
&active_client_name_background, active_client_name_bg);
|
||||||
get_xresource_string("zswm.color.status", &status, status_color);
|
get_xresource_string("zswm.color.active_client_name_color",
|
||||||
|
&active_client_name, active_client_name_color);
|
||||||
|
get_xresource_string("zswm.color.status_net_recv", &status_net_recv,
|
||||||
|
status_net_recv_color);
|
||||||
|
get_xresource_string("zswm.color.status_net_send", &status_net_send,
|
||||||
|
status_net_send_color);
|
||||||
|
get_xresource_string("zswm.color.status_volume", &status_volume,
|
||||||
|
status_volume_color);
|
||||||
|
get_xresource_string("zswm.color.status_memory", &status_memory,
|
||||||
|
status_memory_color);
|
||||||
|
get_xresource_string("zswm.color.status_cpu", &status_cpu, status_cpu_color);
|
||||||
|
get_xresource_string("zswm.color.status_datetime", &status_datetime,
|
||||||
|
status_datetime_color);
|
||||||
get_xresource_string("zswm.color.border", &border, border_color);
|
get_xresource_string("zswm.color.border", &border, border_color);
|
||||||
get_xresource_string("zswm.color.active_border", &active_border,
|
get_xresource_string("zswm.color.active_border", &active_border,
|
||||||
active_border_color);
|
active_border_color);
|
||||||
@@ -412,9 +657,16 @@ void init_color_set(void) {
|
|||||||
parse_color(tag, &color_set->tag_color);
|
parse_color(tag, &color_set->tag_color);
|
||||||
parse_color(active_tag, &color_set->active_tag_color);
|
parse_color(active_tag, &color_set->active_tag_color);
|
||||||
parse_color(layout, &color_set->layout_color);
|
parse_color(layout, &color_set->layout_color);
|
||||||
|
parse_color(client_name_background, &color_set->client_name_bg);
|
||||||
parse_color(client_name, &color_set->client_name_color);
|
parse_color(client_name, &color_set->client_name_color);
|
||||||
|
parse_color(active_client_name_background, &color_set->active_client_name_bg);
|
||||||
parse_color(active_client_name, &color_set->active_client_name_color);
|
parse_color(active_client_name, &color_set->active_client_name_color);
|
||||||
parse_color(status, &color_set->status_color);
|
parse_color(status_net_recv, &color_set->status_net_recv_color);
|
||||||
|
parse_color(status_net_send, &color_set->status_net_send_color);
|
||||||
|
parse_color(status_volume, &color_set->status_volume_color);
|
||||||
|
parse_color(status_memory, &color_set->status_memory_color);
|
||||||
|
parse_color(status_cpu, &color_set->status_cpu_color);
|
||||||
|
parse_color(status_datetime, &color_set->status_datetime_color);
|
||||||
parse_color(border, &color_set->border_color);
|
parse_color(border, &color_set->border_color);
|
||||||
parse_color(active_border, &color_set->active_border_color);
|
parse_color(active_border, &color_set->active_border_color);
|
||||||
|
|
||||||
@@ -424,9 +676,16 @@ void init_color_set(void) {
|
|||||||
free(tag);
|
free(tag);
|
||||||
free(active_tag);
|
free(active_tag);
|
||||||
free(layout);
|
free(layout);
|
||||||
|
free(client_name_background);
|
||||||
free(client_name);
|
free(client_name);
|
||||||
|
free(active_client_name_background);
|
||||||
free(active_client_name);
|
free(active_client_name);
|
||||||
free(status);
|
free(status_net_recv);
|
||||||
|
free(status_net_send);
|
||||||
|
free(status_volume);
|
||||||
|
free(status_memory);
|
||||||
|
free(status_cpu);
|
||||||
|
free(status_datetime);
|
||||||
free(border);
|
free(border);
|
||||||
free(active_border);
|
free(active_border);
|
||||||
}
|
}
|
||||||
@@ -472,10 +731,15 @@ void init_monitor_bar(void) {
|
|||||||
|
|
||||||
void get_xresource_config(void) {
|
void get_xresource_config(void) {
|
||||||
get_xresource_uint32("Xft.dpi", &dpi);
|
get_xresource_uint32("Xft.dpi", &dpi);
|
||||||
|
get_xresource_uint32("zswm.fps", &fps);
|
||||||
get_xresource_string("zswm.font_family", &font_family, default_font_family);
|
get_xresource_string("zswm.font_family", &font_family, default_font_family);
|
||||||
get_xresource_uint32("zswm.font_size", &font_size);
|
get_xresource_uint32("zswm.font_size", &font_size);
|
||||||
get_xresource_uint32("zswm.bar_y_padding", &bar_y_pad);
|
get_xresource_uint32("zswm.bar_y_padding", &bar_y_pad);
|
||||||
get_xresource_uint32("zswm.tag_x_padding", &tag_x_pad);
|
get_xresource_uint32("zswm.tag_x_padding", &tag_x_pad);
|
||||||
|
get_xresource_uint32("zswm.layout_symbol_x_padding", &layout_symbol_x_pad);
|
||||||
|
get_xresource_uint32("zswm.client_name_x_padding", &client_name_x_pad);
|
||||||
|
get_xresource_uint32("zswm.status_x_padding", &status_x_pad);
|
||||||
|
get_xresource_uint32("zswm.status_icon_padding", &status_icon_pad);
|
||||||
get_xresource_uint32("zswm.border_width", &border_px);
|
get_xresource_uint32("zswm.border_width", &border_px);
|
||||||
|
|
||||||
logger("dpi: %u, font family: %s, font size: %u\n", dpi, font_family,
|
logger("dpi: %u, font family: %s, font size: %u\n", dpi, font_family,
|
||||||
@@ -483,15 +747,49 @@ void get_xresource_config(void) {
|
|||||||
clean_xresource();
|
clean_xresource();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void update_status(status_t *s) {
|
||||||
|
status = s;
|
||||||
|
draw_all_monitor_bars();
|
||||||
|
}
|
||||||
|
|
||||||
void draw_all_monitor_bars(void) {
|
void draw_all_monitor_bars(void) {
|
||||||
for (monitor_t *m = monitors; m; m = m->next) draw_monitor_bar(m);
|
for (monitor_t *m = monitors; m; m = m->next) draw_monitor_bar(m);
|
||||||
}
|
}
|
||||||
|
|
||||||
void draw_monitor_bar(monitor_t *m) {
|
void draw_monitor_bar(monitor_t *m) {
|
||||||
draw_bar(m, m == current_monitor, color_set, bar_height, tag_x_pad);
|
draw_bar(m, m == current_monitor, status, icons, color_set, bar_height,
|
||||||
|
tag_x_pad, layout_symbol_x_pad, client_name_x_pad, status_x_pad);
|
||||||
flush_xcb_connection();
|
flush_xcb_connection();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void run_autostart(void) {
|
||||||
|
for (int i = 0; i < LENGTH(autostart_list); i++) run_once(autostart_list[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
void run_once(const char *command) {
|
||||||
|
if (command_already_running(command)) return;
|
||||||
|
|
||||||
|
g_spawn_command_line_async(command, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool command_already_running(const char *command) {
|
||||||
|
char shell_cmd[1024];
|
||||||
|
const char *user = g_getenv("USER");
|
||||||
|
snprintf(shell_cmd, sizeof(shell_cmd), "pgrep -u %s -fx '%s'", user, command);
|
||||||
|
|
||||||
|
char *output = NULL, *error = NULL;
|
||||||
|
g_spawn_command_line_sync(shell_cmd, &output, &error, NULL, NULL);
|
||||||
|
|
||||||
|
bool result = (error == NULL || !strlen(error)) && (output && strlen(output));
|
||||||
|
|
||||||
|
free(output);
|
||||||
|
free(error);
|
||||||
|
output = NULL;
|
||||||
|
error = NULL;
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
void init_xcb_event_loop(void) {
|
void init_xcb_event_loop(void) {
|
||||||
adapter = create_event_adapter();
|
adapter = create_event_adapter();
|
||||||
event_adapter_set_handler(adapter, xcb_event_handler, NULL);
|
event_adapter_set_handler(adapter, xcb_event_handler, NULL);
|
||||||
@@ -588,6 +886,8 @@ void expose(expose_event_t *event) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void motion_notify(motion_notify_event_t *event) {
|
void motion_notify(motion_notify_event_t *event) {
|
||||||
|
record_mointor_pointer_position(event->root_x, event->root_y, event->time);
|
||||||
|
|
||||||
if (event->window != get_root_window()) return;
|
if (event->window != get_root_window()) return;
|
||||||
|
|
||||||
static monitor_t *mon = NULL;
|
static monitor_t *mon = NULL;
|
||||||
@@ -626,8 +926,10 @@ void focus_in(focus_in_event_t *event) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void property_notify(property_notify_event_t *event) {
|
void property_notify(property_notify_event_t *event) {
|
||||||
logger("window: 0x%x state: %u, atom: %u\n", event->window, event->state,
|
char *atom_name = get_xcb_atom_name(event->xcb_atom);
|
||||||
event->xcb_atom);
|
logger("window: 0x%x state: %u, atom: %u, atom name: %s\n", event->window,
|
||||||
|
event->state, event->xcb_atom, atom_name);
|
||||||
|
free(atom_name);
|
||||||
if (event->state == property_delete) return;
|
if (event->state == property_delete) return;
|
||||||
|
|
||||||
client_t *c = get_client_of_window(event->window);
|
client_t *c = get_client_of_window(event->window);
|
||||||
@@ -656,6 +958,10 @@ void property_notify(property_notify_event_t *event) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void client_message(client_message_event_t *event) {
|
void client_message(client_message_event_t *event) {
|
||||||
|
char *atom_name = get_xcb_atom_name(event->message_type);
|
||||||
|
logger("client message: message type: %s\n", atom_name);
|
||||||
|
free(atom_name);
|
||||||
|
|
||||||
client_t *c = get_client_of_window(event->window);
|
client_t *c = get_client_of_window(event->window);
|
||||||
if (!c) return;
|
if (!c) return;
|
||||||
|
|
||||||
@@ -681,6 +987,26 @@ void client_message(client_message_event_t *event) {
|
|||||||
bool toggle_to_fullscreen = event->data.data32[0] == 2 && !c->fullscreen;
|
bool toggle_to_fullscreen = event->data.data32[0] == 2 && !c->fullscreen;
|
||||||
set_client_fullscreen(c, set_fullscreen || toggle_to_fullscreen);
|
set_client_fullscreen(c, set_fullscreen || toggle_to_fullscreen);
|
||||||
}
|
}
|
||||||
|
} else if (event->message_type == get_xcb_atom(atom_net_active_window)) {
|
||||||
|
client_t *client = get_client_of_window(event->window);
|
||||||
|
if (client) {
|
||||||
|
logger("==== _NET_ACTIVE_WINDOW\n");
|
||||||
|
print_client(client);
|
||||||
|
|
||||||
|
uint32_t tag_mask = 0;
|
||||||
|
for (uint32_t i = 0; i < g_strv_length(client->monitor->tags); i++) {
|
||||||
|
tag_mask |= 1 << i;
|
||||||
|
}
|
||||||
|
uint32_t target_tag = tag_mask & client->tags;
|
||||||
|
const user_action_arg_t arg = {.ui = target_tag};
|
||||||
|
|
||||||
|
current_monitor = client->monitor;
|
||||||
|
set_pointer_position(current_monitor->pointer_x,
|
||||||
|
current_monitor->pointer_y);
|
||||||
|
select_tag(&arg);
|
||||||
|
focus(client);
|
||||||
|
restack(current_monitor);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -694,6 +1020,8 @@ void manage_window(xcb_window_t window) {
|
|||||||
c->height = c->old_height = geometry->height;
|
c->height = c->old_height = geometry->height;
|
||||||
c->old_border_width = geometry->border_width;
|
c->old_border_width = geometry->border_width;
|
||||||
c->border_width = border_px;
|
c->border_width = border_px;
|
||||||
|
free(geometry);
|
||||||
|
|
||||||
update_client_name(c);
|
update_client_name(c);
|
||||||
|
|
||||||
logger("== manage window: 0x%x\n", window);
|
logger("== manage window: 0x%x\n", window);
|
||||||
@@ -710,7 +1038,11 @@ void manage_window(xcb_window_t window) {
|
|||||||
apply_rules(c);
|
apply_rules(c);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
set_client_tag_prop(c);
|
||||||
|
grab_buttons_for_client(c, false);
|
||||||
|
|
||||||
set_window_event_mask(window, false);
|
set_window_event_mask(window, false);
|
||||||
|
|
||||||
change_window_border_color(window, color_set->border_color.argb);
|
change_window_border_color(window, color_set->border_color.argb);
|
||||||
|
|
||||||
attach_client(c);
|
attach_client(c);
|
||||||
@@ -727,6 +1059,18 @@ void manage_window(xcb_window_t window) {
|
|||||||
focus(NULL);
|
focus(NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void grab_buttons_for_client(client_t *client, bool focused) {
|
||||||
|
ungrab_any_button(client->window);
|
||||||
|
|
||||||
|
if (!focused) grab_button(client->window, BUTTON_ANY, MODIFIER_ANY);
|
||||||
|
|
||||||
|
for (int i = 0; i < LENGTH(buttons); i++) {
|
||||||
|
if (buttons[i].click_area == click_client_window) {
|
||||||
|
grab_button(client->window, buttons[i].button, buttons[i].modifiers);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void unmanage_client(client_t *client, bool destroyed) {
|
void unmanage_client(client_t *client, bool destroyed) {
|
||||||
monitor_t *monitor = client->monitor;
|
monitor_t *monitor = client->monitor;
|
||||||
|
|
||||||
@@ -734,8 +1078,7 @@ void unmanage_client(client_t *client, bool destroyed) {
|
|||||||
detach_stack_client(client);
|
detach_stack_client(client);
|
||||||
if (!destroyed) {
|
if (!destroyed) {
|
||||||
set_window_event_mask(client->window, true);
|
set_window_event_mask(client->window, true);
|
||||||
configure_window(client->window, client->x, client->y, client->width,
|
change_window_border_width(client->window, client->old_border_width);
|
||||||
client->height, client->old_border_width);
|
|
||||||
set_window_state(client->window, wm_state_withdrawn);
|
set_window_state(client->window, wm_state_withdrawn);
|
||||||
flush_xcb_connection();
|
flush_xcb_connection();
|
||||||
}
|
}
|
||||||
@@ -814,12 +1157,14 @@ void apply_rules(client_t *client) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!client->tags) {
|
if (!client->tags && !restore_client_tag(client)) {
|
||||||
client->monitor = current_monitor;
|
client->monitor = current_monitor;
|
||||||
client->tags = current_monitor->selected_tags;
|
client->tags = current_monitor->selected_tags;
|
||||||
}
|
}
|
||||||
if (switch_to_tag) {
|
if (switch_to_tag) {
|
||||||
current_monitor = client->monitor;
|
current_monitor = client->monitor;
|
||||||
|
set_pointer_position(current_monitor->pointer_x,
|
||||||
|
current_monitor->pointer_y);
|
||||||
current_monitor->selected_tags = client->tags;
|
current_monitor->selected_tags = client->tags;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -872,6 +1217,7 @@ void arrange(monitor_t *monitor) {
|
|||||||
apply_monitor_layout(monitor);
|
apply_monitor_layout(monitor);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
flush_xcb_connection();
|
||||||
}
|
}
|
||||||
|
|
||||||
void restack(monitor_t *monitor) {
|
void restack(monitor_t *monitor) {
|
||||||
@@ -908,6 +1254,7 @@ void focus(client_t *client) {
|
|||||||
if (client->monitor != current_monitor) current_monitor = client->monitor;
|
if (client->monitor != current_monitor) current_monitor = client->monitor;
|
||||||
detach_stack_client(client);
|
detach_stack_client(client);
|
||||||
attach_stack_client(client);
|
attach_stack_client(client);
|
||||||
|
grab_buttons_for_client(client, true);
|
||||||
change_window_border_color(client->window,
|
change_window_border_color(client->window,
|
||||||
color_set->active_border_color.argb);
|
color_set->active_border_color.argb);
|
||||||
focus_window(client->window);
|
focus_window(client->window);
|
||||||
@@ -921,6 +1268,7 @@ void focus(client_t *client) {
|
|||||||
void unfocus(client_t *client, bool set_focus) {
|
void unfocus(client_t *client, bool set_focus) {
|
||||||
if (!client) return;
|
if (!client) return;
|
||||||
|
|
||||||
|
grab_buttons_for_client(client, false);
|
||||||
change_window_border_color(client->window, color_set->border_color.argb);
|
change_window_border_color(client->window, color_set->border_color.argb);
|
||||||
if (set_focus) focus_window(WINDOW_NONE);
|
if (set_focus) focus_window(WINDOW_NONE);
|
||||||
}
|
}
|
||||||
@@ -951,14 +1299,15 @@ void show_hide_client(client_t *client) {
|
|||||||
if (CLIENT_VISIBLE(client)) {
|
if (CLIENT_VISIBLE(client)) {
|
||||||
if (client->fullscreen) {
|
if (client->fullscreen) {
|
||||||
fullscreen_client(client);
|
fullscreen_client(client);
|
||||||
|
resize_window(client->window, client->width, client->height);
|
||||||
} else if (client->maximize) {
|
} else if (client->maximize) {
|
||||||
maximize_client(client);
|
maximize_client(client);
|
||||||
|
resize_window(client->window, client->width, client->height);
|
||||||
} else if (!client->monitor->layout->arrange || client->floating) {
|
} else if (!client->monitor->layout->arrange || client->floating) {
|
||||||
client->x = client->old_x;
|
client->x = client->old_x;
|
||||||
client->y = client->old_y;
|
client->y = client->old_y;
|
||||||
}
|
}
|
||||||
configure_window(client->window, client->x, client->y, client->width,
|
move_window(client->window, client->x, client->y);
|
||||||
client->height, client->border_width);
|
|
||||||
show_hide_client(client->stack_next);
|
show_hide_client(client->stack_next);
|
||||||
} else {
|
} else {
|
||||||
if (!client->monitor->layout->arrange || client->floating) {
|
if (!client->monitor->layout->arrange || client->floating) {
|
||||||
@@ -968,8 +1317,7 @@ void show_hide_client(client_t *client) {
|
|||||||
client->x = client->monitor->monitor_x;
|
client->x = client->monitor->monitor_x;
|
||||||
client->y = client->monitor->monitor_y + client->monitor->monitor_height;
|
client->y = client->monitor->monitor_y + client->monitor->monitor_height;
|
||||||
show_hide_client(client->stack_next);
|
show_hide_client(client->stack_next);
|
||||||
configure_window(client->window, client->x, client->y, client->window,
|
move_window(client->window, client->x, client->y);
|
||||||
client->height, client->border_width);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1009,6 +1357,112 @@ monitor_t *get_monitor_of_window(xcb_window_t window) {
|
|||||||
return current_monitor;
|
return current_monitor;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
monitor_t *get_monitor_by_direction(bool forward) {
|
||||||
|
monitor_t *m = NULL;
|
||||||
|
if (forward) {
|
||||||
|
if (!(m = current_monitor->next)) m = monitors;
|
||||||
|
} else if (current_monitor == monitors) {
|
||||||
|
for (m = monitors; m->next; m = m->next);
|
||||||
|
} else {
|
||||||
|
for (m = monitors; m->next != current_monitor; m = m->next);
|
||||||
|
}
|
||||||
|
return m;
|
||||||
|
}
|
||||||
|
|
||||||
|
void send_client_to_monitor(client_t *client, monitor_t *monitor) {
|
||||||
|
if (client->monitor == monitor) return;
|
||||||
|
|
||||||
|
unfocus(client, true);
|
||||||
|
detach_client(client);
|
||||||
|
detach_stack_client(client);
|
||||||
|
client->monitor = monitor;
|
||||||
|
client->tags = monitor->selected_tags;
|
||||||
|
attach_client(client);
|
||||||
|
attach_stack_client(client);
|
||||||
|
set_client_tag_prop(client);
|
||||||
|
focus(client);
|
||||||
|
arrange(NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
void set_client_tag_prop(client_t *client) {
|
||||||
|
client_tag_info_t info = {
|
||||||
|
.tags = client->tags,
|
||||||
|
.monitor_index = client->monitor->index,
|
||||||
|
};
|
||||||
|
set_window_tag(client->window, &info);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 恢复 client 的 tag 信息
|
||||||
|
* @returns 成功返回 true 否则返回 false
|
||||||
|
*/
|
||||||
|
bool restore_client_tag(client_t *client) {
|
||||||
|
client_tag_info_t client_tag_info;
|
||||||
|
if (!get_window_tag(client->window, &client_tag_info)) return false;
|
||||||
|
|
||||||
|
for (monitor_t *m = monitors; m; m = m->next) {
|
||||||
|
if (client_tag_info.monitor_index == m->index) {
|
||||||
|
uint32_t tag_mask = 0;
|
||||||
|
for (uint32_t i = 0; i < g_strv_length(m->tags); i++) tag_mask |= 1 << i;
|
||||||
|
uint32_t target_tag = tag_mask & client_tag_info.tags;
|
||||||
|
if (target_tag) {
|
||||||
|
client->monitor = m;
|
||||||
|
client->tags = target_tag;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
client_t *get_next_client_with_class(const char *class) {
|
||||||
|
monitor_t *m = NULL;
|
||||||
|
client_t *c = NULL;
|
||||||
|
window_class_instance_t class_instance;
|
||||||
|
|
||||||
|
for (m = current_monitor; m; m = m->next) {
|
||||||
|
if (m == current_monitor && m->selected_client) {
|
||||||
|
c = m->selected_client->next;
|
||||||
|
} else {
|
||||||
|
c = m->clients;
|
||||||
|
}
|
||||||
|
for (; c; c = c->next) {
|
||||||
|
get_window_class_instance(c->window, &class_instance);
|
||||||
|
if (!strncmp(class, class_instance.class, sizeof(class_instance.class))) {
|
||||||
|
return c;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (m = monitors; m && m != current_monitor; m = m->next) {
|
||||||
|
for (c = m->clients; c; c = c->next) {
|
||||||
|
get_window_class_instance(c->window, &class_instance);
|
||||||
|
if (!strncmp(class, class_instance.class, sizeof(class_instance.class))) {
|
||||||
|
return c;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
m = current_monitor;
|
||||||
|
for (c = m->clients; c && m->selected_client && c != m->selected_client;
|
||||||
|
c = c->next) {
|
||||||
|
get_window_class_instance(c->window, &class_instance);
|
||||||
|
if (!strncmp(class, class_instance.class, sizeof(class_instance.class))) {
|
||||||
|
return c;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (current_monitor->selected_client) {
|
||||||
|
get_window_class_instance(c->window, &class_instance);
|
||||||
|
if (!strncmp(class, class_instance.class, sizeof(class_instance.class))) {
|
||||||
|
return c;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
gboolean handle_xcb_event(GIOChannel *channel, GIOCondition condition,
|
gboolean handle_xcb_event(GIOChannel *channel, GIOCondition condition,
|
||||||
gpointer userdata) {
|
gpointer userdata) {
|
||||||
if (condition & (G_IO_HUP | G_IO_ERR)) {
|
if (condition & (G_IO_HUP | G_IO_ERR)) {
|
||||||
@@ -1020,7 +1474,9 @@ gboolean handle_xcb_event(GIOChannel *channel, GIOCondition condition,
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void cleanup(void) {
|
void cleanup(bool will_restart) {
|
||||||
|
clean_status();
|
||||||
|
|
||||||
g_main_loop_unref(loop);
|
g_main_loop_unref(loop);
|
||||||
loop = NULL;
|
loop = NULL;
|
||||||
|
|
||||||
@@ -1028,6 +1484,7 @@ void cleanup(void) {
|
|||||||
client_t *c = m->clients;
|
client_t *c = m->clients;
|
||||||
while (m) {
|
while (m) {
|
||||||
while (c) {
|
while (c) {
|
||||||
|
if (!will_restart) remove_window_tag(c->window);
|
||||||
client_t *tc = c->next;
|
client_t *tc = c->next;
|
||||||
unmanage_client(c, false);
|
unmanage_client(c, false);
|
||||||
c = tc;
|
c = tc;
|
||||||
@@ -1051,6 +1508,8 @@ void cleanup(void) {
|
|||||||
free(color_set);
|
free(color_set);
|
||||||
color_set = NULL;
|
color_set = NULL;
|
||||||
|
|
||||||
|
clean_icons();
|
||||||
|
|
||||||
free(font_family);
|
free(font_family);
|
||||||
font_family = NULL;
|
font_family = NULL;
|
||||||
|
|
||||||
@@ -1065,12 +1524,17 @@ int main(int argc, char *argv[]) {
|
|||||||
die("another window manager is already running");
|
die("another window manager is already running");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
loop = g_main_loop_new(NULL, FALSE);
|
||||||
|
|
||||||
|
logger("========================== ZSWM START ==========================\n");
|
||||||
setup_xcb();
|
setup_xcb();
|
||||||
init_monitors();
|
init_monitors();
|
||||||
init_color_set();
|
init_color_set();
|
||||||
init_monitor_bar();
|
init_monitor_bar();
|
||||||
change_window_cursor(get_root_window(), cursor_normal);
|
change_window_cursor(get_root_window(), cursor_normal);
|
||||||
init_wallpaper();
|
init_wallpaper();
|
||||||
|
init_icons();
|
||||||
|
init_status(g_main_loop_get_context(loop), update_status);
|
||||||
|
|
||||||
draw_all_monitor_bars();
|
draw_all_monitor_bars();
|
||||||
|
|
||||||
@@ -1096,10 +1560,11 @@ int main(int argc, char *argv[]) {
|
|||||||
|
|
||||||
init_xcb_event_loop();
|
init_xcb_event_loop();
|
||||||
|
|
||||||
loop = g_main_loop_new(NULL, FALSE);
|
run_autostart();
|
||||||
|
|
||||||
g_main_loop_run(loop);
|
g_main_loop_run(loop);
|
||||||
|
|
||||||
cleanup();
|
cleanup(need_restart);
|
||||||
|
|
||||||
if (need_restart) {
|
if (need_restart) {
|
||||||
need_restart = false;
|
need_restart = false;
|
||||||
|
|||||||
@@ -1,7 +1,9 @@
|
|||||||
#include <Imlib2.h>
|
#include <Imlib2.h>
|
||||||
|
#include <cairo.h>
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
#include <stddef.h>
|
#include <stddef.h>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
@@ -11,7 +13,7 @@
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 生成需要由 xcb 绘制的壁纸数据
|
* @brief 生成需要由 xcb 绘制的壁纸数据
|
||||||
* @returns 壁纸数据,使用后记得释放
|
* @returns 壁纸像素数据( RGBA 顺序),使用后记得使用 free 释放
|
||||||
*/
|
*/
|
||||||
uint32_t *generate_wallpaper(const wallpaper_config_t *wallpaper_config,
|
uint32_t *generate_wallpaper(const wallpaper_config_t *wallpaper_config,
|
||||||
const monitor_t *monitors, const uint32_t index,
|
const monitor_t *monitors, const uint32_t index,
|
||||||
@@ -23,11 +25,10 @@ uint32_t *generate_wallpaper(const wallpaper_config_t *wallpaper_config,
|
|||||||
if (!final_image) return NULL;
|
if (!final_image) return NULL;
|
||||||
|
|
||||||
imlib_context_set_image(final_image);
|
imlib_context_set_image(final_image);
|
||||||
imlib_image_set_has_alpha(false);
|
imlib_image_set_has_alpha(true);
|
||||||
|
|
||||||
/* 填充背景色 */
|
/* 重置像素数据 */
|
||||||
imlib_context_set_color(0, 0, 0, 255);
|
imlib_image_clear();
|
||||||
imlib_image_fill_rectangle(0, 0, screen_width, screen_height);
|
|
||||||
|
|
||||||
const monitor_t *monitor = monitors;
|
const monitor_t *monitor = monitors;
|
||||||
uint32_t monitor_index = 0;
|
uint32_t monitor_index = 0;
|
||||||
@@ -89,3 +90,46 @@ uint32_t *generate_wallpaper(const wallpaper_config_t *wallpaper_config,
|
|||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 生成图标像素数据
|
||||||
|
* @returns 图标像素数据( RGBA 顺序),使用后记得使用 free 释放
|
||||||
|
*/
|
||||||
|
uint32_t *generate_icon_pixels(const char *image_path, uint32_t width,
|
||||||
|
uint32_t height, uint32_t padding) {
|
||||||
|
uint32_t dst_width = width - padding * 2;
|
||||||
|
uint32_t dst_height = height - padding * 2;
|
||||||
|
if (dst_width <= 0 || dst_height <= 0) return NULL;
|
||||||
|
|
||||||
|
Imlib_Image img = imlib_load_image(image_path);
|
||||||
|
imlib_context_set_image(img);
|
||||||
|
int w = imlib_image_get_width();
|
||||||
|
int h = imlib_image_get_height();
|
||||||
|
|
||||||
|
float width_ratio = (float)dst_width / w;
|
||||||
|
float height_ratio = (float)dst_height / h;
|
||||||
|
float scale = MAX(width_ratio, height_ratio);
|
||||||
|
|
||||||
|
int src_width = dst_width / scale;
|
||||||
|
int src_height = dst_height / scale;
|
||||||
|
int src_x = (w - src_width) / 2;
|
||||||
|
int src_y = (h - src_height) / 2;
|
||||||
|
|
||||||
|
Imlib_Image target_img = imlib_create_image(width, height);
|
||||||
|
imlib_context_set_image(target_img);
|
||||||
|
imlib_image_set_has_alpha(true);
|
||||||
|
imlib_image_clear();
|
||||||
|
|
||||||
|
imlib_blend_image_onto_image(img, 1, src_x, src_y, src_width, src_height,
|
||||||
|
padding, padding, dst_width, dst_height);
|
||||||
|
uint32_t *data = imlib_image_get_data_for_reading_only();
|
||||||
|
uint32_t data_size = width * height * sizeof(uint32_t);
|
||||||
|
uint32_t *pixels = ecalloc(1, data_size);
|
||||||
|
if (pixels) memcpy(pixels, data, data_size);
|
||||||
|
|
||||||
|
imlib_free_image_and_decache();
|
||||||
|
imlib_context_set_image(img);
|
||||||
|
imlib_free_image_and_decache();
|
||||||
|
|
||||||
|
return pixels;
|
||||||
|
}
|
||||||
|
|||||||
@@ -11,9 +11,11 @@
|
|||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
#include "renderer.h"
|
#include "renderer.h"
|
||||||
#include "types.h"
|
#include "types.h"
|
||||||
|
#include "utils.h"
|
||||||
|
|
||||||
static PangoContext *context = NULL;
|
static PangoContext *context = NULL;
|
||||||
static PangoAttrList *attr_list = NULL;
|
static PangoAttrList *attr_list = NULL;
|
||||||
@@ -135,20 +137,80 @@ static void draw_rect(cairo_t *cr, int32_t x, int32_t y, uint32_t width,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void draw_bar(monitor_t *monitor, bool is_current_monitor,
|
static int32_t draw_status(monitor_t *monitor, status_t *status, icons_t *icons,
|
||||||
color_set_t *color_set, uint32_t height, uint32_t tag_x_padding) {
|
color_set_t *color_set, uint32_t height,
|
||||||
|
uint32_t x_padding, int32_t end) {
|
||||||
|
typedef struct {
|
||||||
|
const char *const text;
|
||||||
|
color_t *color;
|
||||||
|
icon_res_t *icon_res;
|
||||||
|
} status_item_t;
|
||||||
|
|
||||||
|
char cpu[16], mem[64], volume[600];
|
||||||
|
sprintf(cpu, "%.1f", status->cpu_usage_percent);
|
||||||
|
sprintf(mem, "%s(%.1f%%)", status->mem_usage.mem_used_text,
|
||||||
|
status->mem_usage.mem_percent);
|
||||||
|
sprintf(volume, "%d%%%s%s%s", status->pulse.avg_volume,
|
||||||
|
status->pulse.mute ? "M" : "",
|
||||||
|
strlen(status->pulse.device) ? "|" : "", status->pulse.device);
|
||||||
|
|
||||||
|
status_item_t items[] = {
|
||||||
|
{status->net_speed.down, &color_set->status_net_recv_color,
|
||||||
|
&icons->net_down},
|
||||||
|
{status->net_speed.up, &color_set->status_net_send_color, &icons->net_up},
|
||||||
|
{volume, &color_set->status_volume_color, &icons->volume},
|
||||||
|
{mem, &color_set->status_memory_color, &icons->memory},
|
||||||
|
{cpu, &color_set->status_cpu_color, &icons->cpu},
|
||||||
|
{status->time, &color_set->status_datetime_color, &icons->clock},
|
||||||
|
};
|
||||||
|
|
||||||
|
int32_t x = monitor->monitor_width;
|
||||||
|
|
||||||
|
for (int i = LENGTH(items) - 1; i >= 0; i--) {
|
||||||
|
const char *text = items[i].text;
|
||||||
|
if (!strlen(text)) continue;
|
||||||
|
|
||||||
|
color_t *color = items[i].color;
|
||||||
|
uint32_t width = get_text_width(text);
|
||||||
|
x -= width + x_padding;
|
||||||
|
if (x < end) return x + (width + x_padding);
|
||||||
|
|
||||||
|
draw_text(monitor->cr, text, color, x, 0, width, height, false);
|
||||||
|
|
||||||
|
cairo_surface_t *surface = items[i].icon_res->surface;
|
||||||
|
if (surface) {
|
||||||
|
if (x - height < end) return x;
|
||||||
|
|
||||||
|
x -= height;
|
||||||
|
cairo_set_source_surface(monitor->cr, surface, x, 0);
|
||||||
|
cairo_paint(monitor->cr);
|
||||||
|
}
|
||||||
|
|
||||||
|
x -= x_padding;
|
||||||
|
if (x < end) return x + x_padding;
|
||||||
|
}
|
||||||
|
return x;
|
||||||
|
};
|
||||||
|
|
||||||
|
void draw_bar(monitor_t *monitor, bool is_current_monitor, status_t *status,
|
||||||
|
icons_t *icons, color_set_t *color_set, uint32_t height,
|
||||||
|
uint32_t tag_x_padding, uint32_t layout_x_padding,
|
||||||
|
uint32_t client_name_x_padding, uint32_t status_x_padding) {
|
||||||
cairo_t *cr = monitor->cr;
|
cairo_t *cr = monitor->cr;
|
||||||
int32_t x = 0;
|
int32_t x = 0;
|
||||||
uint32_t width = 0;
|
uint32_t width = 0;
|
||||||
const char *text;
|
const char *text;
|
||||||
color_t *color;
|
color_t *color, *bg;
|
||||||
|
color_t *bar_bg = &color_set->bar_bg;
|
||||||
uint32_t tag_mask_has_client = 0;
|
uint32_t tag_mask_has_client = 0;
|
||||||
|
uint32_t visible_client_count = 0;
|
||||||
for (client_t *c = monitor->clients; c; c = c->next) {
|
for (client_t *c = monitor->clients; c; c = c->next) {
|
||||||
tag_mask_has_client |= c->tags;
|
tag_mask_has_client |= c->tags;
|
||||||
|
if (CLIENT_VISIBLE(c)) visible_client_count++;
|
||||||
}
|
}
|
||||||
|
|
||||||
clean_cairo_context(cr);
|
clean_cairo_context(cr);
|
||||||
draw_background(cr, &color_set->bar_bg, 0, 0, monitor->monitor_width, height);
|
draw_background(cr, bar_bg, 0, 0, monitor->monitor_width, height);
|
||||||
|
|
||||||
/* 绘制 tags */
|
/* 绘制 tags */
|
||||||
for (uint32_t i = 0; i < g_strv_length(monitor->tags); i++) {
|
for (uint32_t i = 0; i < g_strv_length(monitor->tags); i++) {
|
||||||
@@ -157,15 +219,52 @@ void draw_bar(monitor_t *monitor, bool is_current_monitor,
|
|||||||
text = monitor->tags[i];
|
text = monitor->tags[i];
|
||||||
width = get_text_width(text) + tag_x_padding * 2;
|
width = get_text_width(text) + tag_x_padding * 2;
|
||||||
|
|
||||||
color = selected ? &color_set->active_tag_bg : &color_set->tag_bg;
|
bg = selected ? &color_set->active_tag_bg : &color_set->tag_bg;
|
||||||
if (color->rgba != color_set->bar_bg.rgba) {
|
|
||||||
draw_background(cr, color, x, 0, width, height);
|
|
||||||
}
|
|
||||||
color = selected ? &color_set->active_tag_color : &color_set->tag_color;
|
color = selected ? &color_set->active_tag_color : &color_set->tag_color;
|
||||||
|
|
||||||
|
if (bg->rgba != bar_bg->rgba) {
|
||||||
|
draw_background(cr, bg, x, 0, width, height);
|
||||||
|
}
|
||||||
if (has_client) draw_rect(cr, x, 0, 4, 4, selected, color, 1);
|
if (has_client) draw_rect(cr, x, 0, 4, 4, selected, color, 1);
|
||||||
draw_text(cr, text, color, x, 0, width, height, true);
|
draw_text(cr, text, color, x, 0, width, height, true);
|
||||||
x += width;
|
x += width;
|
||||||
}
|
}
|
||||||
|
/* 绘制 layout 标识 */
|
||||||
|
{
|
||||||
|
text = monitor->layout->symbol;
|
||||||
|
width = get_text_width(text) + layout_x_padding * 2;
|
||||||
|
color = &color_set->layout_color;
|
||||||
|
draw_text(cr, text, color, x, 0, width, height, true);
|
||||||
|
x += width;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 绘制状态栏信息 */
|
||||||
|
int32_t status_start =
|
||||||
|
draw_status(monitor, status, icons, color_set, height, status_x_padding, x);
|
||||||
|
|
||||||
|
/* 绘制 client name 列表 */
|
||||||
|
if (visible_client_count) {
|
||||||
|
uint32_t box_width = (status_start - x) / visible_client_count;
|
||||||
|
width = box_width - 2 * client_name_x_padding;
|
||||||
|
for (client_t *c = monitor->clients; c; c = c->next) {
|
||||||
|
if (!CLIENT_VISIBLE(c)) continue;
|
||||||
|
if (is_current_monitor && c == monitor->selected_client) {
|
||||||
|
bg = &color_set->active_client_name_bg;
|
||||||
|
color = &color_set->active_client_name_color;
|
||||||
|
} else {
|
||||||
|
bg = &color_set->client_name_bg;
|
||||||
|
color = &color_set->client_name_color;
|
||||||
|
}
|
||||||
|
|
||||||
|
text = c->name;
|
||||||
|
if (bg->rgba != bar_bg->rgba) {
|
||||||
|
draw_background(cr, bg, x, 0, box_width, height);
|
||||||
|
}
|
||||||
|
x += client_name_x_padding;
|
||||||
|
draw_text(cr, text, color, x, 0, width, height, false);
|
||||||
|
x += width + client_name_x_padding;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void clean_pango_context(void) {
|
void clean_pango_context(void) {
|
||||||
|
|||||||
BIN
src/resources/icons/corner-left-down-line.png
Normal file
BIN
src/resources/icons/corner-left-down-line.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 766 B |
BIN
src/resources/icons/corner-right-up-line.png
Normal file
BIN
src/resources/icons/corner-right-up-line.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 786 B |
BIN
src/resources/icons/cpu-line.png
Normal file
BIN
src/resources/icons/cpu-line.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 769 B |
BIN
src/resources/icons/ram-line.png
Normal file
BIN
src/resources/icons/ram-line.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 564 B |
BIN
src/resources/icons/time-line.png
Normal file
BIN
src/resources/icons/time-line.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 2.4 KiB |
BIN
src/resources/icons/volume-up-fill.png
Normal file
BIN
src/resources/icons/volume-up-fill.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.9 KiB |
407
src/status.c
Normal file
407
src/status.c
Normal file
@@ -0,0 +1,407 @@
|
|||||||
|
#include "status.h"
|
||||||
|
|
||||||
|
#include <glib.h>
|
||||||
|
#include <math.h>
|
||||||
|
#include <pulse/context.h>
|
||||||
|
#include <pulse/def.h>
|
||||||
|
#include <pulse/glib-mainloop.h>
|
||||||
|
#include <pulse/introspect.h>
|
||||||
|
#include <pulse/subscribe.h>
|
||||||
|
#include <pulse/volume.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include "types.h"
|
||||||
|
#include "utils.h"
|
||||||
|
|
||||||
|
#define CPU_FILE "/proc/stat"
|
||||||
|
#define MEM_FILE "/proc/meminfo"
|
||||||
|
#define NET_FILE "/proc/net/dev"
|
||||||
|
|
||||||
|
#define INTERVAL 1
|
||||||
|
#define TIME_FORMAT "%A %m-%d %H:%M:%S"
|
||||||
|
|
||||||
|
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;
|
||||||
|
|
||||||
|
typedef struct net_stat_t {
|
||||||
|
uint64_t rx_bytes;
|
||||||
|
uint64_t tx_bytes;
|
||||||
|
} net_stat_t;
|
||||||
|
typedef void (*pulse_notify)(pulse_t pulse);
|
||||||
|
|
||||||
|
static status_t status;
|
||||||
|
static guint timer = 0;
|
||||||
|
static status_changed_notify listener = NULL;
|
||||||
|
|
||||||
|
static bool pulse_inited = false;
|
||||||
|
static pa_cvolume current_volume;
|
||||||
|
static pa_context *context = NULL;
|
||||||
|
static pa_glib_mainloop *loop = NULL;
|
||||||
|
|
||||||
|
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 gboolean inited = false;
|
||||||
|
|
||||||
|
gchar *contents = NULL;
|
||||||
|
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 != NULL; ++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 *units[] = {"B", "K", "M", "G"};
|
||||||
|
|
||||||
|
double size = (double)bytes;
|
||||||
|
int i = 0;
|
||||||
|
while (size > 1024) {
|
||||||
|
size /= 1024;
|
||||||
|
++i;
|
||||||
|
}
|
||||||
|
|
||||||
|
sprintf(str, "%.1lf%s", size, units[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;
|
||||||
|
}
|
||||||
|
|
||||||
|
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 = NULL;
|
||||||
|
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 != NULL; ++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;
|
||||||
|
}
|
||||||
|
|
||||||
|
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 = NULL;
|
||||||
|
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 = {.rx_bytes = 0, .tx_bytes = 0};
|
||||||
|
for (gchar **line = lines; *line != NULL; ++line) {
|
||||||
|
if (strchr(*line, ':') == NULL) {
|
||||||
|
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 false;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void get_date_time(char *time_text, size_t length) {
|
||||||
|
time_t current_time = time(NULL);
|
||||||
|
struct tm *time_info = localtime(¤t_time);
|
||||||
|
|
||||||
|
strftime(time_text, length, TIME_FORMAT, time_info);
|
||||||
|
}
|
||||||
|
|
||||||
|
#define volume_to_percent(v) ((int)round((double)(v) * 100 / PA_VOLUME_NORM))
|
||||||
|
|
||||||
|
static void parse_sink(const pa_sink_info *i, pulse_t *pulse) {
|
||||||
|
pulse->index = i->index;
|
||||||
|
pulse->mute = i->mute;
|
||||||
|
memset(pulse->volumes, -1, sizeof(pulse->volumes));
|
||||||
|
pulse->avg_volume = volume_to_percent(pa_cvolume_avg(&i->volume));
|
||||||
|
strncpy(pulse->device, i->description, sizeof(pulse->device));
|
||||||
|
|
||||||
|
int len = 0;
|
||||||
|
for (uint8_t ch = 0; ch < LENGTH(pulse->volumes); ++ch) {
|
||||||
|
if (!pa_channel_map_has_position(&i->channel_map, ch)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
pa_volume_t v = pa_cvolume_get_position(&i->volume, &i->channel_map, ch);
|
||||||
|
int volume = volume_to_percent(v);
|
||||||
|
pulse->volumes[len++] = volume;
|
||||||
|
}
|
||||||
|
for (uint8_t i = len; i < LENGTH(pulse->volumes); ++i) {
|
||||||
|
pulse->volumes[i] = -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *key = NULL;
|
||||||
|
void *state = NULL;
|
||||||
|
while ((key = pa_proplist_iterate(i->proplist, &state)) != NULL) {
|
||||||
|
if (strcmp(key, "api.alsa.path") != 0 &&
|
||||||
|
strcmp(key, "device.description") != 0) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *value = pa_proplist_gets(i->proplist, key);
|
||||||
|
unsigned long len = strlen(value);
|
||||||
|
|
||||||
|
if (len && (len < strlen(pulse->device))) {
|
||||||
|
strncpy(pulse->device, value, sizeof(pulse->device));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void notify_status_change() {
|
||||||
|
if (listener != NULL) listener(&status);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void sink_info_callback(pa_context *c, const pa_sink_info *info, int eol,
|
||||||
|
void *userdata) {
|
||||||
|
if (eol < 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (eol > 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (info) {
|
||||||
|
parse_sink(info, &status.pulse);
|
||||||
|
current_volume = info->volume;
|
||||||
|
pulse_inited = true;
|
||||||
|
notify_status_change();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void server_info_callback(pa_context *c, const pa_server_info *info,
|
||||||
|
void *userdata) {
|
||||||
|
pa_context_get_sink_info_by_name(c, info->default_sink_name,
|
||||||
|
sink_info_callback, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void subscribe_callback(pa_context *c, pa_subscription_event_type_t t,
|
||||||
|
uint32_t idx, void *userdata) {
|
||||||
|
uint32_t facility = t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK;
|
||||||
|
|
||||||
|
if (facility == PA_SUBSCRIPTION_EVENT_SINK) {
|
||||||
|
pa_context_get_server_info(c, server_info_callback, NULL);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void clean_pulse() {
|
||||||
|
memset(&status.pulse, 0, sizeof(status.pulse));
|
||||||
|
notify_status_change();
|
||||||
|
if (context) {
|
||||||
|
pa_context_disconnect(context);
|
||||||
|
pa_context_unref(context);
|
||||||
|
context = NULL;
|
||||||
|
}
|
||||||
|
if (loop) pa_glib_mainloop_free(loop);
|
||||||
|
pulse_inited = false;
|
||||||
|
loop = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void init_pulse(GMainContext *g_context);
|
||||||
|
static void state_callback(pa_context *c, void *userdata) {
|
||||||
|
pa_context_state_t state = pa_context_get_state(c);
|
||||||
|
if (PA_CONTEXT_IS_GOOD(state)) {
|
||||||
|
pa_context_set_subscribe_callback(c, subscribe_callback, NULL);
|
||||||
|
pa_context_subscribe(c, PA_SUBSCRIPTION_MASK_SINK, NULL, NULL);
|
||||||
|
pa_context_get_server_info(c, server_info_callback, NULL);
|
||||||
|
} else if (state == PA_CONTEXT_FAILED) {
|
||||||
|
clean_pulse();
|
||||||
|
init_pulse((GMainContext *)userdata);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void init_pulse(GMainContext *g_context) {
|
||||||
|
if (loop) pa_glib_mainloop_free(loop);
|
||||||
|
pa_glib_mainloop *loop = pa_glib_mainloop_new(g_context);
|
||||||
|
pa_mainloop_api *api = pa_glib_mainloop_get_api(loop);
|
||||||
|
context = pa_context_new(api, "ZSWM-Status-Volume");
|
||||||
|
pa_context_connect(context, NULL, PA_CONTEXT_NOFAIL, NULL);
|
||||||
|
pa_context_set_state_callback(context, state_callback, g_context);
|
||||||
|
}
|
||||||
|
|
||||||
|
static gboolean update_status(gpointer data) {
|
||||||
|
GError *error = NULL;
|
||||||
|
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 init_status(GMainContext *context, status_changed_notify callback) {
|
||||||
|
listener = callback;
|
||||||
|
init_pulse(context);
|
||||||
|
update_status(NULL);
|
||||||
|
timer = g_timeout_add_seconds(INTERVAL, update_status, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
void clean_status() {
|
||||||
|
clean_pulse();
|
||||||
|
g_source_remove(timer);
|
||||||
|
listener = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
void change_pulse_volume(int step) {
|
||||||
|
if (!pulse_inited || !step || step > 100 || step < -100) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
pa_cvolume vol = current_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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
uint32_t index = status.pulse.index;
|
||||||
|
pa_context_set_sink_volume_by_index(context, index, &vol, NULL, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
void toggle_pulse_mute() {
|
||||||
|
if (!pulse_inited) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t index = status.pulse.index;
|
||||||
|
int mute = !status.pulse.mute;
|
||||||
|
pa_context_set_sink_mute_by_index(context, index, mute, NULL, NULL);
|
||||||
|
}
|
||||||
12
src/status.h
Normal file
12
src/status.h
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <glib.h>
|
||||||
|
|
||||||
|
#include "types.h"
|
||||||
|
|
||||||
|
typedef void (*status_changed_notify)(status_t *status);
|
||||||
|
void init_status(GMainContext *context, status_changed_notify callback);
|
||||||
|
void clean_status(void);
|
||||||
|
|
||||||
|
void change_pulse_volume(int step);
|
||||||
|
void toggle_pulse_mute(void);
|
||||||
13
src/utils.c
13
src/utils.c
@@ -2,11 +2,14 @@
|
|||||||
|
|
||||||
#include <stdarg.h>
|
#include <stdarg.h>
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
|
#include <stdint.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
|
||||||
|
#include "types.h"
|
||||||
|
|
||||||
double get_time() {
|
double get_time() {
|
||||||
struct timeval tv;
|
struct timeval tv;
|
||||||
gettimeofday(&tv, NULL);
|
gettimeofday(&tv, NULL);
|
||||||
@@ -105,11 +108,11 @@ void extract_color_channel(const uint32_t rgba, double *red, double *green,
|
|||||||
*alpha = COLOR_SPLIT(rgba, 0);
|
*alpha = COLOR_SPLIT(rgba, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
#define INTERSECT(x, y, w, h, m) \
|
#define INTERSECT(x, y, w, h, m) \
|
||||||
(MAX(0, MIN((x) + (w), (m)->window_x + (m)->window_width) - \
|
(MAX(0, MIN((x) + (w), (m)->monitor_x + (int)(m)->monitor_width) - \
|
||||||
MAX((x), (m)->window_x)) * \
|
MAX((x), (m)->monitor_x)) * \
|
||||||
MAX(0, MIN((y) + (h), (m)->window_y + (m)->window_height) - \
|
MAX(0, MIN((y) + (h), (m)->monitor_y + (int)(m)->monitor_height) - \
|
||||||
MAX((y), (m)->window_y)))
|
MAX((y), (m)->monitor_y)))
|
||||||
monitor_t *rect_to_monitor(monitor_t *monitors, int32_t x, int32_t y,
|
monitor_t *rect_to_monitor(monitor_t *monitors, int32_t x, int32_t y,
|
||||||
uint32_t width, uint32_t height) {
|
uint32_t width, uint32_t height) {
|
||||||
monitor_t *r = monitors;
|
monitor_t *r = monitors;
|
||||||
|
|||||||
@@ -18,11 +18,13 @@ struct event_adapter_t {
|
|||||||
static void convert_event(xcb_generic_event_t *xcb_event,
|
static void convert_event(xcb_generic_event_t *xcb_event,
|
||||||
generic_event_t *event) {
|
generic_event_t *event) {
|
||||||
uint8_t event_type = XCB_EVENT_RESPONSE_TYPE(xcb_event);
|
uint8_t event_type = XCB_EVENT_RESPONSE_TYPE(xcb_event);
|
||||||
const char *label = xcb_event_get_label(event_type);
|
|
||||||
switch (event_type) {
|
switch (event_type) {
|
||||||
case XCB_MAPPING_NOTIFY:
|
case XCB_MAPPING_NOTIFY: {
|
||||||
event->type = EVENT_TYPE_MAPPING_NOTIFY;
|
xcb_mapping_notify_event_t *ev = (xcb_mapping_notify_event_t *)xcb_event;
|
||||||
|
event->mapping_notify.type = EVENT_TYPE_MAPPING_NOTIFY;
|
||||||
|
event->mapping_notify.request = ev->request;
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
case XCB_KEY_PRESS: {
|
case XCB_KEY_PRESS: {
|
||||||
xcb_key_press_event_t *ev = (xcb_key_press_event_t *)xcb_event;
|
xcb_key_press_event_t *ev = (xcb_key_press_event_t *)xcb_event;
|
||||||
int col = ev->state & XCB_MOD_MASK_2 ? 1 : 0;
|
int col = ev->state & XCB_MOD_MASK_2 ? 1 : 0;
|
||||||
@@ -32,9 +34,18 @@ static void convert_event(xcb_generic_event_t *xcb_event,
|
|||||||
event->key_press.modifiers = ev->state;
|
event->key_press.modifiers = ev->state;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case XCB_BUTTON_PRESS:
|
case XCB_BUTTON_PRESS: {
|
||||||
event->type = EVENT_TYPE_BUTTON_PRESS;
|
xcb_button_press_event_t *ev = (xcb_button_press_event_t *)xcb_event;
|
||||||
|
event->button_press.type = EVENT_TYPE_BUTTON_PRESS;
|
||||||
|
event->button_press.window = ev->event;
|
||||||
|
event->button_press.root = ev->root;
|
||||||
|
event->button_press.x = ev->event_x;
|
||||||
|
event->button_press.y = ev->event_y;
|
||||||
|
event->button_press.root_x = ev->root_x;
|
||||||
|
event->button_press.root_y = ev->root_y;
|
||||||
|
event->button_press.time = ev->time;
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
case XCB_CONFIGURE_REQUEST: {
|
case XCB_CONFIGURE_REQUEST: {
|
||||||
xcb_configure_request_event_t *ev =
|
xcb_configure_request_event_t *ev =
|
||||||
(xcb_configure_request_event_t *)xcb_event;
|
(xcb_configure_request_event_t *)xcb_event;
|
||||||
@@ -66,7 +77,8 @@ static void convert_event(xcb_generic_event_t *xcb_event,
|
|||||||
event->unmap_notify.type = EVENT_TYPE_UNMAP_NOTIFY;
|
event->unmap_notify.type = EVENT_TYPE_UNMAP_NOTIFY;
|
||||||
event->unmap_notify.window = ev->window;
|
event->unmap_notify.window = ev->window;
|
||||||
event->unmap_notify.send_event = XCB_EVENT_SENT(ev);
|
event->unmap_notify.send_event = XCB_EVENT_SENT(ev);
|
||||||
} break;
|
break;
|
||||||
|
}
|
||||||
case XCB_DESTROY_NOTIFY: {
|
case XCB_DESTROY_NOTIFY: {
|
||||||
xcb_destroy_notify_event_t *ev = (xcb_destroy_notify_event_t *)xcb_event;
|
xcb_destroy_notify_event_t *ev = (xcb_destroy_notify_event_t *)xcb_event;
|
||||||
event->destroy_notify.type = EVENT_TYPE_DESTROY_NOTIFY;
|
event->destroy_notify.type = EVENT_TYPE_DESTROY_NOTIFY;
|
||||||
@@ -89,6 +101,7 @@ static void convert_event(xcb_generic_event_t *xcb_event,
|
|||||||
event->motiion_notify.window = ev->event;
|
event->motiion_notify.window = ev->event;
|
||||||
event->motiion_notify.x = ev->event_x;
|
event->motiion_notify.x = ev->event_x;
|
||||||
event->motiion_notify.y = ev->event_y;
|
event->motiion_notify.y = ev->event_y;
|
||||||
|
event->motiion_notify.time = ev->time;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case XCB_ENTER_NOTIFY: {
|
case XCB_ENTER_NOTIFY: {
|
||||||
@@ -126,7 +139,8 @@ static void convert_event(xcb_generic_event_t *xcb_event,
|
|||||||
event->type = -1;
|
event->type = -1;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (event->type != EVENT_TYPE_MOTION_NOTIFY) {
|
if (event->type) {
|
||||||
|
const char *label = xcb_event_get_label(event_type);
|
||||||
logger("========================== %s ==========================\n", label);
|
logger("========================== %s ==========================\n", label);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -43,7 +43,8 @@ void get_window_class_instance(xcb_window_t window,
|
|||||||
xcb_icccm_get_wm_class_reply_t prop;
|
xcb_icccm_get_wm_class_reply_t prop;
|
||||||
const char *class = NULL;
|
const char *class = NULL;
|
||||||
const char *instance = NULL;
|
const char *instance = NULL;
|
||||||
if (xcb_icccm_get_wm_class_reply(conn, cookie, &prop, NULL)) {
|
bool has_reply = xcb_icccm_get_wm_class_reply(conn, cookie, &prop, NULL);
|
||||||
|
if (has_reply) {
|
||||||
class = prop.class_name;
|
class = prop.class_name;
|
||||||
instance = prop.instance_name;
|
instance = prop.instance_name;
|
||||||
} else {
|
} else {
|
||||||
@@ -54,7 +55,7 @@ void get_window_class_instance(xcb_window_t window,
|
|||||||
strncpy(class_instance->class, class, LENGTH(class_instance->class));
|
strncpy(class_instance->class, class, LENGTH(class_instance->class));
|
||||||
strncpy(class_instance->instance, instance, LENGTH(class_instance->instance));
|
strncpy(class_instance->instance, instance, LENGTH(class_instance->instance));
|
||||||
|
|
||||||
xcb_icccm_get_wm_class_reply_wipe(&prop);
|
if (has_reply) xcb_icccm_get_wm_class_reply_wipe(&prop);
|
||||||
}
|
}
|
||||||
|
|
||||||
static visual_t *find_alpha_visual() {
|
static visual_t *find_alpha_visual() {
|
||||||
@@ -190,6 +191,22 @@ void configure_window(xcb_window_t window, int32_t x, int32_t y, uint32_t width,
|
|||||||
xcb_aux_configure_window(conn, window, config_mask, &window_config);
|
xcb_aux_configure_window(conn, window, config_mask, &window_config);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void move_window(xcb_window_t window, int32_t x, int32_t y) {
|
||||||
|
xcb_config_window_t mask = XCB_CONFIG_WINDOW_X | XCB_CONFIG_WINDOW_Y;
|
||||||
|
xcb_params_configure_window_t position = {.x = x, .y = y};
|
||||||
|
xcb_aux_configure_window(conn, window, mask, &position);
|
||||||
|
}
|
||||||
|
|
||||||
|
void resize_window(xcb_window_t window, uint32_t width, uint32_t height) {
|
||||||
|
xcb_config_window_t config_mask =
|
||||||
|
XCB_CONFIG_WINDOW_WIDTH | XCB_CONFIG_WINDOW_HEIGHT;
|
||||||
|
xcb_params_configure_window_t window_config = {
|
||||||
|
.width = width,
|
||||||
|
.height = height,
|
||||||
|
};
|
||||||
|
xcb_aux_configure_window(conn, window, config_mask, &window_config);
|
||||||
|
}
|
||||||
|
|
||||||
void change_window_cursor(xcb_window_t window, cursor_t cursor) {
|
void change_window_cursor(xcb_window_t window, cursor_t cursor) {
|
||||||
xcb_change_window_attributes_value_list_t value_list = {
|
xcb_change_window_attributes_value_list_t value_list = {
|
||||||
.cursor = get_xcb_cursor(cursor),
|
.cursor = get_xcb_cursor(cursor),
|
||||||
@@ -271,11 +288,10 @@ char *get_window_name(xcb_window_t window) {
|
|||||||
|
|
||||||
void set_window_event_mask(xcb_window_t window, bool clear) {
|
void set_window_event_mask(xcb_window_t window, bool clear) {
|
||||||
xcb_cw_t change_mask = XCB_CW_EVENT_MASK;
|
xcb_cw_t change_mask = XCB_CW_EVENT_MASK;
|
||||||
uint32_t event_mask = clear ? XCB_EVENT_MASK_NO_EVENT
|
uint32_t init_event_mask =
|
||||||
: XCB_EVENT_MASK_ENTER_WINDOW |
|
XCB_EVENT_MASK_ENTER_WINDOW | XCB_EVENT_MASK_FOCUS_CHANGE |
|
||||||
XCB_EVENT_MASK_FOCUS_CHANGE |
|
XCB_EVENT_MASK_PROPERTY_CHANGE | XCB_EVENT_MASK_SUBSTRUCTURE_NOTIFY;
|
||||||
XCB_EVENT_MASK_PROPERTY_CHANGE |
|
uint32_t event_mask = clear ? XCB_EVENT_MASK_NO_EVENT : init_event_mask;
|
||||||
XCB_EVENT_MASK_SUBSTRUCTURE_NOTIFY;
|
|
||||||
xcb_params_cw_t params = {.event_mask = event_mask};
|
xcb_params_cw_t params = {.event_mask = event_mask};
|
||||||
xcb_aux_change_window_attributes(conn, window, change_mask, ¶ms);
|
xcb_aux_change_window_attributes(conn, window, change_mask, ¶ms);
|
||||||
}
|
}
|
||||||
@@ -285,6 +301,12 @@ void change_window_border_color(xcb_window_t window, uint32_t argb) {
|
|||||||
xcb_aux_change_window_attributes(conn, window, XCB_CW_BORDER_PIXEL, ¶ms);
|
xcb_aux_change_window_attributes(conn, window, XCB_CW_BORDER_PIXEL, ¶ms);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void change_window_border_width(xcb_window_t window, uint32_t border_width) {
|
||||||
|
xcb_config_window_t config_mask = XCB_CONFIG_WINDOW_BORDER_WIDTH;
|
||||||
|
xcb_params_configure_window_t window_config = {.border_width = border_width};
|
||||||
|
xcb_aux_configure_window(conn, window, config_mask, &window_config);
|
||||||
|
}
|
||||||
|
|
||||||
void focus_window(xcb_window_t window) {
|
void focus_window(xcb_window_t window) {
|
||||||
xcb_window_t root = screen->root;
|
xcb_window_t root = screen->root;
|
||||||
if (window == WINDOW_NONE) {
|
if (window == WINDOW_NONE) {
|
||||||
@@ -316,7 +338,6 @@ void place_window_above_sibling(xcb_window_t window, xcb_window_t sibling) {
|
|||||||
xcb_aux_configure_window(conn, window, mask, ¶ms);
|
xcb_aux_configure_window(conn, window, mask, ¶ms);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void place_window_below_sibling(xcb_window_t window, xcb_window_t sibling) {
|
void place_window_below_sibling(xcb_window_t window, xcb_window_t sibling) {
|
||||||
uint16_t mask = XCB_CONFIG_WINDOW_STACK_MODE | XCB_CONFIG_WINDOW_SIBLING;
|
uint16_t mask = XCB_CONFIG_WINDOW_STACK_MODE | XCB_CONFIG_WINDOW_SIBLING;
|
||||||
xcb_params_configure_window_t params = {
|
xcb_params_configure_window_t params = {
|
||||||
@@ -340,3 +361,15 @@ void set_window_fullscreen_state(xcb_window_t window, bool fullscreen) {
|
|||||||
xcb_change_property(conn, XCB_PROP_MODE_REPLACE, window, atom,
|
xcb_change_property(conn, XCB_PROP_MODE_REPLACE, window, atom,
|
||||||
XCB_ATOM_WINDOW, 32, data_len, &atom);
|
XCB_ATOM_WINDOW, 32, data_len, &atom);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ungrab_any_button(xcb_window_t window) {
|
||||||
|
xcb_ungrab_button(conn, XCB_BUTTON_INDEX_ANY, window, XCB_MOD_MASK_ANY);
|
||||||
|
}
|
||||||
|
|
||||||
|
void grab_button(xcb_window_t window, uint8_t button, uint16_t modifiers) {
|
||||||
|
uint8_t owner_events = false; /* 事件仅发送给抓取者,不发送给其他窗口 */
|
||||||
|
uint16_t event_mask =
|
||||||
|
XCB_EVENT_MASK_BUTTON_PRESS | XCB_EVENT_MASK_BUTTON_RELEASE;
|
||||||
|
xcb_grab_button(conn, owner_events, window, event_mask, XCB_GRAB_MODE_SYNC,
|
||||||
|
XCB_GRAB_MODE_ASYNC, XCB_NONE, XCB_NONE, button, modifiers);
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <xcb/xcb.h>
|
#include <xcb/xcb.h>
|
||||||
#include <xcb/xcb_ewmh.h>
|
#include <xcb/xcb_ewmh.h>
|
||||||
@@ -25,11 +26,13 @@ static atom_map_t atom_map[] = {
|
|||||||
{.atom = atom_wm_protocols, .name = "WM_PROTOCOLS"},
|
{.atom = atom_wm_protocols, .name = "WM_PROTOCOLS"},
|
||||||
{.atom = atom_wm_delete, .name = "WM_DELETE_WINDOW"},
|
{.atom = atom_wm_delete, .name = "WM_DELETE_WINDOW"},
|
||||||
{.atom = atom_wm_take_focus, .name = "WM_TAKE_FOCUS"},
|
{.atom = atom_wm_take_focus, .name = "WM_TAKE_FOCUS"},
|
||||||
|
{.atom = atom_net_active_window, .name = "_NET_ACTIVE_WINDOW"},
|
||||||
{.atom = atom_net_wm_name, .name = "_NET_WM_NAME"},
|
{.atom = atom_net_wm_name, .name = "_NET_WM_NAME"},
|
||||||
{.atom = atom_net_wm_state, .name = "_NET_WM_STATE"},
|
{.atom = atom_net_wm_state, .name = "_NET_WM_STATE"},
|
||||||
{.atom = atom_net_wm_fullscreen, .name = "_NET_WM_STATE_FULLSCREEN"},
|
{.atom = atom_net_wm_fullscreen, .name = "_NET_WM_STATE_FULLSCREEN"},
|
||||||
{.atom = atom__xrootpmap_id, .name = "_XROOTPMAP_ID"},
|
{.atom = atom__xrootpmap_id, .name = "_XROOTPMAP_ID"},
|
||||||
{.atom = atom_esetroot_pmap_id, .name = "ESETROOT_PMAP_ID"},
|
{.atom = atom_esetroot_pmap_id, .name = "ESETROOT_PMAP_ID"},
|
||||||
|
{.atom = atom_zswm_client_info, .name = "ZSWM_CLIENT_INFO"},
|
||||||
};
|
};
|
||||||
|
|
||||||
void init_icccm_extension(void) {
|
void init_icccm_extension(void) {
|
||||||
@@ -84,6 +87,7 @@ void init_ewmh_extension(void) {
|
|||||||
xcb_change_property(conn, mode, screen->root, ewmh->_NET_SUPPORTED, atom, 32,
|
xcb_change_property(conn, mode, screen->root, ewmh->_NET_SUPPORTED, atom, 32,
|
||||||
LENGTH(supported), supported);
|
LENGTH(supported), supported);
|
||||||
xcb_delete_property(conn, screen->root, ewmh->_NET_CLIENT_LIST);
|
xcb_delete_property(conn, screen->root, ewmh->_NET_CLIENT_LIST);
|
||||||
|
xcb_delete_property(conn, screen->root, get_xcb_atom(atom_zswm_client_info));
|
||||||
}
|
}
|
||||||
|
|
||||||
void clean_ewmh_extension(void) {
|
void clean_ewmh_extension(void) {
|
||||||
@@ -107,6 +111,23 @@ xcb_atom_t get_xcb_atom(atom_t atom) {
|
|||||||
return atom_map[atom].xcb_atom;
|
return atom_map[atom].xcb_atom;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 获取 xcb atom 名称
|
||||||
|
* @returns xcb atom 对应的名称,使用后记得用 free 释放
|
||||||
|
*/
|
||||||
|
char *get_xcb_atom_name(xcb_atom_t atom) {
|
||||||
|
xcb_get_atom_name_cookie_t cookie = xcb_get_atom_name(conn, atom);
|
||||||
|
xcb_get_atom_name_reply_t *reply =
|
||||||
|
xcb_get_atom_name_reply(conn, cookie, NULL);
|
||||||
|
int length = xcb_get_atom_name_name_length(reply);
|
||||||
|
char *name_buffer = ecalloc(1, length + 1);
|
||||||
|
const char *name = xcb_get_atom_name_name(reply);
|
||||||
|
strncpy(name_buffer, name, length);
|
||||||
|
|
||||||
|
free(reply);
|
||||||
|
return name_buffer;
|
||||||
|
}
|
||||||
|
|
||||||
bool send_event(xcb_window_t window, atom_t atom) {
|
bool send_event(xcb_window_t window, atom_t atom) {
|
||||||
if (atom < 0 || atom >= LENGTH(atom_map)) return false;
|
if (atom < 0 || atom >= LENGTH(atom_map)) return false;
|
||||||
|
|
||||||
@@ -171,3 +192,31 @@ void set_window_list(xcb_window_t *list, uint32_t length) {
|
|||||||
}
|
}
|
||||||
xcb_ewmh_set_client_list(ewmh, screen_nbr, length, list);
|
xcb_ewmh_set_client_list(ewmh, screen_nbr, length, list);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool get_window_tag(xcb_window_t window, client_tag_info_t *client_info) {
|
||||||
|
xcb_get_property_cookie_t cookie =
|
||||||
|
xcb_get_property(conn, 0, window, get_xcb_atom(atom_zswm_client_info),
|
||||||
|
XCB_ATOM_CARDINAL, 0, 2);
|
||||||
|
xcb_get_property_reply_t *reply = xcb_get_property_reply(conn, cookie, NULL);
|
||||||
|
int length = xcb_get_property_value_length(reply);
|
||||||
|
bool result = length >= 2;
|
||||||
|
if (result) {
|
||||||
|
uint32_t *data = xcb_get_property_value(reply);
|
||||||
|
client_info->tags = data[0];
|
||||||
|
client_info->monitor_index = data[1];
|
||||||
|
}
|
||||||
|
|
||||||
|
free(reply);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
void set_window_tag(xcb_window_t window, client_tag_info_t *client_info) {
|
||||||
|
xcb_change_property(conn, XCB_PROP_MODE_REPLACE, window,
|
||||||
|
get_xcb_atom(atom_zswm_client_info), XCB_ATOM_CARDINAL,
|
||||||
|
32, 2, client_info);
|
||||||
|
}
|
||||||
|
|
||||||
|
void remove_window_tag(xcb_window_t window) {
|
||||||
|
xcb_delete_property(conn, window, get_xcb_atom(atom_zswm_client_info));
|
||||||
|
}
|
||||||
|
|||||||
@@ -10,6 +10,7 @@
|
|||||||
#include <xcb/xcb_ewmh.h>
|
#include <xcb/xcb_ewmh.h>
|
||||||
#include <xcb/xcb_icccm.h>
|
#include <xcb/xcb_icccm.h>
|
||||||
#include <xcb/xcb_image.h>
|
#include <xcb/xcb_image.h>
|
||||||
|
#include <xcb/xcb_keysyms.h>
|
||||||
#include <xcb/xinerama.h>
|
#include <xcb/xinerama.h>
|
||||||
#include <xcb/xproto.h>
|
#include <xcb/xproto.h>
|
||||||
|
|
||||||
@@ -288,9 +289,11 @@ void clean_xcb(void) {
|
|||||||
focus_window(XCB_WINDOW_NONE);
|
focus_window(XCB_WINDOW_NONE);
|
||||||
clean_xcb_cursor();
|
clean_xcb_cursor();
|
||||||
clean_ewmh_extension();
|
clean_ewmh_extension();
|
||||||
|
if (key_symbols) xcb_key_symbols_free(key_symbols);
|
||||||
xcb_disconnect(conn);
|
xcb_disconnect(conn);
|
||||||
conn = NULL;
|
conn = NULL;
|
||||||
screen = NULL;
|
screen = NULL;
|
||||||
|
key_symbols = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
void grab_keybindings(const keybinding_t *keys, const size_t length) {
|
void grab_keybindings(const keybinding_t *keys, const size_t length) {
|
||||||
@@ -310,6 +313,7 @@ void grab_keybindings(const keybinding_t *keys, const size_t length) {
|
|||||||
keycode = xcb_key_symbols_get_keycode(key_symbols, keysym);
|
keycode = xcb_key_symbols_get_keycode(key_symbols, keysym);
|
||||||
modifiers = keys[i].modifiers;
|
modifiers = keys[i].modifiers;
|
||||||
cookie = xcb_grab_key(conn, key, root, modifiers, *keycode, mode, mode);
|
cookie = xcb_grab_key(conn, key, root, modifiers, *keycode, mode, mode);
|
||||||
|
free(keycode);
|
||||||
error = xcb_request_check(conn, cookie);
|
error = xcb_request_check(conn, cookie);
|
||||||
if (error) {
|
if (error) {
|
||||||
clean_xcb();
|
clean_xcb();
|
||||||
@@ -333,6 +337,11 @@ bool get_pointer_position(int32_t *x, int32_t *y) {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void set_pointer_position(int32_t x, int32_t y) {
|
||||||
|
xcb_warp_pointer(conn, XCB_WINDOW_NONE, screen->root, 0, 0, 1, 1, x, y);
|
||||||
|
xcb_flush(conn);
|
||||||
|
}
|
||||||
|
|
||||||
bool xcb_check_mask_event(xcb_event_mask_t event_mask) {
|
bool xcb_check_mask_event(xcb_event_mask_t event_mask) {
|
||||||
xcb_generic_event_t *event = xcb_poll_for_event(conn);
|
xcb_generic_event_t *event = xcb_poll_for_event(conn);
|
||||||
if (!event) return false;
|
if (!event) return false;
|
||||||
|
|||||||
Reference in New Issue
Block a user