wlgol/wlgol.c

317 lines
7.8 KiB
C

#include <bits/time.h>
#include <stdint.h>
#include <stdio.h>
#include <string.h>
#define _POSIX_C_SOURCE 200809L
#include <time.h>
#include <sys/mman.h>
#include <syscall.h>
#include <unistd.h>
#include <xdg-shell.h>
#include <wayland-client.h>
#include <tkeyboard.h>
#include <tpointer.h>
struct wl_compositor *compositor;
struct wl_shm *shm;
struct xdg_wm_base *xdg_shell;
struct wl_seat *wl_seat;
struct keyboard_data keyboard_data;
struct pointer_data pointer_data;
int running = 1;
int released = 0;
struct timespec mytime;
struct canvas
{
int width;
int height;
int stride;
int size;
struct wl_buffer *buffer;
unsigned char *data;
struct wl_surface *surface;
struct wl_callback *frame;
} canvas;
// registry
void registry_global_handler(
void *data, struct wl_registry *registry,
uint32_t name, const char *interface,
uint32_t version)
{
// printf(" -> %s\n", interface);
if (strcmp(interface, wl_compositor_interface.name) == 0)
{
compositor = wl_registry_bind(registry, name, &wl_compositor_interface, wl_compositor_interface.version);
printf("Got wl_compositor %d\n", version);
}
else if (strcmp(interface, wl_shm_interface.name) == 0)
{
shm = wl_registry_bind(registry, name, &wl_shm_interface, wl_shm_interface.version);
printf("Got wl_shm %d\n", version);
}
else if (strcmp(interface, xdg_wm_base_interface.name) == 0)
{
xdg_shell = wl_registry_bind(registry, name, &xdg_wm_base_interface, 4);
printf("Got xdg_wm_base %d\n", version);
}
else if (strcmp(interface, wl_seat_interface.name) == 0)
{
wl_seat = wl_registry_bind(registry, name, &wl_seat_interface, 5);
printf("Got wl_seat (%d)\n", version);
}
}
void registry_global_remove_handler(
void *data, struct wl_registry *registry,
uint32_t name)
{
// nothing
}
const struct wl_registry_listener registry_listener = {
.global = registry_global_handler,
.global_remove = registry_global_remove_handler};
// xdg_toplevel
void xdg_toplevel_configure_handler(
void *data,
struct xdg_toplevel *xdg_toplevel,
int32_t width, int32_t height,
struct wl_array *states)
{
printf("xdg_toplevel_configure: %dx%d\n", width, height);
}
void xdg_toplevel_close_handler(void *data, struct xdg_toplevel *xdg_toplevel)
{
printf("xdg_toplevel_close\n");
running = 0;
}
void xdg_toplevel_configure_bounds_handler(
void *data,
struct xdg_toplevel *xdg_toplevel,
int32_t width,
int32_t height)
{
// nothing
}
const struct xdg_toplevel_listener xdg_toplevel_listener = {
.configure = xdg_toplevel_configure_handler,
.close = xdg_toplevel_close_handler,
.configure_bounds = xdg_toplevel_configure_bounds_handler};
// xdg_surface
void xdg_surface_configure_handler(void *data, struct xdg_surface *xdg_surface,
uint32_t serial)
{
printf("xdg_surface_configure\n");
xdg_surface_ack_configure(xdg_surface, serial);
}
const struct xdg_surface_listener xdg_surface_listener = {
.configure = xdg_surface_configure_handler};
// xdg_shell
void xdg_wm_base_ping_handler(void *data, struct xdg_wm_base *xdg_shell,
uint32_t serial)
{
xdg_wm_base_pong(xdg_shell, serial);
// printf("wm_base ping-pong\n");
}
const struct xdg_wm_base_listener xdg_shell_listener = {
.ping = xdg_wm_base_ping_handler};
void wl_buffer_release_handler(void *data, struct wl_buffer *wl_buffer)
{
// printf("wl_buffer_release\n");
released = 1;
}
const struct wl_buffer_listener wl_buffer_listener = {
.release = wl_buffer_release_handler};
void redraw(); // forward declaration
void wl_callack_done(void *data, struct wl_callback *callback, uint32_t time)
{
if (canvas.frame)
{
wl_callback_destroy(canvas.frame);
canvas.frame = NULL;
}
if (released)
{
redraw();
released = 0;
}
}
const struct wl_callback_listener wl_surface_frame_listener = {
.done = wl_callack_done};
int main(void)
{
clock_gettime(CLOCK_MONOTONIC, &mytime);
struct wl_display *display = wl_display_connect(NULL);
struct wl_registry *registry = wl_display_get_registry(display);
wl_registry_add_listener(registry, &registry_listener, NULL);
// wait for the "initial" set of globals to appear
wl_display_roundtrip(display);
xdg_wm_base_add_listener(xdg_shell, &xdg_shell_listener, NULL);
canvas.surface = wl_compositor_create_surface(compositor);
struct xdg_surface *xdg_surface = xdg_wm_base_get_xdg_surface(xdg_shell, canvas.surface);
struct xdg_toplevel *xdg_toplevel = xdg_surface_get_toplevel(xdg_surface);
xdg_surface_add_listener(xdg_surface, &xdg_surface_listener, NULL);
xdg_toplevel_add_listener(xdg_toplevel, &xdg_toplevel_listener, NULL);
xdg_toplevel_set_app_id(xdg_toplevel, "eu.ar76.wlgol");
xdg_toplevel_set_title(xdg_toplevel, "Game Of Life.");
// signal that the surface is ready to be configured
wl_surface_commit(canvas.surface);
keyboard_data.keyboard = wl_seat_get_keyboard(wl_seat);
wl_keyboard_add_listener(keyboard_data.keyboard, &keyboard_listener, &keyboard_data);
pointer_init_cursor(&pointer_data, compositor, shm);
pointer_data.pointer = wl_seat_get_pointer(wl_seat);
wl_pointer_add_listener(pointer_data.pointer, &pointer_listener, &pointer_data);
canvas.width = 400;
canvas.height = 400;
canvas.stride = canvas.width * 4;
canvas.size = canvas.stride * canvas.height; // bytes
// open an anonymous file and write some zero bytes to it
int fd = syscall(SYS_memfd_create, "buffer", 0);
ftruncate(fd, canvas.size);
// map it to the memory
canvas.data =
mmap(NULL, canvas.size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
// turn it into a shared memory pool
struct wl_shm_pool *pool = wl_shm_create_pool(shm, fd, canvas.size);
close(fd); // mapped, so can be closed
// allocate the buffer in that pool
canvas.buffer = wl_shm_pool_create_buffer(
pool, 0, canvas.width, canvas.height, canvas.stride, WL_SHM_FORMAT_ARGB8888);
wl_buffer_add_listener(canvas.buffer, &wl_buffer_listener, NULL);
// wait for the surface to be configured
wl_display_roundtrip(display);
redraw();
while (wl_display_dispatch(display) != -1 && running)
{
// nothing inside
}
xdg_toplevel_destroy(xdg_toplevel);
xdg_surface_destroy(xdg_surface);
wl_surface_destroy(canvas.surface);
wl_shm_pool_destroy(pool);
wl_registry_destroy(registry);
wl_display_disconnect(display);
}
unsigned char mycolour = 0;
char direction = 1;
int framecounter = 0;
void redraw()
{
clock_t start = clock();
mycolour += direction;
switch (mycolour)
{
case 0: // lower limit, start to increase
direction = 1;
break;
case 255: // upper limit, start to decrease
direction = -1;
break;
}
// printf("dred=%d\n", dred);
// draw into buffer
for (int x = 0; x < canvas.width; x++)
{
for (int y = 0; y < canvas.height; y++)
{
struct pixel
{
// little-endian ARGB
unsigned char blue;
unsigned char green;
unsigned char red;
unsigned char alpha;
} *px = (struct pixel *)(canvas.data + y * canvas.stride + x * 4);
// draw a stripes pattern
if ((x + y) % 30 < 10)
{
// transparent
px->alpha = 0;
}
else if ((x + y) % 30 < 20)
{
// yellow
px->alpha = 255;
px->red = 255;
px->green = 255;
px->blue = 0;
}
else
{
// semitransparent red
px->alpha = 128;
px->red = mycolour;
px->green = 0;
px->blue = (255 - mycolour);
}
}
}
// callback
canvas.frame = wl_surface_frame(canvas.surface);
wl_callback_add_listener(canvas.frame, &wl_surface_frame_listener, NULL);
wl_surface_attach(canvas.surface, canvas.buffer, 0, 0);
wl_surface_damage(canvas.surface, 0, 0, canvas.width, canvas.height);
wl_surface_commit(canvas.surface);
framecounter++;
struct timespec ntime;
clock_gettime(CLOCK_MONOTONIC, &ntime);
double elapsed_time = (ntime.tv_sec - mytime.tv_sec) + (ntime.tv_nsec - mytime.tv_nsec) / 1e9;
if (elapsed_time > 1.0)
{
clock_t end = clock();
double dur = ((double)end - start) / CLOCKS_PER_SEC;
printf("FPS: %d in %g sec\n", framecounter, dur);
mytime = ntime;
framecounter = 0;
}
}