Skip to content

Instantly share code, notes, and snippets.

@wilsonsilva
Created December 16, 2024 16:29
Show Gist options
  • Save wilsonsilva/83852969bd0f33fe455ba29abb771c2c to your computer and use it in GitHub Desktop.
Save wilsonsilva/83852969bd0f33fe455ba29abb771c2c to your computer and use it in GitHub Desktop.
hello_wayland.c
#include <stdio.h>
#include <wayland-client.h>
int main(int argc, char *argv[]) {
struct wl_display *display = wl_display_connect(NULL);
if (!display) {
fprintf(stderr, "Failed to connect to Wayland display\n");
return 1;
}
printf("Hello Wayland!\n");
wl_display_disconnect(display);
return 0;
}
@wilsonsilva
Copy link
Author

CC = gcc
CFLAGS = $(shell pkg-config --cflags wayland-client)
LDFLAGS = $(shell pkg-config --libs wayland-client)

hello_wayland: hello_wayland.c
	$(CC) -o hello_wayland hello_wayland.c $(CFLAGS) $(LDFLAGS)

clean:
	rm -f hello_wayland

@wilsonsilva
Copy link
Author

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <wayland-client.h>
#include <wayland-client-protocol.h>
#include <cairo/cairo.h>
#include <unistd.h>
#include "xdg-shell-client-protocol.h"

struct client_state {
    struct wl_display *display;
    struct wl_registry *registry;
    struct wl_compositor *compositor;
    struct wl_surface *surface;
    struct xdg_wm_base *xdg_wm_base;
    struct xdg_surface *xdg_surface;
    struct xdg_toplevel *xdg_toplevel;
    struct wl_shm *shm;
    struct wl_buffer *buffer;
    int width, height;
    void *shm_data;
};

static void handle_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 xdg_wm_base_listener = {
    .ping = handle_ping,
};

static void xdg_surface_configure(void *data, struct xdg_surface *xdg_surface, uint32_t serial)
{
    struct client_state *state = data;
    xdg_surface_ack_configure(xdg_surface, serial);
}

static const struct xdg_surface_listener xdg_surface_listener = {
    .configure = xdg_surface_configure,
};

static void registry_global(void *data, struct wl_registry *registry,
                          uint32_t name, const char *interface, uint32_t version)
{
    struct client_state *state = data;

    if (strcmp(interface, wl_compositor_interface.name) == 0) {
        state->compositor = wl_registry_bind(registry, name,
                                           &wl_compositor_interface, 1);
    } else if (strcmp(interface, xdg_wm_base_interface.name) == 0) {
        state->xdg_wm_base = wl_registry_bind(registry, name,
                                             &xdg_wm_base_interface, 1);
        xdg_wm_base_add_listener(state->xdg_wm_base,
                                &xdg_wm_base_listener, state);
    } else if (strcmp(interface, wl_shm_interface.name) == 0) {
        state->shm = wl_registry_bind(registry, name,
                                    &wl_shm_interface, 1);
    }
}

static void registry_global_remove(void *data, struct wl_registry *registry,
                                 uint32_t name)
{
    // Handle removal of global objects
}

static const struct wl_registry_listener registry_listener = {
    .global = registry_global,
    .global_remove = registry_global_remove,
};

static void create_window(struct client_state *state)
{
    state->surface = wl_compositor_create_surface(state->compositor);
    state->xdg_surface = xdg_wm_base_get_xdg_surface(state->xdg_wm_base,
                                                     state->surface);
    xdg_surface_add_listener(state->xdg_surface, &xdg_surface_listener, state);
    
    state->xdg_toplevel = xdg_surface_get_toplevel(state->xdg_surface);
    xdg_toplevel_set_title(state->xdg_toplevel, "Hello Wayland!");
    
    wl_surface_commit(state->surface);
}

static void draw_frame(struct client_state *state)
{
    cairo_surface_t *cairo_surface;
    cairo_t *cr;
    
    // Create a Cairo surface
    cairo_surface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32,
                                             state->width, state->height);
    cr = cairo_create(cairo_surface);
    
    // Clear the surface
    cairo_set_source_rgb(cr, 1.0, 1.0, 1.0);
    cairo_paint(cr);
    
    // Draw text
    cairo_select_font_face(cr, "Sans", CAIRO_FONT_SLANT_NORMAL,
                          CAIRO_FONT_WEIGHT_BOLD);
    cairo_set_font_size(cr, 24.0);
    cairo_set_source_rgb(cr, 0.0, 0.0, 0.0);
    cairo_move_to(cr, 20.0, 50.0);
    cairo_show_text(cr, "Hello Wayland!");
    
    // Clean up Cairo
    cairo_destroy(cr);
    cairo_surface_destroy(cairo_surface);
}

int main(int argc, char *argv[])
{
    struct client_state state = { 0 };
    state.width = 400;
    state.height = 300;
    
    state.display = wl_display_connect(NULL);
    if (!state.display) {
        fprintf(stderr, "Failed to connect to Wayland display\n");
        return 1;
    }
    
    state.registry = wl_display_get_registry(state.display);
    wl_registry_add_listener(state.registry, &registry_listener, &state);
    
    wl_display_roundtrip(state.display);
    
    if (!state.compositor || !state.xdg_wm_base || !state.shm) {
        fprintf(stderr, "Failed to bind required Wayland interfaces\n");
        return 1;
    }
    
    create_window(&state);
    draw_frame(&state);
    
    while (wl_display_dispatch(state.display) != -1) {
        // Main event loop
    }
    
    return 0;
}

@wilsonsilva
Copy link
Author

CC = gcc
CFLAGS = $(shell pkg-config --cflags wayland-client cairo) -g
LDFLAGS = $(shell pkg-config --libs wayland-client cairo)

SOURCES = main.c xdg-shell-protocol.c
OBJECTS = $(SOURCES:.c=.o)
TARGET = hello_wayland

$(TARGET): $(OBJECTS)
	$(CC) -o $@ $(OBJECTS) $(LDFLAGS)

%.o: %.c
	$(CC) -c -o $@ $< $(CFLAGS)

clean:
	rm -f $(TARGET) $(OBJECTS)

@wilsonsilva
Copy link
Author

wayland-scanner private-code \
    /usr/share/wayland-protocols/stable/xdg-shell/xdg-shell.xml \
    xdg-shell-protocol.c

wayland-scanner client-header \
    /usr/share/wayland-protocols/stable/xdg-shell/xdg-shell.xml \
    xdg-shell-client-protocol.h

@wilsonsilva
Copy link
Author

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <wayland-client.h>
#include <wayland-client-protocol.h>
#include <cairo/cairo.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/mman.h>
#include "xdg-shell-client-protocol.h"

struct client_state {
    struct wl_display *display;
    struct wl_registry *registry;
    struct wl_compositor *compositor;
    struct wl_surface *surface;
    struct xdg_wm_base *xdg_wm_base;
    struct xdg_surface *xdg_surface;
    struct xdg_toplevel *xdg_toplevel;
    struct wl_shm *shm;
    struct wl_buffer *buffer;
    int width, height;
    void *shm_data;
    bool configured;
};

static void handle_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 xdg_wm_base_listener = {
    .ping = handle_ping,
};

static void xdg_surface_configure(void *data, struct xdg_surface *xdg_surface, uint32_t serial)
{
    struct client_state *state = data;
    xdg_surface_ack_configure(xdg_surface, serial);
    state->configured = true;
    wl_surface_commit(state->surface);
}

static const struct xdg_surface_listener xdg_surface_listener = {
    .configure = xdg_surface_configure,
};

static void xdg_toplevel_configure(void *data, struct xdg_toplevel *xdg_toplevel,
                                 int32_t width, int32_t height,
                                 struct wl_array *states)
{
    // Handle window configuration
}

static void xdg_toplevel_close(void *data, struct xdg_toplevel *xdg_toplevel)
{
    // Handle window close request
}

static const struct xdg_toplevel_listener xdg_toplevel_listener = {
    .configure = xdg_toplevel_configure,
    .close = xdg_toplevel_close,
};

static void registry_global(void *data, struct wl_registry *registry,
                          uint32_t name, const char *interface, uint32_t version)
{
    struct client_state *state = data;

    if (strcmp(interface, wl_compositor_interface.name) == 0) {
        state->compositor = wl_registry_bind(registry, name,
                                           &wl_compositor_interface, 1);
    } else if (strcmp(interface, xdg_wm_base_interface.name) == 0) {
        state->xdg_wm_base = wl_registry_bind(registry, name,
                                             &xdg_wm_base_interface, 1);
        xdg_wm_base_add_listener(state->xdg_wm_base,
                                &xdg_wm_base_listener, state);
    } else if (strcmp(interface, wl_shm_interface.name) == 0) {
        state->shm = wl_registry_bind(registry, name,
                                    &wl_shm_interface, 1);
    }
}

static void registry_global_remove(void *data, struct wl_registry *registry,
                                 uint32_t name)
{
    // Handle removal of global objects
}

static const struct wl_registry_listener registry_listener = {
    .global = registry_global,
    .global_remove = registry_global_remove,
};

static int create_shared_memory(size_t size)
{
    int fd = memfd_create("buffer", 0);
    if (fd < 0)
        return -1;
    
    if (ftruncate(fd, size) < 0) {
        close(fd);
        return -1;
    }
    
    return fd;
}

static struct wl_buffer *create_buffer(struct client_state *state)
{
    int stride = state->width * 4;
    int size = stride * state->height;
    
    int fd = create_shared_memory(size);
    if (fd < 0) {
        return NULL;
    }
    
    state->shm_data = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
    if (state->shm_data == MAP_FAILED) {
        close(fd);
        return NULL;
    }
    
    struct wl_shm_pool *pool = wl_shm_create_pool(state->shm, fd, size);
    struct wl_buffer *buffer = wl_shm_pool_create_buffer(pool, 0,
                                                        state->width, state->height,
                                                        stride, WL_SHM_FORMAT_ARGB8888);
    wl_shm_pool_destroy(pool);
    close(fd);
    
    return buffer;
}

static void draw_frame(struct client_state *state)
{
    if (!state->configured)
        return;
        
    if (state->buffer)
        wl_buffer_destroy(state->buffer);
    
    state->buffer = create_buffer(state);
    if (!state->buffer)
        return;
    
    cairo_surface_t *surface = cairo_image_surface_create_for_data(state->shm_data,
                                                                  CAIRO_FORMAT_ARGB32,
                                                                  state->width,
                                                                  state->height,
                                                                  state->width * 4);
    cairo_t *cr = cairo_create(surface);
    
    // Clear the surface
    cairo_set_source_rgb(cr, 1.0, 1.0, 1.0);
    cairo_paint(cr);
    
    // Draw text
    cairo_select_font_face(cr, "Sans", CAIRO_FONT_SLANT_NORMAL,
                          CAIRO_FONT_WEIGHT_BOLD);
    cairo_set_font_size(cr, 24.0);
    cairo_set_source_rgb(cr, 0.0, 0.0, 0.0);
    cairo_move_to(cr, 20.0, 50.0);
    cairo_show_text(cr, "Hello Wayland!");
    
    cairo_destroy(cr);
    cairo_surface_destroy(surface);
    
    wl_surface_attach(state->surface, state->buffer, 0, 0);
    wl_surface_damage_buffer(state->surface, 0, 0, state->width, state->height);
    wl_surface_commit(state->surface);
}

int main(int argc, char *argv[])
{
    struct client_state state = { 0 };
    state.width = 400;
    state.height = 300;
    state.configured = false;
    
    state.display = wl_display_connect(NULL);
    if (!state.display) {
        fprintf(stderr, "Failed to connect to Wayland display\n");
        return 1;
    }
    
    state.registry = wl_display_get_registry(state.display);
    wl_registry_add_listener(state.registry, &registry_listener, &state);
    
    wl_display_roundtrip(state.display);
    
    if (!state.compositor || !state.xdg_wm_base || !state.shm) {
        fprintf(stderr, "Failed to bind required Wayland interfaces\n");
        return 1;
    }
    
    state.surface = wl_compositor_create_surface(state.compositor);
    state.xdg_surface = xdg_wm_base_get_xdg_surface(state.xdg_wm_base, state.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, &xdg_toplevel_listener, &state);
    xdg_toplevel_set_title(state.xdg_toplevel, "Hello Wayland!");
    
    wl_surface_commit(state.surface);
    
    while (wl_display_dispatch(state.display) != -1) {
        if (state.configured) {
            draw_frame(&state);
            state.configured = false;
        }
    }
    
    return 0;
}

@wilsonsilva
Copy link
Author

#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>
#include <wayland-client.h>
#include <wayland-client-protocol.h>
#include <cairo/cairo.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <linux/memfd.h>
#include "xdg-shell-client-protocol.h"

#ifndef MFD_CLOEXEC
#define MFD_CLOEXEC 1U
#endif

struct client_state {
    struct wl_display *display;
    struct wl_registry *registry;
    struct wl_compositor *compositor;
    struct wl_surface *surface;
    struct xdg_wm_base *xdg_wm_base;
    struct xdg_surface *xdg_surface;
    struct xdg_toplevel *xdg_toplevel;
    struct wl_shm *shm;
    struct wl_buffer *buffer;
    int width, height;
    void *shm_data;
    bool configured;
};

static void handle_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 xdg_wm_base_listener = {
    .ping = handle_ping,
};

static void xdg_surface_configure(void *data, struct xdg_surface *xdg_surface, uint32_t serial)
{
    struct client_state *state = data;
    xdg_surface_ack_configure(xdg_surface, serial);
    state->configured = true;
    wl_surface_commit(state->surface);
}

static const struct xdg_surface_listener xdg_surface_listener = {
    .configure = xdg_surface_configure,
};

static void xdg_toplevel_configure(void *data, struct xdg_toplevel *xdg_toplevel,
                                 int32_t width, int32_t height,
                                 struct wl_array *states)
{
    // Handle window configuration
}

static void xdg_toplevel_close(void *data, struct xdg_toplevel *xdg_toplevel)
{
    // Handle window close request
}

static const struct xdg_toplevel_listener xdg_toplevel_listener = {
    .configure = xdg_toplevel_configure,
    .close = xdg_toplevel_close,
};

static void registry_global(void *data, struct wl_registry *registry,
                          uint32_t name, const char *interface, uint32_t version)
{
    struct client_state *state = data;

    if (strcmp(interface, wl_compositor_interface.name) == 0) {
        state->compositor = wl_registry_bind(registry, name,
                                           &wl_compositor_interface, 1);
    } else if (strcmp(interface, xdg_wm_base_interface.name) == 0) {
        state->xdg_wm_base = wl_registry_bind(registry, name,
                                             &xdg_wm_base_interface, 1);
        xdg_wm_base_add_listener(state->xdg_wm_base,
                                &xdg_wm_base_listener, state);
    } else if (strcmp(interface, wl_shm_interface.name) == 0) {
        state->shm = wl_registry_bind(registry, name,
                                    &wl_shm_interface, 1);
    }
}

static void registry_global_remove(void *data, struct wl_registry *registry,
                                 uint32_t name)
{
    // Handle removal of global objects
}

static const struct wl_registry_listener registry_listener = {
    .global = registry_global,
    .global_remove = registry_global_remove,
};

static int create_shared_memory(size_t size)
{
    int fd;
    
    #if defined(__linux__)
        fd = memfd_create("buffer", MFD_CLOEXEC);
        if (fd >= 0)
            return fd;
    #endif
    
    char template[] = "/tmp/wayland-XXXXXX";
    fd = mkstemp(template);
    if (fd >= 0) {
        unlink(template);
    } else {
        return -1;
    }
    
    if (ftruncate(fd, size) < 0) {
        close(fd);
        return -1;
    }
    
    return fd;
}

static struct wl_buffer *create_buffer(struct client_state *state)
{
    int stride = state->width * 4;
    int size = stride * state->height;
    
    int fd = create_shared_memory(size);
    if (fd < 0) {
        return NULL;
    }
    
    state->shm_data = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
    if (state->shm_data == MAP_FAILED) {
        close(fd);
        return NULL;
    }
    
    struct wl_shm_pool *pool = wl_shm_create_pool(state->shm, fd, size);
    struct wl_buffer *buffer = wl_shm_pool_create_buffer(pool, 0,
                                                        state->width, state->height,
                                                        stride, WL_SHM_FORMAT_ARGB8888);
    wl_shm_pool_destroy(pool);
    close(fd);
    
    return buffer;
}

static void draw_frame(struct client_state *state)
{
    if (!state->configured)
        return;
        
    if (state->buffer)
        wl_buffer_destroy(state->buffer);
    
    state->buffer = create_buffer(state);
    if (!state->buffer)
        return;
    
    cairo_surface_t *surface = cairo_image_surface_create_for_data(state->shm_data,
                                                                  CAIRO_FORMAT_ARGB32,
                                                                  state->width,
                                                                  state->height,
                                                                  state->width * 4);
    cairo_t *cr = cairo_create(surface);
    
    // Clear the surface
    cairo_set_source_rgb(cr, 1.0, 1.0, 1.0);
    cairo_paint(cr);
    
    // Draw text
    cairo_select_font_face(cr, "Sans", CAIRO_FONT_SLANT_NORMAL,
                          CAIRO_FONT_WEIGHT_BOLD);
    cairo_set_font_size(cr, 24.0);
    cairo_set_source_rgb(cr, 0.0, 0.0, 0.0);
    cairo_move_to(cr, 20.0, 50.0);
    cairo_show_text(cr, "Hello Wayland!");
    
    cairo_destroy(cr);
    cairo_surface_destroy(surface);
    
    wl_surface_attach(state->surface, state->buffer, 0, 0);
    wl_surface_damage_buffer(state->surface, 0, 0, state->width, state->height);
    wl_surface_commit(state->surface);
}

int main(int argc, char *argv[])
{
    struct client_state state = { 0 };
    state.width = 400;
    state.height = 300;
    state.configured = false;
    
    state.display = wl_display_connect(NULL);
    if (!state.display) {
        fprintf(stderr, "Failed to connect to Wayland display\n");
        return 1;
    }
    
    state.registry = wl_display_get_registry(state.display);
    wl_registry_add_listener(state.registry, &registry_listener, &state);
    
    wl_display_roundtrip(state.display);
    
    if (!state.compositor || !state.xdg_wm_base || !state.shm) {
        fprintf(stderr, "Failed to bind required Wayland interfaces\n");
        return 1;
    }
    
    state.surface = wl_compositor_create_surface(state.compositor);
    state.xdg_surface = xdg_wm_base_get_xdg_surface(state.xdg_wm_base, state.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, &xdg_toplevel_listener, &state);
    xdg_toplevel_set_title(state.xdg_toplevel, "Hello Wayland!");
    
    wl_surface_commit(state.surface);
    
    while (wl_display_dispatch(state.display) != -1) {
        if (state.configured) {
            draw_frame(&state);
            state.configured = false;
        }
    }
    
    return 0;
}

@wilsonsilva
Copy link
Author

CC = gcc
CFLAGS = $(shell pkg-config --cflags wayland-client cairo) -g
LDFLAGS = $(shell pkg-config --libs wayland-client cairo) -lrt

SOURCES = main.c xdg-shell-protocol.c
OBJECTS = $(SOURCES:.c=.o)
TARGET = hello_wayland

$(TARGET): $(OBJECTS)
	$(CC) -o $@ $(OBJECTS) $(LDFLAGS)

%.o: %.c
	$(CC) -c -o $@ $< $(CFLAGS)

clean:
	rm -f $(TARGET) $(OBJECTS)

@wilsonsilva
Copy link
Author

gcc -o hello_wayland main.c xdg-shell-protocol.c $(pkg-config --cflags --libs wayland-client cairo) -lrt

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment