diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index a7ec5ba..88d69fb 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -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 ) diff --git a/src/config.h.in b/src/app.h.in similarity index 100% rename from src/config.h.in rename to src/app.h.in diff --git a/src/backtrace.c b/src/backtrace.c index 26ca326..4fbbe65 100644 --- a/src/backtrace.c +++ b/src/backtrace.c @@ -1,7 +1,7 @@ #include "backtrace.h" +#include "app.h" #include "buffer.h" -#include "config.h" #ifdef HAS_EXECINFO #include @@ -9,7 +9,6 @@ #define MAX_STACK_SIZE 32 - /** * Get a backtrace. * @param buffer The buffer to fill with backtrace. diff --git a/src/bar.h b/src/bar.h new file mode 100644 index 0000000..3f59c93 --- /dev/null +++ b/src/bar.h @@ -0,0 +1,2 @@ +#pragma once + diff --git a/src/base.h b/src/base.h new file mode 100644 index 0000000..a748914 --- /dev/null +++ b/src/base.h @@ -0,0 +1,13 @@ +#pragma once + +#include + +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; diff --git a/src/client.c b/src/client.c new file mode 100644 index 0000000..f679c0d --- /dev/null +++ b/src/client.c @@ -0,0 +1 @@ +#include "client.h" diff --git a/src/client.h b/src/client.h new file mode 100644 index 0000000..9943dda --- /dev/null +++ b/src/client.h @@ -0,0 +1,45 @@ +#pragma once + +#include + +#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); diff --git a/src/color.h b/src/color.h new file mode 100644 index 0000000..746261c --- /dev/null +++ b/src/color.h @@ -0,0 +1,22 @@ +#pragma once + +#include + +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); diff --git a/src/draw.h b/src/draw.h new file mode 100644 index 0000000..45dcbb0 --- /dev/null +++ b/src/draw.h @@ -0,0 +1,3 @@ +#pragma once + + diff --git a/src/monitor.c b/src/monitor.c new file mode 100644 index 0000000..3ac0a1c --- /dev/null +++ b/src/monitor.c @@ -0,0 +1,3 @@ +#include "monitor.h" + + diff --git a/src/monitor.h b/src/monitor.h new file mode 100644 index 0000000..e43f315 --- /dev/null +++ b/src/monitor.h @@ -0,0 +1,7 @@ +#pragma once + +#include + +#include "types.h" + +void monitor_select_tag(monitor_t *monitor, uint32_t tag_mask); diff --git a/src/types.h b/src/types.h index 00e6320..e9effce 100644 --- a/src/types.h +++ b/src/types.h @@ -1,16 +1,74 @@ #pragma once #include +#include #include #include -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; +}; diff --git a/src/utils.c b/src/utils.c index ece8992..9154906 100644 --- a/src/utils.c +++ b/src/utils.c @@ -5,7 +5,7 @@ #include #include -#include "config.h" +#include "app.h" const char *current_time_str(void) { static char buffer[25]; diff --git a/src/main.c b/src/wm.c similarity index 60% rename from src/main.c rename to src/wm.c index 18dbf8a..856fc49 100644 --- a/src/main.c +++ b/src/wm.c @@ -1,20 +1,32 @@ +#include "wm.h" + #include #include #include #include +#include +#include #include #include #include +#include #include #include +#include +#include #include #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); diff --git a/src/wm.h b/src/wm.h new file mode 100644 index 0000000..7f9a29e --- /dev/null +++ b/src/wm.h @@ -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); diff --git a/src/xwindow.h b/src/xwindow.h new file mode 100644 index 0000000..6f70f09 --- /dev/null +++ b/src/xwindow.h @@ -0,0 +1 @@ +#pragma once