Compare commits
1 Commits
6814e5e1f1
...
16371ed7b1
| Author | SHA1 | Date | |
|---|---|---|---|
|
16371ed7b1
|
@@ -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
87
src/color.c
Normal 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);
|
||||
}
|
||||
@@ -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);
|
||||
|
||||
@@ -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";
|
||||
|
||||
127
src/monitor.c
127
src/monitor.c
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
90
src/text.c
90
src/text.c
@@ -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);
|
||||
}
|
||||
|
||||
10
src/text.h
10
src/text.h
@@ -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);
|
||||
|
||||
16
src/types.h
16
src/types.h
@@ -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;
|
||||
|
||||
36
src/wm.c
36
src/wm.c
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
5
src/wm.h
5
src/wm.h
@@ -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
128
src/xcursor.c
Normal 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
13
src/xcursor.h
Normal 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
102
src/xwindow.c
Normal 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;
|
||||
}
|
||||
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user