Skip to content

Instantly share code, notes, and snippets.

@kraftwerk28
Created May 3, 2022 10:07
Show Gist options
  • Save kraftwerk28/0749b381b6587c0c7c530536883aa397 to your computer and use it in GitHub Desktop.
Save kraftwerk28/0749b381b6587c0c7c530536883aa397 to your computer and use it in GitHub Desktop.
xdg-activation-v1
#include <assert.h>
#include <errno.h>
#include <fcntl.h>
#include <getopt.h>
#include <linux/input-event-codes.h>
#include <poll.h>
#include <signal.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <time.h>
#include <unistd.h>
#include <wayland-client.h>
#include <wayland-server.h>
#include "xdg-activation-v1-protocol.h"
#include "xdg-shell-protocol.h"
#define BIND(iface, state_ptr, ...) \
do { \
if (strcmp(interface, iface.name) == 0) { \
state_ptr = wl_registry_bind(wl_registry, name, &iface, version); \
__VA_ARGS__; \
return; \
} \
} while (0);
typedef struct {
int width, height;
int bits_per_pixel;
} surface_dim_t;
struct app_state {
// Globals
struct wl_display *display;
struct wl_registry *registry;
struct wl_compositor *compositor;
struct wl_shm *wl_shm;
struct xdg_wm_base *xdg_wm_base;
struct xdg_activation_v1 *xav1;
// Non-globals
struct wl_surface *main_surface;
struct xdg_surface *xdg_surface;
struct xdg_toplevel *xdg_toplevel;
struct wl_buffer *wl_surface_buffer;
struct wl_callback *frame_callback;
struct xdg_activation_token_v1 *xa_token;
char *xa_token_s;
int timeout;
surface_dim_t main_surface_dim;
uint8_t *raw_surface_buffer;
bool running;
struct wl_event_loop *loop;
struct wl_event_source *sleep_timer;
};
typedef struct app_state app_state_t;
static void xdg_wm_base_on_ping(
void *data, struct xdg_wm_base *xdg_wm_base, uint32_t serial) {
xdg_wm_base_pong(xdg_wm_base, serial);
}
static void wl_display_on_global(
void *data, struct wl_registry *wl_registry, uint32_t name,
const char *interface, uint32_t version) {
struct app_state *state = data;
BIND(wl_compositor_interface, state->compositor);
BIND(xdg_wm_base_interface, state->xdg_wm_base);
BIND(xdg_activation_v1_interface, state->xav1);
BIND(wl_shm_interface, state->wl_shm);
}
static void noop() {
}
static struct wl_callback_listener callback_l;
static void xdg_surface_on_configure(
void *data, struct xdg_surface *xdg_surface, uint32_t serial) {
xdg_surface_ack_configure(xdg_surface, serial);
wl_surface_commit(((app_state_t *)data)->main_surface);
}
static void
xdg_toplevel_on_close(void *data, struct xdg_toplevel *xdg_toplevel) {
((app_state_t *)data)->running = false;
}
static int create_sh_mem(const int pool_size, int *fd, uint8_t **raw) {
const char *shm_name = "/wl_shm-xdg-activation-test";
shm_unlink(shm_name);
int shm_fd = shm_open(shm_name, O_RDWR | O_CREAT | O_EXCL, 0600);
if (shm_fd < 0) {
perror("shm_open");
return 1;
}
if (ftruncate(shm_fd, pool_size) < 0) {
return 1;
}
*fd = shm_fd;
*raw = mmap(NULL, pool_size, PROT_READ | PROT_WRITE, MAP_SHARED, shm_fd, 0);
return 0;
}
int wl_event_loop_fd_func(int fd, uint32_t mask, void *data) {
app_state_t *state = data;
if (mask & WL_EVENT_READABLE) {
wl_display_dispatch(state->display);
}
return 0;
}
int wl_event_loop_signal_func(int signal_number, void *data) {
printf("\nCaught signal\n");
((app_state_t *)data)->running = false;
return 0;
}
void xdg_activation_token_v1_on_done(
void *data, struct xdg_activation_token_v1 *xa_token, const char *token) {
app_state_t *s = data;
s->xa_token_s = strdup(token);
xdg_activation_token_v1_destroy(s->xa_token);
xdg_activation_v1_activate(s->xav1, s->xa_token_s, s->main_surface);
s->xa_token = NULL;
}
static struct xdg_activation_token_v1_listener xav1_l;
int wl_event_loop_timer_func(void *data) {
app_state_t *s = data;
s->xa_token = xdg_activation_v1_get_activation_token(s->xav1);
xdg_activation_token_v1_add_listener(s->xa_token, &xav1_l, s);
xdg_activation_token_v1_set_surface(s->xa_token, s->main_surface);
xdg_activation_token_v1_commit(s->xa_token);
return 0;
}
void draw(const app_state_t *state, const uint32_t time) {
const surface_dim_t *d = &state->main_surface_dim;
uint32_t *raw = (uint32_t *)state->raw_surface_buffer;
int hue = (time / 10) % 360;
int x = (1 - fabs(fmod(((double)hue / 60.), 2.) - 1)) * 0xff;
uint8_t r, g, b;
if (hue < 60) {
r = 0xff, g = x, b = 0;
} else if (hue < 120) {
r = x, g = 0xff, b = 0;
} else if (hue < 180) {
r = 0, g = 0xff, b = x;
} else if (hue < 240) {
r = 0, g = x, b = 0xff;
} else if (hue < 300) {
r = x, g = 0, b = 0xff;
} else {
r = 0xff, g = 0, b = x;
}
for (int i = 0; i < d->width * d->height; i++) {
raw[i] = (r << 16) | (g << 8) | b;
}
}
void wl_callback_on_done(
void *data, struct wl_callback *wl_callback, uint32_t callback_data) {
app_state_t *state = data;
wl_callback_destroy(state->frame_callback);
state->frame_callback = wl_surface_frame(state->main_surface);
wl_callback_add_listener(state->frame_callback, &callback_l, state);
draw(state, callback_data);
surface_dim_t *d = &state->main_surface_dim;
wl_surface_attach(state->main_surface, state->wl_surface_buffer, 0, 0);
wl_surface_damage(state->main_surface, 0, 0, d->width, d->height);
wl_surface_commit(state->main_surface);
}
static struct wl_registry_listener registry_l = {wl_display_on_global, noop};
static struct xdg_wm_base_listener wm_base_l = {.ping = xdg_wm_base_on_ping};
static struct xdg_surface_listener xdg_surface_l = {xdg_surface_on_configure};
static struct xdg_toplevel_listener xdg_toplevel_l = {
.close = xdg_toplevel_on_close,
.configure = noop,
.configure_bounds = noop,
};
static struct wl_callback_listener callback_l = {wl_callback_on_done};
static struct xdg_activation_token_v1_listener xav1_l = {
xdg_activation_token_v1_on_done,
};
int create_wl_buffer(app_state_t *state) {
surface_dim_t *d = &state->main_surface_dim;
const int stride = d->width * d->bits_per_pixel;
const int pool_size = d->height * d->width * d->bits_per_pixel * 2;
int shm_fd;
create_sh_mem(pool_size, &shm_fd, &state->raw_surface_buffer);
struct wl_shm_pool *wl_shm_pool =
wl_shm_create_pool(state->wl_shm, shm_fd, pool_size);
close(shm_fd);
state->wl_surface_buffer = wl_shm_pool_create_buffer(
wl_shm_pool, 0, d->width, d->height, stride, WL_SHM_FORMAT_XRGB8888);
wl_shm_pool_destroy(wl_shm_pool);
return 0;
}
void usage(int argc, char *argv[]) {
fprintf(stderr, "Usage: %s <timeout>\n", argv[0]);
}
int main(int argc, char *argv[]) {
struct app_state state = {0};
if (argc < 2) {
usage(argc, argv);
return 1;
}
char *endptr;
state.timeout = strtol(argv[1], &endptr, 10);
if (errno != 0 || *endptr) {
usage(argc, argv);
return 1;
}
state.running = true;
state.main_surface_dim.width = 100;
state.main_surface_dim.height = 100;
state.main_surface_dim.bits_per_pixel = 4;
state.display = wl_display_connect(NULL);
state.registry = wl_display_get_registry(state.display);
wl_registry_add_listener(state.registry, &registry_l, &state);
wl_display_roundtrip(state.display);
xdg_wm_base_add_listener(state.xdg_wm_base, &wm_base_l, &state);
state.main_surface = wl_compositor_create_surface(state.compositor);
state.xdg_surface =
xdg_wm_base_get_xdg_surface(state.xdg_wm_base, state.main_surface);
xdg_surface_add_listener(state.xdg_surface, &xdg_surface_l, &state);
state.xdg_toplevel = xdg_surface_get_toplevel(state.xdg_surface);
xdg_toplevel_set_title(state.xdg_toplevel, "xdg activation v1 demo");
xdg_toplevel_add_listener(state.xdg_toplevel, &xdg_toplevel_l, &state);
surface_dim_t *d = &state.main_surface_dim;
xdg_toplevel_set_max_size(state.xdg_toplevel, d->width, d->height);
xdg_toplevel_set_min_size(state.xdg_toplevel, d->width, d->height);
wl_surface_commit(state.main_surface);
wl_display_roundtrip(state.display);
create_wl_buffer(&state);
wl_surface_attach(state.main_surface, state.wl_surface_buffer, 0, 0);
wl_surface_commit(state.main_surface);
state.frame_callback = wl_surface_frame(state.main_surface);
wl_callback_add_listener(state.frame_callback, &callback_l, &state);
state.loop = wl_event_loop_create();
struct wl_event_source *fd_src = wl_event_loop_add_fd(
state.loop, wl_display_get_fd(state.display), WL_EVENT_READABLE,
wl_event_loop_fd_func, &state);
struct wl_event_source *sig_src = wl_event_loop_add_signal(
state.loop, SIGINT, wl_event_loop_signal_func, &state);
state.sleep_timer =
wl_event_loop_add_timer(state.loop, wl_event_loop_timer_func, &state);
wl_event_source_timer_update(state.sleep_timer, state.timeout * 1000);
wl_display_roundtrip(state.display);
while (state.running) {
wl_display_flush(state.display);
if (wl_event_loop_dispatch(state.loop, -1) < 0) {
break;
}
}
wl_event_source_remove(state.sleep_timer);
wl_event_source_remove(fd_src);
wl_event_source_remove(sig_src);
wl_event_loop_destroy(state.loop);
wl_surface_destroy(state.main_surface);
xdg_surface_destroy(state.xdg_surface);
xdg_toplevel_destroy(state.xdg_toplevel);
wl_buffer_destroy(state.wl_surface_buffer);
wl_callback_destroy(state.frame_callback);
if (state.xa_token_s)
free(state.xa_token_s);
xdg_activation_v1_destroy(state.xav1);
xdg_wm_base_destroy(state.xdg_wm_base);
wl_shm_destroy(state.wl_shm);
wl_compositor_destroy(state.compositor);
wl_registry_destroy(state.registry);
wl_display_disconnect(state.display);
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment