Compare commits

...

8 Commits

17 changed files with 298 additions and 37 deletions

View File

@@ -1,5 +1,10 @@
cmake_minimum_required(VERSION 3.24)
# use GNU23
set(CMAKE_C_STANDARD 23)
set(CMAKE_C_STANDARD_REQUIRED ON)
set(CMAKE_C_EXTENSIONS ON)
set(APP_NAME "zdwm")
set(SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/src")
set(BUILD_DIR "${CMAKE_CURRENT_BINARY_DIR}")
@@ -34,11 +39,9 @@ add_executable(${APP_NAME}
${SOURCE_DIR}/action.c
${SOURCE_DIR}/xkb.c
${SOURCE_DIR}/atoms.c
${SOURCE_DIR}/layout.c
)
# use C23
set_property(TARGET ${APP_NAME} PROPERTY C_STANDARD 23)
find_package(PkgConfig REQUIRED)
pkg_check_modules(deps REQUIRED

View File

@@ -1,5 +1,6 @@
#include "client.h"
#include <stdint.h>
#include <string.h>
#include <xcb/xcb.h>
#include <xcb/xcb_aux.h>
@@ -7,6 +8,7 @@
#include <xcb/xproto.h>
#include "atoms-extern.h"
#include "monitor.h"
#include "types.h"
#include "utils.h"
#include "wm.h"
@@ -48,17 +50,65 @@ static void client_detach_stack(client_t *client) {
}
}
task_in_tag_t *client_get_task_in_tag(client_t *client, tag_t *tag) {
for (task_in_tag_t *task = tag->task_list; task; task = task->next) {
if (task->client == client) return task;
}
return nullptr;
}
static void client_add_to_tag(client_t *client, tag_t *tag) {
if (client_get_task_in_tag(client, tag)) return;
task_in_tag_t *task = p_new(task_in_tag_t, 1);
task->client = client;
task->next = tag->task_list;
tag->task_list = task;
}
static void client_remove_from_tag(client_t *client, tag_t *tag) {
for (task_in_tag_t **task = &tag->task_list; *task; task = &(*task)->next) {
if ((*task)->client != client) continue;
p_delete(task);
*task = (*task)->next;
}
}
static inline void client_wipe(client_t *client) {
p_delete(&client->class);
p_delete(&client->instance);
p_delete(&client->name);
p_delete(&client->icon_name);
p_delete(&client->net_name);
p_delete(&client->net_icon_name);
p_delete(&client->role);
p_delete(&client);
}
static inline void client_tags_apply(client_t *client) {
for (tag_t *tag = client->monitor->tag_list; tag; tag = tag->next) {
if (client->tags & tag->mask) {
client_add_to_tag(client, tag);
} else {
client_remove_from_tag(client, tag);
}
}
if (client->tags == 0) client_wipe(client);
}
static inline void client_update_names(client_t *c) {
xwindow_get_text_property(c->window, WM_NAME, &c->name);
xwindow_get_text_property(c->window, WM_ICON_NAME, &c->icon_name);
xwindow_get_text_property(c->window, _NET_WM_NAME, &c->net_name);
xwindow_get_text_property(c->window, _NET_WM_ICON_NAME, &c->net_icon_name);
}
void client_manage(xcb_window_t window,
xcb_get_geometry_reply_t *geometry_reply) {
client_t *c = p_new(client_t, 1);
c->window = window;
c->name = xwindow_get_text_property(c->window, WM_NAME);
c->icon_name = xwindow_get_text_property(c->window, WM_ICON_NAME);
c->net_name = xwindow_get_text_property(c->window, _NET_WM_NAME);
c->net_icon_name = xwindow_get_text_property(c->window, _NET_WM_ICON_NAME);
c->role = xwindow_get_text_property(c->window, WM_WINDOW_ROLE);
client_attach_list(c);
client_attach_stack(c);
@@ -71,6 +121,7 @@ void client_manage(xcb_window_t window,
c->instance = strdup(prop.instance_name);
xcb_icccm_get_wm_class_reply_wipe(&prop);
}
xwindow_get_text_property(c->window, WM_WINDOW_ROLE, &c->role);
}
{
@@ -88,8 +139,11 @@ void client_manage(xcb_window_t window,
c->monitor = wm.current_monitor;
c->tags = c->monitor->selected_tag->mask;
}
client_tags_apply(c);
}
monitor_arrange(c->monitor);
{
const xcb_params_cw_t params = {
.event_mask = XCB_EVENT_MASK_ENTER_WINDOW | XCB_EVENT_MASK_FOCUS_CHANGE |
@@ -101,9 +155,49 @@ void client_manage(xcb_window_t window,
xcb_map_window(wm.xcb_conn, window);
}
client_update_names(c);
monitor_draw_bar(c->monitor);
xcb_flush(wm.xcb_conn);
}
char **client_get_task_title(client_t *client) {
if (client->net_icon_name) return &client->net_icon_name;
if (client->net_name) return &client->net_name;
if (client->icon_name) return &client->icon_name;
if (client->name) return &client->name;
return nullptr;
}
bool client_need_layout(client_t *client) {
if (client->floating || client->fullscreen || client->maximize ||
client->minimize) {
return false;
}
return true;
}
void client_move_to(client_t *client, int16_t x, int16_t y) {
client->geometry.x = x;
client->geometry.y = y;
uint16_t mask = XCB_CONFIG_WINDOW_X | XCB_CONFIG_WINDOW_Y;
const xcb_params_configure_window_t params = {.x = x, .y = y};
xcb_aux_configure_window(wm.xcb_conn, client->window, mask, &params);
logger("== client 0x%x move to x: %d, y: %d\n", client->window, x, y);
}
void client_resize(client_t *client, uint16_t width, uint16_t height) {
client->geometry.width = width;
client->geometry.height = height;
uint16_t mask = XCB_CONFIG_WINDOW_WIDTH | XCB_CONFIG_WINDOW_HEIGHT;
const xcb_params_configure_window_t params = {
.width = width,
.height = height,
};
xcb_aux_configure_window(wm.xcb_conn, client->window, mask, &params);
logger("== client 0x%x resize to width: %d, height: %d\n", client->window,
width, height);
}
bool client_is_visible(client_t *client) {
return client->tags & client->monitor->selected_tag->mask;
}

View File

@@ -6,8 +6,11 @@
#include "types.h"
client_t *client_get_by_window(xcb_window_t window);
task_in_tag_t *client_get_task_in_tag(client_t *client, tag_t *tag);
void client_manage(xcb_window_t window,
xcb_get_geometry_reply_t *geometry_reply);
char **client_get_task_title(client_t *client);
bool client_need_layout(client_t *client);
void client_send_to_tag(client_t *client, uint32_t tag_mask);
void client_send_to_monitor(client_t *client, monitor_t *monitor);

View File

@@ -4,6 +4,8 @@
#include <stdint.h>
#include "action.h"
#include "layout.h"
#include "types.h"
static const char *const font_family = "monospace";
static const int font_size = 10;
@@ -26,9 +28,14 @@ static const button_t button_list[] = {
};
static const char launcher[] =
"rofi -show combi -modes window,drun,run,ssh,combi,windowcd";
"rofi -show combi -modes combi -combi-modes window,drun,run,ssh,windowcd";
static const keyboard_t key_list[] = {
{modifier_alt, XK_p, spawn, {.ptr = launcher}},
{modifier_alt, XK_q, quit, {.b = false}},
{modifier_alt, XK_r, quit, {.b = true}},
};
static const layout_t layout_list[] = {
{.symbol = "[M]", .arrange = monocle},
{.symbol = "><=", .arrange = nullptr},
};

View File

@@ -2,7 +2,7 @@
#include <glib.h>
#include <stdint.h>
#include <stdio.h>
#include <string.h>
#include <xcb/xcb.h>
#include <xcb/xcb_event.h>
#include <xcb/xcb_keysyms.h>
@@ -10,12 +10,15 @@
#include <xkbcommon/xkbcommon.h>
#include "action.h"
#include "atoms-extern.h"
#include "client.h"
#include "default_config.h"
#include "monitor.h"
#include "types.h"
#include "utils.h"
#include "wm.h"
#include "xkb.h"
#include "xwindow.h"
static void button_press(xcb_button_press_event_t *ev) {
click_area_t click_area = click_none;
@@ -72,7 +75,7 @@ static void key_press(xcb_key_press_event_t *ev) {
xcb_keysym_t keysym = xcb_key_press_lookup_keysym(wm.key_symbols, ev, 0);
char key_name[16];
if (xkb_keysym_get_name(keysym, key_name, sizeof(key_name)) != -1) {
printf("key %s: %s\n", is_press ? "press" : "release", key_name);
logger("key %s: %s\n", is_press ? "press" : "release", key_name);
}
if (!is_press) return;
@@ -116,10 +119,30 @@ static void map_request(xcb_map_request_event_t *ev) {
p_delete(&wa_reply);
}
static void property_notify(xcb_property_notify_event_t *ev) {
client_t *client = client_get_by_window(ev->window);
if (client == nullptr) return;
if (ev->atom == WM_NAME) {
xwindow_get_text_property(ev->window, ev->atom, &client->name);
monitor_draw_bar(client->monitor);
} else if (ev->atom == WM_ICON_NAME) {
if (client->icon_name) p_delete(&client->icon_name);
xwindow_get_text_property(ev->window, ev->atom, &client->icon_name);
monitor_draw_bar(client->monitor);
} else if (ev->atom == _NET_WM_NAME) {
xwindow_get_text_property(ev->window, ev->atom, &client->net_name);
monitor_draw_bar(client->monitor);
} else if (ev->atom == _NET_WM_ICON_NAME) {
xwindow_get_text_property(ev->window, ev->atom, &client->net_icon_name);
monitor_draw_bar(client->monitor);
}
}
static void handle_xcb_event(xcb_generic_event_t *event) {
uint8_t event_type = XCB_EVENT_RESPONSE_TYPE(event);
const char *label = xcb_event_get_label(event_type);
printf("event type: %u[%s], xkb_event: %u\n", event_type, label,
logger("event type: %u[%s], xkb_event: %u\n", event_type, label,
wm.event_base_xkb);
switch (event_type) {
@@ -133,6 +156,7 @@ static void handle_xcb_event(xcb_generic_event_t *event) {
EVENT(XCB_KEY_RELEASE, key_press);
EVENT(XCB_CONFIGURE_REQUEST, configure_request);
EVENT(XCB_MAP_REQUEST, map_request);
EVENT(XCB_PROPERTY_NOTIFY, property_notify);
#undef EVENT
}

19
src/layout.c Normal file
View File

@@ -0,0 +1,19 @@
#include "layout.h"
#include <stdint.h>
#include "client.h"
#include "types.h"
#include "utils.h"
void monocle(tag_t *tag) {
logger("== monocle tag: %s start ==\n", tag->name);
for (task_in_tag_t *task = tag->task_list; task; task = task->next) {
if (!client_need_layout(task->client)) continue;
task->geometry = task->client->monitor->workarea;
logger("== window 0x%x: x: %d, y: %d, width: %u, height: %u\n",
task->client->window, task->geometry.x, task->geometry.y,
task->geometry.width, task->geometry.height);
}
logger("== monocle tag: %s end ==\n", tag->name);
}

5
src/layout.h Normal file
View File

@@ -0,0 +1,5 @@
#pragma once
#include "types.h"
void monocle(tag_t *tag);

View File

@@ -7,6 +7,7 @@
#include <xcb/xcb_aux.h>
#include "base.h"
#include "client.h"
#include "color.h"
#include "text.h"
#include "types.h"
@@ -27,6 +28,10 @@ uint32_t monitor_initialize_tag(monitor_t *monitor, const char **tags,
tag->mask = 1u << i;
tag->index = tag_index_start_at + tag_count;
if (wm.layout_count > 0 && wm.layout_list) {
tag->layout = &wm.layout_list[0];
}
if (prev_tag) {
prev_tag->next = tag;
} else {
@@ -118,7 +123,7 @@ void monitor_init_bar(monitor_t *monitor) {
uint16_t width = monitor->geometry.width;
monitor->workarea.x = monitor->geometry.x;
monitor->workarea.y = monitor->geometry.y;
monitor->workarea.y = monitor->geometry.y + wm.bar_height;
monitor->workarea.width = width;
monitor->workarea.height = monitor->geometry.height - wm.bar_height;
monitor->bar_window = window;
@@ -174,6 +179,52 @@ static void monitor_draw_tags(monitor_t *monitor) {
monitor->tag_extent.end = x;
}
static void monitor_draw_layout_symbol(monitor_t *monitor) {
const layout_t *layout = monitor->selected_tag->layout;
if (layout && layout->symbol) {
color_t *color = &wm.color_set.tag_color;
int width = 0;
text_get_size(layout->symbol, &width, nullptr);
area_t area = {
.x = monitor->tag_extent.end,
.y = monitor->geometry.y,
.width = width + 2 * wm.padding.tag_x,
.height = wm.bar_height,
};
monitor->layout_symbol_extent.start = area.x;
monitor->layout_symbol_extent.end = area.x + area.width;
draw_text(monitor->bar_cr, layout->symbol, color, area, true);
}
}
static void monitor_draw_tasks(monitor_t *monitor) {
task_in_tag_t *task_list = monitor->selected_tag->task_list;
if (!task_list) return;
uint16_t task_count = 0;
for (task_in_tag_t *task = task_list; task; task = task->next) ++task_count;
if (task_count == 0) return;
int16_t x = monitor->layout_symbol_extent.end;
uint16_t task_width = (monitor->geometry.width - x) / task_count;
for (task_in_tag_t *task = task_list; task; task = task->next) {
task->bar_extent.start = x;
task->bar_extent.end = x + task_width;
x += task_width;
char **title = client_get_task_title(task->client);
if (title == nullptr || *title == nullptr) continue;
color_t *color = &wm.color_set.active_tag_color;
area_t area = {
.x = task->bar_extent.start,
.y = monitor->geometry.y,
.width = task_width,
.height = wm.bar_height,
};
draw_text(monitor->bar_cr, *title, color, area, false);
}
}
void monitor_draw_bar(monitor_t *monitor) {
cairo_t *cr = monitor->bar_cr;
uint16_t height = wm.bar_height;
@@ -186,4 +237,32 @@ void monitor_draw_bar(monitor_t *monitor) {
draw_background(cr, &wm.color_set.bar_bg, bar_area);
monitor_draw_tags(monitor);
monitor_draw_layout_symbol(monitor);
monitor_draw_tasks(monitor);
}
void monitor_arrange(monitor_t *monitor) {
tag_t *tag = monitor->selected_tag;
if (tag->layout && tag->layout->arrange) tag->layout->arrange(tag);
for (client_t *c = wm.client_list; c; c = c->next) {
task_in_tag_t *task = client_get_task_in_tag(c, tag);
if (task) {
if (task->geometry.x != c->geometry.x ||
task->geometry.y != c->geometry.y) {
client_move_to(c, task->geometry.x, task->geometry.y);
}
if (task->geometry.width != c->geometry.width ||
task->geometry.height != c->geometry.height) {
client_resize(c, task->geometry.width, task->geometry.height);
}
} else {
int16_t x = -c->geometry.width;
int16_t y = -c->geometry.height;
if (x != c->geometry.x || y != c->geometry.y) {
client_move_to(c, x, y);
}
}
}
xcb_flush(wm.xcb_conn);
}

View File

@@ -10,3 +10,4 @@ void monitor_select_tag(monitor_t *monitor, uint32_t tag_mask);
void monitor_clean(monitor_t *monitor);
void monitor_init_bar(monitor_t *monitor);
void monitor_draw_bar(monitor_t *monitor);
void monitor_arrange(monitor_t *monitor);

View File

@@ -16,6 +16,7 @@
#include "base.h"
#include "color.h"
#include "utils.h"
static PangoContext *context = nullptr;
static PangoLayout *layout = nullptr;
@@ -29,7 +30,7 @@ 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);
logger("family: %s, size: %u, dpi: %u\n", family, size, dpi);
static const char *layout_family = nullptr;
static uint32_t layout_size = 0;
@@ -38,7 +39,7 @@ int text_init_pango_layout(const char *family, uint32_t size, uint32_t dpi) {
if (layout_family != nullptr && strcmp(layout_family, family) == 0 &&
layout_size == size && layout_dpi == dpi) {
printf("text pango layout config cached\n");
logger("text pango layout config cached\n");
return text_height;
}

View File

@@ -58,6 +58,7 @@ struct monitor_t {
cairo_t *bar_cr;
extent_in_bar_t tag_extent;
extent_in_bar_t layout_symbol_extent;
xcb_window_t bar_window;
};
@@ -68,7 +69,7 @@ struct tag_t {
extent_in_bar_t bar_extent;
tag_t *next;
char *name;
layout_t *layout;
const layout_t *layout;
task_in_tag_t *task_list;
};

View File

@@ -1,5 +1,7 @@
#pragma once
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
@@ -70,3 +72,18 @@ void _warn(int line, const char *function, const char *file, const char *format,
...) __attribute__((format(printf, 4, 5)));
const char *current_time_str(void);
#if defined(RELEASE)
#define logger(format, ...)
#elif defined(LOG_FILE)
static inline void logger(const char *format, ...) {
va_list ap;
va_start(ap, format);
FILE *log_file = fopen(LOG_FILE, "a+t");
vfprintf(log_file, format, ap);
va_end(ap);
fclose(log_file);
}
#else
#define logger(format, ...) printf(format, ##__VA_ARGS__)
#endif

View File

@@ -273,13 +273,15 @@ void wm_clean(void) {
static void wm_setup(void) {
wm.padding.tag_x = tag_x_padding;
wm.layout_list = layout_list;
wm.layout_count = (uint16_t)countof(layout_list);
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);
logger("font height: %d, bar height: %u\n", font_height, wm.bar_height);
uint32_t tag_count = 0;
for (monitor_t *m = wm.monitor_list; m; m = m->next) {
@@ -335,17 +337,17 @@ void wm_quit(void) {
}
static void debug_show_monitor_list(void) {
printf("\n========================== monitors ==========================\n");
logger("\n========================== monitors ==========================\n");
for (monitor_t *m = wm.monitor_list; m; m = m->next) {
printf("x: %4d, y: %4d, width: %4u, height: %4u -> monitor[%s]\n",
logger("x: %4d, y: %4d, width: %4u, height: %4u -> monitor[%s]\n",
m->geometry.x, m->geometry.y, m->geometry.width, m->geometry.height,
m->name);
printf("x: %4d, y: %4d, width: %4u, height: %4u -> workarea[%s]: %u\n",
logger("x: %4d, y: %4d, width: %4u, height: %4u -> workarea[%s]: %u\n",
m->workarea.x, m->workarea.y, m->workarea.width, m->workarea.height,
m->name, wm.bar_height);
printf("tag extend: [%d, %d]\n", m->tag_extent.start, m->tag_extent.end);
logger("tag extend: [%d, %d]\n", m->tag_extent.start, m->tag_extent.end);
for (const tag_t *tag = m->tag_list; tag; tag = tag->next) {
printf("tag[%u]: %4u, \"%s\", [%d, %d]\n", tag->index, tag->mask,
logger("tag[%u]: %4u, \"%s\", [%d, %d]\n", tag->index, tag->mask,
tag->name, tag->bar_extent.start, tag->bar_extent.end);
}
}

View File

@@ -9,6 +9,7 @@
typedef struct wm_t {
padding_t padding;
uint16_t bar_height;
uint16_t layout_count;
bool need_restart;
GMainLoop *loop;
@@ -18,6 +19,7 @@ typedef struct wm_t {
client_t *client_focused;
monitor_t *monitor_list;
monitor_t *current_monitor;
const layout_t *layout_list;
xcb_connection_t *xcb_conn;
xcb_screen_t *screen;

View File

@@ -116,7 +116,7 @@ void xkb_init(void) {
wm.xcb_conn, XKB_X11_MIN_MAJOR_XKB_VERSION, XKB_X11_MIN_MINOR_XKB_VERSION,
XKB_X11_SETUP_XKB_EXTENSION_NO_FLAGS, nullptr, nullptr, &wm.event_base_xkb,
nullptr);
printf("xkb base event: %u\n", wm.event_base_xkb);
logger("xkb base event: %u\n", wm.event_base_xkb);
if (!wm.have_xkb) {
warn("XKB not found or not supported");
xkb_init_keymap();
@@ -182,7 +182,7 @@ static void xkb_schedule_refresh(void) {
void xkb_handle_event(xcb_generic_event_t *event) {
assert(wm.have_xkb);
printf("xkb event type: %u\n", event->pad0);
logger("xkb event type: %u\n", event->pad0);
/* XKB 中没有对应所有事件的泛型类型xcb_generic_event_t 类型中的 pad0
* 字段对应 XKB 事件中的 xkbType 字段,故用该字段判断 XKB 事件类型 */
@@ -205,7 +205,7 @@ void xkb_handle_event(xcb_generic_event_t *event) {
state_notify_event->baseGroup, state_notify_event->latchedGroup,
state_notify_event->lockedGroup);
printf("changed: %u\n",
logger("changed: %u\n",
state_notify_event->changed & XCB_XKB_STATE_PART_GROUP_STATE);
if (state_notify_event->changed & XCB_XKB_STATE_PART_GROUP_STATE) {
xkb_schedule_refresh();

View File

@@ -177,27 +177,29 @@ void xwindow_focus(xcb_window_t window) {
* 获取目标窗口的文本属性
* @param window 目标窗口
* @param property 属性名
* @returns 返回的信息使用后记得使用 free 释放
* @param out 返回的信息使用后记得使用 free 释放
*/
char *xwindow_get_text_property(xcb_window_t window, xcb_atom_t property) {
void xwindow_get_text_property(xcb_window_t window, xcb_atom_t property,
char **out) {
if (out == nullptr) return;
xcb_get_property_cookie_t cookie = xcb_get_property_unchecked(
wm.xcb_conn, false, window, property, XCB_ATOM_STRING, 0, UINT32_MAX);
xcb_get_property_reply_t *reply =
xcb_get_property_reply(wm.xcb_conn, cookie, nullptr);
int length = xcb_get_property_value_length(reply);
char *value = nullptr;
char *value = xcb_get_property_value(reply);
if (reply &&
(reply->type == XCB_ATOM_STRING || reply->type == UTF8_STRING ||
reply->type == COMPOUND_TEXT) &&
reply->format == 8 && length) {
value = p_new(char, length + 1);
memcpy(value, xcb_get_property_value(reply), length);
value[length] = '\0';
reply->format == 8 && length &&
(*out == nullptr || strncmp(*out, value, length) != 0)) {
if (*out) p_delete(out);
*out = p_new(char, length + 1);
memcpy(*out, value, length);
(*out)[length] = '\0';
}
p_delete(&reply);
return value;
}

View File

@@ -18,7 +18,8 @@ void xwindow_grab_keys(xcb_window_t window, const keyboard_t *keys,
int keys_length);
bool xwindow_send_event(xcb_window_t window, xcb_atom_t atom);
void xwindow_focus(xcb_window_t window);
char *xwindow_get_text_property(xcb_window_t window, xcb_atom_t property);
void xwindow_get_text_property(xcb_window_t window, xcb_atom_t property,
char **out);
#define xwindow_set_name_static(window, name) \
xcb_icccm_set_wm_name(wm.xcb_conn, window, XCB_ATOM_STRING, 8, \