/* Compositor restart: run by helpers/run_reconnect.py, which kills the compositor * after "ready" and starts a new one after "lost". Windows must survive, keep * their pixels, and accept new GL and Vulkan objects after DISPLAY_RESTORED. */ #include #include #include #if TEST_VULKAN #include #endif #include #include #include #define CHECK(cond, what) do { if (!(cond)) { const char *why; yasl_get_error(&why); \ fprintf(stderr, "reconnect: %s failed at line %d (%s)\n", what, __LINE__, why); exit(1); } } while (0) typedef void (*clear_color_fn)(float, float, float, float); typedef void (*clear_fn)(unsigned); static yasl_surface *cpu, *gl; /* Waits up to timeout_ms for an event of the given type, failing on any CLOSE. */ static yasl_event expect(enum yasl_event_type type, yasl_surface *surface, int timeout_ms) { yasl_event e; for (int waited = 0; waited < timeout_ms; waited += 50) { CHECK(yasl_wait_event(&e, 50) >= 0, "wait"); CHECK(e.type != YASL_EVENT_CLOSE, "keeping windows open"); if (e.type == type && (!surface || e.surface == surface)) return e; } fprintf(stderr, "reconnect: timed out waiting for event %d\n", type); exit(1); } static void draw_gl(float red) { CHECK(yasl_gl_make_current(gl), "binding GL"); clear_color_fn clear_color = (clear_color_fn)yasl_gl_get_proc_address("glClearColor"); clear_fn clear = (clear_fn)yasl_gl_get_proc_address("glClear"); CHECK(clear_color && clear, "loading GL procedures"); clear_color(red, 0.2f, 0.3f, 1); clear(0x00004000u); CHECK(yasl_gl_swap_buffers(gl), "swapping GL buffers"); } #if TEST_VULKAN static VkInstance instance; static yasl_surface *vk; static VkSurfaceKHR vulkan_surface(void) { VkSurfaceKHR out = VK_NULL_HANDLE; CHECK(yasl_vulkan_create_surface(vk, instance, NULL, &out) == VK_SUCCESS, "creating Vulkan surface"); return out; } #endif int main(void) { CHECK(yasl_set_backend(YASL_BACKEND_WAYLAND), "selecting Wayland"); cpu = yasl_surface_create("reconnect CPU", 160, 120, YASL_SURFACE_CPU); CHECK(cpu, "creating CPU window"); gl = yasl_gl_surface_create("reconnect GL", 160, 120, 0, NULL, NULL); CHECK(gl, "creating GL window"); expect(YASL_EVENT_REFRESH, cpu, 5000); int w, h; uint32_t *pixels = yasl_cpu_pixels(cpu, &w, &h); CHECK(pixels, "getting pixels"); for (size_t i = 0; i < (size_t)w * h; i++) pixels[i] = 0xff336699u; CHECK(yasl_cpu_present(cpu), "presenting"); draw_gl(0.8f); #if TEST_VULKAN CHECK(yasl_vulkan_init(vkGetInstanceProcAddr), "loading Vulkan"); vk = yasl_surface_create("reconnect Vulkan", 160, 120, 0); CHECK(vk, "creating Vulkan window"); uint32_t count = 0; const char *const *extensions = yasl_vulkan_instance_extensions(vk, &count); VkInstanceCreateInfo info = {.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO, .enabledExtensionCount = count, .ppEnabledExtensionNames = extensions}; CHECK(vkCreateInstance(&info, NULL, &instance) == VK_SUCCESS, "creating Vulkan instance"); VkSurfaceKHR surface = vulkan_surface(); #endif puts("ready"); fflush(stdout); expect(YASL_EVENT_DISPLAY_LOST, NULL, 10000); #if TEST_VULKAN vkDestroySurfaceKHR(instance, surface, NULL); #endif CHECK(!yasl_surface_create("offline", 64, 64, YASL_SURFACE_CPU), "refusing windows while offline"); CHECK(yasl_get_error(NULL) == YASL_ERROR_DISCONNECTED, "reporting the disconnect"); puts("lost"); fflush(stdout); expect(YASL_EVENT_DISPLAY_RESTORED, NULL, 15000); expect(YASL_EVENT_REFRESH, cpu, 5000); int rw, rh; pixels = yasl_cpu_pixels(cpu, &rw, &rh); CHECK(pixels && rw == w && rh == h, "keeping the window size"); CHECK(pixels[0] == 0xff336699u && pixels[(size_t)w * h - 1] == 0xff336699u, "keeping the pixels"); CHECK(yasl_cpu_present(cpu), "presenting after reconnect"); draw_gl(0.1f); #if TEST_VULKAN surface = vulkan_surface(); vkDestroySurfaceKHR(instance, surface, NULL); vkDestroyInstance(instance, NULL); #endif /* A second window proves the new connection accepts creation. */ CHECK(yasl_surface_create("after reconnect", 64, 64, YASL_SURFACE_CPU), "creating a window after reconnect"); yasl_gl_make_current(NULL); yasl_shutdown(); puts("restored"); return 0; }