1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
|
/* 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 <yasl/yasl.h>
#include <yasl/cpu.h>
#include <yasl/gl.h>
#if TEST_VULKAN
#include <yasl/vulkan.h>
#endif
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#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;
}
|