diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 4b94df3..8869308 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -28,14 +28,17 @@ pkg_check_modules(XCB REQUIRED xcb-cursor xcb-ewmh xcb-icccm + xcb-image xcb-keysyms xcb-util xcb-xfixes xcb-xinerama xcb-xrm ) +pkg_check_modules(RENDERER REQUIRED imlib2) add_subdirectory(xcb) +add_subdirectory(renderer) configure_file( "${CMAKE_CURRENT_SOURCE_DIR}/app.h.in" @@ -54,4 +57,5 @@ target_include_directories(${APP_NAME} target_link_libraries(${APP_NAME} PRIVATE ${GLIB_LIBRARIES} PRIVATE ${LIB_XCB_NAME} + PRIVATE ${LIB_RENDERER_NAME} ) diff --git a/src/include/renderer.h b/src/include/renderer.h new file mode 100644 index 0000000..920a3a7 --- /dev/null +++ b/src/include/renderer.h @@ -0,0 +1,10 @@ +#pragma once + +#include + +#include "types.h" + +uint32_t *generate_wallpaper(const wallpaper_config_t *wallpaper_config, + const monitor_t *monitors, const uint32_t index, + const uint32_t screen_width, + const uint32_t screen_height); diff --git a/src/include/utils.h b/src/include/utils.h index 830329f..22917d9 100644 --- a/src/include/utils.h +++ b/src/include/utils.h @@ -5,6 +5,9 @@ #define LENGTH(X) (sizeof(X) / sizeof(X)[0]) +#define MIN(a, b) (((a) < (b)) ? (a) : (b)) +#define MAX(a, b) (((a) > (b)) ? (a) : (b)) + void die(const char *format, ...); void *ecalloc(size_t number_of_member, size_t member_size); diff --git a/src/include/xcb.h b/src/include/xcb.h index 89dd3b8..80b73c1 100644 --- a/src/include/xcb.h +++ b/src/include/xcb.h @@ -16,6 +16,13 @@ struct monitor_rect_t { }; monitor_rect_t *detect_monitor_rect(void); +typedef struct rect_size_t { + uint32_t width; + uint32_t height; +} rect_size_t; +rect_size_t *get_screen_size(void); +void set_wallpaper(uint32_t *wallpaper_data); + typedef struct window_list_t { uint32_t count; xcb_window_t *list; diff --git a/src/main.c b/src/main.c index ad7d571..9d5ce4f 100644 --- a/src/main.c +++ b/src/main.c @@ -1,4 +1,5 @@ #include +#include #include #include #include @@ -7,11 +8,14 @@ #include #include "config.h" +#include "renderer.h" #include "types.h" #include "utils.h" #include "xcb.h" static void init_monitors(void); +static void init_wallpaper(void); +static void set_wallpaper_by_index(uint32_t index); static wallpaper_config_t *parse_wallpaper_config(const char **wallpapers); static void free_wallpaper_config(wallpaper_config_t *wallpaper_config); static void cleanup(void); @@ -19,9 +23,40 @@ static void cleanup(void); static monitor_t *monitors = NULL; static wallpaper_config_t *wallpaper_config = NULL; +/* 调试函数,开发完成后删除 */ +static void print_monitor_list_info(monitor_t *monitor_list) { + for (monitor_t *m = monitor_list; m; m = m->next) { + printf("mon[%u]: x: %d, y: %d, width: %u, height: %u\n", m->index, + m->monitor_x, m->monitor_y, m->monitor_width, m->monitor_height); + } +} +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; @@ -30,10 +65,12 @@ void init_monitors(void) { m->monitor_height = mr->height; m->index = i; - printf("mon[%u]: x: %d, y: %d, w: %u, h: %u\n", i, mr->x, mr->y, mr->width, - mr->height); - - if (!i) monitors = m; + if (prev_monitor) { + prev_monitor->next = m; + } else { + monitors = m; + } + prev_monitor = m; i++; monitor_rect_t *next_mr = mr->next; @@ -42,6 +79,25 @@ void init_monitors(void) { } } +void init_wallpaper(void) { + wallpaper_config = parse_wallpaper_config(wallpager_list); + if (!wallpaper_config) return; + + set_wallpaper_by_index(0); +} + +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); + set_wallpaper(wallpaper); + + free(screen_size); + free(wallpaper); +} + wallpaper_config_t *parse_wallpaper_config(const char **wallpapers) { if (!wallpapers) return NULL; @@ -105,26 +161,20 @@ int main(int argc, char *argv[]) { die("another window manager is already running"); } - wallpaper_config = parse_wallpaper_config(wallpager_list); - if (wallpaper_config) { - printf("wallpaper count: %u\n", wallpaper_config->count); - for (uint32_t i = 0; i < wallpaper_config->count; i++) { - printf("wallpaper[%02u]: %s\n", i, wallpaper_config->path_list[i]); - } - } - init_monitors(); + init_wallpaper(); window_list_t *window_list = scan_window_list(); - if (window_list) { - for (uint32_t i = 0; i < window_list->count; i++) { - printf("window[%u]: 0x%x\n", i, window_list->list[i]); - } - } else { - printf("no window\n"); - } + + print_monitor_list_info(monitors); + print_wallpaper_config(wallpaper_config); + print_window_list(window_list); free_window_list(window_list); + + GMainLoop *loop = g_main_loop_new(NULL, FALSE); + g_main_loop_run(loop); + cleanup(); return 0; diff --git a/src/renderer/CMakeLists.txt b/src/renderer/CMakeLists.txt new file mode 100644 index 0000000..c17b181 --- /dev/null +++ b/src/renderer/CMakeLists.txt @@ -0,0 +1,14 @@ +set(LIB_RENDERER "zswm-renderer") +set(LIB_RENDERER_NAME ${LIB_RENDERER} PARENT_SCOPE) + +add_library(${LIB_RENDERER} STATIC image.c) +target_include_directories(${LIB_RENDERER} SYSTEM + PRIVATE ${RENDERER_INCLUDE_DIRS} +) +target_include_directories(${LIB_RENDERER} + PRIVATE "${CMAKE_SOURCE_DIR}/include" +) + +target_link_libraries(${LIB_RENDERER} + PRIVATE ${RENDERER_LIBRARIES} +) diff --git a/src/renderer/image.c b/src/renderer/image.c new file mode 100644 index 0000000..cae6e23 --- /dev/null +++ b/src/renderer/image.c @@ -0,0 +1,95 @@ +#include +#include +#include +#include +#include +#include + +#include "renderer.h" +#include "types.h" +#include "utils.h" + +/** + * @brief 生成需要由 xcb 绘制的壁纸数据 + * @returns 壁纸数据,使用后记得释放 + */ +uint32_t *generate_wallpaper(const wallpaper_config_t *wallpaper_config, + const monitor_t *monitors, const uint32_t index, + const uint32_t screen_width, + const uint32_t screen_height) { + if (!wallpaper_config || !monitors) return NULL; + + Imlib_Image final_image = imlib_create_image(screen_width, screen_height); + if (!final_image) return NULL; + + imlib_context_set_image(final_image); + imlib_image_set_has_alpha(false); + + /* 填充背景色 */ + imlib_context_set_color(0, 0, 0, 255); + imlib_image_fill_rectangle(0, 0, screen_width, screen_height); + + const monitor_t *monitor = monitors; + uint32_t monitor_index = 0; + while (monitor) { + uint32_t img_index = (index + monitor_index) % wallpaper_config->count; + + Imlib_Image wallpaper_img = + imlib_load_image(wallpaper_config->path_list[img_index]); + if (!wallpaper_img) { + monitor = monitor->next; + monitor_index++; + continue; + } + + uint32_t dst_width = monitor->monitor_width; + uint32_t dst_height = monitor->monitor_height; + + /* 获取图片原始尺寸 */ + imlib_context_set_image(wallpaper_img); + uint32_t img_width = imlib_image_get_width(); + uint32_t img_height = imlib_image_get_height(); + + /* 计算缩放比例,保持原始宽高比 */ + float width_ratio = (float)dst_width / img_width; + float height_ratio = (float)dst_height / img_height; + float scale = MAX(width_ratio, height_ratio); + + uint32_t scaled_width = img_width * scale; + uint32_t scaled_height = img_height * scale; + + /* 图片居中显示,计算原图绘制区域 */ + int src_x = (scaled_width - dst_width) / (2 * scale); + int src_y = (scaled_height - dst_height) / (2 * scale); + int src_width = dst_width / scale; + int src_height = dst_height / scale; + + /* 绘制图片到最终图像上 */ + imlib_context_set_image(final_image); + imlib_blend_image_onto_image(wallpaper_img, 1, src_x, src_y, src_width, + src_height, monitor->monitor_x, + monitor->monitor_y, dst_width, dst_height); + + /* 释放图片资源 */ + imlib_context_set_image(wallpaper_img); + imlib_free_image(); + + monitor = monitor->next; + monitor_index++; + } + + /* 获取最终图像数据 */ + imlib_context_set_image(final_image); + uint32_t *data = imlib_image_get_data_for_reading_only(); + + /* 复制数据到自己分配的内存中(原数据会在图像释放后无效) */ + size_t data_size = screen_width * screen_height * sizeof(uint32_t); + uint32_t *result = malloc(data_size); + if (result) memcpy(result, data, data_size); + + /* 释放图像 */ + imlib_context_set_image(final_image); + imlib_free_image(); + + return result; +} diff --git a/src/xcb/xcb.c b/src/xcb/xcb.c index 7bdf2b8..9061428 100644 --- a/src/xcb/xcb.c +++ b/src/xcb/xcb.c @@ -2,13 +2,13 @@ #include #include -#include #include #include #include #include #include #include +#include #include #include #include @@ -30,7 +30,6 @@ bool has_other_wm_running(void) { const xcb_setup_t *setup = xcb_get_setup(conn); xcb_screen_iterator_t iter = xcb_setup_roots_iterator(setup); screen = iter.data; - printf("screen number: %d\n", iter.rem); uint32_t mask = XCB_CW_EVENT_MASK; xcb_params_cw_t params = { .event_mask = XCB_EVENT_MASK_LEAVE_WINDOW | XCB_EVENT_MASK_ENTER_WINDOW | @@ -124,6 +123,44 @@ monitor_rect_t *detect_monitor_rect(void) { return rect; } +/** + * @brief 获取整个屏幕的宽高 + * @returns 屏幕大小数据的指针,使用完后记得释放对应内存 + */ +rect_size_t *get_screen_size(void) { + rect_size_t *size = ecalloc(1, sizeof(rect_size_t)); + size->width = screen->width_in_pixels; + size->height = screen->height_in_pixels; + return size; +} + +void set_wallpaper(uint32_t *wallpaper_data) { + uint32_t width = screen->width_in_pixels; + uint32_t height = screen->height_in_pixels; + uint8_t depth = screen->root_depth; + xcb_window_t root = screen->root; + uint32_t data_size = width * height * sizeof(uint32_t); + uint8_t *data = (uint8_t *)wallpaper_data; + xcb_image_t *xcb_img = + xcb_image_create_native(conn, width, height, XCB_IMAGE_FORMAT_Z_PIXMAP, + depth, NULL, data_size, data); + xcb_pixmap_t pixmap = xcb_generate_id(conn); + xcb_create_pixmap(conn, depth, pixmap, root, width, height); + xcb_gcontext_t gc = xcb_generate_id(conn); + xcb_create_gc(conn, gc, pixmap, 0, NULL); + xcb_image_put(conn, pixmap, gc, xcb_img, 0, 0, 0); + xcb_change_window_attributes_value_list_t value_list = { + .background_pixmap = pixmap, + }; + xcb_change_window_attributes_aux(conn, root, XCB_CW_BACK_PIXMAP, &value_list); + + xcb_clear_area(conn, 0, root, 0, 0, width, height); + xcb_image_destroy(xcb_img); + xcb_free_gc(conn, gc); + + xcb_flush(conn); +} + static xcb_ewmh_connection_t *ewmh = NULL; static bool ewmh_init_failed = false; static xcb_window_t wm_check_window = XCB_NONE;