窗口管理器状态机设计

This commit is contained in:
2025-11-10 23:20:12 +08:00
parent e77c986109
commit 79425dc890
16 changed files with 277 additions and 52 deletions

View File

@@ -19,7 +19,7 @@ project(${APP_NAME}
LANGUAGES C
)
add_executable(${APP_NAME} main.c utils.c buffer.c backtrace.c)
add_executable(${APP_NAME} wm.c utils.c buffer.c backtrace.c monitor.c client.c)
# use C23
set_property(TARGET ${APP_NAME} PROPERTY C_STANDARD 23)
@@ -31,6 +31,9 @@ pkg_check_modules(deps REQUIRED
xcb
xcb-aux
xcb-randr
xcb-xfixes
xcb-xinerama
)
target_include_directories(${APP_NAME} SYSTEM
@@ -63,8 +66,8 @@ else()
message(STATUS "checking for execinfo -- not found")
endif()
configure_file("${SOURCE_DIR}/config.h.in"
"${BUILD_DIR}/config.h"
configure_file("${SOURCE_DIR}/app.h.in"
"${BUILD_DIR}/app.h"
ESCAPE_QUOTES
@ONLY
)

View File

@@ -1,7 +1,7 @@
#include "backtrace.h"
#include "app.h"
#include "buffer.h"
#include "config.h"
#ifdef HAS_EXECINFO
#include <execinfo.h>
@@ -9,7 +9,6 @@
#define MAX_STACK_SIZE 32
/**
* Get a backtrace.
* @param buffer The buffer to fill with backtrace.

2
src/bar.h Normal file
View File

@@ -0,0 +1,2 @@
#pragma once

13
src/base.h Normal file
View File

@@ -0,0 +1,13 @@
#pragma once
#include <stdint.h>
typedef struct area_t {
int16_t x, y;
uint16_t width, height;
} area_t;
typedef struct extent_in_bar_t {
int16_t start;
int16_t end;
} extent_in_bar_t;

1
src/client.c Normal file
View File

@@ -0,0 +1 @@
#include "client.h"

45
src/client.h Normal file
View File

@@ -0,0 +1,45 @@
#pragma once
#include <stdint.h>
#include "color.h"
#include "types.h"
void client_manage(xcb_window_t window);
void client_send_to_tag(client_t *client, uint32_t tag_mask);
void client_send_to_monitor(client_t *client, monitor_t *monitor);
void client_move_to(client_t *client, int16_t x, int16_t y);
void client_resize(client_t *client, uint16_t width, uint16_t height);
void client_change_border_color(client_t *client, color_t *color);
void client_change_border_width(client_t *client, uint16_t border_width);
void client_set_floating(client_t *client, bool floating);
void client_set_fullscreen(client_t *client, bool fullscreen);
void client_set_maximize(client_t *client, bool maximize);
void client_set_minimize(client_t *client, bool minimize);
void client_set_sticky(client_t *client, bool sticky);
void client_set_urgent(client_t *client, bool urgent);
void client_set_class_instance(client_t *client, const char *class,
const char *instance);
void client_set_name(client_t *client, char *name);
void client_set_icon_name(client_t *client, char *icon_name);
void client_set_net_name(client_t *client, char *name);
void client_set_net_icon_name(client_t *client, char *icon_name);
void client_set_role(client_t *client, char *role);
void client_set_transient_for_window(client_t *client,
xcb_window_t transient_for_window);
void client_set_leader_window(client_t *client, xcb_window_t leader_window);
void client_kill(client_t *client);
void client_unmanage(client_t *client);
void client_focus(client_t *client);
void client_stack_raise(client_t *client);
bool client_is_visible(client_t *client);

22
src/color.h Normal file
View File

@@ -0,0 +1,22 @@
#pragma once
#include <stdint.h>
typedef struct color_t {
uint32_t rgba; /* use for human reading */
uint32_t argb; /* use for xcb */
/* channels of color, use for cairo */
double red; /* red channel */
double green; /* green channel */
double blue; /* blue channel */
double alpha; /* alpha channel */
} color_t;
/**
* @brief parsing color represented in hexadecimal format
* @param hex color represented in hexadecimal format, the supported format
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);

3
src/draw.h Normal file
View File

@@ -0,0 +1,3 @@
#pragma once

3
src/monitor.c Normal file
View File

@@ -0,0 +1,3 @@
#include "monitor.h"

7
src/monitor.h Normal file
View File

@@ -0,0 +1,7 @@
#pragma once
#include <stdint.h>
#include "types.h"
void monitor_select_tag(monitor_t *monitor, uint32_t tag_mask);

View File

@@ -1,16 +1,74 @@
#pragma once
#include <glib.h>
#include <stdint.h>
#include <xcb/xcb.h>
#include <xcb/xproto.h>
typedef struct global_state_t {
bool need_restart;
GMainLoop *loop;
#include "base.h"
#include "color.h"
xcb_connection_t *xcb_conn;
xcb_screen_t *screen;
int default_screen;
} global_state_t;
typedef struct client_t client_t;
typedef struct monitor_t monitor_t;
typedef struct tag_t tag_t;
typedef struct task_in_tag_t task_in_tag_t;
extern global_state_t global_state;
typedef struct layout_t {
char *symbol;
void (*arrange)(tag_t *tag);
} layout_t;
struct client_t {
client_t *stack_next;
client_t *next;
monitor_t *monitor;
uint32_t tags;
area_t geometry;
color_t *border_color;
uint16_t border_width;
bool floating;
bool fullscreen;
bool maximize;
bool minimize;
bool sticky;
bool urgent;
char *class, *instance;
char *name, *icon_name, *net_name, *net_icon_name;
char *role;
xcb_window_t transient_for_window;
xcb_window_t leader_window;
xcb_window_t frame_window;
xcb_window_t window;
};
struct monitor_t {
monitor_t *next;
area_t geometry;
area_t workarea;
tag_t *tag_list;
tag_t *selected_tag;
xcb_window_t bar_window;
};
struct tag_t {
uint32_t index; /* index in all tags */
uint32_t mask;
extent_in_bar_t bar_extent;
tag_t *next;
char *name;
layout_t *layout;
task_in_tag_t *task_list;
};
struct task_in_tag_t {
client_t *client;
task_in_tag_t *next;
area_t geometry;
extent_in_bar_t bar_extent;
};

View File

@@ -5,7 +5,7 @@
#include <stdlib.h>
#include <time.h>
#include "config.h"
#include "app.h"
const char *current_time_str(void) {
static char buffer[25];

View File

@@ -1,20 +1,32 @@
#include "wm.h"
#include <glib-unix.h>
#include <glib.h>
#include <glibconfig.h>
#include <signal.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <xcb/randr.h>
#include <xcb/xcb.h>
#include <xcb/xcb_aux.h>
#include <xcb/xfixes.h>
#include <xcb/xinerama.h>
#include <xcb/xproto.h>
#include "backtrace.h"
#include "buffer.h"
#include "types.h"
#include "utils.h"
global_state_t global_state;
static void wm_setup_signal(void);
static void wm_check_other_wm(void);
static void wm_check_xcb_extensions(void);
static void wm_detect_monitor(void);
static void wm_scan_clients(void);
wm_t wm;
/** argv used to run wm */
static char **cmd_argv;
@@ -22,13 +34,7 @@ static char **cmd_argv;
/** A pipe that is used to asynchronously handle SIGCHLD */
static int sigchld_pipe[2];
static void wm_atexit(bool restart) { global_state.need_restart = restart; }
static void wm_restart(void) {
wm_atexit(true);
execvp(cmd_argv[0], cmd_argv);
fatal("execv() failed: %s", strerror(errno));
}
static void wm_atexit(bool restart) { wm.need_restart = restart; }
static gboolean restart_on_signal(gpointer data) {
wm_restart();
@@ -36,7 +42,7 @@ static gboolean restart_on_signal(gpointer data) {
}
static gboolean exit_on_signal(gpointer data) {
g_main_loop_quit(global_state.loop);
g_main_loop_quit(wm.loop);
return TRUE;
}
@@ -54,7 +60,26 @@ static void signal_child(int signum) {
assert(res == 1);
}
static void check_other_wm(void) {
void wm_setup_signal(void) {
g_unix_signal_add(SIGINT, exit_on_signal, NULL);
g_unix_signal_add(SIGTERM, exit_on_signal, NULL);
g_unix_signal_add(SIGHUP, restart_on_signal, NULL);
struct sigaction sa = {.sa_handler = signal_fatal, .sa_flags = SA_RESETHAND};
sigemptyset(&sa.sa_mask);
sigaction(SIGABRT, &sa, 0);
sigaction(SIGBUS, &sa, 0);
sigaction(SIGFPE, &sa, 0);
sigaction(SIGILL, &sa, 0);
sigaction(SIGSEGV, &sa, 0);
signal(SIGPIPE, SIG_IGN);
sa.sa_handler = signal_child;
sa.sa_flags = SA_NOCLDSTOP | SA_RESTART;
sigaction(SIGCHLD, &sa, 0);
}
void wm_check_other_wm(void) {
/* connect X server */
int default_screen;
xcb_connection_t *conn = xcb_connect(NULL, &default_screen);
@@ -77,41 +102,58 @@ static void check_other_wm(void) {
"SubstructureRedirect)");
}
global_state.xcb_conn = conn;
global_state.default_screen = default_screen;
global_state.screen = screen;
wm.xcb_conn = conn;
wm.default_screen = default_screen;
wm.screen = screen;
}
void wm_check_xcb_extensions(void) {
xcb_prefetch_extension_data(wm.xcb_conn, &xcb_xfixes_id);
xcb_prefetch_extension_data(wm.xcb_conn, &xcb_xinerama_id);
xcb_prefetch_extension_data(wm.xcb_conn, &xcb_randr_id);
const xcb_query_extension_reply_t *query;
query = xcb_get_extension_data(wm.xcb_conn, &xcb_xfixes_id);
wm.have_xfixes = query && query->present;
query = xcb_get_extension_data(wm.xcb_conn, &xcb_xinerama_id);
wm.have_xinerama = query && query->present;
query = xcb_get_extension_data(wm.xcb_conn, &xcb_randr_id);
wm.have_randr = query && query->present;
}
void wm_detect_monitor(void) {}
void wm_scan_clients(void) {}
void wm_restart(void) {
wm_atexit(true);
execvp(cmd_argv[0], cmd_argv);
fatal("execv() failed: %s", strerror(errno));
}
void wm_quit(void) {
if (g_main_loop_is_running(wm.loop)) {
g_main_loop_quit(wm.loop);
}
}
int main(int argc, char *argv[]) {
p_clear(&global_state, 1);
p_clear(&wm, 1);
cmd_argv = argv;
wm_setup_signal();
g_unix_signal_add(SIGINT, exit_on_signal, NULL);
g_unix_signal_add(SIGTERM, exit_on_signal, NULL);
g_unix_signal_add(SIGHUP, restart_on_signal, NULL);
wm_check_other_wm();
wm_check_xcb_extensions();
struct sigaction sa = {.sa_handler = signal_fatal, .sa_flags = SA_RESETHAND};
sigemptyset(&sa.sa_mask);
sigaction(SIGABRT, &sa, 0);
sigaction(SIGBUS, &sa, 0);
sigaction(SIGFPE, &sa, 0);
sigaction(SIGILL, &sa, 0);
sigaction(SIGSEGV, &sa, 0);
signal(SIGPIPE, SIG_IGN);
wm_detect_monitor();
sa.sa_handler = signal_child;
sa.sa_flags = SA_NOCLDSTOP | SA_RESTART;
sigaction(SIGCHLD, &sa, 0);
check_other_wm();
if (global_state.loop == NULL) {
global_state.loop = g_main_loop_new(NULL, FALSE);
g_main_loop_run(global_state.loop);
if (wm.loop == NULL) {
wm.loop = g_main_loop_new(NULL, FALSE);
g_main_loop_run(wm.loop);
}
g_main_loop_unref(global_state.loop);
global_state.loop = NULL;
g_main_loop_unref(wm.loop);
wm.loop = NULL;
wm_atexit(false);

26
src/wm.h Normal file
View File

@@ -0,0 +1,26 @@
#pragma once
#include "types.h"
typedef struct wm_t {
bool need_restart;
GMainLoop *loop;
client_t *client_list;
client_t *client_stack_list;
client_t *client_focused;
monitor_t *monitor_list;
monitor_t *current_monitor;
xcb_connection_t *xcb_conn;
xcb_screen_t *screen;
int default_screen;
bool have_xfixes;
bool have_xinerama;
bool have_randr;
} wm_t;
extern wm_t wm;
void wm_restart(void);
void wm_quit(void);

1
src/xwindow.h Normal file
View File

@@ -0,0 +1 @@
#pragma once