diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 8806281..97280a8 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -35,7 +35,8 @@ pkg_check_modules(XCB REQUIRED xcb-xinerama xcb-xrm ) -pkg_check_modules(RENDERER REQUIRED imlib2) +pkg_check_modules(RENDERER REQUIRED cairo pango pangocairo imlib2) +pkg_check_modules(CAIRO_XCB REQUIRED cairo-xcb) add_subdirectory(xcb) add_subdirectory(renderer) @@ -48,6 +49,7 @@ configure_file( target_include_directories(${APP_NAME} SYSTEM PRIVATE ${GLIB_INCLUDE_DIRS} + PRIVATE ${CAIRO_XCB_INCLUDE_DIRS} ) target_include_directories(${APP_NAME} PRIVATE "${SOURCE_DIR}/include" @@ -57,6 +59,7 @@ target_include_directories(${APP_NAME} target_link_libraries(${APP_NAME} PRIVATE ${GLIB_LIBRARIES} + PRIVATE ${CAIRO_XCB_LIBRARIES} PRIVATE ${LIB_XCB_NAME} PRIVATE ${LIB_RENDERER_NAME} ) diff --git a/src/config.h b/src/config.h index 409f57f..ade6971 100644 --- a/src/config.h +++ b/src/config.h @@ -68,3 +68,10 @@ const char *const active_client_name_color = "#005577"; const char *const status_color = "#bbbbbb"; const char *const border_color = "#444444"; const char *const active_border_color = "#005577"; + +const char *const default_font_family = "sans-serif, monospace"; +const uint32_t default_font_size = 10; +const uint32_t dpi_fallback = 96; + +const uint32_t bar_y_padding = 1; +const uint32_t tag_x_padding = 10; diff --git a/src/include/renderer.h b/src/include/renderer.h index 920a3a7..cd90d93 100644 --- a/src/include/renderer.h +++ b/src/include/renderer.h @@ -8,3 +8,6 @@ uint32_t *generate_wallpaper(const wallpaper_config_t *wallpaper_config, const monitor_t *monitors, const uint32_t index, const uint32_t screen_width, const uint32_t screen_height); + +uint32_t get_font_height(const char *font_family, const uint32_t font_size, + const uint32_t dpi); diff --git a/src/include/xcb.h b/src/include/xcb.h index c2aea36..7b13cb4 100644 --- a/src/include/xcb.h +++ b/src/include/xcb.h @@ -1,5 +1,6 @@ #pragma once +#include #include #include #include @@ -7,6 +8,27 @@ bool has_other_wm_running(void); int get_xcb_connection_fd(void); +typedef enum cursor_t { + cursor_normal = XC_left_ptr, + cursor_resize = XC_bottom_right_corner, + cursor_move = XC_fleur, +} cursor_t; +void change_window_cursor(xcb_window_t window, cursor_t cursor); +void set_root_cursor(cursor_t cursor); + +typedef struct bar_cairo_params_t { + xcb_connection_t *conn; + xcb_window_t window; + xcb_visualtype_t *visual; +} bar_cairo_params_t; +/** + * @brief 创建每个屏幕的 bar 窗口 + * @returns 返回创建 cairo surface 需要的参数,使用完记得使用 free 释放内存 + */ +bar_cairo_params_t *create_bar_window(int16_t x, int16_t y, uint16_t width, + uint16_t height, uint32_t background_argb, + cursor_t cursor); + typedef struct monitor_rect_t monitor_rect_t; struct monitor_rect_t { int32_t x; diff --git a/src/main.c b/src/main.c index 49039ac..2b5471d 100644 --- a/src/main.c +++ b/src/main.c @@ -1,3 +1,4 @@ +#include #include #include #include @@ -23,6 +24,8 @@ static wallpaper_config_t *parse_wallpaper_config(void); static void free_wallpaper_config(wallpaper_config_t *wallpaper_config); static void init_color_set(void); static void parse_color(const char *hex, color_t *color); +static void init_monitor_bar(void); +static void get_xresource_config(void); static void init_xcb_event_loop(void); static gboolean handle_xcb_event(GIOChannel *channel, GIOCondition condition, gpointer userdata); @@ -32,6 +35,12 @@ static monitor_t *monitors = NULL; static wallpaper_config_t *wallpaper_config = NULL; static color_set_t *color_set = NULL; static GMainLoop *loop = NULL; +static char *font_family = NULL; +static uint32_t font_size = default_font_size; +static uint32_t dpi = dpi_fallback; +static uint32_t bar_y_pad = bar_y_padding; +static uint32_t tag_x_pad = tag_x_padding; +static uint32_t bar_height = 0; /* 调试函数,开发完成后删除 */ static void print_monitor_list_info(monitor_t *monitor_list) { @@ -278,6 +287,39 @@ void parse_color(const char *hex, color_t *color) { &color->alpha); } +void init_monitor_bar(void) { + get_xresource_config(); + uint32_t font_height = get_font_height(font_family, font_size, dpi); + bar_height = font_height + bar_y_pad * 2; + for (monitor_t *m = monitors; m; m = m->next) { + bar_cairo_params_t *p = + create_bar_window(m->monitor_x, m->monitor_y, m->monitor_width, + bar_height, color_set->bar_bg.argb, cursor_normal); + m->window_x = m->monitor_x; + m->window_width = m->monitor_width; + m->window_y = m->monitor_y + bar_height; + m->window_height = m->monitor_height - bar_height; + m->surface = cairo_xcb_surface_create(p->conn, p->window, p->visual, + m->monitor_width, bar_height); + m->cr = cairo_create(m->surface); + m->bar_window = p->window; + free(p); + } + printf("font height: %u, bar height: %u\n", font_height, bar_height); +} + +void get_xresource_config(void) { + get_xresource_uint32("Xft.dpi", &dpi); + get_xresource_string("zswm.font_family", &font_family, default_font_family); + get_xresource_uint32("zswm.font_size", &font_size); + get_xresource_uint32("zswm.bar_y_padding", &bar_y_pad); + get_xresource_uint32("zswm.tag_x_padding", &tag_x_pad); + + printf("dpi: %u, font family: %s, font size: %u\n", dpi, font_family, + font_size); + clean_xresource(); +} + void init_xcb_event_loop(void) { GIOChannel *channel = g_io_channel_unix_new(get_xcb_connection_fd()); g_io_channel_set_encoding(channel, NULL, NULL); @@ -306,6 +348,9 @@ void cleanup(void) { free(color_set); color_set = NULL; + free(font_family); + font_family = NULL; + monitor_t *m = monitors; client_t *c = m->clients; while (m) { @@ -329,6 +374,8 @@ int main(int argc, char *argv[]) { init_monitors(); init_color_set(); + init_monitor_bar(); + set_root_cursor(cursor_normal); init_wallpaper(); window_list_t *window_list = scan_window_list(); diff --git a/src/renderer/CMakeLists.txt b/src/renderer/CMakeLists.txt index c17b181..1ab1c63 100644 --- a/src/renderer/CMakeLists.txt +++ b/src/renderer/CMakeLists.txt @@ -1,7 +1,7 @@ set(LIB_RENDERER "zswm-renderer") set(LIB_RENDERER_NAME ${LIB_RENDERER} PARENT_SCOPE) -add_library(${LIB_RENDERER} STATIC image.c) +add_library(${LIB_RENDERER} STATIC image.c text.c) target_include_directories(${LIB_RENDERER} SYSTEM PRIVATE ${RENDERER_INCLUDE_DIRS} ) diff --git a/src/renderer/text.c b/src/renderer/text.c new file mode 100644 index 0000000..413b023 --- /dev/null +++ b/src/renderer/text.c @@ -0,0 +1,35 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "renderer.h" + +static PangoContext *context = NULL; + +uint32_t get_font_height(const char *font_family, const uint32_t font_size, + const uint32_t dpi) { + PangoFontMap *fontmap = pango_cairo_font_map_new(); + context = pango_font_map_create_context(fontmap); + pango_cairo_context_set_resolution(context, dpi); + PangoLanguage *lang = pango_context_get_language(context); + PangoAttrList *attr_list = pango_attr_list_new(); + + PangoFontDescription *desc = pango_font_description_from_string(font_family); + pango_font_description_set_size(desc, font_size * PANGO_SCALE); + PangoAttribute *attr = pango_attr_font_desc_new(desc); + pango_attr_list_insert(attr_list, attr); + + PangoFontMetrics *metrics = pango_context_get_metrics(context, desc, lang); + int height = PANGO_PIXELS(pango_font_metrics_get_height(metrics)); + int ascent = PANGO_PIXELS(pango_font_metrics_get_ascent(metrics)); + int descent = PANGO_PIXELS(pango_font_metrics_get_descent(metrics)); + printf("height: %d, ad: %d\n", height, ascent + descent); + + return MIN(height, ascent + descent); +} diff --git a/src/xcb/CMakeLists.txt b/src/xcb/CMakeLists.txt index ab01bf4..5365586 100644 --- a/src/xcb/CMakeLists.txt +++ b/src/xcb/CMakeLists.txt @@ -1,7 +1,7 @@ set(LIB_XCB "zswm-xcb") set(LIB_XCB_NAME ${LIB_XCB} PARENT_SCOPE) -add_library(${LIB_XCB} STATIC xcb.c window.c res.c) +add_library(${LIB_XCB} STATIC xcb.c window.c res.c cursor.c) target_include_directories(${LIB_XCB} SYSTEM PRIVATE ${XCB_INCLUDE_DIRS} diff --git a/src/xcb/cursor.c b/src/xcb/cursor.c new file mode 100644 index 0000000..f6b8116 --- /dev/null +++ b/src/xcb/cursor.c @@ -0,0 +1,116 @@ +#include +#include + +#include "utils.h" +#include "xcb-private.h" +#include "xcb.h" + +static const char *const xcursor_font[] = { + [XC_X_cursor] = "X_cursor", + [XC_arrow] = "arrow", + [XC_based_arrow_down] = "based_arrow_down", + [XC_based_arrow_up] = "based_arrow_up", + [XC_boat] = "boat", + [XC_bogosity] = "bogosity", + [XC_bottom_left_corner] = "bottom_left_corner", + [XC_bottom_right_corner] = "bottom_right_corner", + [XC_bottom_side] = "bottom_side", + [XC_bottom_tee] = "bottom_tee", + [XC_box_spiral] = "box_spiral", + [XC_center_ptr] = "center_ptr", + [XC_circle] = "circle", + [XC_clock] = "clock", + [XC_coffee_mug] = "coffee_mug", + [XC_cross] = "cross", + [XC_cross_reverse] = "cross_reverse", + [XC_crosshair] = "crosshair", + [XC_diamond_cross] = "diamond_cross", + [XC_dot] = "dot", + [XC_dotbox] = "dotbox", + [XC_double_arrow] = "double_arrow", + [XC_draft_large] = "draft_large", + [XC_draft_small] = "draft_small", + [XC_draped_box] = "draped_box", + [XC_exchange] = "exchange", + [XC_fleur] = "fleur", + [XC_gobbler] = "gobbler", + [XC_gumby] = "gumby", + [XC_hand1] = "hand1", + [XC_hand2] = "hand2", + [XC_heart] = "heart", + [XC_icon] = "icon", + [XC_iron_cross] = "iron_cross", + [XC_left_ptr] = "left_ptr", + [XC_left_side] = "left_side", + [XC_left_tee] = "left_tee", + [XC_leftbutton] = "leftbutton", + [XC_ll_angle] = "ll_angle", + [XC_lr_angle] = "lr_angle", + [XC_man] = "man", + [XC_middlebutton] = "middlebutton", + [XC_mouse] = "mouse", + [XC_pencil] = "pencil", + [XC_pirate] = "pirate", + [XC_plus] = "plus", + [XC_question_arrow] = "question_arrow", + [XC_right_ptr] = "right_ptr", + [XC_right_side] = "right_side", + [XC_right_tee] = "right_tee", + [XC_rightbutton] = "rightbutton", + [XC_rtl_logo] = "rtl_logo", + [XC_sailboat] = "sailboat", + [XC_sb_down_arrow] = "sb_down_arrow", + [XC_sb_h_double_arrow] = "sb_h_double_arrow", + [XC_sb_left_arrow] = "sb_left_arrow", + [XC_sb_right_arrow] = "sb_right_arrow", + [XC_sb_up_arrow] = "sb_up_arrow", + [XC_sb_v_double_arrow] = "sb_v_double_arrow", + [XC_shuttle] = "shuttle", + [XC_sizing] = "sizing", + [XC_spider] = "spider", + [XC_spraycan] = "spraycan", + [XC_star] = "star", + [XC_target] = "target", + [XC_tcross] = "tcross", + [XC_top_left_arrow] = "top_left_arrow", + [XC_top_left_corner] = "top_left_corner", + [XC_top_right_corner] = "top_right_corner", + [XC_top_side] = "top_side", + [XC_top_tee] = "top_tee", + [XC_trek] = "trek", + [XC_ul_angle] = "ul_angle", + [XC_umbrella] = "umbrella", + [XC_ur_angle] = "ur_angle", + [XC_watch] = "watch", + [XC_xterm] = "xterm", +}; + +static xcb_cursor_context_t *get_xcb_cursor_context(void) { + static xcb_cursor_context_t *ctx = NULL; + if (ctx) return ctx; + + if (xcb_cursor_context_new(conn, screen, &ctx) < 0) { + return NULL; + } + + return ctx; +} + +static const char *xcursor_font_tostr(uint16_t c) { + if (c < LENGTH(xcursor_font)) { + return xcursor_font[c]; + } + return NULL; +} + +xcb_cursor_t get_xcb_cursor(cursor_t cursor) { + xcb_cursor_context_t *ctx = get_xcb_cursor_context(); + if (!ctx) die("cannot get cursor:"); + + static xcb_cursor_t cursor_list[LENGTH(xcursor_font)]; + if (!cursor_list[cursor]) { + const char *name = xcursor_font_tostr(cursor); + cursor_list[cursor] = xcb_cursor_load_cursor(ctx, name); + } + return cursor_list[cursor]; +} diff --git a/src/xcb/window.c b/src/xcb/window.c index c3167df..d39d41e 100644 --- a/src/xcb/window.c +++ b/src/xcb/window.c @@ -1,10 +1,17 @@ #include "window.h" +#include +#include #include #include +#include #include +#include +#include "app.h" +#include "utils.h" #include "xcb-private.h" +#include "xcb.h" void set_window_class_instance(xcb_window_t window, const char *class, const char *instance) { @@ -24,3 +31,136 @@ void set_window_class_instance(xcb_window_t window, const char *class, xcb_icccm_set_wm_class(conn, window, length, str); free(str); } + +typedef struct visual_t { + uint8_t depth; + xcb_visualtype_t *visual; +} visual_t; + +static visual_t *find_alpha_visual() { + xcb_depth_iterator_t depth_iter; + depth_iter = xcb_screen_allowed_depths_iterator(screen); + for (; depth_iter.rem; xcb_depth_next(&depth_iter)) { + if (depth_iter.data->depth != 32) continue; + + xcb_visualtype_iterator_t visual_iter; + visual_iter = xcb_depth_visuals_iterator(depth_iter.data); + for (; visual_iter.rem; xcb_visualtype_next(&visual_iter)) { + if (visual_iter.data->_class == XCB_VISUAL_CLASS_TRUE_COLOR) { + visual_t *r = ecalloc(1, sizeof(visual_t)); + r->visual = visual_iter.data; + r->depth = depth_iter.data->depth; + return r; + } + } + } + + return NULL; +} +static visual_t *find_root_visual() { + xcb_depth_iterator_t depth_iter; + depth_iter = xcb_screen_allowed_depths_iterator(screen); + xcb_visualid_t vid = screen->root_visual; + for (; depth_iter.rem; xcb_depth_next(&depth_iter)) { + xcb_visualtype_iterator_t visual_iter; + visual_iter = xcb_depth_visuals_iterator(depth_iter.data); + for (; visual_iter.rem; xcb_visualtype_next(&visual_iter)) { + if (visual_iter.data->visual_id == vid) { + visual_t *r = ecalloc(1, sizeof(visual_t)); + r->visual = visual_iter.data; + r->depth = depth_iter.data->depth; + return r; + } + } + } + + return NULL; +} + +/** + * @brief 获取 xcb 的 visual 及其对应的 depth + * @param prefer_alpha 支持 alpha 的 visual 优先 + * @returns 返回的信息使用后记得使用 free 释放 + */ +visual_t *get_xcb_visual(bool prefer_alpha) { + if (!prefer_alpha) return find_root_visual(); + + visual_t *visual = find_alpha_visual(); + if (visual == NULL) visual = find_root_visual(); + + return visual; +} + +typedef struct bar_window_param_t { + uint8_t depth; + xcb_colormap_t colormap; + xcb_visualid_t visual_id; + xcb_visualtype_t *visual; +} bar_window_param_t; +static bar_window_param_t *prepare_bar_window_params(void) { + static xcb_colormap_t colormap = XCB_NONE; + visual_t *visual = get_xcb_visual(true); + xcb_visualid_t visual_id = visual->visual->visual_id; + if (colormap == XCB_NONE) { + colormap = xcb_generate_id(conn); + uint8_t alloc = XCB_COLORMAP_ALLOC_NONE; + xcb_create_colormap(conn, alloc, colormap, screen->root, visual_id); + } + + bar_window_param_t *params = ecalloc(1, sizeof(bar_window_param_t)); + params->depth = visual->depth; + params->visual = visual->visual; + params->visual_id = visual_id; + params->colormap = colormap; + free(visual); + return params; +} + +bar_cairo_params_t *create_bar_window(int16_t x, int16_t y, uint16_t width, + uint16_t height, uint32_t background_argb, + cursor_t cursor) { + bar_window_param_t *params = prepare_bar_window_params(); + xcb_visualtype_t *visual = params->visual; + xcb_window_t window = xcb_generate_id(conn); + uint32_t value_mask = XCB_CW_OVERRIDE_REDIRECT | XCB_CW_BACK_PIXEL | + XCB_CW_BORDER_PIXEL | XCB_CW_EVENT_MASK | + XCB_CW_CURSOR | XCB_CW_COLORMAP; + const xcb_create_window_value_list_t value_list = { + .override_redirect = true, + .background_pixel = background_argb, + .border_pixel = 0, + .event_mask = XCB_EVENT_MASK_BUTTON_PRESS | XCB_EVENT_MASK_EXPOSURE, + .cursor = get_xcb_cursor(cursor), + .colormap = params->colormap, + }; + xcb_void_cookie_t cookie; + cookie = xcb_create_window_aux_checked( + conn, params->depth, window, screen->root, x, y, width, height, 0, + XCB_WINDOW_CLASS_INPUT_OUTPUT, params->visual_id, value_mask, &value_list); + free(params); + if (xcb_request_check(conn, cookie)) die("create bar window:"); + + cookie = xcb_map_window(conn, window); + if (xcb_request_check(conn, cookie)) die("map bar window:"); + + set_window_class_instance(window, APP_NAME, APP_NAME); + xcb_flush(conn); + + bar_cairo_params_t *p = ecalloc(1, sizeof(bar_cairo_params_t)); + p->conn = conn; + p->visual = visual; + p->window = window; + return p; +} + +void change_window_cursor(xcb_window_t window, cursor_t cursor) { + xcb_change_window_attributes_value_list_t value_list = { + .cursor = get_xcb_cursor(cursor), + }; + xcb_change_window_attributes_aux(conn, window, XCB_CW_CURSOR, &value_list); + xcb_flush(conn); +} + +void set_root_cursor(cursor_t cursor) { + change_window_cursor(screen->root, cursor); +} diff --git a/src/xcb/xcb-private.h b/src/xcb/xcb-private.h index e8e58f7..5b0e2e1 100644 --- a/src/xcb/xcb-private.h +++ b/src/xcb/xcb-private.h @@ -1,6 +1,8 @@ #pragma once -#include +#include "xcb.h" extern xcb_connection_t *conn; extern xcb_screen_t *screen; + +xcb_cursor_t get_xcb_cursor(cursor_t cursor);