Compare commits

...

1 Commits

Author SHA1 Message Date
16371ed7b1 绘制 tag 标签 2025-11-14 12:00:10 +08:00
15 changed files with 642 additions and 9 deletions

View File

@@ -27,6 +27,9 @@ add_executable(${APP_NAME}
monitor.c
client.c
text.c
color.c
xcursor.c
xwindow.c
)
# use C23
@@ -37,12 +40,16 @@ find_package(PkgConfig REQUIRED)
pkg_check_modules(deps REQUIRED
glib-2.0
x11
xcb
xcb-aux
xcb-cursor
xcb-randr
xcb-xfixes
xcb-xinerama
cairo
cairo-xcb
pango
pangocairo
)

87
src/color.c Normal file
View File

@@ -0,0 +1,87 @@
#include "color.h"
#include "utils.h"
/**
* @brief 将 16 进制的颜色配置转换成 uint32 格式的 rgba数据
*
* @param hex 16 进制的颜色表示,支持的格式有
* RGB/RGBA/RRGGBB/RRGGBBAA/#RGB/#RGBA/#RRGGBB/#RRGGBBAA
* @param rgba 返回数据的指针,解析结果会放到这个指针指向的内存中
* @return 如果 hex 是一个能正常解析的颜色,返回 true ,否则返回 false
*/
static bool hex_color_to_rgba(const char *hex, uint32_t *rgba);
/**
* @brief 将 rgba 通道顺序的颜色转换成 argb 顺序
* @description xcb 库用到的颜色需要是 argb 通道顺序
*/
static uint32_t rgba_to_argb(uint32_t rgba);
/**
* @brief 提取颜色中的各个颜色通道数值
* @param rgba uint32 表示的颜色
* @param red red channel, range 0~1
* @param green green channel, range 0~1
* @param blue blue channel, range 0~1
* @param alpha alpha channel, range 0~1
*/
static void extract_color_channel(const uint32_t rgba, double *red,
double *green, double *blue, double *alpha);
void color_parse(const char *hex, color_t *const color) {
bool success = hex_color_to_rgba(hex, &color->rgba);
if (!success) fatal("invalid hex color: %s", hex);
color->argb = rgba_to_argb(color->rgba);
extract_color_channel(color->rgba, &color->red, &color->green, &color->blue,
&color->alpha);
}
bool hex_color_to_rgba(const char *hex, uint32_t *rgba) {
if (hex[0] == '#') hex++;
size_t len = strlen(hex);
char extended[9] = {0};
uint32_t color = 0x00000000;
if (len == 3 || len == 4) {
extended[0] = extended[1] = hex[0];
extended[2] = extended[3] = hex[1];
extended[4] = extended[5] = hex[2];
if (len == 3) {
extended[6] = extended[7] = 'F';
} else {
extended[6] = extended[7] = hex[3];
}
} else if (len == 6) {
strncpy(extended, hex, len);
extended[6] = extended[7] = 'F';
} else if (len == 8) {
strncpy(extended, hex, len);
} else {
return false;
}
color = strtoul(extended, NULL, 16) & 0xffffffff;
*rgba = color;
return true;
}
#define EXTRACE_COLOR_BIT(C, R) (((C) >> (R)) & 0xff)
uint32_t rgba_to_argb(uint32_t rgba) {
uint32_t r = EXTRACE_COLOR_BIT(rgba, 24);
uint32_t g = EXTRACE_COLOR_BIT(rgba, 16);
uint32_t b = EXTRACE_COLOR_BIT(rgba, 8);
uint32_t a = EXTRACE_COLOR_BIT(rgba, 0);
uint32_t argb = (a << 24) | (r << 16) | (g << 8) | b;
return argb;
}
#define COLOR_SPLIT(C, R) (EXTRACE_COLOR_BIT((C), (R)) / (double)0xff)
void extract_color_channel(const uint32_t rgba, double *red, double *green,
double *blue, double *alpha) {
*red = COLOR_SPLIT(rgba, 24);
*green = COLOR_SPLIT(rgba, 16);
*blue = COLOR_SPLIT(rgba, 8);
*alpha = COLOR_SPLIT(rgba, 0);
}

View File

@@ -19,4 +19,4 @@ typedef struct color_t {
have: RGB/RGBA/RRGGBB/RRGGBBAA/#RGB/#RGBA/#RRGGBB/#RRGGBBAA
* @param color return the parsed color
*/
void parse_color(const char *hex, color_t *const color);
void color_parse(const char *hex, color_t *const color);

View File

@@ -1,6 +1,17 @@
#include <stdint.h>
const char *font_family = "monospace";
const int font_size = 10;
const int default_dpi = 144;
static const char *const tags[] = {
"1", "2", "3", "4", "5", "6", "7", "8", "9", nullptr,
};
static const uint32_t bar_y_padding = 1;
static const uint16_t bar_y_padding = 1;
static const uint16_t tag_x_padding = 10;
static const char *const bar_bg = "#222222";
static const char *const tag_bg = "#222222";
static const char *const active_tag_bg = "#005577";
static const char *const tag_color = "#bbbbbb";
static const char *const active_tag_color = "#eeeeee";

View File

@@ -1,11 +1,23 @@
#include "monitor.h"
#include <cairo-xcb.h>
#include <cairo.h>
#include <stdint.h>
#include <string.h>
#include <xcb/xcb_aux.h>
#include "base.h"
#include "color.h"
#include "text.h"
#include "types.h"
#include "utils.h"
#include "wm.h"
#include "xcursor.h"
#include "xwindow.h"
void monitor_initialize_tag(monitor_t *monitor, const char **tags) {
uint32_t monitor_initialize_tag(monitor_t *monitor, const char **tags,
uint32_t tag_index_start_at) {
uint32_t tag_count = 0;
int i = 0;
const char *tag_name = nullptr;
tag_t *prev_tag = nullptr;
@@ -13,6 +25,7 @@ void monitor_initialize_tag(monitor_t *monitor, const char **tags) {
tag_t *tag = p_new(tag_t, 1);
tag->name = strdup(tag_name);
tag->mask = 1u << i;
tag->index = tag_index_start_at + tag_count;
if (prev_tag) {
prev_tag->next = tag;
@@ -21,9 +34,11 @@ void monitor_initialize_tag(monitor_t *monitor, const char **tags) {
}
prev_tag = tag;
tag_count++;
}
monitor->selected_tag = monitor->tag_list;
return tag_count;
}
static void tag_clean(tag_t *tag) {
@@ -45,7 +60,117 @@ void monitor_clean(monitor_t *monitor) {
m->selected_tag = nullptr;
m->tag_list = nullptr;
cairo_destroy(m->bar_cr);
m->bar_cr = nullptr;
next_monitor = m->next;
p_delete(&m);
}
}
void monitor_init_bar(monitor_t *monitor) {
static xcb_colormap_t colormap = XCB_NONE;
visual_t *visual = xwindow_get_xcb_visual(true);
xcb_visualid_t visual_id = visual->visual->visual_id;
xcb_connection_t *conn = wm.xcb_conn;
if (colormap == XCB_NONE) {
colormap = xcb_generate_id(conn);
uint8_t alloc = XCB_COLORMAP_ALLOC_NONE;
xcb_create_colormap(conn, alloc, colormap, wm.screen->root, visual_id);
}
xcb_window_t window = xcb_generate_id(conn);
uint32_t value_mask = XCB_CW_OVERRIDE_REDIRECT | XCB_CW_BACK_PIXEL |
XCB_CW_BORDER_PIXEL | XCB_CW_EVENT_MASK |
XCB_CW_CURSOR | XCB_CW_COLORMAP;
const xcb_create_window_value_list_t value_list = {
.override_redirect = true,
.background_pixel = wm.color_set.bar_bg.argb,
.border_pixel = 0,
.event_mask = XCB_EVENT_MASK_BUTTON_PRESS | XCB_EVENT_MASK_EXPOSURE,
.cursor = xcursor_get_xcb_cursor(cursor_normal),
.colormap = colormap,
};
xcb_void_cookie_t cookie;
cookie = xcb_create_window_aux_checked(
conn, visual->depth, window, wm.screen->root, monitor->geometry.x,
monitor->geometry.y, monitor->geometry.width, wm.bar_height, 0,
XCB_WINDOW_CLASS_INPUT_OUTPUT, visual_id, value_mask, &value_list);
if (xcb_request_check(conn, cookie)) fatal("cannot create bar window");
cookie = xcb_map_window(conn, window);
if (xcb_request_check(conn, cookie)) fatal("cannot map bar window:");
xcb_aux_sync(conn);
uint16_t width = monitor->geometry.width;
monitor->workarea.x = monitor->geometry.x;
monitor->workarea.y = monitor->geometry.y;
monitor->workarea.width = width;
monitor->workarea.height = monitor->geometry.height - wm.bar_height;
monitor->bar_window = window;
cairo_surface_t *surface = cairo_xcb_surface_create(
wm.xcb_conn, window, visual->visual, width, wm.bar_height);
cairo_t *cr = cairo_create(surface);
cairo_surface_destroy(surface);
p_delete(&visual);
cairo_status_t status = cairo_status(cr);
if (status != CAIRO_STATUS_SUCCESS) {
fatal("cannot create cairo context: %s", cairo_status_to_string(status));
}
monitor->bar_cr = cr;
}
static void monitor_draw_tags(monitor_t *monitor) {
int16_t x = 0;
for (tag_t *tag = monitor->tag_list; tag; tag = tag->next) {
bool selected = monitor->selected_tag == tag;
bool has_client = tag->task_list;
color_t *bg = nullptr;
color_t *color = nullptr;
if (selected) {
bg = &wm.color_set.active_tag_bg;
color = &wm.color_set.active_tag_color;
} else {
bg = &wm.color_set.tag_bg;
color = &wm.color_set.tag_color;
}
int width = 0, height = 0;
text_get_size(tag->name, &width, &height);
width += 2 * wm.padding.tag_x;
tag->bar_extent.start = x;
tag->bar_extent.end = x + width;
area_t tag_rect = {.x = x, .y = 0, .width = width, .height = wm.bar_height};
if (bg->rgba != wm.color_set.bar_bg.rgba) {
draw_background(monitor->bar_cr, bg, tag_rect);
}
if (has_client) {
area_t rect = {.x = x, .y = 0, .width = 4, .height = 4};
draw_rect(monitor->bar_cr, rect, selected, color, 1);
}
area_t text_rect = tag_rect;
tag_rect.y = (int16_t)((int)wm.bar_height - height) / 2;
tag_rect.height = (int16_t)height;
draw_text(monitor->bar_cr, tag->name, color, text_rect, true);
x += width;
}
monitor->tag_extent.start = 0;
monitor->tag_extent.end = x;
}
void monitor_draw_bar(monitor_t *monitor) {
cairo_t *cr = monitor->bar_cr;
uint16_t height = wm.bar_height;
area_t bar_area = {
.x = 0,
.y = 0,
.width = monitor->geometry.width,
.height = height,
};
draw_background(cr, &wm.color_set.bar_bg, bar_area);
monitor_draw_tags(monitor);
}

View File

@@ -4,6 +4,9 @@
#include "types.h"
void monitor_initialize_tag(monitor_t *monitor, const char **tags);
uint32_t monitor_initialize_tag(monitor_t *monitor, const char **tags,
uint32_t tag_index_start_at);
void monitor_select_tag(monitor_t *monitor, uint32_t tag_mask);
void monitor_clean(monitor_t *monitor);
void monitor_init_bar(monitor_t *monitor);
void monitor_draw_bar(monitor_t *monitor);

View File

@@ -1,9 +1,11 @@
#include "text.h"
#include <cairo.h>
#include <glib-object.h>
#include <glibconfig.h>
#include <pango/pango-attributes.h>
#include <pango/pango-context.h>
#include <pango/pango-enum-types.h>
#include <pango/pango-font.h>
#include <pango/pango-fontmap.h>
#include <pango/pango-layout.h>
@@ -12,6 +14,9 @@
#include <stdint.h>
#include <stdio.h>
#include "base.h"
#include "color.h"
static PangoContext *context = nullptr;
static PangoLayout *layout = nullptr;
static PangoAttrList *attr_list = nullptr;
@@ -24,6 +29,8 @@ static PangoAttrList *attr_list = nullptr;
* @return layout 高度,可用于确定 bar 高度
*/
int text_init_pango_layout(const char *family, uint32_t size, uint32_t dpi) {
printf("family: %s, size: %u, dpi: %u\n", family, size, dpi);
static const char *layout_family = nullptr;
static uint32_t layout_size = 0;
static uint32_t layout_dpi = 0;
@@ -55,6 +62,10 @@ int text_init_pango_layout(const char *family, uint32_t size, uint32_t dpi) {
PangoAttribute *attr = pango_attr_font_desc_new(desc);
attr_list = pango_attr_list_new();
pango_attr_list_insert(attr_list, attr);
pango_layout_set_attributes(layout, attr_list);
pango_layout_set_wrap(layout, PANGO_WRAP_NONE);
pango_layout_set_ellipsize(layout, PANGO_ELLIPSIZE_END);
int height = pango_font_metrics_get_height(metrics);
int ascent = pango_font_metrics_get_ascent(metrics);
@@ -90,3 +101,82 @@ void text_clean_pango_layout(void) {
context = nullptr;
}
}
static void get_layout_size(int *width, int *height) {
PangoRectangle logical_rect;
pango_layout_get_pixel_extents(layout, nullptr, &logical_rect);
if (width) *width = logical_rect.width;
if (height) *height = logical_rect.height;
}
void text_get_size(const char *text, int *width, int *height) {
pango_layout_set_width(layout, -1);
pango_layout_set_text(layout, text, -1);
get_layout_size(width, height);
}
static void text_layout_prepare(bool align_center) {
if (layout == nullptr) {
layout = pango_layout_new(context);
pango_layout_set_attributes(layout, attr_list);
pango_layout_set_wrap(layout, PANGO_WRAP_NONE);
pango_layout_set_ellipsize(layout, PANGO_ELLIPSIZE_END);
}
PangoAlignment align = align_center ? PANGO_ALIGN_CENTER : PANGO_ALIGN_LEFT;
pango_layout_set_alignment(layout, align);
}
void draw_text(cairo_t *cr, const char *text, color_t *color, area_t area,
bool align_center) {
text_layout_prepare(align_center);
pango_layout_set_width(layout, (int)area.width * PANGO_SCALE);
pango_layout_set_text(layout, text, -1);
cairo_set_source_rgba(cr, (double)color->red, (double)color->green,
(double)color->blue, (double)color->alpha);
int height = 0;
get_layout_size(nullptr, &height);
double offset_y = ((double)area.height - (double)height) / 2;
pango_cairo_update_layout(cr, layout);
cairo_move_to(cr, (double)area.x, offset_y + (double)area.y);
pango_cairo_show_layout(cr, layout);
}
void draw_rect(cairo_t *cr, area_t area, bool fill, color_t *color,
uint16_t line_width) {
cairo_set_line_width(cr, (double)line_width);
double x = (double)area.x;
double y = (double)area.y;
double width = (double)area.width;
double height = (double)area.height;
if (fill) {
cairo_rectangle(cr, x, y, width, height);
cairo_set_source_rgba(cr, (double)color->red, (double)color->green,
(double)color->blue, (double)color->alpha);
cairo_fill(cr);
} else {
double half_line_width = (double)line_width / 2;
x += half_line_width;
y += half_line_width;
width -= half_line_width;
height -= half_line_width;
cairo_rectangle(cr, x, y, width, height);
cairo_set_source_rgba(cr, (double)color->red, (double)color->green,
(double)color->blue, (double)color->alpha);
cairo_stroke(cr);
}
}
void draw_background(cairo_t *cr, color_t *color, area_t area) {
double x = (double)area.x;
double y = (double)area.y;
double width = (double)area.width;
double height = (double)area.height;
cairo_move_to(cr, x, y);
cairo_set_source_rgba(cr, (double)color->red, (double)color->green,
(double)color->blue, (double)color->alpha);
cairo_rectangle(cr, x, y, width, height);
cairo_fill(cr);
}

View File

@@ -1,6 +1,16 @@
#pragma once
#include <cairo.h>
#include <stdint.h>
#include "base.h"
#include "color.h"
int text_init_pango_layout(const char *family, uint32_t size, uint32_t dpi);
void text_clean_pango_layout(void);
void text_get_size(const char *text, int *width, int *height);
void draw_text(cairo_t *cr, const char *text, color_t *color, area_t area,
bool align_center);
void draw_rect(cairo_t *cr, area_t area, bool fill, color_t *color,
uint16_t line_width);
void draw_background(cairo_t *cr, color_t *color, area_t area);

View File

@@ -1,5 +1,6 @@
#pragma once
#include <cairo.h>
#include <glib.h>
#include <stdint.h>
#include <xcb/xcb.h>
@@ -54,6 +55,9 @@ struct monitor_t {
tag_t *selected_tag;
char *name;
cairo_t *bar_cr;
extent_in_bar_t tag_extent;
xcb_window_t bar_window;
};
@@ -74,3 +78,15 @@ struct task_in_tag_t {
area_t geometry;
extent_in_bar_t bar_extent;
};
typedef struct color_set_t {
color_t bar_bg;
color_t tag_bg;
color_t active_tag_bg;
color_t tag_color;
color_t active_tag_color;
} color_set_t;
typedef struct padding_t {
uint16_t tag_x;
} padding_t;

View File

@@ -18,11 +18,13 @@
#include "backtrace.h"
#include "buffer.h"
#include "color.h"
#include "default_config.h"
#include "monitor.h"
#include "text.h"
#include "types.h"
#include "utils.h"
#include "xcursor.h"
static void wm_setup_signal(void);
static void wm_check_other_wm(void);
@@ -31,6 +33,7 @@ static void wm_detect_monitor(void);
static void wm_scan_clients(void);
static void wm_clean(void);
static void wm_setup(void);
static void wm_init_color_set(void);
wm_t wm;
@@ -255,17 +258,37 @@ void wm_clean(void) {
guint source_id = sources[i];
if (source_id) g_source_remove(source_id);
}
xcursor_clean();
xcb_disconnect(wm.xcb_conn);
}
static void wm_setup(void) {
int font_height = text_init_pango_layout("Terminus, Emacs Simsun", 10, 144);
wm.bar_height = (uint32_t)font_height + 2 * bar_y_padding;
wm.padding.tag_x = tag_x_padding;
wm_init_color_set();
int font_height = text_init_pango_layout(font_family, font_size, default_dpi);
wm.bar_height = (uint16_t)font_height + 2 * bar_y_padding;
printf("font height: %d, bar height: %u\n", font_height, wm.bar_height);
uint32_t tag_count = 0;
for (monitor_t *m = wm.monitor_list; m; m = m->next) {
monitor_initialize_tag(m, (const char **)tags);
tag_count += monitor_initialize_tag(m, (const char **)tags, tag_count);
monitor_init_bar(m);
monitor_draw_bar(m);
}
xcb_flush(wm.xcb_conn);
}
void wm_init_color_set(void) {
color_parse(bar_bg, &wm.color_set.bar_bg);
color_parse(tag_bg, &wm.color_set.tag_bg);
color_parse(active_tag_bg, &wm.color_set.active_tag_bg);
color_parse(tag_color, &wm.color_set.tag_color);
color_parse(active_tag_color, &wm.color_set.active_tag_color);
}
void wm_restart(void) {
@@ -286,8 +309,13 @@ static void debug_show_monitor_list(void) {
printf("x: %4d, y: %4d, width: %4u, height: %4u -> monitor[%s]\n",
m->geometry.x, m->geometry.y, m->geometry.width, m->geometry.height,
m->name);
printf("x: %4d, y: %4d, width: %4u, height: %4u -> workarea[%s]: %u\n",
m->workarea.x, m->workarea.y, m->workarea.width, m->workarea.height,
m->name, wm.bar_height);
printf("tag extend: [%d, %d]\n", m->tag_extent.start, m->tag_extent.end);
for (const tag_t *tag = m->tag_list; tag; tag = tag->next) {
printf("tag[\"%s\"]: 0b%09b\n", tag->name, tag->mask);
printf("tag[%u]: 0b%09b, \"%s\", [%d, %d]\n", tag->index, tag->mask,
tag->name, tag->bar_extent.start, tag->bar_extent.end);
}
}
}

View File

@@ -5,7 +5,8 @@
#include "types.h"
typedef struct wm_t {
uint32_t bar_height;
padding_t padding;
uint16_t bar_height;
bool need_restart;
GMainLoop *loop;
@@ -22,6 +23,8 @@ typedef struct wm_t {
bool have_xfixes;
bool have_xinerama;
bool have_randr;
color_set_t color_set;
} wm_t;
extern wm_t wm;

128
src/xcursor.c Normal file
View File

@@ -0,0 +1,128 @@
#include "xcursor.h"
#include <stdint.h>
#include <xcb/xcb_cursor.h>
#include "utils.h"
#include "wm.h"
static const char *const xcursor_font[] = {
[XC_X_cursor] = "X_cursor",
[XC_arrow] = "arrow",
[XC_based_arrow_down] = "based_arrow_down",
[XC_based_arrow_up] = "based_arrow_up",
[XC_boat] = "boat",
[XC_bogosity] = "bogosity",
[XC_bottom_left_corner] = "bottom_left_corner",
[XC_bottom_right_corner] = "bottom_right_corner",
[XC_bottom_side] = "bottom_side",
[XC_bottom_tee] = "bottom_tee",
[XC_box_spiral] = "box_spiral",
[XC_center_ptr] = "center_ptr",
[XC_circle] = "circle",
[XC_clock] = "clock",
[XC_coffee_mug] = "coffee_mug",
[XC_cross] = "cross",
[XC_cross_reverse] = "cross_reverse",
[XC_crosshair] = "crosshair",
[XC_diamond_cross] = "diamond_cross",
[XC_dot] = "dot",
[XC_dotbox] = "dotbox",
[XC_double_arrow] = "double_arrow",
[XC_draft_large] = "draft_large",
[XC_draft_small] = "draft_small",
[XC_draped_box] = "draped_box",
[XC_exchange] = "exchange",
[XC_fleur] = "fleur",
[XC_gobbler] = "gobbler",
[XC_gumby] = "gumby",
[XC_hand1] = "hand1",
[XC_hand2] = "hand2",
[XC_heart] = "heart",
[XC_icon] = "icon",
[XC_iron_cross] = "iron_cross",
[XC_left_ptr] = "left_ptr",
[XC_left_side] = "left_side",
[XC_left_tee] = "left_tee",
[XC_leftbutton] = "leftbutton",
[XC_ll_angle] = "ll_angle",
[XC_lr_angle] = "lr_angle",
[XC_man] = "man",
[XC_middlebutton] = "middlebutton",
[XC_mouse] = "mouse",
[XC_pencil] = "pencil",
[XC_pirate] = "pirate",
[XC_plus] = "plus",
[XC_question_arrow] = "question_arrow",
[XC_right_ptr] = "right_ptr",
[XC_right_side] = "right_side",
[XC_right_tee] = "right_tee",
[XC_rightbutton] = "rightbutton",
[XC_rtl_logo] = "rtl_logo",
[XC_sailboat] = "sailboat",
[XC_sb_down_arrow] = "sb_down_arrow",
[XC_sb_h_double_arrow] = "sb_h_double_arrow",
[XC_sb_left_arrow] = "sb_left_arrow",
[XC_sb_right_arrow] = "sb_right_arrow",
[XC_sb_up_arrow] = "sb_up_arrow",
[XC_sb_v_double_arrow] = "sb_v_double_arrow",
[XC_shuttle] = "shuttle",
[XC_sizing] = "sizing",
[XC_spider] = "spider",
[XC_spraycan] = "spraycan",
[XC_star] = "star",
[XC_target] = "target",
[XC_tcross] = "tcross",
[XC_top_left_arrow] = "top_left_arrow",
[XC_top_left_corner] = "top_left_corner",
[XC_top_right_corner] = "top_right_corner",
[XC_top_side] = "top_side",
[XC_top_tee] = "top_tee",
[XC_trek] = "trek",
[XC_ul_angle] = "ul_angle",
[XC_umbrella] = "umbrella",
[XC_ur_angle] = "ur_angle",
[XC_watch] = "watch",
[XC_xterm] = "xterm",
};
static xcb_cursor_context_t *get_xcb_cursor_context(void) {
static xcb_cursor_context_t *ctx = nullptr;
if (ctx) return ctx;
if (xcb_cursor_context_new(wm.xcb_conn, wm.screen, &ctx) < 0) {
return nullptr;
}
return ctx;
}
static const char *xcursor_font_tostr(uint16_t c) {
if (c < (uint16_t)countof(xcursor_font)) {
return xcursor_font[c];
}
return nullptr;
}
static xcb_cursor_t cursor_list[countof(xcursor_font)];
static xcb_cursor_context_t *cursor_ctx = nullptr;
xcb_cursor_t xcursor_get_xcb_cursor(cursor_t cursor) {
if (!cursor_ctx) cursor_ctx = get_xcb_cursor_context();
if (!cursor_ctx) fatal("cannot get cursor");
if (!cursor_list[cursor]) {
const char *name = xcursor_font_tostr(cursor);
cursor_list[cursor] = xcb_cursor_load_cursor(cursor_ctx, name);
}
return cursor_list[cursor];
}
void xcursor_clean(void) {
for (int i = 0; i < countof(xcursor_font); i++) {
xcb_cursor_t cursor = cursor_list[i];
if (cursor) xcb_free_cursor(wm.xcb_conn, cursor);
}
if (cursor_ctx) xcb_cursor_context_free(cursor_ctx);
}

13
src/xcursor.h Normal file
View File

@@ -0,0 +1,13 @@
#pragma once
#include <X11/cursorfont.h>
#include <xcb/xproto.h>
typedef enum cursor_t {
cursor_normal = XC_left_ptr,
cursor_resize = XC_bottom_right_corner,
cursor_move = XC_fleur,
} cursor_t;
xcb_cursor_t xcursor_get_xcb_cursor(cursor_t cursor);
void xcursor_clean(void);

102
src/xwindow.c Normal file
View File

@@ -0,0 +1,102 @@
#include "xwindow.h"
#include <xcb/xcb_aux.h>
#include "utils.h"
#include "wm.h"
#include "xcursor.h"
xcb_window_t xwindow_create_bar_window(area_t area, uint32_t background_argb,
cursor_t cursor) {
static xcb_colormap_t colormap = XCB_NONE;
visual_t *visual = xwindow_get_xcb_visual(true);
xcb_visualid_t visual_id = visual->visual->visual_id;
xcb_connection_t *conn = wm.xcb_conn;
if (colormap == XCB_NONE) {
colormap = xcb_generate_id(conn);
uint8_t alloc = XCB_COLORMAP_ALLOC_NONE;
xcb_create_colormap(conn, alloc, colormap, wm.screen->root, visual_id);
}
xcb_window_t window = xcb_generate_id(conn);
uint32_t value_mask = XCB_CW_OVERRIDE_REDIRECT | XCB_CW_BACK_PIXEL |
XCB_CW_BORDER_PIXEL | XCB_CW_EVENT_MASK |
XCB_CW_CURSOR | XCB_CW_COLORMAP;
const xcb_create_window_value_list_t value_list = {
.override_redirect = true,
.background_pixel = background_argb,
.border_pixel = 0,
.event_mask = XCB_EVENT_MASK_BUTTON_PRESS | XCB_EVENT_MASK_EXPOSURE,
.cursor = xcursor_get_xcb_cursor(cursor),
.colormap = colormap,
};
xcb_void_cookie_t cookie;
cookie = xcb_create_window_aux_checked(
conn, visual->depth, window, wm.screen->root, area.x, area.y, area.width,
area.height, 0, XCB_WINDOW_CLASS_INPUT_OUTPUT, visual_id, value_mask,
&value_list);
p_delete(&visual);
if (xcb_request_check(conn, cookie)) fatal("cannot create bar window");
cookie = xcb_map_window(conn, window);
if (xcb_request_check(conn, cookie)) fatal("cannot map bar window:");
xcb_aux_sync(conn);
return window;
}
static visual_t *find_alpha_visual() {
xcb_depth_iterator_t depth_iter;
depth_iter = xcb_screen_allowed_depths_iterator(wm.screen);
for (; depth_iter.rem; xcb_depth_next(&depth_iter)) {
if (depth_iter.data->depth != 32) continue;
xcb_visualtype_iterator_t visual_iter;
visual_iter = xcb_depth_visuals_iterator(depth_iter.data);
for (; visual_iter.rem; xcb_visualtype_next(&visual_iter)) {
if (visual_iter.data->_class == XCB_VISUAL_CLASS_TRUE_COLOR) {
visual_t *r = p_new(visual_t, 1);
r->visual = visual_iter.data;
r->depth = depth_iter.data->depth;
return r;
}
}
}
return NULL;
}
static visual_t *find_root_visual() {
xcb_depth_iterator_t depth_iter;
depth_iter = xcb_screen_allowed_depths_iterator(wm.screen);
xcb_visualid_t vid = wm.screen->root_visual;
for (; depth_iter.rem; xcb_depth_next(&depth_iter)) {
xcb_visualtype_iterator_t visual_iter;
visual_iter = xcb_depth_visuals_iterator(depth_iter.data);
for (; visual_iter.rem; xcb_visualtype_next(&visual_iter)) {
if (visual_iter.data->visual_id == vid) {
visual_t *r = p_new(visual_t, 1);
r->visual = visual_iter.data;
r->depth = depth_iter.data->depth;
return r;
}
}
}
return NULL;
}
/**
* @brief 获取 xcb 的 visual 及其对应的 depth
* @param prefer_alpha 支持 alpha 的 visual 优先
* @returns 返回的信息使用后记得使用 free 释放
*/
visual_t *xwindow_get_xcb_visual(bool prefer_alpha) {
if (!prefer_alpha) return find_root_visual();
visual_t *visual = find_alpha_visual();
if (visual == NULL) visual = find_root_visual();
return visual;
}

View File

@@ -1 +1,11 @@
#pragma once
#include <stdint.h>
#include <xcb/xproto.h>
typedef struct visual_t {
uint8_t depth;
xcb_visualtype_t *visual;
} visual_t;
visual_t *xwindow_get_xcb_visual(bool prefer_alpha);