You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
232 lines
6.7 KiB
232 lines
6.7 KiB
4 days ago
|
#include <stdlib.h>
|
||
|
#include <unistd.h>
|
||
|
#include <string.h>
|
||
|
#include <stdio.h>
|
||
|
|
||
|
#include <sys/mman.h>
|
||
|
|
||
|
#include <wayland-client-core.h>
|
||
|
#include <wayland-client-protocol.h>
|
||
|
#include <wayland-client.h>
|
||
|
|
||
|
#include <cairo/cairo.h>
|
||
|
|
||
|
#include "xdg-shell.prot.h"
|
||
|
|
||
|
// Not included in any standard header
|
||
|
// Thus, declaring manually.
|
||
|
// See memfd_create(2).
|
||
|
int memfd_create(const char* name, unsigned int flags);
|
||
|
|
||
|
#define ASSERT(cond, msg)\
|
||
|
if(!(cond)) {\
|
||
|
fprintf(stderr, "Runtime assertion %s at %s:%d failed: %s\n", #cond, __FILE__, __LINE__, msg);\
|
||
|
exit(-1);\
|
||
|
}
|
||
|
|
||
|
typedef struct state {
|
||
|
struct wl_shm* wl_shm;
|
||
|
struct wl_compositor* wl_compositor;
|
||
|
struct xdg_wm_base* xdg_wm_base;
|
||
|
struct wl_surface* surface;
|
||
|
struct wl_buffer* buffer;
|
||
|
|
||
|
uint32_t conf_x;
|
||
|
uint32_t conf_y;
|
||
|
|
||
|
uint32_t curr_x;
|
||
|
uint32_t curr_y;
|
||
|
|
||
|
cairo_surface_t* sfc;
|
||
|
cairo_t* ctx;
|
||
|
|
||
|
void* shm_data;
|
||
|
int shm_fd;
|
||
|
|
||
|
uint8_t should_close;
|
||
|
} state_t;
|
||
|
|
||
|
state_t state;
|
||
|
|
||
|
void noop_wl_registry(void* data, struct wl_registry* object, uint32_t serial) {}
|
||
|
void noop_xdg_toplevel(void* data, struct xdg_toplevel* object, uint32_t serial) {}
|
||
|
|
||
|
void handle_global(
|
||
|
void* data,
|
||
|
struct wl_registry* registry,
|
||
|
uint32_t name,
|
||
|
const char* interface,
|
||
|
uint32_t version) {
|
||
|
if(strcmp(interface, wl_shm_interface.name) == 0) {
|
||
|
state.wl_shm = wl_registry_bind(registry, name, &wl_shm_interface, version);
|
||
|
}
|
||
|
if(strcmp(interface, wl_compositor_interface.name) == 0) {
|
||
|
state.wl_compositor = wl_registry_bind(registry, name, &wl_compositor_interface, version);
|
||
|
}
|
||
|
if(strcmp(interface, xdg_wm_base_interface.name) == 0) {
|
||
|
state.xdg_wm_base = wl_registry_bind(registry, name, &xdg_wm_base_interface, version);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void handle_global_remove(
|
||
|
void* data,
|
||
|
struct wl_registry* registry,
|
||
|
uint32_t name) {
|
||
|
// Who cares
|
||
|
}
|
||
|
|
||
|
const struct wl_registry_listener reg_callbacks = {
|
||
|
.global = handle_global,
|
||
|
.global_remove = noop_wl_registry,
|
||
|
};
|
||
|
|
||
|
void resize_buffer(int32_t, int32_t);
|
||
|
void draw();
|
||
|
|
||
|
static void xdg_surface_handle_configure(
|
||
|
void *data,
|
||
|
struct xdg_surface* xdg_surface,
|
||
|
uint32_t serial) {
|
||
|
xdg_surface_ack_configure(xdg_surface, serial);
|
||
|
static uint8_t configured;
|
||
|
if (configured) {
|
||
|
wl_surface_commit(state.surface);
|
||
|
} else {
|
||
|
configured = 1;
|
||
|
}
|
||
|
}
|
||
|
void xdg_toplevel_handle_configure(void *data,
|
||
|
struct xdg_toplevel *xdg_toplevel,
|
||
|
int32_t width,
|
||
|
int32_t height,
|
||
|
struct wl_array *states) {
|
||
|
if(width == 0 || height == 0) return;
|
||
|
state.conf_x = width;
|
||
|
state.conf_y = height;
|
||
|
printf("x: %d y: %d\n", width, height);
|
||
|
}
|
||
|
void xdg_toplevel_handle_configure_bounds(void *data,
|
||
|
struct xdg_toplevel *xdg_toplevel,
|
||
|
int32_t width,
|
||
|
int32_t height) {
|
||
|
|
||
|
}
|
||
|
void xdg_toplevel_handle_wm_capabilities(void *data,
|
||
|
struct xdg_toplevel *xdg_toplevel,
|
||
|
struct wl_array *capabilities) {
|
||
|
|
||
|
}
|
||
|
|
||
|
static void xdg_toplevel_handle_close(
|
||
|
void *data,
|
||
|
struct xdg_toplevel* xdg_surface) {
|
||
|
state.should_close = 1;
|
||
|
}
|
||
|
|
||
|
const struct xdg_surface_listener xdg_surface_listener = {
|
||
|
.configure = xdg_surface_handle_configure,
|
||
|
};
|
||
|
|
||
|
const struct xdg_toplevel_listener xdg_toplevel_listener = {
|
||
|
.configure = xdg_toplevel_handle_configure,
|
||
|
.close = xdg_toplevel_handle_close,
|
||
|
.configure_bounds = xdg_toplevel_handle_configure_bounds,
|
||
|
.wm_capabilities = xdg_toplevel_handle_wm_capabilities,
|
||
|
};
|
||
|
|
||
|
void resize_buffer(int32_t width, int32_t height) {
|
||
|
// Buffer params
|
||
|
int chan = 4;
|
||
|
int stride = cairo_format_stride_for_width(CAIRO_FORMAT_ARGB32, width);
|
||
|
int size = height * stride;
|
||
|
|
||
|
state.curr_x = width;
|
||
|
state.curr_y = height;
|
||
|
|
||
|
// Create & map memfd
|
||
|
close(state.shm_fd);
|
||
|
state.shm_fd = memfd_create("wl_shm data", 0);
|
||
|
ASSERT(state.shm_fd != -1, "Failed to create memfd.");
|
||
|
ASSERT(ftruncate(state.shm_fd, size) != -1, "Failed to ftruncate the memfd.");
|
||
|
state.shm_data = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, state.shm_fd, 0);
|
||
|
ASSERT(state.shm_data != MAP_FAILED, "Failed to mmap the memfd.");
|
||
|
|
||
|
// Make pool from memfd
|
||
|
struct wl_shm_pool* pool = wl_shm_create_pool(state.wl_shm, state.shm_fd, size);
|
||
|
ASSERT(pool != NULL, "Failed to create the wl_shm_pool.");
|
||
|
|
||
|
// Allocate full Wayland buffer
|
||
|
state.buffer = wl_shm_pool_create_buffer(pool, 0, width, height, chan * width, WL_SHM_FORMAT_ARGB8888);
|
||
|
ASSERT(state.buffer != NULL, "Failed to create the wl_buffer.");
|
||
|
|
||
|
wl_shm_pool_destroy(pool);
|
||
|
|
||
|
// Debugging data
|
||
|
memset(state.shm_data, 00, size);
|
||
|
|
||
|
cairo_destroy(state.ctx);
|
||
|
cairo_surface_destroy(state.sfc);
|
||
|
|
||
|
// Initialize cairo
|
||
|
state.sfc = cairo_image_surface_create_for_data(state.shm_data, CAIRO_FORMAT_ARGB32, width, height, stride);
|
||
|
ASSERT(cairo_surface_status(state.sfc) == CAIRO_STATUS_SUCCESS, "Failed to create the cairo surface.");
|
||
|
state.ctx = cairo_create(state.sfc);
|
||
|
}
|
||
|
|
||
|
void draw() {
|
||
|
// Paint background
|
||
|
cairo_set_source_rgba(state.ctx, 0.87, 0.68, 0.76, 1.0);
|
||
|
cairo_paint(state.ctx);
|
||
|
cairo_surface_flush(state.sfc);
|
||
|
|
||
|
// Commit buffer
|
||
|
wl_surface_damage_buffer(state.surface, 0, 0, INT32_MAX, INT32_MAX);
|
||
|
wl_surface_attach(state.surface, state.buffer, 0, 0);
|
||
|
wl_surface_commit(state.surface);
|
||
|
}
|
||
|
|
||
|
int main() {
|
||
|
// Connect
|
||
|
struct wl_display* dpy = wl_display_connect(NULL);
|
||
|
ASSERT(dpy != NULL, "Unable to connect to Wayland");
|
||
|
|
||
|
// Fetch globals
|
||
|
struct wl_registry* registry = wl_display_get_registry(dpy);
|
||
|
wl_registry_add_listener(registry, ®_callbacks, NULL);
|
||
|
wl_display_roundtrip(dpy);
|
||
|
ASSERT(state.wl_shm != NULL, "core.wl_shm not supported.");
|
||
|
ASSERT(state.wl_compositor != NULL, "core.wl_compositor not supported.");
|
||
|
ASSERT(state.xdg_wm_base != NULL, "xdg_shell.xdg_wm_base not supported.");
|
||
|
|
||
|
resize_buffer(3440, 1440);
|
||
|
|
||
|
// Create Wayland surface
|
||
|
state.surface = wl_compositor_create_surface(state.wl_compositor);
|
||
|
ASSERT(state.surface != NULL, "Failed to create the wl_surface.");
|
||
|
struct xdg_surface* xdg_surface = xdg_wm_base_get_xdg_surface(state.xdg_wm_base, state.surface);
|
||
|
ASSERT(xdg_surface != NULL, "Failed to create the xdg_surface.");
|
||
|
struct xdg_toplevel* xdg_toplevel = xdg_surface_get_toplevel(xdg_surface);
|
||
|
ASSERT(xdg_toplevel != NULL, "Failed to create the xdg_toplevel.");
|
||
|
xdg_toplevel_set_title(xdg_toplevel, "Hello, world!");
|
||
|
xdg_surface_add_listener(xdg_surface, &xdg_surface_listener, NULL);
|
||
|
xdg_toplevel_add_listener(xdg_toplevel, &xdg_toplevel_listener, NULL);
|
||
|
wl_surface_commit(state.surface);
|
||
|
wl_display_dispatch(dpy);
|
||
|
|
||
|
// Attach Wayland buffer to Wayland surface
|
||
|
wl_surface_attach(state.surface, state.buffer, 0, 0);
|
||
|
wl_surface_commit(state.surface);
|
||
|
wl_display_dispatch(dpy);
|
||
|
|
||
|
// Loop until should close
|
||
|
while(!state.should_close) {
|
||
|
wl_display_dispatch(dpy);
|
||
|
//printf("Refresh\n");
|
||
|
if(state.curr_x != state.conf_x || state.curr_y != state.conf_y) {
|
||
|
resize_buffer(state.conf_x, state.conf_y);
|
||
|
draw();
|
||
|
}
|
||
|
}
|
||
|
return 0;
|
||
|
}
|