diff --git a/CMakeLists.txt b/CMakeLists.txt index 2af113c..abe7f2e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -8,7 +8,7 @@ find_package(X11 REQUIRED) set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -fsanitize=address -pg") -add_executable(aswm +add_library(libaswm src/aswm/aswm.c src/aswm/mapper/mapper.c src/aswm/event_handlers/create.c @@ -21,5 +21,25 @@ add_executable(aswm src/aswm/event_handlers/message.c src/aswm/log/log.c ) -target_include_directories(aswm PUBLIC ${X11_INCLUDE_DIR} src) -target_link_libraries(aswm ${X11_LIBRARIES}) +target_include_directories(libaswm PUBLIC ${X11_INCLUDE_DIR} src) +target_link_libraries(libaswm ${X11_LIBRARIES}) + +add_executable(aswm src/aswm/main.c) +target_link_libraries(aswm libaswm) + +include(FetchContent) + +FetchContent_Declare( + unity + GIT_REPOSITORY https://github.com/ThrowTheSwitch/Unity.git + GIT_TAG v2.6.1 +) + +FetchContent_MakeAvailable(unity) + +add_executable(test_aswm test/test_aswm.c) +target_include_directories(test_aswm PUBLIC unity) +target_link_libraries(test_aswm libaswm unity) + +enable_testing() +add_test(aswm test_aswm) diff --git a/src/aswm/aswm.c b/src/aswm/aswm.c index ec6304b..ff5dbfe 100644 --- a/src/aswm/aswm.c +++ b/src/aswm/aswm.c @@ -12,6 +12,7 @@ #include "event_handlers/property.h" #include "event_handlers/message.h" #include "log/error.h" +#include "aswm/log/log.h" Aswm _aswm; @@ -50,6 +51,8 @@ void aswm_open() { printf("X Root window %lu\n", x_root_window); printf("ASWM Root window %lu\n", _aswm.root_window); + log_tree(&_aswm, DefaultRootWindow(_aswm.display), 1); + atexit(aswm_close); } @@ -204,9 +207,3 @@ void aswm_activate_window(Window window) { /* XSendEvent(_aswm.display, _aswm.root_window, True, 0 [> N. A. <], &event); */ XSetInputFocus(_aswm.display, window, RevertToParent, CurrentTime); } - -int main(int argc, char** argv) { - aswm_open(); - aswm_event_loop(); - aswm_close(); -} diff --git a/src/aswm/aswm.h b/src/aswm/aswm.h index 97be4ee..80a2134 100644 --- a/src/aswm/aswm.h +++ b/src/aswm/aswm.h @@ -23,6 +23,4 @@ void aswm_event_loop(); void aswm_close(); void aswm_activate_window(Window window); -int main(int argc, char** argv); - #endif diff --git a/src/aswm/event_handlers/message.c b/src/aswm/event_handlers/message.c new file mode 100644 index 0000000..6f0b083 --- /dev/null +++ b/src/aswm/event_handlers/message.c @@ -0,0 +1,20 @@ +#include "message.h" +#include +#include + +void OnClientMessage(Aswm* aswm, XClientMessageEvent* e) { + // TODO: handle this + printf("Client message for window %lu:\n", e->window); + char* message_type = XGetAtomName(e->display, e->message_type); + printf("\t%s\n", message_type); + if(strcmp(message_type, "_NET_ACTIVE_WINDOW") == 0) { + printf("\t\twindow to activate: %lu\n", e->window); + printf("\t\tsource indication: %lu\n", e->data.l[0]); + printf("\t\ttimestamp: %lu\n", e->data.l[1]); + printf("\t\trequestor's currently active window: %lu\n", e->data.l[2]); + aswm_activate_window(e->window); + } else { + printf("\t-> Unhandled\n"); + } + XFree(message_type); +} diff --git a/src/aswm/event_handlers/message.h b/src/aswm/event_handlers/message.h new file mode 100644 index 0000000..253a574 --- /dev/null +++ b/src/aswm/event_handlers/message.h @@ -0,0 +1,8 @@ +#ifndef ASWM_MESSAGE +#define ASWM_MESSAGE + +#include "aswm/aswm.h" + +void OnClientMessage(Aswm* aswm, XClientMessageEvent* e); + +#endif diff --git a/src/aswm/main.c b/src/aswm/main.c new file mode 100644 index 0000000..6535ada --- /dev/null +++ b/src/aswm/main.c @@ -0,0 +1,7 @@ +#include "aswm.h" + +int main(int argc, char** argv) { + aswm_open(); + aswm_event_loop(); + aswm_close(); +} diff --git a/test/test_aswm.c b/test/test_aswm.c new file mode 100644 index 0000000..54d3f37 --- /dev/null +++ b/test/test_aswm.c @@ -0,0 +1,132 @@ +#include "unity.h" +#include "aswm/aswm.h" +#include +#include +#include +#include +#include + +unsigned int display_count = 100; +char display_name[5]; +char display_fd[128]; +int xephyr_pid; +int aswm_pid; +Display* test_display; +Window test_root; + +void run_xephyr() { + FILE* displayfd = fopen(display_fd, "w"); // Used to store the name of the + // display on which Xephyr is + // running once it's ready. + char dfd[256]; // File descriptor of the opened temporary file. + sprintf(dfd, "%i", displayfd->_fileno); + char* args[] = {"Xephyr", display_name, "-br", "-ac", "-screen", "1440x810", "-displayfd", dfd, "-nopn", NULL}; + execvp(args[0], args); +} + +void setUp(void) { + tmpnam(display_fd); // Creates a temporary file name + sprintf(display_name, ":%u", display_count); // Loads initial display count + // in display name + // Tries to create the first xephyr instance + xephyr_pid = fork(); + if(xephyr_pid == 0) { + run_xephyr(); + } else { + printf("Trying to run Xephyr instance on :%u...\n", display_count); + } + + // The following loop waits for one of those two things: + // 1. The display name on which Xephyr is running is loaded into content, + // what indicates that the X server is ready. + // 2. The Xephyr process as stopped, what indicates that the proposed + // display count was already in use. In that case, try again after + // incrementing display count, until valid Xephyr display is running. + char content[256]; + while(strlen(content) == 0) { + FILE* displayfd = fopen(display_fd, "r"); + if(displayfd != NULL) { + fgets(content, 256, displayfd); + fseek(displayfd, 0, SEEK_SET); + fclose(displayfd); + } + + int stat_loc; + if(waitpid(xephyr_pid, &stat_loc, WNOHANG) < 0) { + display_count++; + sprintf(display_name, ":%u", display_count); + xephyr_pid = fork(); + if(xephyr_pid == 0) { + run_xephyr(); + } else { + printf("Trying to run Xephyr instance on :%u...\n", display_count); + } + } + } + sscanf(content, "%i", &display_count); + + aswm_pid = fork(); + if(aswm_pid == 0) { + printf("Running aswm instance on %s...\n", display_name); + char* args[] = {"./aswm", NULL}; + char display_env[256]; + sprintf(display_env, "DISPLAY=%s", display_name); + char* envp[] = {display_env, NULL}; + execve(args[0], args, envp); + } + // TODO: condition in aswm to ensure it is running + sleep(2); + + test_display = XOpenDisplay(display_name); + test_root = DefaultRootWindow(test_display); +} + +void tearDown(void) { + XCloseDisplay(test_display); + + kill(aswm_pid, SIGTERM); + kill(xephyr_pid, SIGTERM); +} + +void run_aswm(void) { + int stat_loc; + // Ensures aswm is running + TEST_ASSERT_EQUAL_INT(0, waitpid(aswm_pid, &stat_loc, WNOHANG)); +} + +void create_default_top_level_window(void) { + Window window = XCreateSimpleWindow(test_display, test_root, + 10 /*x*/, 10 /*y*/, 128 /*w*/, 128 /*h*/, + 2 /*border width*/, CopyFromParent /*depth*/, 0xFF00FF00 /* background */); + XMapWindow(test_display, window); + XSelectInput(test_display, window, StructureNotifyMask | VisibilityNotify); + + bool mapped = false; + int visibility_received = false; + int visibility; + while(!mapped && !visibility_received) { + XEvent e; + XNextEvent(test_display, &e); + + switch(e.type) { + case MapNotify: + mapped = true; + break; + case VisibilityNotify: + visibility_received = true; + visibility = e.xvisibility.state; + break; + default: + } + } + + TEST_ASSERT_EQUAL_INT(VisibilityUnobscured, visibility); +} + +// not needed when using generate_test_runner.rb +int main(void) { + UNITY_BEGIN(); + RUN_TEST(run_aswm); + RUN_TEST(create_default_top_level_window); + return UNITY_END(); +}