Tssss... I am playing with libwayland-client

This commit is contained in:
Андреев Григорий 2025-08-13 03:23:00 +03:00
parent 71cb47a22d
commit 78c33325d4
5 changed files with 602 additions and 8 deletions

3
.gitignore vendored
View File

@ -10,4 +10,5 @@ vgcore.*
*.a8
*.xcf
*_NORMAL.png
*_TEMPLATE.png
*_TEMPLATE.png
/out

View File

@ -19,6 +19,9 @@ target_link_libraries(0_render_test -lvulkan -lX11 -lm)
add_executable(0_render_test_tex_init_prep src/l2/tests/r0/r0_tex_init_prep.c)
target_link_libraries(0_render_test_tex_init_prep -lm)
add_executable(1_render_test src/l2/tests/r1/r1.c gen/l_wl_protocols/xdg-shell-private.c)
target_link_libraries(1_render_test -lwayland-client -lrt -lm -lxkbcommon)
add_executable(0_play_test src/l3/tests/p0.c)
target_link_libraries(0_play_test -lncurses)

View File

@ -1,12 +1,44 @@
HEADERS := $(shell find src -type f -name '*.h')
#HEADERS := $(shell find src -type f -name '*.h')
all: prototype1
cflags := -Wall -Wextra -Werror=implicit-function-declaration -Werror=return-type --std=c99 -g -ggdb -O0 \
-fno-trapping-math -D_POSIX_C_SOURCE=200112L -D_GNU_SOURCE
cc := 'gcc'
prototype1: src/l1/main.c $(HEADERS)
@gcc --std c99 -o $@ src/l1/main.c
wl_protocols := $(shell pkg-config --variable=pkgdatadir wayland-protocols)
gen/l_wl_protocols/xdg-shell-client.h: $(wl_protocols)/stable/xdg-shell/xdg-shell.xml
mkdir -p gen/l_wl_protocols
wayland-scanner client-header $< $@
gen/l_wl_protocols/xdg-shell-private.c: $(wl_protocols)/stable/xdg-shell/xdg-shell.xml
mkdir -p gen/l_wl_protocols
wayland-scanner private-code $< $@
out/l1/t0: src/l1/tests/t0.c $(HEADERS)
mkdir -p out/l1
$(cc) $(cflags) -o $@ $<
out/l1/t1: src/l1/tests/t1.c $(HEADERS)
mkdir -p out/l1
$(cc) $(cflags) -o $@ $<
out/l2/codegen_l: src/l2/codegen.c $(HEADERS)
mkdir -p out/l2
$(cc) $(cflags) -o $@ $<
out/l2/r0: src/l2/tests/r0/r0.c $(HEADERS)
mkdir -p out/l2
$(cc) $(cflags) -o $@ $< -lvulkan -lX11 -lm
out/l2/r0: src/l2/tests/r0/r0_tex_init_prep.c $(HEADERS)
mkdir -p out/l2
$(cc) $(cflags) -o $@ $< -lm
out/l2/r1: src/l2/tests/r1/r1.c $(HEADERS)
mkdir -p out/l2
$(cc) $(cflags) -o $@ $< gen/l_wl_protocols/xdg-shell-private.c -lwayland-client -lrt -lxkbcommon
clean:
@rm -f prototype1
.PHONY: all clean
rm -rf gen out
.PHONY: clean

View File

@ -13,6 +13,14 @@ mat4 marie_translation_mat4(vec3 vec) {
);
}
mat2 marie_2d_rot_mat2(float a) {
float cosa = cosf(a);
float sina = sinf(a);
return mat2_new(
cosa, -sina,
sina, cosa);
}
mat3 marie_3d_rot_mat3(vec3 r, float a) {
float cosa = cosf(a);
float sina = sinf(a);

View File

@ -0,0 +1,550 @@
#include <fcntl.h>
#include <limits.h>
#include <sys/mman.h>
#include <time.h>
#include <unistd.h>
#include <errno.h>
#include <wayland-client.h>
#include "../../../../gen/l_wl_protocols/xdg-shell-client.h"
#include <xkbcommon/xkbcommon.h>
#include "../../../l1/core/util.h"
#include "../../marie/graphics_geom.h"
#include "../../marie/rasterization.h"
// todo: remove
#include <linux/input-event-codes.h>
#define MAX_BUFFER_WIDTH 1000
#define MAX_BUFFER_HEIGHT 800
#define SWAPCHAIN_SLOTS 2
_Static_assert(INT32_MAX / MAX_BUFFER_WIDTH / MAX_BUFFER_HEIGHT / 4 / SWAPCHAIN_SLOTS > 1, "Swapchain is too big");
/* Shared memory support code */
static void randname(char *buf) {
struct timespec ts;
clock_gettime(CLOCK_REALTIME, &ts);
long r = ts.tv_nsec;
for (int i = 0; i < 6; ++i) {
buf[i] = 'A'+(r&15)+(r&16)*2;
r >>= 5;
}
}
static int create_shm_file(void){
int retries = 100;
do {
char name[] = "/wl_shm-XXXXXX";
randname(name + sizeof(name) - 7);
--retries;
int fd = shm_open(name, O_RDWR | O_CREAT | O_EXCL, 0600);
if (fd >= 0) {
shm_unlink(name);
return fd;
}
} while (retries > 0 && errno == EEXIST);
return -1;
}
static int allocate_shm_file(size_t size) {
int fd = create_shm_file();
if (fd < 0)
return -1;
int ret;
do {
ret = ftruncate(fd, size);
} while (ret < 0 && errno == EINTR);
if (ret < 0) {
close(fd);
return -1;
}
return fd;
}
typedef struct {
int fd;
uint32_t* data;
struct wl_shm_pool* pool;
bool want_to_draw;
struct wl_buffer* used_buffers[SWAPCHAIN_SLOTS];
} swapchain_t;
typedef struct {
vec2 pos;
vec2 speed;
} durackaya_tochka;
typedef struct {
/* Globals */
struct wl_display *wl_display;
struct wl_registry *wl_registry;
struct wl_shm *wl_shm;
struct wl_compositor *wl_compositor;
struct xdg_wm_base *xdg_wm_base;
struct wl_seat *wl_seat;
/* Objects */
swapchain_t swapchain;
struct wl_surface *wl_surface;
struct xdg_surface *xdg_surface;
struct xdg_toplevel *xdg_toplevel;
/* inputs */
struct wl_pointer* pointer;
struct wl_keyboard* keyboard;
/* xkb */
struct xkb_state* xkb_state;
struct xkb_context* xkb_context;
struct xkb_keymap* xkb_keymap;
/* app state */
vec2 pointer_pos;
bool first_0x80_keys[0x80];
durackaya_tochka v0;
durackaya_tochka v1;
durackaya_tochka v2;
uint32_t last_frame_time;
int32_t width;
int32_t height;
bool closed;
} my_state;
typedef struct {
uint32_t* data;
int32_t width;
int32_t height;
} WhereToDrawTriangle;
U32 color_vec4_to_color_u32(vec4 color) {
return (((U32)roundf(color.w * 255)) << 24) + (((U32)roundf(color.x * color.w * 255)) << 16) +
(((U32)roundf(color.y * color.w * 255)) << 8) + (((U32)roundf(color.z * color.w * 255)) << 0);
}
void draw_frame_h_rasterizer_cb(void* ug, S32 x, S32 y, vec4 attr) {
WhereToDrawTriangle* g = ug;
if (0 <= x && x < g->width && 0 <= y && y < g->height)
g->data[y * g->width + x] = color_vec4_to_color_u32(attr);
}
void draw_frame(my_state* state, uint32_t* data, int32_t width, int32_t height) {
for (int y = 0; y < height; ++y) {
for (int x = 0; x < width; ++x) {
data[y * width + x] = 0xFF00FFFF;
}
}
WhereToDrawTriangle aboba = {data, width, height};
marie_rasterize_triangle_with_attr(
(MariePlaneVertAttr){.pos = state->v0.pos, .attr = {1, 0, 0, 1}},
(MariePlaneVertAttr){.pos = state->v1.pos, .attr = {0, 1, 0, .5f}},
(MariePlaneVertAttr){.pos = state->v2.pos, .attr = {0, 0, 1, .0f}},
(FnMarieRasterizerCallback){.fn = draw_frame_h_rasterizer_cb, .guest = &aboba});
vec2 points[3] = {state->v0.pos, state->v1.pos, state->v2.pos};
for (int t = 0; t < 3; t++) {
int x = (int)roundf(points[t].x - .5f);
int y = (int)roundf(points[t].y - .5f);
for (int i = -5; i <= 5; i++)
for (int j = -5; j <= 5; j++)
if (0 <= x+i && x+i < width && 0 <= y+j && y+j < height)
data[(y+j) * width + x + i] = 0;
}
}
void durackaya_tochka_update(durackaya_tochka* dot, float dur, float width, float height, bool turning_left, bool turning_right) {
vec2 *pos = &dot->pos;
vec2 *speed = &dot->speed;
float a = 0.9f * dur * ((turning_left ? -1.f : 0) + (turning_right ? 1.f : 0));
*speed = mat2_mul_vec2(marie_2d_rot_mat2(a), *speed);
*pos = vec2_add_vec2(*pos, vec2_mul_scal(*speed, dur));
if (pos->x < 0) {
speed->x = -speed->x;
pos->x = 0;
}
if (pos->y < 0) {
speed->y = -speed->y;
pos->y = 0;
}
if (pos->x > width) {
speed->x = -speed->x;
pos->x = width;
}
if (pos->y > height) {
speed->y = -speed->y;
pos->y = height;
}
}
void update_state(my_state* state, uint32_t fl) {
const float width = (float)(state->width < MAX_BUFFER_WIDTH ? state->width : MAX_BUFFER_WIDTH);
const float height = (float)(state->height < MAX_BUFFER_HEIGHT ? state->height : MAX_BUFFER_HEIGHT);
float dur = (float)fl / 1000;
durackaya_tochka_update(&state->v0, dur, width, height, state->first_0x80_keys[XKB_KEY_u], state->first_0x80_keys[XKB_KEY_i]);
durackaya_tochka_update(&state->v1, dur, width, height, state->first_0x80_keys[XKB_KEY_j], state->first_0x80_keys[XKB_KEY_k]);
durackaya_tochka_update(&state->v2, dur, width, height, state->first_0x80_keys[XKB_KEY_m], state->first_0x80_keys[XKB_KEY_less]);
}
static void main_h_wl_buffer_release(void *data, struct wl_buffer *buffer) {
my_state* state = data;
for (int ij = 0; ij < SWAPCHAIN_SLOTS; ij++) {
if (state->swapchain.used_buffers[ij] == buffer) {
// printf("Buffer %p was released and destroyed\n", buffer);
wl_buffer_destroy(buffer);
state->swapchain.used_buffers[ij] = NULL;
return;
}
}
abortf("HUH??!!!\n");
}
static const struct wl_buffer_listener main_h_wl_buffer_listener = {
.release = main_h_wl_buffer_release,
};
int swapchain_take_slot(my_state *state) {
for (int ij = 0; ij < SWAPCHAIN_SLOTS; ij++) {
if (!state->swapchain.used_buffers[ij]) {
const int32_t width = state->width < MAX_BUFFER_WIDTH ? state->width : MAX_BUFFER_WIDTH;
const int32_t height = state->height < MAX_BUFFER_HEIGHT ? state->height : MAX_BUFFER_HEIGHT;
struct wl_buffer* s = wl_shm_pool_create_buffer(
state->swapchain.pool, ij * 4 * MAX_BUFFER_WIDTH * MAX_BUFFER_HEIGHT,
width, height, 4 * width, WL_SHM_FORMAT_ARGB8888);
state->swapchain.used_buffers[ij] = s;
wl_buffer_add_listener(s, &main_h_wl_buffer_listener, state);
return ij;
}
}
return -1;
}
void try_drawing_frame(my_state *state){
if (!state->swapchain.want_to_draw)
return;
int ij = swapchain_take_slot(state);
// printf("Got slot %d", ij);
if (ij < 0)
return;
assert(ij < SWAPCHAIN_SLOTS);
state->swapchain.want_to_draw = false;
const int32_t width = state->width < MAX_BUFFER_WIDTH ? state->width : MAX_BUFFER_WIDTH;
const int32_t height = state->height < MAX_BUFFER_HEIGHT ? state->height : MAX_BUFFER_HEIGHT;
/* Draw checkerboxed background */
uint32_t* data = state->swapchain.data + ij * MAX_BUFFER_WIDTH * MAX_BUFFER_HEIGHT;
draw_frame(state, data, width, height);
wl_surface_attach(state->wl_surface, state->swapchain.used_buffers[ij], 0, 0);
wl_surface_damage_buffer(state->wl_surface, 0, 0, INT32_MAX, INT32_MAX);
wl_surface_commit(state->wl_surface);
}
static void main_h_xdg_surface_configure(void *data, struct xdg_surface *xdg_surface, uint32_t serial){
my_state *state = data;
printf("XDG surface configured!\n");
xdg_surface_ack_configure(xdg_surface, serial);
// todo: synchronize with frame event
state->swapchain.want_to_draw = true;
try_drawing_frame(state);
}
static void main_h_xdg_toplevel_configure(
void *data, struct xdg_toplevel *xdg_toplevel, int32_t width, int32_t height, struct wl_array *states
){
my_state *state = data;
printf("XDG toplevel configured to (%d %d)\n", width, height);
if (width <= 0 || height < 0)
return;
state->width = width;
state->height = height;
}
static void main_h_xdg_toplevel_close(void *data, struct xdg_toplevel *toplevel){
my_state *state = data;
state->closed = true;
}
static const struct xdg_toplevel_listener main_h_xdg_toplevel_listener = {
.configure = main_h_xdg_toplevel_configure,
.close = main_h_xdg_toplevel_close,
};
static const struct xdg_surface_listener xdg_surface_listener = {
.configure = main_h_xdg_surface_configure,
};
static void main_h_xdg_wm_base_ping(void *data, struct xdg_wm_base *xdg_wm_base, uint32_t serial){
xdg_wm_base_pong(xdg_wm_base, serial);
}
static const struct xdg_wm_base_listener main_h_xdg_wm_base_listener = {
.ping = main_h_xdg_wm_base_ping,
};
static void main_h_wl_keyboard_keymap(
void *data, struct wl_keyboard *wl_keyboard, uint32_t format, int32_t fd, uint32_t size
) {
my_state* state = data;
// todo: replace asserts with abortf
assert(format == WL_KEYBOARD_KEYMAP_FORMAT_XKB_V1);
char *map_shm = mmap(NULL, size, PROT_READ, MAP_SHARED, fd, 0);
assert(map_shm != MAP_FAILED);
xkb_keymap_unref(state->xkb_keymap);
xkb_state_unref(state->xkb_state);
state->xkb_keymap = xkb_keymap_new_from_string(
state->xkb_context, map_shm, XKB_KEYMAP_FORMAT_TEXT_V1, XKB_KEYMAP_COMPILE_NO_FLAGS);
munmap(map_shm, size);
close(fd);
state->xkb_state = xkb_state_new(state->xkb_keymap);
printf("Keymap changed!\n");
memset(&state->first_0x80_keys, 0, sizeof(state->first_0x80_keys));
}
static void main_h_wl_keyboard_enter(
void *data, struct wl_keyboard *wl_keyboard, uint32_t serial, struct wl_surface *surface, struct wl_array *keys
) {
my_state* state = data;
printf("keyboard enter; keys pressed are:\n");
uint32_t *key;
wl_array_for_each(key, keys) {
char buf1[128];
char buf2[128];
uint32_t actual_key = *key + 8;
xkb_keysym_t sym = xkb_state_key_get_one_sym(state->xkb_state, actual_key);
// xkb_keysym_get_name(sym, buf1, sizeof(buf1));
// xkb_state_key_get_utf8(state->xkb_state, actual_key, buf2, sizeof(buf2));
// printf("sym: %-12s (%d), utf8: '%s'\n", buf1, sym, buf2);
if (sym < 0x80)
state->first_0x80_keys[sym] = true;
}
}
static void main_h_wl_keyboard_leave(
void *data, struct wl_keyboard *wl_keyboard, uint32_t serial, struct wl_surface *surface
) {
my_state* state = data;
memset(&state->first_0x80_keys, 0, sizeof(state->first_0x80_keys));
}
static void main_h_wl_keyboard_key(
void *data, struct wl_keyboard *wl_keyboard, uint32_t serial, uint32_t time, uint32_t key, uint32_t key_action
) {
my_state* state = data;
char buf1[128];
char buf2[128];
uint32_t actual_key = key + 8;
/* It actually could be nokey keysym. You never know without checking */
xkb_keysym_t keysym = xkb_state_key_get_one_sym(state->xkb_state, actual_key);
xkb_keysym_get_name(keysym, buf1, sizeof(buf1));
xkb_state_key_get_utf8(state->xkb_state, actual_key, buf2, sizeof(buf2));
printf("Key %s (%d) %s, utf8: '%s'\n",
buf1, keysym, key_action == WL_KEYBOARD_KEY_STATE_PRESSED ? "pressed" : "released", buf2);
if (keysym < 0x80 && key_action == WL_KEYBOARD_KEY_STATE_RELEASED) {
state->first_0x80_keys[keysym] = false;
} else if (keysym < 0x80 && key_action == WL_KEYBOARD_KEY_STATE_PRESSED) {
state->first_0x80_keys[keysym] = true;
}
}
static void main_h_wl_keyboard_modifiers(
void *data, struct wl_keyboard *wl_keyboard, uint32_t serial,
uint32_t mods_depressed, uint32_t mods_latched, uint32_t mods_locked, uint32_t group
) {
my_state* state = data;
xkb_state_update_mask(state->xkb_state, mods_depressed, mods_latched, mods_locked, 0, 0, group);
}
static void main_h_wl_keyboard_repeat_info(void *data, struct wl_keyboard *wl_keyboard, int32_t rate, int32_t delay){
printf("Repeat timings changed: rate = %d, delay = %d", rate, delay);
}
static const struct wl_keyboard_listener main_h_wl_keyboard_listener = {
.keymap = main_h_wl_keyboard_keymap,
.enter = main_h_wl_keyboard_enter,
.leave = main_h_wl_keyboard_leave,
.key = main_h_wl_keyboard_key,
.modifiers = main_h_wl_keyboard_modifiers,
.repeat_info = main_h_wl_keyboard_repeat_info,
};
static void main_h_wl_pointer_enter(
void *data, struct wl_pointer *wl_pointer, uint32_t serial,
struct wl_surface *surface, wl_fixed_t surface_x, wl_fixed_t surface_y
) {
my_state *state = data;
state->pointer_pos = (vec2){(float)surface_x / 256.f, (float)surface_y / 256.f};
}
static void main_h_wl_pointer_leave(
void *data, struct wl_pointer *wl_pointer, uint32_t serial, struct wl_surface *surface
) {
my_state *state = data;
}
static void main_h_wl_pointer_motion(
void *data,struct wl_pointer *wl_pointer, uint32_t time, wl_fixed_t surface_x, wl_fixed_t surface_y
) {
my_state *state = data;
state->pointer_pos = (vec2){(float)surface_x / 256.f, (float)surface_y / 256.f};
}
static void main_h_wl_pointer_button(
void *data, struct wl_pointer *wl_pointer, uint32_t serial, uint32_t time, uint32_t button, uint32_t btn_action
) {
my_state *state = data;
if (btn_action == WL_POINTER_BUTTON_STATE_PRESSED) {
printf("button = %u\n", button);
if (button == 0x110) {
state->v0.pos = state->pointer_pos;
}
}
}
static void main_h_wl_pointer_axis(
void *data, struct wl_pointer *wl_pointer, uint32_t time, uint32_t axis, wl_fixed_t value
) {
my_state *state = data;
}
static void main_h_wl_pointer_frame(void *data, struct wl_pointer *wl_pointer) {
my_state *state = data;
}
const struct wl_pointer_listener main_h_wl_pointer_listener = {
.enter = main_h_wl_pointer_enter,
.leave = main_h_wl_pointer_leave,
.motion = main_h_wl_pointer_motion,
.button = main_h_wl_pointer_button,
.axis = main_h_wl_pointer_axis,
.frame = main_h_wl_pointer_frame
};
static void main_h_wl_seat_capabilities(void *data, struct wl_seat *wl_seat, uint32_t capabilities) {
my_state* state = data;
if (capabilities & WL_SEAT_CAPABILITY_POINTER) {
state->pointer = wl_seat_get_pointer(wl_seat);
assert(state->pointer);
wl_pointer_add_listener(state->pointer, &main_h_wl_pointer_listener, state);
}
if (capabilities & WL_SEAT_CAPABILITY_KEYBOARD) {
state->keyboard = wl_seat_get_keyboard(wl_seat);
assert(state->keyboard);
wl_keyboard_add_listener(state->keyboard, &main_h_wl_keyboard_listener, state);
}
}
static void main_h_wl_seat_name(void* data, struct wl_seat* wl_seat, const char* name) {
printf("Our seat name: %s\n", name);
}
static const struct wl_seat_listener main_h_wl_seat_listener = {
.capabilities = main_h_wl_seat_capabilities,
.name = main_h_wl_seat_name,
};
static void main_h_wl_registry_global(
void *data, struct wl_registry *wl_registry, uint32_t name, const char *interface, uint32_t version
) {
my_state *state = data;
if (strcmp(interface, wl_shm_interface.name) == 0) {
state->wl_shm = wl_registry_bind(wl_registry, name, &wl_shm_interface, 1);
} else if (strcmp(interface, wl_compositor_interface.name) == 0) {
state->wl_compositor = wl_registry_bind(wl_registry, name, &wl_compositor_interface, 4);
} else if (strcmp(interface, xdg_wm_base_interface.name) == 0) {
state->xdg_wm_base = wl_registry_bind(wl_registry, name, &xdg_wm_base_interface, 1);
xdg_wm_base_add_listener(state->xdg_wm_base, &main_h_xdg_wm_base_listener, state);
} else if (strcmp(interface, wl_seat_interface.name) == 0) {
if (state->wl_seat) {
printf("We got second seat, but we only need one\n");
return;
}
state->wl_seat = wl_registry_bind(wl_registry, name, &wl_seat_interface, 4);
wl_seat_add_listener(state->wl_seat, &main_h_wl_seat_listener, state);
}
}
static void main_h_wl_registry_global_remove(void *data, struct wl_registry *wl_registry, uint32_t name){
// todo: delete seats
}
static const struct wl_registry_listener main_h_wl_registry_listener = {
.global = main_h_wl_registry_global,
.global_remove = main_h_wl_registry_global_remove,
};
static const struct wl_callback_listener main_h_wl_surface_frame_listener;
static void main_h_wl_surface_frame_done(void *data, struct wl_callback *cb, uint32_t time){
my_state *state = data;
wl_callback_destroy(cb);
cb = wl_surface_frame(state->wl_surface);
wl_callback_add_listener(cb, &main_h_wl_surface_frame_listener, state);
if (state->last_frame_time != 0) {
update_state(state, time - state->last_frame_time);
}
state->swapchain.want_to_draw = true;
try_drawing_frame(state);
state->last_frame_time = time;
}
static const struct wl_callback_listener main_h_wl_surface_frame_listener = {
.done = main_h_wl_surface_frame_done,
};
int main(int argc, char *argv[]) {
my_state state = { .width = 800, .height = 480 };
state.v0 = (durackaya_tochka){.pos = {10, 10}, .speed = {100, 100}};
state.v1 = (durackaya_tochka){.pos = {100, 10}, .speed = {100, -50}};
state.v2 = (durackaya_tochka){.pos = {5, 330}, .speed = {-20, 170}};
state.wl_display = wl_display_connect(NULL);
state.wl_registry = wl_display_get_registry(state.wl_display);
wl_registry_add_listener(state.wl_registry, &main_h_wl_registry_listener, &state);
wl_display_roundtrip(state.wl_display);
assert(state.wl_shm);
assert(state.wl_compositor);
assert(state.xdg_wm_base);
{
size_t size = SWAPCHAIN_SLOTS * MAX_BUFFER_WIDTH * MAX_BUFFER_HEIGHT * 4;
assert(size < INT32_MAX);
state.swapchain.fd = allocate_shm_file(size);
if (state.swapchain.fd == -1) {
abortf("AAA");
}
state.swapchain.data = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, state.swapchain.fd, 0);
if (state.swapchain.data == MAP_FAILED) {
close(state.swapchain.fd);
abortf("what the dog doing");
}
state.swapchain.pool = wl_shm_create_pool(state.wl_shm, state.swapchain.fd, size);
assert(state.swapchain.pool);
close(state.swapchain.fd);
}
state.xkb_context = xkb_context_new(XKB_CONTEXT_NO_FLAGS);
printf("I am gonna create surfaces\n");
state.wl_surface = wl_compositor_create_surface(state.wl_compositor);
state.xdg_surface = xdg_wm_base_get_xdg_surface(
state.xdg_wm_base, state.wl_surface);
xdg_surface_add_listener(state.xdg_surface, &xdg_surface_listener, &state);
state.xdg_toplevel = xdg_surface_get_toplevel(state.xdg_surface);
xdg_toplevel_add_listener(state.xdg_toplevel, &main_h_xdg_toplevel_listener, &state);
xdg_toplevel_set_title(state.xdg_toplevel, "r1");
wl_surface_commit(state.wl_surface);
struct wl_callback* cb = wl_surface_frame(state.wl_surface);
wl_callback_add_listener(cb, &main_h_wl_surface_frame_listener, &state);
while (wl_display_dispatch(state.wl_display)) {
if (state.closed)
break;
}
// todo: clean up this mess
munmap(state.swapchain.data, SWAPCHAIN_SLOTS * MAX_BUFFER_WIDTH * MAX_BUFFER_HEIGHT * 2);
wl_display_disconnect(state.wl_display);
return 0;
}