summaryrefslogtreecommitdiff
path: root/tests/wayland/test_reconnect.c
blob: 0c42043575e7f189cacf68a31a3476499e58af44 (plain)
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;
}