窗口管理器状态机设计
This commit is contained in:
@@ -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
|
||||
)
|
||||
|
||||
@@ -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.
|
||||
|
||||
13
src/base.h
Normal file
13
src/base.h
Normal 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
1
src/client.c
Normal file
@@ -0,0 +1 @@
|
||||
#include "client.h"
|
||||
45
src/client.h
Normal file
45
src/client.h
Normal 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
22
src/color.h
Normal 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
3
src/draw.h
Normal file
@@ -0,0 +1,3 @@
|
||||
#pragma once
|
||||
|
||||
|
||||
3
src/monitor.c
Normal file
3
src/monitor.c
Normal file
@@ -0,0 +1,3 @@
|
||||
#include "monitor.h"
|
||||
|
||||
|
||||
7
src/monitor.h
Normal file
7
src/monitor.h
Normal file
@@ -0,0 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include "types.h"
|
||||
|
||||
void monitor_select_tag(monitor_t *monitor, uint32_t tag_mask);
|
||||
74
src/types.h
74
src/types.h
@@ -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;
|
||||
};
|
||||
|
||||
@@ -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];
|
||||
|
||||
@@ -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
26
src/wm.h
Normal 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
1
src/xwindow.h
Normal file
@@ -0,0 +1 @@
|
||||
#pragma once
|
||||
Reference in New Issue
Block a user