From ac22a2b5295c843b7c71cb68c278e6cef52061ed Mon Sep 17 00:00:00 2001 From: Zedhugh Chen Date: Sun, 22 Mar 2026 04:18:59 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E6=B7=BB=E5=8A=A0=E5=B1=8F=E5=B9=95?= =?UTF-8?q?=E5=8E=BB=E9=87=8D=E5=8A=9F=E8=83=BD=E5=B9=B6=E8=B0=83=E6=95=B4?= =?UTF-8?q?=E6=B5=8B=E8=AF=95=E7=94=A8=E4=BE=8B=E7=9B=AE=E5=BD=95=E7=BB=93?= =?UTF-8?q?=E6=9E=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CMakeLists.txt | 36 +++--------------- Makefile | 6 ++- src/backend/output_utils.c | 56 +++++++++++++++++++++++++++ src/backend/output_utils.h | 9 +++++ src/backend/x11/backend.c | 38 ++++++++++--------- tests/CMakeLists.txt | 2 + tests/backend/CMakeLists.txt | 19 ++++++++++ tests/backend/output_utils_test.c | 63 +++++++++++++++++++++++++++++++ tests/core/CMakeLists.txt | 34 +++++++++++++++++ tests/{ => core}/main.c | 0 10 files changed, 214 insertions(+), 49 deletions(-) create mode 100644 src/backend/output_utils.c create mode 100644 src/backend/output_utils.h create mode 100644 tests/CMakeLists.txt create mode 100644 tests/backend/CMakeLists.txt create mode 100644 tests/backend/output_utils_test.c create mode 100644 tests/core/CMakeLists.txt rename tests/{ => core}/main.c (100%) diff --git a/CMakeLists.txt b/CMakeLists.txt index 356ed4e..cf38372 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -6,7 +6,6 @@ set(CMAKE_C_STANDARD_REQUIRED ON) set(CMAKE_C_EXTENSIONS ON) set(APP_NAME "zdwm") -set(TEST_APP_NAME "zdwm-tests") set(SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/src") set(BUILD_DIR "${CMAKE_CURRENT_BINARY_DIR}") @@ -25,7 +24,7 @@ project(${APP_NAME} LANGUAGES C ) -enable_testing() +option(BUILD_TESTING "Build tests" OFF) add_executable(${APP_NAME} ${SOURCE_DIR}/wm.c @@ -54,15 +53,6 @@ add_executable(${APP_NAME} ${SOURCE_DIR}/tray.c ) -add_executable(${TEST_APP_NAME} - ${CMAKE_CURRENT_SOURCE_DIR}/tests/main.c - ${SOURCE_DIR}/utils.c - ${SOURCE_DIR}/backend/x11/backend.c - ${SOURCE_DIR}/core/layout.c - ${SOURCE_DIR}/core/runtime.c - ${SOURCE_DIR}/core/state.c -) - find_package(PkgConfig REQUIRED) pkg_check_modules(deps REQUIRED @@ -98,29 +88,11 @@ target_include_directories(${APP_NAME} PRIVATE ${BUILD_DIR} ) -target_include_directories(${TEST_APP_NAME} SYSTEM - PRIVATE ${deps_INCLUDE_DIRS} -) -target_include_directories(${TEST_APP_NAME} - PRIVATE ${SOURCE_DIR} - PRIVATE ${BUILD_DIR} -) - target_link_libraries(${APP_NAME} PRIVATE m PRIVATE ${deps_LIBRARIES} ) -target_link_libraries(${TEST_APP_NAME} - PRIVATE m - PRIVATE ${deps_LIBRARIES} -) - -add_test(NAME ${TEST_APP_NAME} COMMAND $) -set_tests_properties(${TEST_APP_NAME} PROPERTIES - ENVIRONMENT "DISPLAY=:3" -) - ## 配置项 # Check for backtrace_symbols() include(CheckSymbolExists) @@ -130,7 +102,6 @@ if(NOT HAS_EXECINFO) if(LIB_EXECINFO) set(HAS_EXECINFO TRUE) target_link_libraries(${APP_NAME} ${LIB_EXECINFO}) - target_link_libraries(${TEST_APP_NAME} ${LIB_EXECINFO}) endif() endif() if(HAS_EXECINFO) @@ -168,3 +139,8 @@ add_custom_target(generate_atoms DEPENDS ${BUILD_DIR}/atoms-extern.h ${BUILD_DIR}/atoms-intern.h ) add_dependencies(${APP_NAME} generate_atoms) + +if(BUILD_TESTING) + enable_testing() + add_subdirectory(tests) +endif() diff --git a/Makefile b/Makefile index d17f50d..4d4c323 100644 --- a/Makefile +++ b/Makefile @@ -39,7 +39,9 @@ build: prepare @cmake --build $(BUILD_DIR) @cp $(BUILD_DIR)/$(TARGET_NAME) $(TARGET) -test: build - @ctest --test-dir $(BUILD_DIR) +test: + cmake -S $(SRC_DIR) -B $(BUILD_DIR) -DBUILD_TESTING=ON + cmake --build $(BUILD_DIR) + ctest --test-dir $(BUILD_DIR) .PHONY: clean run wm prepare build install uninstall reinstall test diff --git a/src/backend/output_utils.c b/src/backend/output_utils.c new file mode 100644 index 0000000..7666a20 --- /dev/null +++ b/src/backend/output_utils.c @@ -0,0 +1,56 @@ +#include "backend/output_utils.h" + +#include +#include + +#include "core/backend.h" +#include "core/types.h" +#include "core/wm_desc.h" +#include "utils.h" + +static inline int32_t right(wm_rect_t a) { return a.x + a.width; } +static inline int32_t bottom(wm_rect_t a) { return a.y + a.height; } +static inline bool fully_contains(wm_rect_t outer, wm_rect_t inner) { + return inner.x >= outer.x && inner.y >= outer.y && + right(inner) <= right(outer) && bottom(inner) <= bottom(outer); +} + +wm_backend_detect_t *output_remove_duplication(const wm_output_info_t *output, + const size_t count) { + if (!output || count < 1) return nullptr; + + bool *remove = p_new(bool, count); + + for (int32_t i = (int32_t)count - 1; i >= 0; i--) { + if (remove[i]) continue; + for (int32_t j = i - 1; j >= 0; j--) { + if (remove[j]) continue; + if (fully_contains(output[j].geometry, output[i].geometry)) { + remove[i] = true; + } else if (fully_contains(output[i].geometry, output[j].geometry)) { + remove[j] = true; + } + } + } + + int32_t amount = 0; + for (size_t i = 0; i < count; i++) { + if (!remove[i]) amount++; + } + if (!amount) { + p_delete(&remove); + return nullptr; + } + + wm_output_info_t *list = p_new(wm_output_info_t, amount); + amount = 0; + for (size_t i = 0; i < count; i++) { + if (remove[i]) continue; + list[amount++] = output[i]; + } + p_delete(&remove); + wm_backend_detect_t *detect = p_new(wm_backend_detect_t, 1); + detect->outputs = list; + detect->output_count = amount; + return detect; +} diff --git a/src/backend/output_utils.h b/src/backend/output_utils.h new file mode 100644 index 0000000..969c61c --- /dev/null +++ b/src/backend/output_utils.h @@ -0,0 +1,9 @@ +#pragma once + +#include + +#include "core/backend.h" +#include "core/wm_desc.h" + +wm_backend_detect_t *output_remove_duplication(const wm_output_info_t *output, + const size_t count); diff --git a/src/backend/x11/backend.c b/src/backend/x11/backend.c index 7953f26..68ff6d9 100644 --- a/src/backend/x11/backend.c +++ b/src/backend/x11/backend.c @@ -1,3 +1,5 @@ +#include "core/backend.h" + #include #include #include @@ -9,7 +11,7 @@ #include #include -#include "core/backend.h" +#include "backend/output_utils.h" #include "core/wm_desc.h" #include "utils.h" @@ -215,28 +217,30 @@ static bool detect_monitor_by_xinerama(const wm_backend_t *backend, } wm_backend_detect_t *wm_backend_detect(wm_backend_t *backend) { - wm_backend_detect_t *detect = p_new(wm_backend_detect_t, 1); - wm_output_info_t *outputs = nullptr; size_t count = 0; if (detect_monitor_by_randr(backend, &outputs, &count)) { - detect->output_count = count; - detect->outputs = outputs; - } else if (detect_monitor_by_xinerama(backend, &outputs, &count)) { - detect->output_count = count; - detect->outputs = outputs; - } else { - wm_output_info_t *output = p_new(wm_output_info_t, 1); - output->geometry.x = 0; - output->geometry.y = 0; - output->geometry.width = backend->screen->width_in_pixels; - output->geometry.height = backend->screen->height_in_pixels; - - detect->outputs = output; - detect->output_count = 1; + wm_backend_detect_t *detect = output_remove_duplication(outputs, count); + p_delete(&outputs); + return detect; } + if (detect_monitor_by_xinerama(backend, &outputs, &count)) { + wm_backend_detect_t *detect = output_remove_duplication(outputs, count); + p_delete(&outputs); + return detect; + } + + wm_output_info_t *output = p_new(wm_output_info_t, 1); + output->geometry.x = 0; + output->geometry.y = 0; + output->geometry.width = backend->screen->width_in_pixels; + output->geometry.height = backend->screen->height_in_pixels; + + wm_backend_detect_t *detect = p_new(wm_backend_detect_t, 1); + detect->outputs = output; + detect->output_count = 1; return detect; } diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt new file mode 100644 index 0000000..fb0d203 --- /dev/null +++ b/tests/CMakeLists.txt @@ -0,0 +1,2 @@ +add_subdirectory(backend) +add_subdirectory(core) diff --git a/tests/backend/CMakeLists.txt b/tests/backend/CMakeLists.txt new file mode 100644 index 0000000..56a53e8 --- /dev/null +++ b/tests/backend/CMakeLists.txt @@ -0,0 +1,19 @@ +set(OUTPUT_UTILS_TEST_APP_NAME "zdwm-output-utils-tests") + +add_executable(${OUTPUT_UTILS_TEST_APP_NAME} + ${CMAKE_CURRENT_SOURCE_DIR}/output_utils_test.c + ${SOURCE_DIR}/backend/output_utils.c +) + +target_include_directories(${OUTPUT_UTILS_TEST_APP_NAME} + PRIVATE ${SOURCE_DIR} + PRIVATE ${BUILD_DIR} +) + +target_link_libraries(${OUTPUT_UTILS_TEST_APP_NAME} + PRIVATE m +) + +add_test(NAME ${OUTPUT_UTILS_TEST_APP_NAME} + COMMAND $ +) diff --git a/tests/backend/output_utils_test.c b/tests/backend/output_utils_test.c new file mode 100644 index 0000000..b014de1 --- /dev/null +++ b/tests/backend/output_utils_test.c @@ -0,0 +1,63 @@ +#include +#include + +#include "backend/output_utils.h" +#include "utils.h" + +static void assert_output_geometry(const wm_output_info_t *output, int32_t x, + int32_t y, int32_t width, + int32_t height) { + assert(output); + assert(output->geometry.x == x); + assert(output->geometry.y == y); + assert(output->geometry.width == width); + assert(output->geometry.height == height); +} + +static void destroy_detect_result(wm_backend_detect_t *detect) { + assert(detect); + for (size_t i = 0; i < detect->output_count; i++) { + p_delete(&detect->outputs[i].name); + } + p_delete(&detect->outputs); + free(detect); +} + +static void test_output_remove_duplication_invalid_input(void) { + wm_output_info_t output = {0}; + + assert(output_remove_duplication(nullptr, 0) == nullptr); + assert(output_remove_duplication(&output, 0) == nullptr); +} + +static void test_output_remove_duplication_keeps_non_nested_outputs(void) { + wm_output_info_t outputs[] = { + { + .geometry = {.x = 0, .y = 0, .width = 3840, .height = 1080}, + }, + { + .geometry = {.x = 0, .y = 0, .width = 1920, .height = 1080}, + }, + { + .geometry = {.x = 1920, .y = 0, .width = 1920, .height = 1080}, + }, + { + .geometry = {.x = 3840, .y = 0, .width = 1280, .height = 1024}, + }, + }; + + wm_backend_detect_t *detect = + output_remove_duplication(outputs, countof(outputs)); + assert(detect); + assert(detect->output_count == 2); + assert_output_geometry(&detect->outputs[0], 0, 0, 3840, 1080); + assert_output_geometry(&detect->outputs[1], 3840, 0, 1280, 1024); + + destroy_detect_result(detect); +} + +int main(void) { + test_output_remove_duplication_invalid_input(); + test_output_remove_duplication_keeps_non_nested_outputs(); + return 0; +} diff --git a/tests/core/CMakeLists.txt b/tests/core/CMakeLists.txt new file mode 100644 index 0000000..686a8ba --- /dev/null +++ b/tests/core/CMakeLists.txt @@ -0,0 +1,34 @@ +set(TEST_APP_NAME "zdwm-tests") + +add_executable(${TEST_APP_NAME} + ${CMAKE_CURRENT_SOURCE_DIR}/main.c + ${SOURCE_DIR}/utils.c + ${SOURCE_DIR}/backend/output_utils.c + ${SOURCE_DIR}/backend/x11/backend.c + ${SOURCE_DIR}/core/layout.c + ${SOURCE_DIR}/core/runtime.c + ${SOURCE_DIR}/core/state.c +) + +target_include_directories(${TEST_APP_NAME} SYSTEM + PRIVATE ${deps_INCLUDE_DIRS} +) +target_include_directories(${TEST_APP_NAME} + PRIVATE ${SOURCE_DIR} + PRIVATE ${BUILD_DIR} +) + +target_link_libraries(${TEST_APP_NAME} + PRIVATE m + PRIVATE ${deps_LIBRARIES} +) + +if(HAS_EXECINFO AND LIB_EXECINFO) + target_link_libraries(${TEST_APP_NAME} PRIVATE ${LIB_EXECINFO}) +endif() + +add_test(NAME ${TEST_APP_NAME} COMMAND $) + +set_tests_properties(${TEST_APP_NAME} PROPERTIES + ENVIRONMENT "DISPLAY=:3" +) diff --git a/tests/main.c b/tests/core/main.c similarity index 100% rename from tests/main.c rename to tests/core/main.c