Created
May 3, 2022 10:07
-
-
Save kraftwerk28/0749b381b6587c0c7c530536883aa397 to your computer and use it in GitHub Desktop.
xdg-activation-v1
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#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, ®istry_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