#include #include #include #include #include #include #include #include #include #include #include #include #include "action.h" #include "config.h" #include "event-adapter.h" #include "renderer.h" #include "types.h" #include "utils.h" #include "xcb.h" static void init_monitors(void); static char **get_monitor_tags(uint32_t monitor_count, uint32_t monitor_index); static void init_wallpaper(void); static gboolean update_wallpaper(gpointer user_data); static void set_wallpaper_by_index(uint32_t index); static wallpaper_config_t *parse_wallpaper_config(void); static void free_wallpaper_config(wallpaper_config_t *wallpaper_config); static void init_color_set(void); static void parse_color(const char *hex, color_t *color); static void init_monitor_bar(void); static void get_xresource_config(void); static void update_bars(void); static void init_xcb_event_loop(void); static void xcb_event_handler(generic_event_t *event, void *user_data); static void key_press(key_press_event_t *event); static gboolean handle_xcb_event(GIOChannel *channel, GIOCondition condition, gpointer userdata); static void cleanup(void); static monitor_t *monitors = NULL; static monitor_t *current_monitor = NULL; static wallpaper_config_t *wallpaper_config = NULL; static color_set_t *color_set = NULL; static GMainLoop *loop = NULL; static event_adapter_t *adapter = NULL; static char *font_family = NULL; static uint32_t font_size = default_font_size; static uint32_t dpi = dpi_fallback; static uint32_t bar_y_pad = bar_y_padding; static uint32_t tag_x_pad = tag_x_padding; static uint32_t bar_height = 0; static bool need_restart = false; void spawn(const user_action_arg_t *arg) { g_spawn_command_line_async(arg->ptr, NULL); } void quit(const user_action_arg_t *arg) { need_restart = arg->b; g_main_loop_quit(loop); } /* 调试函数,开发完成后删除 */ static void print_monitor_list_info(monitor_t *monitor_list) { printf("========================= monitor info =========================\n"); for (monitor_t *m = monitor_list; m; m = m->next) { printf("========== mon[%u]:\n", m->index); printf("x: %d, y: %d, width: %u, height: %u\n", m->monitor_x, m->monitor_y, m->monitor_width, m->monitor_height); printf("mon[%u] tags: ", m->index); for (int i = 0; m->tags[i]; i++) { printf("%s%s", i > 0 ? ", " : "", m->tags[i]); } printf("\n"); } printf("======================= monitor info end =======================\n"); } static void print_color(const char *prompt, const color_t *color) { const char *p = strlen(prompt) ? " " : ""; printf("%s%srgba: #%08x, argb: #%08x\n", prompt, p, color->rgba, color->argb); } static void print_color_set(color_set_t *set) { print_color("== bar bg:", &set->bar_bg); print_color("== tag bg:", &set->tag_bg); print_color("== active tag bg:", &set->active_tag_bg); print_color("== tag color:", &set->tag_color); print_color("== active tag color:", &set->active_tag_color); print_color("== layout bg:", &set->layout_color); print_color("== client name color:", &set->client_name_color); print_color("== active client name color:", &set->active_client_name_color); print_color("== status color:", &set->status_color); print_color("== border color:", &set->border_color); print_color("== active border color:", &set->active_border_color); } static void print_wallpaper_config(wallpaper_config_t *config) { if (!config) { printf("no wallpaper\n"); return; } printf("wallpaper count: %u\n", config->count); for (uint32_t i = 0; i < config->count; i++) { printf("wallpaper[%02u]: %s\n", i, config->path_list[i]); } } static void print_window_list(window_list_t *window_list) { if (!window_list) { printf("no window\n"); return; } for (uint32_t i = 0; i < window_list->count; i++) { printf("window[%u]: 0x%x\n", i, window_list->list[i]); } } /* 调试函数结束 */ void init_monitors(void) { monitor_rect_t *mr = detect_monitor_rect(); uint32_t i = 0; monitor_t *prev_monitor = NULL; while (mr) { monitor_t *m = ecalloc(1, sizeof(monitor_t)); m->monitor_x = mr->x; m->monitor_y = mr->y; m->monitor_width = mr->width; m->monitor_height = mr->height; m->index = i; if (prev_monitor) { prev_monitor->next = m; } else { monitors = m; current_monitor = m; } prev_monitor = m; i++; monitor_rect_t *next_mr = mr->next; free(mr); mr = next_mr; } for (monitor_t *m = monitors; m; m = m->next) { m->tags = get_monitor_tags(i, m->index); m->selected_tag = 0x1; } } char **get_monitor_tags(uint32_t monitor_count, uint32_t monitor_index) { GStrvBuilder *builder = g_strv_builder_new(); const char *const *tags = NULL; /* 倒序遍历,达到后面的配置覆盖前面相同配置的效果 */ for (int i = LENGTH(monitor_tags) - 1; i >= 0; i--) { const char *const *const *config = monitor_tags[i]; uint32_t monitor_count_in_tags_config = 0; while (config[monitor_count_in_tags_config]) monitor_count_in_tags_config++; if (monitor_count_in_tags_config != monitor_count) continue; tags = config[monitor_index]; break; const char *const *const tags = config[monitor_index]; for (int j = 0; tags[j]; j++) g_strv_builder_add(builder, tags[j]); return g_strv_builder_unref_to_strv(builder); } if (tags == NULL) tags = default_monitor_tags; for (int i = 0; tags[i]; i++) g_strv_builder_add(builder, tags[i]); return g_strv_builder_unref_to_strv(builder); } void init_wallpaper(void) { wallpaper_config = parse_wallpaper_config(); if (!wallpaper_config) return; update_wallpaper(wallpaper_config); if (!wallpaper_interval) return; g_timeout_add_seconds(wallpaper_interval, update_wallpaper, wallpaper_config); } gboolean update_wallpaper(gpointer user_data) { wallpaper_config_t *wc = user_data; if (!wc) return false; static uint32_t index = 0; set_wallpaper_by_index(index); uint32_t monitor_count = 0; for (monitor_t *m = monitors; m; m = m->next) monitor_count++; /* 屏幕比壁纸多则不动态切换 */ if (!monitor_count || monitor_count >= wc->count) return false; if (monitor_count) { index += monitor_count; index %= wc->count; } return true; } void set_wallpaper_by_index(uint32_t index) { if (!wallpaper_config) return; rect_size_t *screen_size = get_screen_size(); uint32_t *wallpaper = generate_wallpaper( wallpaper_config, monitors, index, screen_size->width, screen_size->height); if (wallpaper) { double start = get_time(); set_wallpaper(wallpaper); double end = get_time(); free(wallpaper); printf("set wallpaper cost: %fs\n", end - start); } free(screen_size); } wallpaper_config_t *parse_wallpaper_config(void) { if (!LENGTH(wallpapers)) return NULL; wallpaper_config_t *wc = ecalloc(1, sizeof(wallpaper_config_t)); GStrvBuilder *builder = g_strv_builder_new(); for (int i = 0; i < LENGTH(wallpapers); i++) { wordexp_t p; if (wordexp(wallpapers[i], &p, 0)) continue; for (size_t i = 0; i < p.we_wordc; i++) { size_t len = strlen(p.we_wordv[i]) + 1; char *realpath = ecalloc(len, sizeof(char)); strncpy(realpath, p.we_wordv[i], len); g_strstrip(realpath); if (file_exist(realpath)) g_strv_builder_add(builder, realpath); free(realpath); } wordfree(&p); } wc->path_list = g_strv_builder_unref_to_strv(builder); wc->count = g_strv_length(wc->path_list); if (wc->count) return wc; free_wallpaper_config(wc); return NULL; } void free_wallpaper_config(wallpaper_config_t *wallpaper_config) { if (!wallpaper_config) return; g_strfreev(wallpaper_config->path_list); free(wallpaper_config); } void init_color_set(void) { color_set = ecalloc(1, sizeof(color_set_t)); char *bar_background = NULL; char *tag_background = NULL; char *active_tag_background = NULL; char *tag = NULL; char *active_tag = NULL; char *layout = NULL; char *client_name = NULL; char *active_client_name = NULL; char *status = NULL; char *border = NULL; char *active_border = NULL; get_xresource_string("zswm.color.bar_bg", &bar_background, bar_bg); get_xresource_string("zswm.color.tag_bg", &tag_background, tag_bg); get_xresource_string("zswm.color.active_tag_bg", &active_tag_background, active_tag_bg); get_xresource_string("zswm.color.tag", &tag, tag_color); get_xresource_string("zswm.color.active_tag", &active_tag, active_tag_color); get_xresource_string("zswm.color.layout", &layout, layout_color); get_xresource_string("zswm.color.client_name", &client_name, client_name_color); get_xresource_string("zswm.color.active_client_name", &active_client_name, active_client_name_color); get_xresource_string("zswm.color.status", &status, status_color); get_xresource_string("zswm.color.border", &border, border_color); get_xresource_string("zswm.color.active_border", &active_border, active_border_color); parse_color(bar_background, &color_set->bar_bg); parse_color(tag_background, &color_set->tag_bg); parse_color(active_tag_background, &color_set->active_tag_bg); parse_color(tag, &color_set->tag_color); parse_color(active_tag, &color_set->active_tag_color); parse_color(layout, &color_set->layout_color); parse_color(client_name, &color_set->client_name_color); parse_color(active_client_name, &color_set->active_client_name_color); parse_color(status, &color_set->status_color); parse_color(border, &color_set->border_color); parse_color(active_border, &color_set->active_border_color); free(bar_background); free(tag_background); free(active_tag_background); free(tag); free(active_tag); free(layout); free(client_name); free(active_client_name); free(status); free(border); free(active_border); } void parse_color(const char *hex, color_t *color) { bool success = hex_color_to_rgba(hex, &color->rgba); if (!success) die("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); } void init_monitor_bar(void) { get_xresource_config(); uint32_t font_height = get_font_height(font_family, font_size, dpi); bar_height = font_height + bar_y_pad * 2; for (monitor_t *m = monitors; m; m = m->next) { bar_cairo_params_t *p = create_bar_window(m->monitor_x, m->monitor_y, m->monitor_width, bar_height, color_set->bar_bg.argb, cursor_normal); m->window_x = m->monitor_x; m->window_width = m->monitor_width; m->window_y = m->monitor_y + bar_height; m->window_height = m->monitor_height - bar_height; m->bar_window = p->window; cairo_surface_t *surface = cairo_xcb_surface_create( p->conn, p->window, p->visual, m->monitor_width, bar_height); cairo_t *cr = cairo_create(surface); cairo_surface_destroy(surface); free(p); cairo_status_t status = cairo_status(cr); if (status != CAIRO_STATUS_SUCCESS) { die("cannot create cairo context: %s\n", cairo_status_to_string(status)); } m->cr = cr; } printf("font height: %u, bar height: %u\n", font_height, bar_height); } void get_xresource_config(void) { get_xresource_uint32("Xft.dpi", &dpi); get_xresource_string("zswm.font_family", &font_family, default_font_family); get_xresource_uint32("zswm.font_size", &font_size); get_xresource_uint32("zswm.bar_y_padding", &bar_y_pad); get_xresource_uint32("zswm.tag_x_padding", &tag_x_pad); printf("dpi: %u, font family: %s, font size: %u\n", dpi, font_family, font_size); clean_xresource(); } void update_bars(void) { for (monitor_t *m = monitors; m; m = m->next) { draw_bar(m, m == current_monitor, color_set, bar_height, tag_x_padding); } flush_xcb_connection(); } void init_xcb_event_loop(void) { adapter = create_event_adapter(); event_adapter_set_handler(adapter, xcb_event_handler, NULL); GIOChannel *channel = g_io_channel_unix_new(get_xcb_connection_fd()); g_io_channel_set_encoding(channel, NULL, NULL); GIOCondition cond = G_IO_IN | G_IO_HUP | G_IO_ERR; g_io_add_watch(channel, cond, handle_xcb_event, NULL); g_io_channel_unref(channel); } void xcb_event_handler(generic_event_t *event, void *user_data) { switch (event->type) { case EVENT_TYPE_KEY_PRESS: key_press((key_press_event_t *)event); break; default: break; } } void key_press(key_press_event_t *event) { const keybinding_t *key = NULL; for (int i = 0; i < LENGTH(keys); i++) { key = &keys[i]; if (key->key_symbol == event->key_symbol && key->modifiers == event->modifiers && key->func) { key->func(&key->arg); } } } gboolean handle_xcb_event(GIOChannel *channel, GIOCondition condition, gpointer userdata) { if (condition & (G_IO_HUP | G_IO_ERR)) { g_main_loop_quit(loop); return false; } event_adapter_poll(adapter); return true; } void cleanup(void) { clean_pango_context(); free_wallpaper_config(wallpaper_config); wallpaper_config = NULL; free(color_set); color_set = NULL; free(font_family); font_family = NULL; monitor_t *m = monitors; client_t *c = m->clients; while (m) { while (c) { client_t *tc = c->next; free(c); c = tc; } monitor_t *next_monitor = m->next; if (m->cr) cairo_destroy(m->cr); destroy_window(m->bar_window); g_strfreev(m->tags); free(m); m = next_monitor; } monitors = NULL; destroy_event_adapter(adapter); adapter = NULL; clean_xcb(); } int main(int argc, char *argv[]) { if (has_other_wm_running()) { die("another window manager is already running"); } init_monitors(); init_color_set(); init_monitor_bar(); set_root_cursor(cursor_normal); init_wallpaper(); update_bars(); window_list_t *window_list = scan_window_list(); print_monitor_list_info(monitors); print_color_set(color_set); print_wallpaper_config(wallpaper_config); print_window_list(window_list); free_window_list(window_list); grab_keybindings(keys, LENGTH(keys)); init_xcb_event_loop(); loop = g_main_loop_new(NULL, FALSE); g_main_loop_run(loop); cleanup(); if (need_restart) { need_restart = false; execvp(argv[0], argv); } return 0; }