mirror of
https://gitlab.freedesktop.org/wlroots/wlroots.git
synced 2024-11-22 07:02:28 +00:00
examples: split clients in separate repository
The client examples are useful to try out protocols, however they don't need to live in the wlroots repository. Having both clients and compositors in the same place is confusing. The wlroots API changes often but protocols are set in stone.
This commit is contained in:
parent
b82a53a918
commit
0bb445eeff
@ -1,122 +0,0 @@
|
|||||||
#define _POSIX_C_SOURCE 200809L
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include <stdbool.h>
|
|
||||||
#include <unistd.h>
|
|
||||||
#include <wayland-client.h>
|
|
||||||
|
|
||||||
#include <EGL/egl.h>
|
|
||||||
#include <EGL/eglext.h>
|
|
||||||
|
|
||||||
#include "egl_common.h"
|
|
||||||
|
|
||||||
EGLDisplay egl_display;
|
|
||||||
EGLConfig egl_config;
|
|
||||||
EGLContext egl_context;
|
|
||||||
|
|
||||||
PFNEGLGETPLATFORMDISPLAYEXTPROC eglGetPlatformDisplayEXT;
|
|
||||||
PFNEGLCREATEPLATFORMWINDOWSURFACEEXTPROC eglCreatePlatformWindowSurfaceEXT;
|
|
||||||
|
|
||||||
const EGLint config_attribs[] = {
|
|
||||||
EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
|
|
||||||
EGL_RED_SIZE, 8,
|
|
||||||
EGL_GREEN_SIZE, 8,
|
|
||||||
EGL_BLUE_SIZE, 8,
|
|
||||||
EGL_ALPHA_SIZE, 8,
|
|
||||||
EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
|
|
||||||
EGL_NONE,
|
|
||||||
};
|
|
||||||
|
|
||||||
const EGLint context_attribs[] = {
|
|
||||||
EGL_CONTEXT_CLIENT_VERSION, 2,
|
|
||||||
EGL_NONE,
|
|
||||||
};
|
|
||||||
|
|
||||||
bool egl_init(struct wl_display *display) {
|
|
||||||
const char *client_exts_str =
|
|
||||||
eglQueryString(EGL_NO_DISPLAY, EGL_EXTENSIONS);
|
|
||||||
if (client_exts_str == NULL) {
|
|
||||||
if (eglGetError() == EGL_BAD_DISPLAY) {
|
|
||||||
fprintf(stderr, "EGL_EXT_client_extensions not supported\n");
|
|
||||||
} else {
|
|
||||||
fprintf(stderr, "Failed to query EGL client extensions\n");
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!strstr(client_exts_str, "EGL_EXT_platform_base")) {
|
|
||||||
fprintf(stderr, "EGL_EXT_platform_base not supported\n");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!strstr(client_exts_str, "EGL_EXT_platform_wayland")) {
|
|
||||||
fprintf(stderr, "EGL_EXT_platform_wayland not supported\n");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
eglGetPlatformDisplayEXT =
|
|
||||||
(void *)eglGetProcAddress("eglGetPlatformDisplayEXT");
|
|
||||||
if (eglGetPlatformDisplayEXT == NULL) {
|
|
||||||
fprintf(stderr, "Failed to get eglGetPlatformDisplayEXT\n");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
eglCreatePlatformWindowSurfaceEXT =
|
|
||||||
(void *)eglGetProcAddress("eglCreatePlatformWindowSurfaceEXT");
|
|
||||||
if (eglCreatePlatformWindowSurfaceEXT == NULL) {
|
|
||||||
fprintf(stderr, "Failed to get eglCreatePlatformWindowSurfaceEXT\n");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
egl_display =
|
|
||||||
eglGetPlatformDisplayEXT(EGL_PLATFORM_WAYLAND_EXT,
|
|
||||||
display, NULL);
|
|
||||||
if (egl_display == EGL_NO_DISPLAY) {
|
|
||||||
fprintf(stderr, "Failed to create EGL display\n");
|
|
||||||
goto error;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (eglInitialize(egl_display, NULL, NULL) == EGL_FALSE) {
|
|
||||||
fprintf(stderr, "Failed to initialize EGL\n");
|
|
||||||
goto error;
|
|
||||||
}
|
|
||||||
|
|
||||||
EGLint matched = 0;
|
|
||||||
if (!eglChooseConfig(egl_display, config_attribs,
|
|
||||||
&egl_config, 1, &matched)) {
|
|
||||||
fprintf(stderr, "eglChooseConfig failed\n");
|
|
||||||
goto error;
|
|
||||||
}
|
|
||||||
if (matched == 0) {
|
|
||||||
fprintf(stderr, "Failed to match an EGL config\n");
|
|
||||||
goto error;
|
|
||||||
}
|
|
||||||
|
|
||||||
egl_context =
|
|
||||||
eglCreateContext(egl_display, egl_config,
|
|
||||||
EGL_NO_CONTEXT, context_attribs);
|
|
||||||
if (egl_context == EGL_NO_CONTEXT) {
|
|
||||||
fprintf(stderr, "Failed to create EGL context\n");
|
|
||||||
goto error;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
|
|
||||||
error:
|
|
||||||
eglMakeCurrent(EGL_NO_DISPLAY, EGL_NO_SURFACE,
|
|
||||||
EGL_NO_SURFACE, EGL_NO_CONTEXT);
|
|
||||||
if (egl_display) {
|
|
||||||
eglTerminate(egl_display);
|
|
||||||
}
|
|
||||||
eglReleaseThread();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
void egl_finish(void) {
|
|
||||||
eglMakeCurrent(egl_display, EGL_NO_SURFACE,
|
|
||||||
EGL_NO_SURFACE, EGL_NO_CONTEXT);
|
|
||||||
eglDestroyContext(egl_display, egl_context);
|
|
||||||
eglTerminate(egl_display);
|
|
||||||
eglReleaseThread();
|
|
||||||
}
|
|
@ -1,19 +0,0 @@
|
|||||||
#ifndef _EGL_COMMON_H
|
|
||||||
#define _EGL_COMMON_H
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include <stdbool.h>
|
|
||||||
#include <wayland-client.h>
|
|
||||||
|
|
||||||
#include <EGL/egl.h>
|
|
||||||
#include <EGL/eglext.h>
|
|
||||||
|
|
||||||
extern EGLDisplay egl_display;
|
|
||||||
extern EGLConfig egl_config;
|
|
||||||
extern EGLContext egl_context;
|
|
||||||
|
|
||||||
extern PFNEGLCREATEPLATFORMWINDOWSURFACEEXTPROC eglCreatePlatformWindowSurfaceEXT;
|
|
||||||
|
|
||||||
bool egl_init(struct wl_display *display);
|
|
||||||
|
|
||||||
void egl_finish(void);
|
|
@ -1,4 +1,6 @@
|
|||||||
#define _POSIX_C_SOURCE 200112L
|
#define _POSIX_C_SOURCE 200112L
|
||||||
|
#include <EGL/egl.h>
|
||||||
|
#include <EGL/eglext.h>
|
||||||
#include <GLES2/gl2.h>
|
#include <GLES2/gl2.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
@ -13,7 +15,6 @@
|
|||||||
#include <wlr/types/wlr_xdg_shell.h>
|
#include <wlr/types/wlr_xdg_shell.h>
|
||||||
#include <wlr/util/log.h>
|
#include <wlr/util/log.h>
|
||||||
|
|
||||||
#include "egl_common.h"
|
|
||||||
#include "xdg-shell-client-protocol.h"
|
#include "xdg-shell-client-protocol.h"
|
||||||
|
|
||||||
static struct wl_display *remote_display = NULL;
|
static struct wl_display *remote_display = NULL;
|
||||||
@ -31,8 +32,12 @@ static struct wlr_scene_output *scene_output = NULL;
|
|||||||
static struct wl_listener new_surface = {0};
|
static struct wl_listener new_surface = {0};
|
||||||
static struct wl_listener output_frame = {0};
|
static struct wl_listener output_frame = {0};
|
||||||
|
|
||||||
int width = 500;
|
static EGLDisplay egl_display;
|
||||||
int height = 500;
|
static EGLConfig egl_config;
|
||||||
|
static EGLContext egl_context;
|
||||||
|
|
||||||
|
static int width = 500;
|
||||||
|
static int height = 500;
|
||||||
|
|
||||||
static void draw_main_surface(void);
|
static void draw_main_surface(void);
|
||||||
|
|
||||||
@ -120,6 +125,29 @@ static void handle_new_surface(struct wl_listener *listener, void *data) {
|
|||||||
wlr_scene_surface_create(&scene->tree, wlr_surface);
|
wlr_scene_surface_create(&scene->tree, wlr_surface);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void init_egl(struct wl_display *display) {
|
||||||
|
egl_display = eglGetPlatformDisplay(EGL_PLATFORM_WAYLAND_KHR, display, NULL);
|
||||||
|
eglInitialize(egl_display, NULL, NULL);
|
||||||
|
|
||||||
|
EGLint matched = 0;
|
||||||
|
const EGLint config_attribs[] = {
|
||||||
|
EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
|
||||||
|
EGL_RED_SIZE, 8,
|
||||||
|
EGL_GREEN_SIZE, 8,
|
||||||
|
EGL_BLUE_SIZE, 8,
|
||||||
|
EGL_ALPHA_SIZE, 8,
|
||||||
|
EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
|
||||||
|
EGL_NONE,
|
||||||
|
};
|
||||||
|
eglChooseConfig(egl_display, config_attribs, &egl_config, 1, &matched);
|
||||||
|
|
||||||
|
const EGLint context_attribs[] = {
|
||||||
|
EGL_CONTEXT_CLIENT_VERSION, 2,
|
||||||
|
EGL_NONE,
|
||||||
|
};
|
||||||
|
egl_context = eglCreateContext(egl_display, egl_config, EGL_NO_CONTEXT, context_attribs);
|
||||||
|
}
|
||||||
|
|
||||||
int main(int argc, char *argv[]) {
|
int main(int argc, char *argv[]) {
|
||||||
wlr_log_init(WLR_DEBUG, NULL);
|
wlr_log_init(WLR_DEBUG, NULL);
|
||||||
|
|
||||||
@ -128,7 +156,7 @@ int main(int argc, char *argv[]) {
|
|||||||
wl_registry_add_listener(registry, ®istry_listener, NULL);
|
wl_registry_add_listener(registry, ®istry_listener, NULL);
|
||||||
wl_display_roundtrip(remote_display);
|
wl_display_roundtrip(remote_display);
|
||||||
|
|
||||||
egl_init(remote_display);
|
init_egl(remote_display);
|
||||||
|
|
||||||
struct wl_display *local_display = wl_display_create();
|
struct wl_display *local_display = wl_display_create();
|
||||||
struct wlr_backend *backend = wlr_wl_backend_create(local_display, remote_display);
|
struct wlr_backend *backend = wlr_wl_backend_create(local_display, remote_display);
|
||||||
@ -152,7 +180,7 @@ int main(int argc, char *argv[]) {
|
|||||||
xdg_toplevel_add_listener(xdg_toplevel, &xdg_toplevel_listener, NULL);
|
xdg_toplevel_add_listener(xdg_toplevel, &xdg_toplevel_listener, NULL);
|
||||||
|
|
||||||
egl_window = wl_egl_window_create(main_surface, width, height);
|
egl_window = wl_egl_window_create(main_surface, width, height);
|
||||||
egl_surface = eglCreatePlatformWindowSurfaceEXT(egl_display,
|
egl_surface = eglCreatePlatformWindowSurface(egl_display,
|
||||||
egl_config, egl_window, NULL);
|
egl_config, egl_window, NULL);
|
||||||
|
|
||||||
struct wl_surface *child_surface = wl_compositor_create_surface(compositor);
|
struct wl_surface *child_surface = wl_compositor_create_surface(compositor);
|
||||||
|
@ -1,439 +0,0 @@
|
|||||||
#define _POSIX_C_SOURCE 200809L
|
|
||||||
#include <getopt.h>
|
|
||||||
#include <stdbool.h>
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include <wayland-client.h>
|
|
||||||
#include "wlr-foreign-toplevel-management-unstable-v1-client-protocol.h"
|
|
||||||
|
|
||||||
#define WLR_FOREIGN_TOPLEVEL_MANAGEMENT_VERSION 3
|
|
||||||
|
|
||||||
|
|
||||||
static void print_help(void) {
|
|
||||||
static const char usage[] =
|
|
||||||
"Usage: foreign-toplevel [OPTIONS] ...\n"
|
|
||||||
"Manage and view information about toplevel windows.\n"
|
|
||||||
"\n"
|
|
||||||
" -f <id> focus\n"
|
|
||||||
" -s <id> fullscreen\n"
|
|
||||||
" -o <output_id> select output for fullscreen toplevel to appear on. Use this\n"
|
|
||||||
" option with -s. View available outputs with wayland-info.\n"
|
|
||||||
" -S <id> unfullscreen\n"
|
|
||||||
" -a <id> maximize\n"
|
|
||||||
" -u <id> unmaximize\n"
|
|
||||||
" -i <id> minimize\n"
|
|
||||||
" -r <id> restore(unminimize)\n"
|
|
||||||
" -c <id> close\n"
|
|
||||||
" -m continuously print changes to the list of opened toplevels\n"
|
|
||||||
" Can be used together with some of the previous options.\n"
|
|
||||||
" -h print help message and quit\n";
|
|
||||||
fprintf(stderr, "%s", usage);
|
|
||||||
}
|
|
||||||
|
|
||||||
enum toplevel_state_field {
|
|
||||||
TOPLEVEL_STATE_MAXIMIZED = (1 << 0),
|
|
||||||
TOPLEVEL_STATE_MINIMIZED = (1 << 1),
|
|
||||||
TOPLEVEL_STATE_ACTIVATED = (1 << 2),
|
|
||||||
TOPLEVEL_STATE_FULLSCREEN = (1 << 3),
|
|
||||||
TOPLEVEL_STATE_INVALID = (1 << 4),
|
|
||||||
};
|
|
||||||
|
|
||||||
static const uint32_t no_parent = (uint32_t)-1;
|
|
||||||
static struct wl_output *pref_output = NULL;
|
|
||||||
static uint32_t pref_output_id = UINT32_MAX;
|
|
||||||
|
|
||||||
struct toplevel_state {
|
|
||||||
char *title;
|
|
||||||
char *app_id;
|
|
||||||
|
|
||||||
uint32_t state;
|
|
||||||
uint32_t parent_id;
|
|
||||||
};
|
|
||||||
|
|
||||||
static void copy_state(struct toplevel_state *current,
|
|
||||||
struct toplevel_state *pending) {
|
|
||||||
if (current->title && pending->title) {
|
|
||||||
free(current->title);
|
|
||||||
}
|
|
||||||
if (current->app_id && pending->app_id) {
|
|
||||||
free(current->app_id);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (pending->title) {
|
|
||||||
current->title = pending->title;
|
|
||||||
pending->title = NULL;
|
|
||||||
}
|
|
||||||
if (pending->app_id) {
|
|
||||||
current->app_id = pending->app_id;
|
|
||||||
pending->app_id = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!(pending->state & TOPLEVEL_STATE_INVALID)) {
|
|
||||||
current->state = pending->state;
|
|
||||||
}
|
|
||||||
|
|
||||||
current->parent_id = pending->parent_id;
|
|
||||||
|
|
||||||
pending->state = TOPLEVEL_STATE_INVALID;
|
|
||||||
}
|
|
||||||
|
|
||||||
static uint32_t global_id = 0;
|
|
||||||
struct toplevel_v1 {
|
|
||||||
struct wl_list link;
|
|
||||||
struct zwlr_foreign_toplevel_handle_v1 *zwlr_toplevel;
|
|
||||||
|
|
||||||
uint32_t id;
|
|
||||||
struct toplevel_state current, pending;
|
|
||||||
};
|
|
||||||
|
|
||||||
static void print_toplevel(struct toplevel_v1 *toplevel, bool print_endl) {
|
|
||||||
printf("-> %d. title=%s app_id=%s", toplevel->id,
|
|
||||||
toplevel->current.title ?: "(nil)",
|
|
||||||
toplevel->current.app_id ?: "(nil)");
|
|
||||||
|
|
||||||
if (toplevel->current.parent_id != no_parent) {
|
|
||||||
printf(" parent=%u", toplevel->current.parent_id);
|
|
||||||
} else {
|
|
||||||
printf(" no parent");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (print_endl) {
|
|
||||||
printf("\n");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void print_toplevel_state(struct toplevel_v1 *toplevel, bool print_endl) {
|
|
||||||
if (toplevel->current.state & TOPLEVEL_STATE_MAXIMIZED) {
|
|
||||||
printf(" maximized");
|
|
||||||
} else {
|
|
||||||
printf(" unmaximized");
|
|
||||||
}
|
|
||||||
if (toplevel->current.state & TOPLEVEL_STATE_MINIMIZED) {
|
|
||||||
printf(" minimized");
|
|
||||||
} else {
|
|
||||||
printf(" unminimized");
|
|
||||||
}
|
|
||||||
if (toplevel->current.state & TOPLEVEL_STATE_ACTIVATED) {
|
|
||||||
printf(" active");
|
|
||||||
} else {
|
|
||||||
printf(" inactive");
|
|
||||||
}
|
|
||||||
if (toplevel->current.state & TOPLEVEL_STATE_FULLSCREEN) {
|
|
||||||
printf(" fullscreen");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (print_endl) {
|
|
||||||
printf("\n");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void finish_toplevel_state(struct toplevel_state *state) {
|
|
||||||
free(state->title);
|
|
||||||
free(state->app_id);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void toplevel_handle_title(void *data,
|
|
||||||
struct zwlr_foreign_toplevel_handle_v1 *zwlr_toplevel,
|
|
||||||
const char *title) {
|
|
||||||
struct toplevel_v1 *toplevel = data;
|
|
||||||
free(toplevel->pending.title);
|
|
||||||
toplevel->pending.title = strdup(title);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void toplevel_handle_app_id(void *data,
|
|
||||||
struct zwlr_foreign_toplevel_handle_v1 *zwlr_toplevel,
|
|
||||||
const char *app_id) {
|
|
||||||
struct toplevel_v1 *toplevel = data;
|
|
||||||
free(toplevel->pending.app_id);
|
|
||||||
toplevel->pending.app_id = strdup(app_id);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void toplevel_handle_output_enter(void *data,
|
|
||||||
struct zwlr_foreign_toplevel_handle_v1 *zwlr_toplevel,
|
|
||||||
struct wl_output *output) {
|
|
||||||
struct toplevel_v1 *toplevel = data;
|
|
||||||
print_toplevel(toplevel, false);
|
|
||||||
printf(" enter output %u\n",
|
|
||||||
(uint32_t)(size_t)wl_output_get_user_data(output));
|
|
||||||
}
|
|
||||||
|
|
||||||
static void toplevel_handle_output_leave(void *data,
|
|
||||||
struct zwlr_foreign_toplevel_handle_v1 *zwlr_toplevel,
|
|
||||||
struct wl_output *output) {
|
|
||||||
struct toplevel_v1 *toplevel = data;
|
|
||||||
print_toplevel(toplevel, false);
|
|
||||||
printf(" leave output %u\n",
|
|
||||||
(uint32_t)(size_t)wl_output_get_user_data(output));
|
|
||||||
}
|
|
||||||
|
|
||||||
static uint32_t array_to_state(struct wl_array *array) {
|
|
||||||
uint32_t state = 0;
|
|
||||||
uint32_t *entry;
|
|
||||||
wl_array_for_each(entry, array) {
|
|
||||||
if (*entry == ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_STATE_MAXIMIZED)
|
|
||||||
state |= TOPLEVEL_STATE_MAXIMIZED;
|
|
||||||
if (*entry == ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_STATE_MINIMIZED)
|
|
||||||
state |= TOPLEVEL_STATE_MINIMIZED;
|
|
||||||
if (*entry == ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_STATE_ACTIVATED)
|
|
||||||
state |= TOPLEVEL_STATE_ACTIVATED;
|
|
||||||
if (*entry == ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_STATE_FULLSCREEN)
|
|
||||||
state |= TOPLEVEL_STATE_FULLSCREEN;
|
|
||||||
}
|
|
||||||
|
|
||||||
return state;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void toplevel_handle_state(void *data,
|
|
||||||
struct zwlr_foreign_toplevel_handle_v1 *zwlr_toplevel,
|
|
||||||
struct wl_array *state) {
|
|
||||||
struct toplevel_v1 *toplevel = data;
|
|
||||||
toplevel->pending.state = array_to_state(state);
|
|
||||||
}
|
|
||||||
|
|
||||||
static struct zwlr_foreign_toplevel_manager_v1 *toplevel_manager = NULL;
|
|
||||||
static struct wl_list toplevel_list;
|
|
||||||
|
|
||||||
static void toplevel_handle_parent(void *data,
|
|
||||||
struct zwlr_foreign_toplevel_handle_v1 *zwlr_toplevel,
|
|
||||||
struct zwlr_foreign_toplevel_handle_v1 *zwlr_parent) {
|
|
||||||
struct toplevel_v1 *toplevel = data;
|
|
||||||
toplevel->pending.parent_id = no_parent;
|
|
||||||
if (zwlr_parent) {
|
|
||||||
struct toplevel_v1 *toplevel_tmp;
|
|
||||||
wl_list_for_each(toplevel_tmp, &toplevel_list, link) {
|
|
||||||
if (toplevel_tmp->zwlr_toplevel == zwlr_parent) {
|
|
||||||
toplevel->pending.parent_id = toplevel_tmp->id;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (toplevel->pending.parent_id == no_parent) {
|
|
||||||
fprintf(stderr, "Cannot find parent toplevel!\n");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void toplevel_handle_done(void *data,
|
|
||||||
struct zwlr_foreign_toplevel_handle_v1 *zwlr_toplevel) {
|
|
||||||
struct toplevel_v1 *toplevel = data;
|
|
||||||
bool state_changed = toplevel->current.state != toplevel->pending.state;
|
|
||||||
|
|
||||||
copy_state(&toplevel->current, &toplevel->pending);
|
|
||||||
|
|
||||||
print_toplevel(toplevel, !state_changed);
|
|
||||||
if (state_changed) {
|
|
||||||
print_toplevel_state(toplevel, true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void toplevel_handle_closed(void *data,
|
|
||||||
struct zwlr_foreign_toplevel_handle_v1 *zwlr_toplevel) {
|
|
||||||
struct toplevel_v1 *toplevel = data;
|
|
||||||
print_toplevel(toplevel, false);
|
|
||||||
printf(" closed\n");
|
|
||||||
|
|
||||||
zwlr_foreign_toplevel_handle_v1_destroy(zwlr_toplevel);
|
|
||||||
finish_toplevel_state(&toplevel->current);
|
|
||||||
finish_toplevel_state(&toplevel->pending);
|
|
||||||
free(toplevel);
|
|
||||||
}
|
|
||||||
|
|
||||||
static const struct zwlr_foreign_toplevel_handle_v1_listener toplevel_impl = {
|
|
||||||
.title = toplevel_handle_title,
|
|
||||||
.app_id = toplevel_handle_app_id,
|
|
||||||
.output_enter = toplevel_handle_output_enter,
|
|
||||||
.output_leave = toplevel_handle_output_leave,
|
|
||||||
.state = toplevel_handle_state,
|
|
||||||
.done = toplevel_handle_done,
|
|
||||||
.closed = toplevel_handle_closed,
|
|
||||||
.parent = toplevel_handle_parent
|
|
||||||
};
|
|
||||||
|
|
||||||
static void toplevel_manager_handle_toplevel(void *data,
|
|
||||||
struct zwlr_foreign_toplevel_manager_v1 *toplevel_manager,
|
|
||||||
struct zwlr_foreign_toplevel_handle_v1 *zwlr_toplevel) {
|
|
||||||
struct toplevel_v1 *toplevel = calloc(1, sizeof(*toplevel));
|
|
||||||
if (!toplevel) {
|
|
||||||
fprintf(stderr, "Failed to allocate memory for toplevel\n");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
toplevel->id = global_id++;
|
|
||||||
toplevel->zwlr_toplevel = zwlr_toplevel;
|
|
||||||
toplevel->current.parent_id = no_parent;
|
|
||||||
toplevel->pending.parent_id = no_parent;
|
|
||||||
wl_list_insert(&toplevel_list, &toplevel->link);
|
|
||||||
|
|
||||||
zwlr_foreign_toplevel_handle_v1_add_listener(zwlr_toplevel, &toplevel_impl,
|
|
||||||
toplevel);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void toplevel_manager_handle_finished(void *data,
|
|
||||||
struct zwlr_foreign_toplevel_manager_v1 *toplevel_manager) {
|
|
||||||
zwlr_foreign_toplevel_manager_v1_destroy(toplevel_manager);
|
|
||||||
}
|
|
||||||
|
|
||||||
static const struct zwlr_foreign_toplevel_manager_v1_listener toplevel_manager_impl = {
|
|
||||||
.toplevel = toplevel_manager_handle_toplevel,
|
|
||||||
.finished = toplevel_manager_handle_finished,
|
|
||||||
};
|
|
||||||
|
|
||||||
struct wl_seat *seat = NULL;
|
|
||||||
static void handle_global(void *data, struct wl_registry *registry,
|
|
||||||
uint32_t name, const char *interface, uint32_t version) {
|
|
||||||
if (strcmp(interface, wl_output_interface.name) == 0) {
|
|
||||||
if (name == pref_output_id) {
|
|
||||||
pref_output = wl_registry_bind(registry, name,
|
|
||||||
&wl_output_interface, version);
|
|
||||||
|
|
||||||
}
|
|
||||||
} else if (strcmp(interface,
|
|
||||||
zwlr_foreign_toplevel_manager_v1_interface.name) == 0) {
|
|
||||||
toplevel_manager = wl_registry_bind(registry, name,
|
|
||||||
&zwlr_foreign_toplevel_manager_v1_interface,
|
|
||||||
WLR_FOREIGN_TOPLEVEL_MANAGEMENT_VERSION);
|
|
||||||
|
|
||||||
wl_list_init(&toplevel_list);
|
|
||||||
zwlr_foreign_toplevel_manager_v1_add_listener(toplevel_manager,
|
|
||||||
&toplevel_manager_impl, NULL);
|
|
||||||
} else if (strcmp(interface, wl_seat_interface.name) == 0 && seat == NULL) {
|
|
||||||
seat = wl_registry_bind(registry, name, &wl_seat_interface, version);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void handle_global_remove(void *data, struct wl_registry *registry,
|
|
||||||
uint32_t name) {
|
|
||||||
// who cares
|
|
||||||
}
|
|
||||||
|
|
||||||
static const struct wl_registry_listener registry_listener = {
|
|
||||||
.global = handle_global,
|
|
||||||
.global_remove = handle_global_remove,
|
|
||||||
};
|
|
||||||
|
|
||||||
/* return NULL when id == -1
|
|
||||||
* exit if the given ID cannot be found in the list of toplevels */
|
|
||||||
static struct toplevel_v1 *toplevel_by_id_or_bail(int32_t id) {
|
|
||||||
if (id == -1) {
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct toplevel_v1 *toplevel;
|
|
||||||
wl_list_for_each(toplevel, &toplevel_list, link) {
|
|
||||||
if (toplevel->id == (uint32_t)id) {
|
|
||||||
return toplevel;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fprintf(stderr, "No toplevel with the given id: %d\n", id);
|
|
||||||
exit(EXIT_FAILURE);
|
|
||||||
}
|
|
||||||
|
|
||||||
int main(int argc, char **argv) {
|
|
||||||
int focus_id = -1, close_id = -1;
|
|
||||||
int maximize_id = -1, unmaximize_id = -1;
|
|
||||||
int minimize_id = -1, restore_id = -1;
|
|
||||||
int fullscreen_id = -1, unfullscreen_id = -1;
|
|
||||||
int one_shot = 1;
|
|
||||||
int c;
|
|
||||||
|
|
||||||
while ((c = getopt(argc, argv, "f:a:u:i:r:c:s:S:mo:h")) != -1) {
|
|
||||||
switch (c) {
|
|
||||||
case 'f':
|
|
||||||
focus_id = atoi(optarg);
|
|
||||||
break;
|
|
||||||
case 'a':
|
|
||||||
maximize_id = atoi(optarg);
|
|
||||||
break;
|
|
||||||
case 'u':
|
|
||||||
unmaximize_id = atoi(optarg);
|
|
||||||
break;
|
|
||||||
case 'i':
|
|
||||||
minimize_id = atoi(optarg);
|
|
||||||
break;
|
|
||||||
case 'r':
|
|
||||||
restore_id = atoi(optarg);
|
|
||||||
break;
|
|
||||||
case 'c':
|
|
||||||
close_id = atoi(optarg);
|
|
||||||
break;
|
|
||||||
case 's':
|
|
||||||
fullscreen_id = atoi(optarg);
|
|
||||||
break;
|
|
||||||
case 'S':
|
|
||||||
unfullscreen_id = atoi(optarg);
|
|
||||||
break;
|
|
||||||
case 'm':
|
|
||||||
one_shot = 0;
|
|
||||||
break;
|
|
||||||
case 'o':
|
|
||||||
pref_output_id = atoi(optarg);
|
|
||||||
break;
|
|
||||||
case '?':
|
|
||||||
print_help();
|
|
||||||
return EXIT_FAILURE;
|
|
||||||
break;
|
|
||||||
case 'h':
|
|
||||||
print_help();
|
|
||||||
return EXIT_SUCCESS;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
struct wl_display *display = wl_display_connect(NULL);
|
|
||||||
if (display == NULL) {
|
|
||||||
fprintf(stderr, "Failed to create display\n");
|
|
||||||
return EXIT_FAILURE;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct wl_registry *registry = wl_display_get_registry(display);
|
|
||||||
wl_registry_add_listener(registry, ®istry_listener, NULL);
|
|
||||||
wl_display_roundtrip(display);
|
|
||||||
|
|
||||||
if (toplevel_manager == NULL) {
|
|
||||||
fprintf(stderr, "wlr-foreign-toplevel not available\n");
|
|
||||||
return EXIT_FAILURE;
|
|
||||||
}
|
|
||||||
wl_display_roundtrip(display); // load list of toplevels
|
|
||||||
wl_display_roundtrip(display); // load toplevel details
|
|
||||||
|
|
||||||
struct toplevel_v1 *toplevel;
|
|
||||||
if ((toplevel = toplevel_by_id_or_bail(focus_id))) {
|
|
||||||
zwlr_foreign_toplevel_handle_v1_activate(toplevel->zwlr_toplevel, seat);
|
|
||||||
}
|
|
||||||
if ((toplevel = toplevel_by_id_or_bail(maximize_id))) {
|
|
||||||
zwlr_foreign_toplevel_handle_v1_set_maximized(toplevel->zwlr_toplevel);
|
|
||||||
}
|
|
||||||
if ((toplevel = toplevel_by_id_or_bail(unmaximize_id))) {
|
|
||||||
zwlr_foreign_toplevel_handle_v1_unset_maximized(toplevel->zwlr_toplevel);
|
|
||||||
}
|
|
||||||
if ((toplevel = toplevel_by_id_or_bail(minimize_id))) {
|
|
||||||
zwlr_foreign_toplevel_handle_v1_set_minimized(toplevel->zwlr_toplevel);
|
|
||||||
}
|
|
||||||
if ((toplevel = toplevel_by_id_or_bail(restore_id))) {
|
|
||||||
zwlr_foreign_toplevel_handle_v1_unset_minimized(toplevel->zwlr_toplevel);
|
|
||||||
}
|
|
||||||
if ((toplevel = toplevel_by_id_or_bail(fullscreen_id))) {
|
|
||||||
if (pref_output_id != UINT32_MAX && pref_output == NULL) {
|
|
||||||
fprintf(stderr, "Could not find output %i\n", pref_output_id);
|
|
||||||
}
|
|
||||||
|
|
||||||
zwlr_foreign_toplevel_handle_v1_set_fullscreen(toplevel->zwlr_toplevel, pref_output);
|
|
||||||
}
|
|
||||||
if ((toplevel = toplevel_by_id_or_bail(unfullscreen_id))) {
|
|
||||||
zwlr_foreign_toplevel_handle_v1_unset_fullscreen(toplevel->zwlr_toplevel);
|
|
||||||
}
|
|
||||||
if ((toplevel = toplevel_by_id_or_bail(close_id))) {
|
|
||||||
zwlr_foreign_toplevel_handle_v1_close(toplevel->zwlr_toplevel);
|
|
||||||
}
|
|
||||||
|
|
||||||
wl_display_flush(display);
|
|
||||||
|
|
||||||
if (one_shot == 0) {
|
|
||||||
while (wl_display_dispatch(display) != -1) {
|
|
||||||
// This space intentionally left blank
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return EXIT_SUCCESS;
|
|
||||||
}
|
|
@ -1,194 +0,0 @@
|
|||||||
#define _POSIX_C_SOURCE 200809L
|
|
||||||
#include <errno.h>
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include <sys/mman.h>
|
|
||||||
#include <sys/types.h>
|
|
||||||
#include <unistd.h>
|
|
||||||
#include <wayland-client-protocol.h>
|
|
||||||
#include <wayland-client.h>
|
|
||||||
#include "wlr-gamma-control-unstable-v1-client-protocol.h"
|
|
||||||
|
|
||||||
struct output {
|
|
||||||
struct wl_output *wl_output;
|
|
||||||
struct zwlr_gamma_control_v1 *gamma_control;
|
|
||||||
uint32_t ramp_size;
|
|
||||||
int table_fd;
|
|
||||||
uint16_t *table;
|
|
||||||
struct wl_list link;
|
|
||||||
};
|
|
||||||
|
|
||||||
static struct wl_list outputs;
|
|
||||||
static struct zwlr_gamma_control_manager_v1 *gamma_control_manager = NULL;
|
|
||||||
|
|
||||||
static int create_anonymous_file(off_t size) {
|
|
||||||
char template[] = "/tmp/wlroots-shared-XXXXXX";
|
|
||||||
int fd = mkstemp(template);
|
|
||||||
if (fd < 0) {
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
int ret;
|
|
||||||
do {
|
|
||||||
errno = 0;
|
|
||||||
ret = ftruncate(fd, size);
|
|
||||||
} while (errno == EINTR);
|
|
||||||
if (ret < 0) {
|
|
||||||
close(fd);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
unlink(template);
|
|
||||||
return fd;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int create_gamma_table(uint32_t ramp_size, uint16_t **table) {
|
|
||||||
size_t table_size = ramp_size * 3 * sizeof(uint16_t);
|
|
||||||
int fd = create_anonymous_file(table_size);
|
|
||||||
if (fd < 0) {
|
|
||||||
fprintf(stderr, "failed to create anonymous file\n");
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
void *data =
|
|
||||||
mmap(NULL, table_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
|
|
||||||
if (data == MAP_FAILED) {
|
|
||||||
fprintf(stderr, "failed to mmap()\n");
|
|
||||||
close(fd);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
*table = data;
|
|
||||||
return fd;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void gamma_control_handle_gamma_size(void *data,
|
|
||||||
struct zwlr_gamma_control_v1 *gamma_control, uint32_t ramp_size) {
|
|
||||||
struct output *output = data;
|
|
||||||
output->ramp_size = ramp_size;
|
|
||||||
output->table_fd = create_gamma_table(ramp_size, &output->table);
|
|
||||||
if (output->table_fd < 0) {
|
|
||||||
exit(EXIT_FAILURE);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void gamma_control_handle_failed(void *data,
|
|
||||||
struct zwlr_gamma_control_v1 *gamma_control) {
|
|
||||||
fprintf(stderr, "failed to set gamma table\n");
|
|
||||||
exit(EXIT_FAILURE);
|
|
||||||
}
|
|
||||||
|
|
||||||
static const struct zwlr_gamma_control_v1_listener gamma_control_listener = {
|
|
||||||
.gamma_size = gamma_control_handle_gamma_size,
|
|
||||||
.failed = gamma_control_handle_failed,
|
|
||||||
};
|
|
||||||
|
|
||||||
static void registry_handle_global(void *data, struct wl_registry *registry,
|
|
||||||
uint32_t name, const char *interface, uint32_t version) {
|
|
||||||
if (strcmp(interface, wl_output_interface.name) == 0) {
|
|
||||||
struct output *output = calloc(1, sizeof(*output));
|
|
||||||
output->wl_output = wl_registry_bind(registry, name,
|
|
||||||
&wl_output_interface, 1);
|
|
||||||
wl_list_insert(&outputs, &output->link);
|
|
||||||
} else if (strcmp(interface,
|
|
||||||
zwlr_gamma_control_manager_v1_interface.name) == 0) {
|
|
||||||
gamma_control_manager = wl_registry_bind(registry, name,
|
|
||||||
&zwlr_gamma_control_manager_v1_interface, 1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void registry_handle_global_remove(void *data,
|
|
||||||
struct wl_registry *registry, uint32_t name) {
|
|
||||||
// Who cares?
|
|
||||||
}
|
|
||||||
|
|
||||||
static const struct wl_registry_listener registry_listener = {
|
|
||||||
.global = registry_handle_global,
|
|
||||||
.global_remove = registry_handle_global_remove,
|
|
||||||
};
|
|
||||||
|
|
||||||
static void fill_gamma_table(uint16_t *table, uint32_t ramp_size,
|
|
||||||
double contrast, double brightness, double gamma) {
|
|
||||||
uint16_t *r = table;
|
|
||||||
uint16_t *g = table + ramp_size;
|
|
||||||
uint16_t *b = table + 2 * ramp_size;
|
|
||||||
for (uint32_t i = 0; i < ramp_size; ++i) {
|
|
||||||
double val = (double)i / (ramp_size - 1);
|
|
||||||
val = contrast * pow(val, 1.0 / gamma) + (brightness - 1);
|
|
||||||
if (val > 1.0) {
|
|
||||||
val = 1.0;
|
|
||||||
} else if (val < 0.0) {
|
|
||||||
val = 0.0;
|
|
||||||
}
|
|
||||||
r[i] = g[i] = b[i] = (uint16_t)(UINT16_MAX * val);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static const char usage[] = "usage: gamma-control [options]\n"
|
|
||||||
" -h show this help message\n"
|
|
||||||
" -c <value> set contrast (default: 1)\n"
|
|
||||||
" -b <value> set brightness (default: 1)\n"
|
|
||||||
" -g <value> set gamma (default: 1)\n";
|
|
||||||
|
|
||||||
int main(int argc, char *argv[]) {
|
|
||||||
wl_list_init(&outputs);
|
|
||||||
|
|
||||||
double contrast = 1, brightness = 1, gamma = 1;
|
|
||||||
int opt;
|
|
||||||
while ((opt = getopt(argc, argv, "hc:b:g:")) != -1) {
|
|
||||||
switch (opt) {
|
|
||||||
case 'c':
|
|
||||||
contrast = strtod(optarg, NULL);
|
|
||||||
break;
|
|
||||||
case 'b':
|
|
||||||
brightness = strtod(optarg, NULL);
|
|
||||||
break;
|
|
||||||
case 'g':
|
|
||||||
gamma = strtod(optarg, NULL);
|
|
||||||
break;
|
|
||||||
case 'h':
|
|
||||||
default:
|
|
||||||
fprintf(stderr, usage);
|
|
||||||
return opt == 'h' ? EXIT_SUCCESS : EXIT_FAILURE;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
struct wl_display *display = wl_display_connect(NULL);
|
|
||||||
if (display == NULL) {
|
|
||||||
fprintf(stderr, "failed to create display\n");
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct wl_registry *registry = wl_display_get_registry(display);
|
|
||||||
wl_registry_add_listener(registry, ®istry_listener, NULL);
|
|
||||||
wl_display_roundtrip(display);
|
|
||||||
|
|
||||||
if (gamma_control_manager == NULL) {
|
|
||||||
fprintf(stderr,
|
|
||||||
"compositor doesn't support wlr-gamma-control-unstable-v1\n");
|
|
||||||
return EXIT_FAILURE;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct output *output;
|
|
||||||
wl_list_for_each(output, &outputs, link) {
|
|
||||||
output->gamma_control = zwlr_gamma_control_manager_v1_get_gamma_control(
|
|
||||||
gamma_control_manager, output->wl_output);
|
|
||||||
zwlr_gamma_control_v1_add_listener(output->gamma_control,
|
|
||||||
&gamma_control_listener, output);
|
|
||||||
}
|
|
||||||
wl_display_roundtrip(display);
|
|
||||||
|
|
||||||
wl_list_for_each(output, &outputs, link) {
|
|
||||||
fill_gamma_table(output->table, output->ramp_size,
|
|
||||||
contrast, brightness, gamma);
|
|
||||||
zwlr_gamma_control_v1_set_gamma(output->gamma_control,
|
|
||||||
output->table_fd);
|
|
||||||
}
|
|
||||||
|
|
||||||
while (wl_display_dispatch(display) != -1) {
|
|
||||||
// This space is intentionnally left blank
|
|
||||||
}
|
|
||||||
|
|
||||||
return EXIT_SUCCESS;
|
|
||||||
}
|
|
@ -1,227 +0,0 @@
|
|||||||
#include <GLES2/gl2.h>
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include <wayland-client.h>
|
|
||||||
#include <wayland-egl.h>
|
|
||||||
#include "egl_common.h"
|
|
||||||
#include "idle-inhibit-unstable-v1-client-protocol.h"
|
|
||||||
#include "xdg-shell-client-protocol.h"
|
|
||||||
|
|
||||||
#include <linux/input-event-codes.h>
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Usage: idle-inhibit
|
|
||||||
* Creates a xdg-toplevel using the idle-inhibit protocol.
|
|
||||||
* It will be solid green, when it has an idle inhibitor, and solid yellow if
|
|
||||||
* it does not.
|
|
||||||
* Left click with a pointer will toggle this state. (Touch is not supported
|
|
||||||
* for now).
|
|
||||||
*/
|
|
||||||
|
|
||||||
static int width = 500, height = 300;
|
|
||||||
|
|
||||||
static struct wl_compositor *compositor = NULL;
|
|
||||||
static struct wl_seat *seat = NULL;
|
|
||||||
static struct xdg_wm_base *wm_base = NULL;
|
|
||||||
static struct zwp_idle_inhibit_manager_v1 *idle_inhibit_manager = NULL;
|
|
||||||
static struct zwp_idle_inhibitor_v1 *idle_inhibitor = NULL;
|
|
||||||
|
|
||||||
struct wl_egl_window *egl_window;
|
|
||||||
struct wlr_egl_surface *egl_surface;
|
|
||||||
|
|
||||||
static void draw(void) {
|
|
||||||
eglMakeCurrent(egl_display, egl_surface, egl_surface, egl_context);
|
|
||||||
|
|
||||||
float color[] = {1.0, 1.0, 0.0, 1.0};
|
|
||||||
if (idle_inhibitor) {
|
|
||||||
color[0] = 0.0;
|
|
||||||
}
|
|
||||||
|
|
||||||
glViewport(0, 0, width, height);
|
|
||||||
glClearColor(color[0], color[1], color[2], 1.0);
|
|
||||||
glClear(GL_COLOR_BUFFER_BIT);
|
|
||||||
|
|
||||||
eglSwapBuffers(egl_display, egl_surface);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void pointer_handle_button(void *data, struct wl_pointer *pointer, uint32_t serial,
|
|
||||||
uint32_t time, uint32_t button, uint32_t state_w) {
|
|
||||||
struct wl_surface *surface = data;
|
|
||||||
|
|
||||||
if (button == BTN_LEFT && state_w == WL_POINTER_BUTTON_STATE_PRESSED) {
|
|
||||||
if (idle_inhibitor) {
|
|
||||||
zwp_idle_inhibitor_v1_destroy(idle_inhibitor);
|
|
||||||
idle_inhibitor = NULL;
|
|
||||||
} else {
|
|
||||||
idle_inhibitor = zwp_idle_inhibit_manager_v1_create_inhibitor(
|
|
||||||
idle_inhibit_manager, surface);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
draw();
|
|
||||||
}
|
|
||||||
|
|
||||||
static void pointer_handle_enter(void *data, struct wl_pointer *wl_pointer,
|
|
||||||
uint32_t serial, struct wl_surface *surface,
|
|
||||||
wl_fixed_t surface_x, wl_fixed_t surface_y) {
|
|
||||||
// This space intentionally left blank
|
|
||||||
}
|
|
||||||
|
|
||||||
static void pointer_handle_leave(void *data, struct wl_pointer *wl_pointer,
|
|
||||||
uint32_t serial, struct wl_surface *surface) {
|
|
||||||
// This space intentionally left blank
|
|
||||||
}
|
|
||||||
|
|
||||||
static void pointer_handle_motion(void *data, struct wl_pointer *wl_pointer,
|
|
||||||
uint32_t time, wl_fixed_t surface_x, wl_fixed_t surface_y) {
|
|
||||||
// This space intentionally left blank
|
|
||||||
}
|
|
||||||
|
|
||||||
static void pointer_handle_axis(void *data, struct wl_pointer *wl_pointer,
|
|
||||||
uint32_t time, uint32_t axis, wl_fixed_t value) {
|
|
||||||
// This space intentionally left blank
|
|
||||||
}
|
|
||||||
|
|
||||||
static void pointer_handle_frame(void *data, struct wl_pointer *wl_pointer) {
|
|
||||||
// This space intentionally left blank
|
|
||||||
}
|
|
||||||
|
|
||||||
static void pointer_handle_axis_source(void *data,
|
|
||||||
struct wl_pointer *wl_pointer, uint32_t axis_source) {
|
|
||||||
// This space intentionally left blank
|
|
||||||
}
|
|
||||||
|
|
||||||
static void pointer_handle_axis_stop(void *data,
|
|
||||||
struct wl_pointer *wl_pointer, uint32_t time, uint32_t axis) {
|
|
||||||
// This space intentionally left blank
|
|
||||||
}
|
|
||||||
|
|
||||||
static void pointer_handle_axis_discrete(void *data,
|
|
||||||
struct wl_pointer *wl_pointer, uint32_t axis, int32_t discrete) {
|
|
||||||
// This space intentionally left blank
|
|
||||||
}
|
|
||||||
|
|
||||||
static const struct wl_pointer_listener pointer_listener = {
|
|
||||||
.enter = pointer_handle_enter,
|
|
||||||
.leave = pointer_handle_leave,
|
|
||||||
.motion = pointer_handle_motion,
|
|
||||||
.button = pointer_handle_button,
|
|
||||||
.axis = pointer_handle_axis,
|
|
||||||
.frame = pointer_handle_frame,
|
|
||||||
.axis_source = pointer_handle_axis_source,
|
|
||||||
.axis_stop = pointer_handle_axis_stop,
|
|
||||||
.axis_discrete = pointer_handle_axis_discrete,
|
|
||||||
};
|
|
||||||
|
|
||||||
static void xdg_surface_handle_configure(void *data,
|
|
||||||
struct xdg_surface *xdg_surface, uint32_t serial) {
|
|
||||||
xdg_surface_ack_configure(xdg_surface, serial);
|
|
||||||
wl_egl_window_resize(egl_window, width, height, 0, 0);
|
|
||||||
draw();
|
|
||||||
}
|
|
||||||
|
|
||||||
static const struct xdg_surface_listener xdg_surface_listener = {
|
|
||||||
.configure = xdg_surface_handle_configure,
|
|
||||||
};
|
|
||||||
|
|
||||||
static void xdg_toplevel_handle_configure(void *data,
|
|
||||||
struct xdg_toplevel *xdg_toplevel, int32_t w, int32_t h,
|
|
||||||
struct wl_array *states) {
|
|
||||||
width = w;
|
|
||||||
height = h;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void xdg_toplevel_handle_close(void *data,
|
|
||||||
struct xdg_toplevel *xdg_toplevel) {
|
|
||||||
exit(EXIT_SUCCESS);
|
|
||||||
}
|
|
||||||
|
|
||||||
static const struct xdg_toplevel_listener xdg_toplevel_listener = {
|
|
||||||
.configure = xdg_toplevel_handle_configure,
|
|
||||||
.close = xdg_toplevel_handle_close,
|
|
||||||
};
|
|
||||||
|
|
||||||
static void handle_global(void *data, struct wl_registry *registry,
|
|
||||||
uint32_t name, const char *interface, uint32_t version) {
|
|
||||||
if (strcmp(interface, "wl_compositor") == 0) {
|
|
||||||
compositor = wl_registry_bind(registry, name,
|
|
||||||
&wl_compositor_interface, 1);
|
|
||||||
} else if (strcmp(interface, xdg_wm_base_interface.name) == 0) {
|
|
||||||
wm_base = wl_registry_bind(registry, name, &xdg_wm_base_interface, 1);
|
|
||||||
} else if (strcmp(interface, zwp_idle_inhibit_manager_v1_interface.name) == 0) {
|
|
||||||
idle_inhibit_manager = wl_registry_bind(registry, name,
|
|
||||||
&zwp_idle_inhibit_manager_v1_interface, 1);
|
|
||||||
} else if (strcmp(interface, wl_seat_interface.name) == 0) {
|
|
||||||
seat = wl_registry_bind(registry, name, &wl_seat_interface, version);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void handle_global_remove(void *data, struct wl_registry *registry,
|
|
||||||
uint32_t name) {
|
|
||||||
// who cares
|
|
||||||
}
|
|
||||||
|
|
||||||
static const struct wl_registry_listener registry_listener = {
|
|
||||||
.global = handle_global,
|
|
||||||
.global_remove = handle_global_remove,
|
|
||||||
};
|
|
||||||
|
|
||||||
int main(int argc, char **argv) {
|
|
||||||
struct wl_display *display = wl_display_connect(NULL);
|
|
||||||
if (display == NULL) {
|
|
||||||
fprintf(stderr, "Failed to create display\n");
|
|
||||||
return EXIT_FAILURE;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct wl_registry *registry = wl_display_get_registry(display);
|
|
||||||
wl_registry_add_listener(registry, ®istry_listener, NULL);
|
|
||||||
wl_display_roundtrip(display);
|
|
||||||
|
|
||||||
if (compositor == NULL) {
|
|
||||||
fprintf(stderr, "wl-compositor not available\n");
|
|
||||||
return EXIT_FAILURE;
|
|
||||||
}
|
|
||||||
if (wm_base == NULL) {
|
|
||||||
fprintf(stderr, "xdg-shell not available\n");
|
|
||||||
return EXIT_FAILURE;
|
|
||||||
}
|
|
||||||
if (idle_inhibit_manager == NULL) {
|
|
||||||
fprintf(stderr, "idle-inhibit not available\n");
|
|
||||||
return EXIT_FAILURE;
|
|
||||||
}
|
|
||||||
|
|
||||||
egl_init(display);
|
|
||||||
|
|
||||||
struct wl_surface *surface = wl_compositor_create_surface(compositor);
|
|
||||||
struct xdg_surface *xdg_surface =
|
|
||||||
xdg_wm_base_get_xdg_surface(wm_base, surface);
|
|
||||||
struct xdg_toplevel *xdg_toplevel = xdg_surface_get_toplevel(xdg_surface);
|
|
||||||
|
|
||||||
idle_inhibitor =
|
|
||||||
zwp_idle_inhibit_manager_v1_create_inhibitor(idle_inhibit_manager,
|
|
||||||
surface);
|
|
||||||
|
|
||||||
struct wl_pointer *pointer = wl_seat_get_pointer(seat);
|
|
||||||
wl_pointer_add_listener(pointer, &pointer_listener, surface);
|
|
||||||
|
|
||||||
|
|
||||||
xdg_surface_add_listener(xdg_surface, &xdg_surface_listener, NULL);
|
|
||||||
xdg_toplevel_add_listener(xdg_toplevel, &xdg_toplevel_listener, NULL);
|
|
||||||
|
|
||||||
wl_surface_commit(surface);
|
|
||||||
|
|
||||||
egl_window = wl_egl_window_create(surface, width, height);
|
|
||||||
egl_surface = eglCreatePlatformWindowSurfaceEXT(
|
|
||||||
egl_display, egl_config, egl_window, NULL);
|
|
||||||
|
|
||||||
wl_display_roundtrip(display);
|
|
||||||
|
|
||||||
draw();
|
|
||||||
|
|
||||||
while (wl_display_dispatch(display) != -1) {
|
|
||||||
// This space intentionally left blank
|
|
||||||
}
|
|
||||||
|
|
||||||
return EXIT_SUCCESS;
|
|
||||||
}
|
|
@ -1,187 +0,0 @@
|
|||||||
#include <GLES2/gl2.h>
|
|
||||||
#include <assert.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include <wayland-client.h>
|
|
||||||
#include <wayland-egl.h>
|
|
||||||
#include "egl_common.h"
|
|
||||||
#include "wlr-input-inhibitor-unstable-v1-client-protocol.h"
|
|
||||||
#include "xdg-shell-client-protocol.h"
|
|
||||||
|
|
||||||
static int width = 500, height = 300;
|
|
||||||
static int keys = 0;
|
|
||||||
|
|
||||||
static struct wl_compositor *compositor = NULL;
|
|
||||||
static struct wl_seat *seat = NULL;
|
|
||||||
static struct xdg_wm_base *wm_base = NULL;
|
|
||||||
static struct zwlr_input_inhibit_manager_v1 *input_inhibit_manager = NULL;
|
|
||||||
static struct zwlr_input_inhibitor_v1 *input_inhibitor = NULL;
|
|
||||||
|
|
||||||
struct wl_egl_window *egl_window;
|
|
||||||
struct wlr_egl_surface *egl_surface;
|
|
||||||
|
|
||||||
static void render_frame(void) {
|
|
||||||
eglMakeCurrent(egl_display, egl_surface, egl_surface, egl_context);
|
|
||||||
|
|
||||||
glViewport(0, 0, width, height);
|
|
||||||
if (keys) {
|
|
||||||
glClearColor(1.0, 1.0, 1.0, 1.0);
|
|
||||||
} else {
|
|
||||||
glClearColor(0.8, 0.4, 1.0, 1.0);
|
|
||||||
}
|
|
||||||
glClear(GL_COLOR_BUFFER_BIT);
|
|
||||||
|
|
||||||
eglSwapBuffers(egl_display, egl_surface);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void xdg_surface_handle_configure(void *data,
|
|
||||||
struct xdg_surface *xdg_surface, uint32_t serial) {
|
|
||||||
xdg_surface_ack_configure(xdg_surface, serial);
|
|
||||||
wl_egl_window_resize(egl_window, width, height, 0, 0);
|
|
||||||
render_frame();
|
|
||||||
}
|
|
||||||
|
|
||||||
static const struct xdg_surface_listener xdg_surface_listener = {
|
|
||||||
.configure = xdg_surface_handle_configure,
|
|
||||||
};
|
|
||||||
|
|
||||||
static void xdg_toplevel_handle_configure(void *data,
|
|
||||||
struct xdg_toplevel *xdg_toplevel, int32_t w, int32_t h,
|
|
||||||
struct wl_array *states) {
|
|
||||||
width = w;
|
|
||||||
height = h;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void xdg_toplevel_handle_close(void *data,
|
|
||||||
struct xdg_toplevel *xdg_toplevel) {
|
|
||||||
exit(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
static const struct xdg_toplevel_listener xdg_toplevel_listener = {
|
|
||||||
.configure = xdg_toplevel_handle_configure,
|
|
||||||
.close = xdg_toplevel_handle_close,
|
|
||||||
};
|
|
||||||
|
|
||||||
static void wl_keyboard_keymap(void *data, struct wl_keyboard *wl_keyboard,
|
|
||||||
uint32_t format, int32_t fd, uint32_t size) {
|
|
||||||
}
|
|
||||||
static void wl_keyboard_enter(void *data, struct wl_keyboard *wl_keyboard,
|
|
||||||
uint32_t serial, struct wl_surface *surface, struct wl_array *keys) {
|
|
||||||
}
|
|
||||||
static void wl_keyboard_leave(void *data, struct wl_keyboard *wl_keyboard,
|
|
||||||
uint32_t serial, struct wl_surface *surface) {
|
|
||||||
}
|
|
||||||
static void wl_keyboard_modifiers(void *data, struct wl_keyboard *wl_keyboard,
|
|
||||||
uint32_t serial, uint32_t mods_depressed, uint32_t mods_latched,
|
|
||||||
uint32_t mods_locked, uint32_t group) {
|
|
||||||
}
|
|
||||||
static void wl_keyboard_repeat_info(void *data, struct wl_keyboard *wl_keyboard,
|
|
||||||
int32_t rate, int32_t delay) {
|
|
||||||
}
|
|
||||||
|
|
||||||
static void wl_keyboard_key(void *data, struct wl_keyboard *wl_keyboard,
|
|
||||||
uint32_t serial, uint32_t time, uint32_t key, uint32_t state) {
|
|
||||||
if (state == WL_KEYBOARD_KEY_STATE_PRESSED) {
|
|
||||||
++keys;
|
|
||||||
} else {
|
|
||||||
--keys;
|
|
||||||
}
|
|
||||||
render_frame();
|
|
||||||
}
|
|
||||||
|
|
||||||
static struct wl_keyboard_listener keyboard_listener = {
|
|
||||||
.keymap = wl_keyboard_keymap,
|
|
||||||
.enter = wl_keyboard_enter,
|
|
||||||
.leave = wl_keyboard_leave,
|
|
||||||
.key = wl_keyboard_key,
|
|
||||||
.modifiers = wl_keyboard_modifiers,
|
|
||||||
.repeat_info = wl_keyboard_repeat_info,
|
|
||||||
};
|
|
||||||
|
|
||||||
static void seat_handle_capabilities(void *data, struct wl_seat *wl_seat,
|
|
||||||
enum wl_seat_capability caps) {
|
|
||||||
if ((caps & WL_SEAT_CAPABILITY_KEYBOARD)) {
|
|
||||||
struct wl_keyboard *keyboard = wl_seat_get_keyboard(wl_seat);
|
|
||||||
wl_keyboard_add_listener(keyboard, &keyboard_listener, NULL);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void seat_handle_name(void *data, struct wl_seat *wl_seat,
|
|
||||||
const char *name) {
|
|
||||||
// Who cares
|
|
||||||
}
|
|
||||||
|
|
||||||
const struct wl_seat_listener seat_listener = {
|
|
||||||
.capabilities = seat_handle_capabilities,
|
|
||||||
.name = seat_handle_name,
|
|
||||||
};
|
|
||||||
|
|
||||||
static void handle_global(void *data, struct wl_registry *registry,
|
|
||||||
uint32_t name, const char *interface, uint32_t version) {
|
|
||||||
if (strcmp(interface, wl_compositor_interface.name) == 0) {
|
|
||||||
compositor = wl_registry_bind(registry, name,
|
|
||||||
&wl_compositor_interface, 1);
|
|
||||||
} else if (strcmp(interface, xdg_wm_base_interface.name) == 0) {
|
|
||||||
wm_base = wl_registry_bind(registry, name, &xdg_wm_base_interface, 1);
|
|
||||||
} else if (strcmp(interface, zwlr_input_inhibit_manager_v1_interface.name) == 0) {
|
|
||||||
input_inhibit_manager = wl_registry_bind(registry, name,
|
|
||||||
&zwlr_input_inhibit_manager_v1_interface, 1);
|
|
||||||
} else if (strcmp(interface, wl_seat_interface.name) == 0) {
|
|
||||||
seat = wl_registry_bind(registry, name, &wl_seat_interface, version);
|
|
||||||
wl_seat_add_listener(seat, &seat_listener, seat);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void handle_global_remove(void *data, struct wl_registry *registry,
|
|
||||||
uint32_t name) {
|
|
||||||
// who cares
|
|
||||||
}
|
|
||||||
|
|
||||||
static const struct wl_registry_listener registry_listener = {
|
|
||||||
.global = handle_global,
|
|
||||||
.global_remove = handle_global_remove,
|
|
||||||
};
|
|
||||||
|
|
||||||
int main(int argc, char **argv) {
|
|
||||||
struct wl_display *display = wl_display_connect(NULL);
|
|
||||||
assert(display);
|
|
||||||
struct wl_registry *registry = wl_display_get_registry(display);
|
|
||||||
assert(registry);
|
|
||||||
wl_registry_add_listener(registry, ®istry_listener, NULL);
|
|
||||||
wl_display_roundtrip(display);
|
|
||||||
assert(compositor && seat && wm_base && input_inhibit_manager);
|
|
||||||
|
|
||||||
input_inhibitor = zwlr_input_inhibit_manager_v1_get_inhibitor(
|
|
||||||
input_inhibit_manager);
|
|
||||||
assert(input_inhibitor);
|
|
||||||
|
|
||||||
egl_init(display);
|
|
||||||
|
|
||||||
struct wl_surface *surface = wl_compositor_create_surface(compositor);
|
|
||||||
assert(surface);
|
|
||||||
struct xdg_surface *xdg_surface =
|
|
||||||
xdg_wm_base_get_xdg_surface(wm_base, surface);
|
|
||||||
assert(xdg_surface);
|
|
||||||
struct xdg_toplevel *xdg_toplevel = xdg_surface_get_toplevel(xdg_surface);
|
|
||||||
assert(xdg_toplevel);
|
|
||||||
|
|
||||||
xdg_surface_add_listener(xdg_surface, &xdg_surface_listener, NULL);
|
|
||||||
xdg_toplevel_add_listener(xdg_toplevel, &xdg_toplevel_listener, NULL);
|
|
||||||
|
|
||||||
wl_surface_commit(surface);
|
|
||||||
|
|
||||||
egl_window = wl_egl_window_create(surface, width, height);
|
|
||||||
egl_surface = eglCreatePlatformWindowSurfaceEXT(
|
|
||||||
egl_display, egl_config, egl_window, NULL);
|
|
||||||
|
|
||||||
wl_display_roundtrip(display);
|
|
||||||
|
|
||||||
render_frame();
|
|
||||||
|
|
||||||
while (wl_display_dispatch(display) != -1) {
|
|
||||||
// This space intentionally left blank
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
@ -1,216 +0,0 @@
|
|||||||
#include <stdbool.h>
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include <sys/mman.h>
|
|
||||||
#include <unistd.h>
|
|
||||||
#include <wayland-client.h>
|
|
||||||
#include <xkbcommon/xkbcommon.h>
|
|
||||||
#include "input-method-unstable-v2-client-protocol.h"
|
|
||||||
|
|
||||||
static struct wl_display *display = NULL;
|
|
||||||
static struct wl_seat *seat = NULL;
|
|
||||||
static struct zwp_input_method_manager_v2 *input_method_manager = NULL;
|
|
||||||
static struct zwp_input_method_v2 *input_method = NULL;
|
|
||||||
static struct zwp_input_method_keyboard_grab_v2 *kb_grab = NULL;
|
|
||||||
|
|
||||||
static bool active = false;
|
|
||||||
static bool pending_active = false;
|
|
||||||
|
|
||||||
static struct xkb_context *xkb_context = NULL;
|
|
||||||
static struct xkb_keymap *keymap = NULL;
|
|
||||||
static struct xkb_state *xkb_state = NULL;
|
|
||||||
|
|
||||||
static void handle_key(void *data,
|
|
||||||
struct zwp_input_method_keyboard_grab_v2 *im_keyboard_grab,
|
|
||||||
uint32_t serial, uint32_t time, uint32_t key, uint32_t state) {
|
|
||||||
printf("handle_key %u %u %u %u\n", serial, time, key, state);
|
|
||||||
xkb_keysym_t keysym = xkb_state_key_get_one_sym(xkb_state, key + 8);
|
|
||||||
char keysym_name[64];
|
|
||||||
xkb_keysym_get_name(keysym, keysym_name, sizeof(keysym_name));
|
|
||||||
printf("xkb translated to %s\n", keysym_name);
|
|
||||||
if (state == WL_KEYBOARD_KEY_STATE_PRESSED) {
|
|
||||||
if (keysym == XKB_KEY_KP_Enter || keysym == XKB_KEY_Return) {
|
|
||||||
printf("Stopping grab\n");
|
|
||||||
zwp_input_method_keyboard_grab_v2_release(kb_grab);
|
|
||||||
kb_grab = NULL;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
static void handle_modifiers(void *data,
|
|
||||||
struct zwp_input_method_keyboard_grab_v2 *im_keyboard_grab,
|
|
||||||
uint32_t serial, uint32_t mods_depressed, uint32_t mods_latched,
|
|
||||||
uint32_t mods_locked, uint32_t group) {
|
|
||||||
printf("handle_modifiers %u %u %u %u %u\n", serial, mods_depressed,
|
|
||||||
mods_latched, mods_locked, group);
|
|
||||||
xkb_state_update_mask(xkb_state, mods_depressed, mods_latched,
|
|
||||||
mods_locked, 0, 0, group);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void handle_keymap(void *data,
|
|
||||||
struct zwp_input_method_keyboard_grab_v2 *im_keyboard_grab,
|
|
||||||
uint32_t format, int32_t fd, uint32_t size) {
|
|
||||||
printf("handle_keymap\n");
|
|
||||||
char *keymap_string = mmap(NULL, size, PROT_READ, MAP_SHARED, fd, 0);
|
|
||||||
xkb_keymap_unref(keymap);
|
|
||||||
keymap = xkb_keymap_new_from_string(xkb_context, keymap_string,
|
|
||||||
XKB_KEYMAP_FORMAT_TEXT_V1, XKB_KEYMAP_COMPILE_NO_FLAGS);
|
|
||||||
munmap(keymap_string, size);
|
|
||||||
close(fd);
|
|
||||||
xkb_state_unref(xkb_state);
|
|
||||||
xkb_state = xkb_state_new(keymap);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void handle_repeat_info(void *data,
|
|
||||||
struct zwp_input_method_keyboard_grab_v2 *im_keyboard_grab,
|
|
||||||
int32_t rate, int32_t delay) {
|
|
||||||
printf("handle_repeat_info %d %d", rate, delay);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
static const struct zwp_input_method_keyboard_grab_v2_listener grab_listener = {
|
|
||||||
.key = handle_key,
|
|
||||||
.modifiers = handle_modifiers,
|
|
||||||
.keymap = handle_keymap,
|
|
||||||
.repeat_info = handle_repeat_info,
|
|
||||||
};
|
|
||||||
|
|
||||||
static void handle_activate(void *data,
|
|
||||||
struct zwp_input_method_v2 *zwp_input_method_v2) {
|
|
||||||
pending_active = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void handle_deactivate(void *data,
|
|
||||||
struct zwp_input_method_v2 *zwp_input_method_v2) {
|
|
||||||
pending_active = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void handle_unavailable(void *data,
|
|
||||||
struct zwp_input_method_v2 *zwp_input_method_v2) {
|
|
||||||
printf("IM unavailable\n");
|
|
||||||
zwp_input_method_v2_destroy(zwp_input_method_v2);
|
|
||||||
input_method = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void im_activate(void *data,
|
|
||||||
struct zwp_input_method_v2 *id) {
|
|
||||||
kb_grab = zwp_input_method_v2_grab_keyboard(input_method);
|
|
||||||
if (kb_grab == NULL) {
|
|
||||||
fprintf(stderr, "Failed to grab\n");
|
|
||||||
exit(EXIT_FAILURE);
|
|
||||||
}
|
|
||||||
zwp_input_method_keyboard_grab_v2_add_listener(kb_grab, &grab_listener,
|
|
||||||
NULL);
|
|
||||||
printf("Started grab, press enter to stop grab\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
static void im_deactivate(void *data,
|
|
||||||
struct zwp_input_method_v2 *context) {
|
|
||||||
if (kb_grab != NULL) {
|
|
||||||
zwp_input_method_keyboard_grab_v2_release(kb_grab);
|
|
||||||
kb_grab = NULL;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void handle_done(void *data,
|
|
||||||
struct zwp_input_method_v2 *zwp_input_method_v2) {
|
|
||||||
bool prev_active = active;
|
|
||||||
if (active != pending_active) {
|
|
||||||
printf("Now %s\n", pending_active ? "active" : "inactive");
|
|
||||||
}
|
|
||||||
active = pending_active;
|
|
||||||
if (active && !prev_active) {
|
|
||||||
im_activate(data, zwp_input_method_v2);
|
|
||||||
} else if (!active && prev_active) {
|
|
||||||
im_deactivate(data, zwp_input_method_v2);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void handle_surrounding_text(void *data,
|
|
||||||
struct zwp_input_method_v2 *zwp_input_method_v2,
|
|
||||||
const char *text, uint32_t cursor, uint32_t anchor) {
|
|
||||||
// not for this test
|
|
||||||
}
|
|
||||||
|
|
||||||
static void handle_text_change_cause(void *data,
|
|
||||||
struct zwp_input_method_v2 *zwp_input_method_v2,
|
|
||||||
uint32_t cause) {
|
|
||||||
// not for this test
|
|
||||||
}
|
|
||||||
|
|
||||||
static void handle_content_type(void *data,
|
|
||||||
struct zwp_input_method_v2 *zwp_input_method_v2,
|
|
||||||
uint32_t hint, uint32_t purpose) {
|
|
||||||
// not for this test
|
|
||||||
}
|
|
||||||
|
|
||||||
static const struct zwp_input_method_v2_listener im_listener = {
|
|
||||||
.activate = handle_activate,
|
|
||||||
.deactivate = handle_deactivate,
|
|
||||||
.surrounding_text = handle_surrounding_text,
|
|
||||||
.text_change_cause = handle_text_change_cause,
|
|
||||||
.content_type = handle_content_type,
|
|
||||||
.done = handle_done,
|
|
||||||
.unavailable = handle_unavailable,
|
|
||||||
};
|
|
||||||
|
|
||||||
static void handle_global(void *data, struct wl_registry *registry,
|
|
||||||
uint32_t name, const char *interface, uint32_t version) {
|
|
||||||
if (strcmp(interface, zwp_input_method_manager_v2_interface.name) == 0) {
|
|
||||||
input_method_manager = wl_registry_bind(registry, name,
|
|
||||||
&zwp_input_method_manager_v2_interface, 1);
|
|
||||||
} else if (strcmp(interface, wl_seat_interface.name) == 0) {
|
|
||||||
seat = wl_registry_bind(registry, name, &wl_seat_interface, version);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void handle_global_remove(void *data, struct wl_registry *registry,
|
|
||||||
uint32_t name) {
|
|
||||||
// who cares
|
|
||||||
}
|
|
||||||
|
|
||||||
static const struct wl_registry_listener registry_listener = {
|
|
||||||
.global = handle_global,
|
|
||||||
.global_remove = handle_global_remove,
|
|
||||||
};
|
|
||||||
|
|
||||||
int main(int argc, char **argv) {
|
|
||||||
xkb_context = xkb_context_new(XKB_CONTEXT_NO_FLAGS);
|
|
||||||
if (xkb_context == NULL) {
|
|
||||||
fprintf(stderr, "Failed to create xkb context\n");
|
|
||||||
return EXIT_FAILURE;
|
|
||||||
}
|
|
||||||
|
|
||||||
display = wl_display_connect(NULL);
|
|
||||||
if (display == NULL) {
|
|
||||||
fprintf(stderr, "Failed to create display\n");
|
|
||||||
return EXIT_FAILURE;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct wl_registry *registry = wl_display_get_registry(display);
|
|
||||||
wl_registry_add_listener(registry, ®istry_listener, NULL);
|
|
||||||
wl_display_roundtrip(display);
|
|
||||||
|
|
||||||
if (input_method_manager == NULL) {
|
|
||||||
fprintf(stderr, "input-method not available\n");
|
|
||||||
return EXIT_FAILURE;
|
|
||||||
}
|
|
||||||
if (seat == NULL) {
|
|
||||||
fprintf(stderr, "seat not available\n");
|
|
||||||
return EXIT_FAILURE;
|
|
||||||
}
|
|
||||||
|
|
||||||
input_method = zwp_input_method_manager_v2_get_input_method(
|
|
||||||
input_method_manager, seat);
|
|
||||||
zwp_input_method_v2_add_listener(input_method, &im_listener, NULL);
|
|
||||||
|
|
||||||
wl_display_roundtrip(display);
|
|
||||||
|
|
||||||
while (wl_display_dispatch(display) != -1) {
|
|
||||||
// This space is intentionally left blank
|
|
||||||
};
|
|
||||||
|
|
||||||
return EXIT_SUCCESS;
|
|
||||||
}
|
|
@ -1,401 +0,0 @@
|
|||||||
#define _POSIX_C_SOURCE 200809L
|
|
||||||
#include <assert.h>
|
|
||||||
#include <errno.h>
|
|
||||||
#include <stdbool.h>
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include <sys/epoll.h>
|
|
||||||
#include <sys/timerfd.h>
|
|
||||||
#include <unistd.h>
|
|
||||||
#include <wayland-client.h>
|
|
||||||
#include <wayland-egl.h>
|
|
||||||
#include "input-method-unstable-v2-client-protocol.h"
|
|
||||||
#include "text-input-unstable-v3-client-protocol.h"
|
|
||||||
#include "xdg-shell-client-protocol.h"
|
|
||||||
|
|
||||||
const char usage[] = "Usage: input-method [seconds]\n\
|
|
||||||
\n\
|
|
||||||
Creates an input method using the input-method protocol.\n\
|
|
||||||
\n\
|
|
||||||
Whenever a text input is activated, this program sends a few sequences of\n\
|
|
||||||
commands and checks the validity of the responses, relying on returned\n\
|
|
||||||
surrounding text.\n\
|
|
||||||
\n\
|
|
||||||
The \"seconds\" argument is optional and defines the maximum delay between\n\
|
|
||||||
stages.";
|
|
||||||
|
|
||||||
struct input_method_state {
|
|
||||||
enum zwp_text_input_v3_change_cause change_cause;
|
|
||||||
struct {
|
|
||||||
enum zwp_text_input_v3_content_hint hint;
|
|
||||||
enum zwp_text_input_v3_content_purpose purpose;
|
|
||||||
} content_type;
|
|
||||||
struct {
|
|
||||||
char *text;
|
|
||||||
uint32_t cursor;
|
|
||||||
uint32_t anchor;
|
|
||||||
} surrounding;
|
|
||||||
};
|
|
||||||
|
|
||||||
static int sleeptime = 0;
|
|
||||||
|
|
||||||
static struct wl_display *display = NULL;
|
|
||||||
static struct wl_compositor *compositor = NULL;
|
|
||||||
static struct wl_seat *seat = NULL;
|
|
||||||
static struct zwp_input_method_manager_v2 *input_method_manager = NULL;
|
|
||||||
static struct zwp_input_method_v2 *input_method = NULL;
|
|
||||||
|
|
||||||
struct input_method_state pending;
|
|
||||||
struct input_method_state current;
|
|
||||||
|
|
||||||
static uint32_t serial = 0;
|
|
||||||
bool active = false;
|
|
||||||
bool pending_active = false;
|
|
||||||
bool unavailable = false;
|
|
||||||
bool running = false;
|
|
||||||
|
|
||||||
uint32_t update_stage = 0;
|
|
||||||
|
|
||||||
int timer_fd = 0;
|
|
||||||
|
|
||||||
static void print_state_diff(struct input_method_state previous,
|
|
||||||
struct input_method_state future) {
|
|
||||||
if (previous.content_type.hint != future.content_type.hint) {
|
|
||||||
char *strs[] = { "COMPLETION", "SPELLCHECK", "AUTO_CAPITALIZATION",
|
|
||||||
"LOWERCASE", "UPPERCASE", "TITLECASE", "HIDDEN_TEXT",
|
|
||||||
"SENSITIVE_DATA", "LATIN", "MULTILINE"};
|
|
||||||
printf("content_type.hint:");
|
|
||||||
uint32_t hint = future.content_type.hint;
|
|
||||||
if (!hint) {
|
|
||||||
printf(" NONE");
|
|
||||||
}
|
|
||||||
for (unsigned i = 0; i < sizeof(strs) / sizeof(*strs); i++) {
|
|
||||||
if (hint & 1 << i) {
|
|
||||||
printf(" %s", strs[i]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
printf("\n");
|
|
||||||
}
|
|
||||||
if (previous.content_type.purpose != future.content_type.purpose) {
|
|
||||||
char *strs[] = { "NORMAL", "ALPHA", "DIGITS", "NUMBER", "PHONE", "URL",
|
|
||||||
"EMAIL", "NAME", "PASSWORD", "PIN", "DATE", "TIME", "DATETIME",
|
|
||||||
"TERMINAL" };
|
|
||||||
printf("content_type.purpose: %s\n", strs[future.content_type.purpose]);
|
|
||||||
}
|
|
||||||
if (!!previous.surrounding.text != !!future.surrounding.text
|
|
||||||
|| (previous.surrounding.text && future.surrounding.text
|
|
||||||
&& strcmp(previous.surrounding.text, future.surrounding.text) != 0)
|
|
||||||
|| previous.surrounding.anchor != future.surrounding.anchor
|
|
||||||
|| previous.surrounding.cursor != future.surrounding.cursor) {
|
|
||||||
char *text = future.surrounding.text;
|
|
||||||
if (!text) {
|
|
||||||
printf("Removed surrounding text\n");
|
|
||||||
} else {
|
|
||||||
printf("Surrounding text: %s\n", text);
|
|
||||||
uint32_t anchor = future.surrounding.anchor;
|
|
||||||
uint32_t cursor = future.surrounding.cursor;
|
|
||||||
if (cursor == anchor) {
|
|
||||||
char *temp = strndup(text, cursor);
|
|
||||||
printf("Cursor after %d: %s\n", cursor, temp);
|
|
||||||
free(temp);
|
|
||||||
} else {
|
|
||||||
if (cursor > anchor) {
|
|
||||||
uint32_t tmp = anchor;
|
|
||||||
anchor = cursor;
|
|
||||||
cursor = tmp;
|
|
||||||
}
|
|
||||||
char *temp = strndup(&text[cursor], anchor - cursor);
|
|
||||||
printf("Selection: %s\n", temp);
|
|
||||||
free(temp);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (previous.change_cause != future.change_cause) {
|
|
||||||
char *strs[] = { "INPUT_METHOD", "OTHER" };
|
|
||||||
printf("Change cause: %s\n", strs[future.change_cause]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void handle_content_type(void *data,
|
|
||||||
struct zwp_input_method_v2 *zwp_input_method_v2,
|
|
||||||
uint32_t hint, uint32_t purpose) {
|
|
||||||
pending.content_type.hint = hint;
|
|
||||||
pending.content_type.purpose = purpose;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void handle_surrounding_text(void *data,
|
|
||||||
struct zwp_input_method_v2 *zwp_input_method_v2,
|
|
||||||
const char *text, uint32_t cursor, uint32_t anchor) {
|
|
||||||
free(pending.surrounding.text);
|
|
||||||
pending.surrounding.text = strdup(text);
|
|
||||||
pending.surrounding.cursor = cursor;
|
|
||||||
pending.surrounding.anchor = anchor;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void handle_text_change_cause(void *data,
|
|
||||||
struct zwp_input_method_v2 *zwp_input_method_v2,
|
|
||||||
uint32_t cause) {
|
|
||||||
pending.change_cause = cause;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void handle_activate(void *data,
|
|
||||||
struct zwp_input_method_v2 *zwp_input_method_v2) {
|
|
||||||
pending_active = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void handle_deactivate(void *data,
|
|
||||||
struct zwp_input_method_v2 *zwp_input_method_v2) {
|
|
||||||
pending_active = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void handle_unavailable(void *data,
|
|
||||||
struct zwp_input_method_v2 *zwp_input_method_v2) {
|
|
||||||
printf("IM disappeared\n");
|
|
||||||
zwp_input_method_v2_destroy(zwp_input_method_v2);
|
|
||||||
input_method = NULL;
|
|
||||||
running = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void im_activate(void *data,
|
|
||||||
struct zwp_input_method_v2 *id) {
|
|
||||||
update_stage = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void timer_arm(unsigned seconds) {
|
|
||||||
printf("Timer armed\n");
|
|
||||||
struct itimerspec spec = {
|
|
||||||
.it_interval = {0},
|
|
||||||
.it_value = {
|
|
||||||
.tv_sec = seconds,
|
|
||||||
.tv_nsec = 0
|
|
||||||
}
|
|
||||||
};
|
|
||||||
if (timerfd_settime(timer_fd, 0, &spec, NULL)) {
|
|
||||||
fprintf(stderr, "Failed to arm timer: %s\n", strerror(errno));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void do_updates(void) {
|
|
||||||
printf("Update %d\n", update_stage);
|
|
||||||
switch (update_stage) {
|
|
||||||
case 0:
|
|
||||||
// TODO: remember initial surrounding text
|
|
||||||
zwp_input_method_v2_set_preedit_string(input_method, "Preedit", 2, 4);
|
|
||||||
zwp_input_method_v2_commit(input_method, serial);
|
|
||||||
// don't expect an answer, preedit doesn't change anything visible
|
|
||||||
timer_arm(sleeptime);
|
|
||||||
update_stage++;
|
|
||||||
return;
|
|
||||||
case 1:
|
|
||||||
zwp_input_method_v2_set_preedit_string(input_method, "Præedit2", strlen("Pr"), strlen("Præed"));
|
|
||||||
zwp_input_method_v2_commit_string(input_method, "_Commit_");
|
|
||||||
zwp_input_method_v2_commit(input_method, serial);
|
|
||||||
update_stage++;
|
|
||||||
break;
|
|
||||||
case 2:
|
|
||||||
if (current.surrounding.text && strcmp(current.surrounding.text, "_Commit_") != 0) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
zwp_input_method_v2_commit_string(input_method, "_CommitNoPreed_");
|
|
||||||
zwp_input_method_v2_commit(input_method, serial);
|
|
||||||
timer_arm(sleeptime);
|
|
||||||
update_stage++;
|
|
||||||
break;
|
|
||||||
case 3:
|
|
||||||
if (current.surrounding.text && strcmp(current.surrounding.text, "_Commit__CommitNoPreed_") != 0) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
zwp_input_method_v2_commit_string(input_method, "_WaitNo_");
|
|
||||||
zwp_input_method_v2_delete_surrounding_text(input_method, strlen("_CommitNoPreed_"), 0);
|
|
||||||
zwp_input_method_v2_commit(input_method, serial);
|
|
||||||
update_stage++;
|
|
||||||
break;
|
|
||||||
case 4:
|
|
||||||
if (current.surrounding.text && strcmp(current.surrounding.text, "_Commit__WaitNo_") != 0) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
zwp_input_method_v2_set_preedit_string(input_method, "PreedWithDel", strlen("Preed"), strlen("Preed"));
|
|
||||||
zwp_input_method_v2_delete_surrounding_text(input_method, strlen("_WaitNo_"), 0);
|
|
||||||
zwp_input_method_v2_commit(input_method, serial);
|
|
||||||
update_stage++;
|
|
||||||
break;
|
|
||||||
case 5:
|
|
||||||
if (current.surrounding.text && strcmp(current.surrounding.text, "_Commit_") != 0) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
zwp_input_method_v2_delete_surrounding_text(input_method, strlen("mit_"), 0);
|
|
||||||
zwp_input_method_v2_commit(input_method, serial);
|
|
||||||
update_stage++;
|
|
||||||
break;
|
|
||||||
case 6:
|
|
||||||
if (current.surrounding.text && strcmp(current.surrounding.text, "_Com") != 0) {
|
|
||||||
printf("Failed\n");
|
|
||||||
}
|
|
||||||
update_stage++;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
printf("Submitted everything\n");
|
|
||||||
return;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
static void handle_timer(void) {
|
|
||||||
printf("Timer dispatched at %d\n", update_stage);
|
|
||||||
do_updates();
|
|
||||||
}
|
|
||||||
|
|
||||||
static void im_deactivate(void *data,
|
|
||||||
struct zwp_input_method_v2 *context) {
|
|
||||||
// No special action needed
|
|
||||||
}
|
|
||||||
|
|
||||||
static void handle_done(void *data,
|
|
||||||
struct zwp_input_method_v2 *zwp_input_method_v2) {
|
|
||||||
bool prev_active = active;
|
|
||||||
serial++;
|
|
||||||
printf("Handle serial %d\n", serial);
|
|
||||||
if (active != pending_active) {
|
|
||||||
printf("Now %s\n", pending_active ? "active" : "inactive");
|
|
||||||
}
|
|
||||||
if (pending_active) {
|
|
||||||
print_state_diff(current, pending);
|
|
||||||
}
|
|
||||||
active = pending_active;
|
|
||||||
free(current.surrounding.text);
|
|
||||||
struct input_method_state default_state = {0};
|
|
||||||
current = pending;
|
|
||||||
pending = default_state;
|
|
||||||
if (active && !prev_active) {
|
|
||||||
im_activate(data, zwp_input_method_v2);
|
|
||||||
} else if (!active && prev_active) {
|
|
||||||
im_deactivate(data, zwp_input_method_v2);
|
|
||||||
}
|
|
||||||
|
|
||||||
do_updates();
|
|
||||||
}
|
|
||||||
|
|
||||||
static const struct zwp_input_method_v2_listener im_listener = {
|
|
||||||
.activate = handle_activate,
|
|
||||||
.deactivate = handle_deactivate,
|
|
||||||
.surrounding_text = handle_surrounding_text,
|
|
||||||
.text_change_cause = handle_text_change_cause,
|
|
||||||
.content_type = handle_content_type,
|
|
||||||
.done = handle_done,
|
|
||||||
.unavailable = handle_unavailable,
|
|
||||||
};
|
|
||||||
|
|
||||||
static void handle_global(void *data, struct wl_registry *registry,
|
|
||||||
uint32_t name, const char *interface, uint32_t version) {
|
|
||||||
if (strcmp(interface, "wl_compositor") == 0) {
|
|
||||||
compositor = wl_registry_bind(registry, name,
|
|
||||||
&wl_compositor_interface, 1);
|
|
||||||
} else if (strcmp(interface, zwp_input_method_manager_v2_interface.name) == 0) {
|
|
||||||
input_method_manager = wl_registry_bind(registry, name,
|
|
||||||
&zwp_input_method_manager_v2_interface, 1);
|
|
||||||
} else if (strcmp(interface, wl_seat_interface.name) == 0) {
|
|
||||||
seat = wl_registry_bind(registry, name, &wl_seat_interface, version);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void handle_global_remove(void *data, struct wl_registry *registry,
|
|
||||||
uint32_t name) {
|
|
||||||
// who cares
|
|
||||||
}
|
|
||||||
|
|
||||||
static const struct wl_registry_listener registry_listener = {
|
|
||||||
.global = handle_global,
|
|
||||||
.global_remove = handle_global_remove,
|
|
||||||
};
|
|
||||||
|
|
||||||
int main(int argc, char **argv) {
|
|
||||||
if (argc > 1) {
|
|
||||||
if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-h") == 0) {
|
|
||||||
printf(usage);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
sleeptime = atoi(argv[1]);
|
|
||||||
}
|
|
||||||
display = wl_display_connect(NULL);
|
|
||||||
if (display == NULL) {
|
|
||||||
fprintf(stderr, "Failed to create display\n");
|
|
||||||
return EXIT_FAILURE;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct wl_registry *registry = wl_display_get_registry(display);
|
|
||||||
wl_registry_add_listener(registry, ®istry_listener, NULL);
|
|
||||||
wl_display_roundtrip(display);
|
|
||||||
|
|
||||||
if (compositor == NULL) {
|
|
||||||
fprintf(stderr, "wl-compositor not available\n");
|
|
||||||
return EXIT_FAILURE;
|
|
||||||
}
|
|
||||||
if (input_method_manager == NULL) {
|
|
||||||
fprintf(stderr, "input-method not available\n");
|
|
||||||
return EXIT_FAILURE;
|
|
||||||
}
|
|
||||||
if (seat == NULL) {
|
|
||||||
fprintf(stderr, "seat not available\n");
|
|
||||||
return EXIT_FAILURE;
|
|
||||||
}
|
|
||||||
|
|
||||||
input_method = zwp_input_method_manager_v2_get_input_method(
|
|
||||||
input_method_manager, seat);
|
|
||||||
running = true;
|
|
||||||
zwp_input_method_v2_add_listener(input_method, &im_listener, NULL);
|
|
||||||
|
|
||||||
int display_fd = wl_display_get_fd(display);
|
|
||||||
timer_fd = timerfd_create(CLOCK_MONOTONIC, TFD_CLOEXEC | TFD_NONBLOCK);
|
|
||||||
if (timer_fd < 0) {
|
|
||||||
fprintf(stderr, "Failed to start timer\n");
|
|
||||||
return EXIT_FAILURE;
|
|
||||||
}
|
|
||||||
int epoll = epoll_create1(EPOLL_CLOEXEC);
|
|
||||||
if (epoll < 0) {
|
|
||||||
fprintf(stderr, "Failed to start epoll\n");
|
|
||||||
return EXIT_FAILURE;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct epoll_event epoll_display = {
|
|
||||||
.events = EPOLLIN | EPOLLOUT,
|
|
||||||
.data = {.fd = display_fd},
|
|
||||||
};
|
|
||||||
if (epoll_ctl(epoll, EPOLL_CTL_ADD, display_fd, &epoll_display)) {
|
|
||||||
fprintf(stderr, "Failed to epoll display\n");
|
|
||||||
return EXIT_FAILURE;
|
|
||||||
}
|
|
||||||
|
|
||||||
wl_display_roundtrip(display); // timer may be armed here
|
|
||||||
|
|
||||||
struct epoll_event epoll_timer = {
|
|
||||||
.events = EPOLLIN,
|
|
||||||
.data = {.fd = timer_fd},
|
|
||||||
};
|
|
||||||
if (epoll_ctl(epoll, EPOLL_CTL_ADD, timer_fd, &epoll_timer)) {
|
|
||||||
fprintf(stderr, "Failed to epoll timer\n");
|
|
||||||
return EXIT_FAILURE;
|
|
||||||
}
|
|
||||||
|
|
||||||
timer_arm(2);
|
|
||||||
|
|
||||||
struct epoll_event caught;
|
|
||||||
while (epoll_wait(epoll, &caught, 1, -1)) {
|
|
||||||
if (!running) {
|
|
||||||
printf("Exiting\n");
|
|
||||||
return EXIT_SUCCESS;
|
|
||||||
}
|
|
||||||
if (caught.data.fd == display_fd) {
|
|
||||||
if (wl_display_dispatch(display) == -1) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
} else if (caught.data.fd == timer_fd) {
|
|
||||||
uint64_t expirations;
|
|
||||||
ssize_t n = read(timer_fd, &expirations, sizeof(expirations));
|
|
||||||
assert(n >= 0);
|
|
||||||
handle_timer();
|
|
||||||
} else {
|
|
||||||
printf("Unknown source\n");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return EXIT_SUCCESS;
|
|
||||||
}
|
|
@ -1,261 +0,0 @@
|
|||||||
#include <GLES2/gl2.h>
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include <wayland-client.h>
|
|
||||||
#include <wayland-egl.h>
|
|
||||||
#include "egl_common.h"
|
|
||||||
#include "keyboard-shortcuts-inhibit-unstable-v1-client-protocol.h"
|
|
||||||
#include "xdg-shell-client-protocol.h"
|
|
||||||
|
|
||||||
#include <linux/input-event-codes.h>
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Usage: keyboard-shortcuts-inhibit
|
|
||||||
* Creates a xdg-toplevel using the keyboard-shortcuts-inhibit protocol.
|
|
||||||
* It will be solid green, when it has an keyboard shortcuts inhibitor, and
|
|
||||||
* solid yellow if it does not.
|
|
||||||
* Left click with a pointer will toggle this state. (Touch is not supported
|
|
||||||
* for now).
|
|
||||||
* The compositor (de-)activating the inhibitor will also toggle state.
|
|
||||||
* With a compositor supporting the protocol, compositor shortcuts will be
|
|
||||||
* suspended while the inhibitor is active and the window has focus.
|
|
||||||
*/
|
|
||||||
|
|
||||||
static int width = 500, height = 300;
|
|
||||||
|
|
||||||
static struct wl_compositor *compositor = NULL;
|
|
||||||
static struct wl_seat *seat = NULL;
|
|
||||||
static struct xdg_wm_base *wm_base = NULL;
|
|
||||||
static struct zwp_keyboard_shortcuts_inhibit_manager_v1 *
|
|
||||||
keyboard_shortcuts_inhibit_manager = NULL;
|
|
||||||
static struct zwp_keyboard_shortcuts_inhibitor_v1 *
|
|
||||||
keyboard_shortcuts_inhibitor = NULL;
|
|
||||||
static bool active = false;
|
|
||||||
|
|
||||||
struct wl_egl_window *egl_window;
|
|
||||||
struct wlr_egl_surface *egl_surface;
|
|
||||||
|
|
||||||
static void draw(void) {
|
|
||||||
eglMakeCurrent(egl_display, egl_surface, egl_surface, egl_context);
|
|
||||||
|
|
||||||
float color[] = {1.0, 1.0, 0.0, 1.0};
|
|
||||||
if (keyboard_shortcuts_inhibitor) {
|
|
||||||
color[0] = 0.0;
|
|
||||||
}
|
|
||||||
|
|
||||||
glViewport(0, 0, width, height);
|
|
||||||
glClearColor(color[0], color[1], color[2], 1.0);
|
|
||||||
glClear(GL_COLOR_BUFFER_BIT);
|
|
||||||
|
|
||||||
eglSwapBuffers(egl_display, egl_surface);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void keyboard_shortcuts_inhibit_handle_active(void *data,
|
|
||||||
struct zwp_keyboard_shortcuts_inhibitor_v1 *
|
|
||||||
zwp_keyboard_shortcuts_inhibitor_v1) {
|
|
||||||
active = 1;
|
|
||||||
draw();
|
|
||||||
}
|
|
||||||
|
|
||||||
static void keyboard_shortcuts_inhibit_handle_inactive(void *data,
|
|
||||||
struct zwp_keyboard_shortcuts_inhibitor_v1 *
|
|
||||||
zwp_keyboard_shortcuts_inhibitor_v1) {
|
|
||||||
active = 0;
|
|
||||||
draw();
|
|
||||||
}
|
|
||||||
|
|
||||||
static const struct zwp_keyboard_shortcuts_inhibitor_v1_listener
|
|
||||||
keyboard_shortcuts_inhibitor_listener = {
|
|
||||||
.active = keyboard_shortcuts_inhibit_handle_active,
|
|
||||||
.inactive = keyboard_shortcuts_inhibit_handle_inactive,
|
|
||||||
};
|
|
||||||
|
|
||||||
static void pointer_handle_button(void *data, struct wl_pointer *pointer, uint32_t serial,
|
|
||||||
uint32_t time, uint32_t button, uint32_t state_w) {
|
|
||||||
struct wl_surface *surface = data;
|
|
||||||
|
|
||||||
if (button == BTN_LEFT && state_w == WL_POINTER_BUTTON_STATE_PRESSED) {
|
|
||||||
if (keyboard_shortcuts_inhibitor) {
|
|
||||||
zwp_keyboard_shortcuts_inhibitor_v1_destroy(
|
|
||||||
keyboard_shortcuts_inhibitor);
|
|
||||||
keyboard_shortcuts_inhibitor = NULL;
|
|
||||||
active = false;
|
|
||||||
} else {
|
|
||||||
keyboard_shortcuts_inhibitor =
|
|
||||||
zwp_keyboard_shortcuts_inhibit_manager_v1_inhibit_shortcuts(
|
|
||||||
keyboard_shortcuts_inhibit_manager, surface, seat);
|
|
||||||
zwp_keyboard_shortcuts_inhibitor_v1_add_listener(
|
|
||||||
keyboard_shortcuts_inhibitor,
|
|
||||||
&keyboard_shortcuts_inhibitor_listener, NULL);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
draw();
|
|
||||||
}
|
|
||||||
|
|
||||||
static void pointer_handle_enter(void *data, struct wl_pointer *wl_pointer,
|
|
||||||
uint32_t serial, struct wl_surface *surface,
|
|
||||||
wl_fixed_t surface_x, wl_fixed_t surface_y) {
|
|
||||||
// This space intentionally left blank
|
|
||||||
}
|
|
||||||
|
|
||||||
static void pointer_handle_leave(void *data, struct wl_pointer *wl_pointer,
|
|
||||||
uint32_t serial, struct wl_surface *surface) {
|
|
||||||
// This space intentionally left blank
|
|
||||||
}
|
|
||||||
|
|
||||||
static void pointer_handle_motion(void *data, struct wl_pointer *wl_pointer,
|
|
||||||
uint32_t time, wl_fixed_t surface_x, wl_fixed_t surface_y) {
|
|
||||||
// This space intentionally left blank
|
|
||||||
}
|
|
||||||
|
|
||||||
static void pointer_handle_axis(void *data, struct wl_pointer *wl_pointer,
|
|
||||||
uint32_t time, uint32_t axis, wl_fixed_t value) {
|
|
||||||
// This space intentionally left blank
|
|
||||||
}
|
|
||||||
|
|
||||||
static void pointer_handle_frame(void *data, struct wl_pointer *wl_pointer) {
|
|
||||||
// This space intentionally left blank
|
|
||||||
}
|
|
||||||
|
|
||||||
static void pointer_handle_axis_source(void *data,
|
|
||||||
struct wl_pointer *wl_pointer, uint32_t axis_source) {
|
|
||||||
// This space intentionally left blank
|
|
||||||
}
|
|
||||||
|
|
||||||
static void pointer_handle_axis_stop(void *data,
|
|
||||||
struct wl_pointer *wl_pointer, uint32_t time, uint32_t axis) {
|
|
||||||
// This space intentionally left blank
|
|
||||||
}
|
|
||||||
|
|
||||||
static void pointer_handle_axis_discrete(void *data,
|
|
||||||
struct wl_pointer *wl_pointer, uint32_t axis, int32_t discrete) {
|
|
||||||
// This space intentionally left blank
|
|
||||||
}
|
|
||||||
|
|
||||||
static const struct wl_pointer_listener pointer_listener = {
|
|
||||||
.enter = pointer_handle_enter,
|
|
||||||
.leave = pointer_handle_leave,
|
|
||||||
.motion = pointer_handle_motion,
|
|
||||||
.button = pointer_handle_button,
|
|
||||||
.axis = pointer_handle_axis,
|
|
||||||
.frame = pointer_handle_frame,
|
|
||||||
.axis_source = pointer_handle_axis_source,
|
|
||||||
.axis_stop = pointer_handle_axis_stop,
|
|
||||||
.axis_discrete = pointer_handle_axis_discrete,
|
|
||||||
};
|
|
||||||
|
|
||||||
static void xdg_surface_handle_configure(void *data,
|
|
||||||
struct xdg_surface *xdg_surface, uint32_t serial) {
|
|
||||||
xdg_surface_ack_configure(xdg_surface, serial);
|
|
||||||
wl_egl_window_resize(egl_window, width, height, 0, 0);
|
|
||||||
draw();
|
|
||||||
}
|
|
||||||
|
|
||||||
static const struct xdg_surface_listener xdg_surface_listener = {
|
|
||||||
.configure = xdg_surface_handle_configure,
|
|
||||||
};
|
|
||||||
|
|
||||||
static void xdg_toplevel_handle_configure(void *data,
|
|
||||||
struct xdg_toplevel *xdg_toplevel, int32_t w, int32_t h,
|
|
||||||
struct wl_array *states) {
|
|
||||||
width = w;
|
|
||||||
height = h;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void xdg_toplevel_handle_close(void *data,
|
|
||||||
struct xdg_toplevel *xdg_toplevel) {
|
|
||||||
exit(EXIT_SUCCESS);
|
|
||||||
}
|
|
||||||
|
|
||||||
static const struct xdg_toplevel_listener xdg_toplevel_listener = {
|
|
||||||
.configure = xdg_toplevel_handle_configure,
|
|
||||||
.close = xdg_toplevel_handle_close,
|
|
||||||
};
|
|
||||||
|
|
||||||
static void handle_global(void *data, struct wl_registry *registry,
|
|
||||||
uint32_t name, const char *interface, uint32_t version) {
|
|
||||||
if (strcmp(interface, "wl_compositor") == 0) {
|
|
||||||
compositor = wl_registry_bind(registry, name,
|
|
||||||
&wl_compositor_interface, 1);
|
|
||||||
} else if (strcmp(interface, xdg_wm_base_interface.name) == 0) {
|
|
||||||
wm_base = wl_registry_bind(registry, name, &xdg_wm_base_interface, 1);
|
|
||||||
} else if (strcmp(interface, zwp_keyboard_shortcuts_inhibit_manager_v1_interface.name) == 0) {
|
|
||||||
keyboard_shortcuts_inhibit_manager = wl_registry_bind(registry, name,
|
|
||||||
&zwp_keyboard_shortcuts_inhibit_manager_v1_interface, 1);
|
|
||||||
} else if (strcmp(interface, wl_seat_interface.name) == 0) {
|
|
||||||
seat = wl_registry_bind(registry, name, &wl_seat_interface, version);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void handle_global_remove(void *data, struct wl_registry *registry,
|
|
||||||
uint32_t name) {
|
|
||||||
// who cares
|
|
||||||
}
|
|
||||||
|
|
||||||
static const struct wl_registry_listener registry_listener = {
|
|
||||||
.global = handle_global,
|
|
||||||
.global_remove = handle_global_remove,
|
|
||||||
};
|
|
||||||
|
|
||||||
int main(int argc, char **argv) {
|
|
||||||
struct wl_display *display = wl_display_connect(NULL);
|
|
||||||
if (display == NULL) {
|
|
||||||
fprintf(stderr, "Failed to create display\n");
|
|
||||||
return EXIT_FAILURE;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct wl_registry *registry = wl_display_get_registry(display);
|
|
||||||
wl_registry_add_listener(registry, ®istry_listener, NULL);
|
|
||||||
wl_display_roundtrip(display);
|
|
||||||
|
|
||||||
if (compositor == NULL) {
|
|
||||||
fprintf(stderr, "wl-compositor not available\n");
|
|
||||||
return EXIT_FAILURE;
|
|
||||||
}
|
|
||||||
if (wm_base == NULL) {
|
|
||||||
fprintf(stderr, "xdg-shell not available\n");
|
|
||||||
return EXIT_FAILURE;
|
|
||||||
}
|
|
||||||
if (keyboard_shortcuts_inhibit_manager == NULL) {
|
|
||||||
fprintf(stderr, "keyboard-shortcuts-inhibit not available\n");
|
|
||||||
return EXIT_FAILURE;
|
|
||||||
}
|
|
||||||
|
|
||||||
egl_init(display);
|
|
||||||
|
|
||||||
struct wl_surface *surface = wl_compositor_create_surface(compositor);
|
|
||||||
struct xdg_surface *xdg_surface =
|
|
||||||
xdg_wm_base_get_xdg_surface(wm_base, surface);
|
|
||||||
struct xdg_toplevel *xdg_toplevel = xdg_surface_get_toplevel(xdg_surface);
|
|
||||||
|
|
||||||
struct wl_pointer *pointer = wl_seat_get_pointer(seat);
|
|
||||||
wl_pointer_add_listener(pointer, &pointer_listener, surface);
|
|
||||||
|
|
||||||
xdg_surface_add_listener(xdg_surface, &xdg_surface_listener, NULL);
|
|
||||||
xdg_toplevel_add_listener(xdg_toplevel, &xdg_toplevel_listener, NULL);
|
|
||||||
|
|
||||||
wl_surface_commit(surface);
|
|
||||||
|
|
||||||
egl_window = wl_egl_window_create(surface, width, height);
|
|
||||||
egl_surface = eglCreatePlatformWindowSurfaceEXT(
|
|
||||||
egl_display, egl_config, egl_window, NULL);
|
|
||||||
|
|
||||||
wl_display_roundtrip(display);
|
|
||||||
|
|
||||||
keyboard_shortcuts_inhibitor =
|
|
||||||
zwp_keyboard_shortcuts_inhibit_manager_v1_inhibit_shortcuts(
|
|
||||||
keyboard_shortcuts_inhibit_manager, surface, seat);
|
|
||||||
zwp_keyboard_shortcuts_inhibitor_v1_add_listener(
|
|
||||||
keyboard_shortcuts_inhibitor,
|
|
||||||
&keyboard_shortcuts_inhibitor_listener, NULL);
|
|
||||||
|
|
||||||
draw();
|
|
||||||
|
|
||||||
while (wl_display_dispatch(display) != -1) {
|
|
||||||
// This space intentionally left blank
|
|
||||||
}
|
|
||||||
|
|
||||||
return EXIT_SUCCESS;
|
|
||||||
}
|
|
@ -1,668 +0,0 @@
|
|||||||
#define _POSIX_C_SOURCE 200112L
|
|
||||||
#include <linux/input-event-codes.h>
|
|
||||||
#include <assert.h>
|
|
||||||
#include <GLES2/gl2.h>
|
|
||||||
#include <limits.h>
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include <time.h>
|
|
||||||
#include <unistd.h>
|
|
||||||
#include <wayland-client.h>
|
|
||||||
#include <wayland-cursor.h>
|
|
||||||
#include <wayland-egl.h>
|
|
||||||
#include "egl_common.h"
|
|
||||||
#include "wlr-layer-shell-unstable-v1-client-protocol.h"
|
|
||||||
#include "xdg-shell-client-protocol.h"
|
|
||||||
|
|
||||||
static struct wl_display *display;
|
|
||||||
static struct wl_compositor *compositor;
|
|
||||||
static struct wl_seat *seat;
|
|
||||||
static struct wl_shm *shm;
|
|
||||||
static struct wl_pointer *pointer;
|
|
||||||
static struct wl_keyboard *keyboard;
|
|
||||||
static struct xdg_wm_base *xdg_wm_base;
|
|
||||||
static struct zwlr_layer_shell_v1 *layer_shell;
|
|
||||||
|
|
||||||
struct zwlr_layer_surface_v1 *layer_surface;
|
|
||||||
static struct wl_output *wl_output;
|
|
||||||
|
|
||||||
struct wl_surface *wl_surface;
|
|
||||||
struct wl_egl_window *egl_window;
|
|
||||||
struct wlr_egl_surface *egl_surface;
|
|
||||||
struct wl_callback *frame_callback;
|
|
||||||
|
|
||||||
static uint32_t output = UINT32_MAX;
|
|
||||||
struct xdg_popup *popup;
|
|
||||||
struct wl_surface *popup_wl_surface;
|
|
||||||
struct wl_egl_window *popup_egl_window;
|
|
||||||
static uint32_t popup_width = 256, popup_height = 256;
|
|
||||||
struct wlr_egl_surface *popup_egl_surface;
|
|
||||||
struct wl_callback *popup_frame_callback;
|
|
||||||
float popup_alpha = 1.0, popup_red = 0.5f;
|
|
||||||
|
|
||||||
static uint32_t layer = ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND;
|
|
||||||
static uint32_t anchor = 0;
|
|
||||||
static uint32_t width = 256, height = 256;
|
|
||||||
static int32_t margin_top = 0;
|
|
||||||
static double alpha = 1.0;
|
|
||||||
static bool run_display = true;
|
|
||||||
static bool animate = false;
|
|
||||||
static enum zwlr_layer_surface_v1_keyboard_interactivity keyboard_interactive =
|
|
||||||
ZWLR_LAYER_SURFACE_V1_KEYBOARD_INTERACTIVITY_NONE;
|
|
||||||
static double frame = 0;
|
|
||||||
static int cur_x = -1, cur_y = -1;
|
|
||||||
static int buttons = 0;
|
|
||||||
|
|
||||||
struct wl_cursor_image *cursor_image;
|
|
||||||
struct wl_cursor_image *popup_cursor_image;
|
|
||||||
struct wl_surface *cursor_surface, *input_surface;
|
|
||||||
|
|
||||||
static struct {
|
|
||||||
struct timespec last_frame;
|
|
||||||
float color[3];
|
|
||||||
int dec;
|
|
||||||
} demo;
|
|
||||||
|
|
||||||
static void draw(void);
|
|
||||||
static void draw_popup(void);
|
|
||||||
|
|
||||||
static void surface_frame_callback(
|
|
||||||
void *data, struct wl_callback *cb, uint32_t time) {
|
|
||||||
wl_callback_destroy(cb);
|
|
||||||
frame_callback = NULL;
|
|
||||||
draw();
|
|
||||||
}
|
|
||||||
|
|
||||||
static struct wl_callback_listener frame_listener = {
|
|
||||||
.done = surface_frame_callback
|
|
||||||
};
|
|
||||||
|
|
||||||
static void popup_surface_frame_callback(
|
|
||||||
void *data, struct wl_callback *cb, uint32_t time) {
|
|
||||||
wl_callback_destroy(cb);
|
|
||||||
popup_frame_callback = NULL;
|
|
||||||
if (popup) {
|
|
||||||
draw_popup();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static struct wl_callback_listener popup_frame_listener = {
|
|
||||||
.done = popup_surface_frame_callback
|
|
||||||
};
|
|
||||||
|
|
||||||
static void draw(void) {
|
|
||||||
eglMakeCurrent(egl_display, egl_surface, egl_surface, egl_context);
|
|
||||||
struct timespec ts;
|
|
||||||
clock_gettime(CLOCK_MONOTONIC, &ts);
|
|
||||||
|
|
||||||
long ms = (ts.tv_sec - demo.last_frame.tv_sec) * 1000 +
|
|
||||||
(ts.tv_nsec - demo.last_frame.tv_nsec) / 1000000;
|
|
||||||
int inc = (demo.dec + 1) % 3;
|
|
||||||
|
|
||||||
if (!buttons) {
|
|
||||||
demo.color[inc] += ms / 2000.0f;
|
|
||||||
demo.color[demo.dec] -= ms / 2000.0f;
|
|
||||||
|
|
||||||
if (demo.color[demo.dec] < 0.0f) {
|
|
||||||
demo.color[inc] = 1.0f;
|
|
||||||
demo.color[demo.dec] = 0.0f;
|
|
||||||
demo.dec = inc;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (animate) {
|
|
||||||
frame += ms / 50.0;
|
|
||||||
int32_t old_top = margin_top;
|
|
||||||
margin_top = -(20 - ((int)frame % 20));
|
|
||||||
if (old_top != margin_top) {
|
|
||||||
zwlr_layer_surface_v1_set_margin(layer_surface,
|
|
||||||
margin_top, 0, 0, 0);
|
|
||||||
wl_surface_commit(wl_surface);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
glViewport(0, 0, width, height);
|
|
||||||
if (buttons) {
|
|
||||||
glClearColor(alpha, alpha, alpha, alpha);
|
|
||||||
} else {
|
|
||||||
glClearColor(demo.color[0] * alpha, demo.color[1] * alpha,
|
|
||||||
demo.color[2] * alpha, alpha);
|
|
||||||
}
|
|
||||||
glClear(GL_COLOR_BUFFER_BIT);
|
|
||||||
|
|
||||||
if (cur_x != -1 && cur_y != -1) {
|
|
||||||
glEnable(GL_SCISSOR_TEST);
|
|
||||||
glScissor(cur_x, height - cur_y, 5, 5);
|
|
||||||
glClearColor(0, 0, 0, 1);
|
|
||||||
glClear(GL_COLOR_BUFFER_BIT);
|
|
||||||
glDisable(GL_SCISSOR_TEST);
|
|
||||||
}
|
|
||||||
|
|
||||||
frame_callback = wl_surface_frame(wl_surface);
|
|
||||||
wl_callback_add_listener(frame_callback, &frame_listener, NULL);
|
|
||||||
|
|
||||||
eglSwapBuffers(egl_display, egl_surface);
|
|
||||||
|
|
||||||
demo.last_frame = ts;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void draw_popup(void) {
|
|
||||||
static float alpha_mod = -0.01;
|
|
||||||
|
|
||||||
eglMakeCurrent(egl_display, popup_egl_surface, popup_egl_surface, egl_context);
|
|
||||||
glViewport(0, 0, popup_width, popup_height);
|
|
||||||
glClearColor(popup_red * popup_alpha, 0.5f * popup_alpha,
|
|
||||||
0.5f * popup_alpha, popup_alpha);
|
|
||||||
popup_alpha += alpha_mod;
|
|
||||||
if (popup_alpha < 0.01 || popup_alpha >= 1.0f) {
|
|
||||||
alpha_mod *= -1.0;
|
|
||||||
}
|
|
||||||
glClear(GL_COLOR_BUFFER_BIT);
|
|
||||||
|
|
||||||
popup_frame_callback = wl_surface_frame(popup_wl_surface);
|
|
||||||
assert(popup_frame_callback);
|
|
||||||
wl_callback_add_listener(popup_frame_callback, &popup_frame_listener, NULL);
|
|
||||||
eglSwapBuffers(egl_display, popup_egl_surface);
|
|
||||||
wl_surface_commit(popup_wl_surface);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void xdg_surface_handle_configure(void *data,
|
|
||||||
struct xdg_surface *xdg_surface, uint32_t serial) {
|
|
||||||
xdg_surface_ack_configure(xdg_surface, serial);
|
|
||||||
}
|
|
||||||
|
|
||||||
static const struct xdg_surface_listener xdg_surface_listener = {
|
|
||||||
.configure = xdg_surface_handle_configure,
|
|
||||||
};
|
|
||||||
|
|
||||||
static void xdg_popup_configure(void *data, struct xdg_popup *xdg_popup,
|
|
||||||
int32_t x, int32_t y, int32_t width, int32_t height) {
|
|
||||||
fprintf(stderr, "Popup configured %dx%d@%d,%d\n", width, height, x, y);
|
|
||||||
popup_width = width;
|
|
||||||
popup_height = height;
|
|
||||||
if (popup_egl_window) {
|
|
||||||
wl_egl_window_resize(popup_egl_window, width, height, 0, 0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void popup_destroy(void) {
|
|
||||||
eglDestroySurface(egl_display, popup_egl_surface);
|
|
||||||
wl_egl_window_destroy(popup_egl_window);
|
|
||||||
xdg_popup_destroy(popup);
|
|
||||||
wl_surface_destroy(popup_wl_surface);
|
|
||||||
popup_wl_surface = NULL;
|
|
||||||
popup = NULL;
|
|
||||||
popup_egl_window = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void xdg_popup_done(void *data, struct xdg_popup *xdg_popup) {
|
|
||||||
fprintf(stderr, "Popup done\n");
|
|
||||||
popup_destroy();
|
|
||||||
}
|
|
||||||
|
|
||||||
static const struct xdg_popup_listener xdg_popup_listener = {
|
|
||||||
.configure = xdg_popup_configure,
|
|
||||||
.popup_done = xdg_popup_done,
|
|
||||||
};
|
|
||||||
|
|
||||||
static void create_popup(uint32_t serial) {
|
|
||||||
if (popup) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
struct wl_surface *surface = wl_compositor_create_surface(compositor);
|
|
||||||
assert(xdg_wm_base && surface);
|
|
||||||
struct xdg_surface *xdg_surface =
|
|
||||||
xdg_wm_base_get_xdg_surface(xdg_wm_base, surface);
|
|
||||||
struct xdg_positioner *xdg_positioner =
|
|
||||||
xdg_wm_base_create_positioner(xdg_wm_base);
|
|
||||||
assert(xdg_surface && xdg_positioner);
|
|
||||||
|
|
||||||
xdg_positioner_set_size(xdg_positioner, popup_width, popup_height);
|
|
||||||
xdg_positioner_set_offset(xdg_positioner, 0, 0);
|
|
||||||
xdg_positioner_set_anchor_rect(xdg_positioner, cur_x, cur_y, 1, 1);
|
|
||||||
xdg_positioner_set_anchor(xdg_positioner, XDG_POSITIONER_ANCHOR_BOTTOM_RIGHT);
|
|
||||||
|
|
||||||
popup = xdg_surface_get_popup(xdg_surface, NULL, xdg_positioner);
|
|
||||||
xdg_popup_grab(popup, seat, serial);
|
|
||||||
|
|
||||||
assert(popup);
|
|
||||||
|
|
||||||
zwlr_layer_surface_v1_get_popup(layer_surface, popup);
|
|
||||||
|
|
||||||
xdg_surface_add_listener(xdg_surface, &xdg_surface_listener, NULL);
|
|
||||||
xdg_popup_add_listener(popup, &xdg_popup_listener, NULL);
|
|
||||||
|
|
||||||
wl_surface_commit(surface);
|
|
||||||
wl_display_roundtrip(display);
|
|
||||||
|
|
||||||
xdg_positioner_destroy(xdg_positioner);
|
|
||||||
|
|
||||||
popup_wl_surface = surface;
|
|
||||||
popup_egl_window = wl_egl_window_create(surface, popup_width, popup_height);
|
|
||||||
assert(popup_egl_window);
|
|
||||||
popup_egl_surface = eglCreatePlatformWindowSurfaceEXT(
|
|
||||||
egl_display, egl_config, popup_egl_window, NULL);
|
|
||||||
assert(popup_egl_surface != EGL_NO_SURFACE);
|
|
||||||
draw_popup();
|
|
||||||
}
|
|
||||||
|
|
||||||
static void layer_surface_configure(void *data,
|
|
||||||
struct zwlr_layer_surface_v1 *surface,
|
|
||||||
uint32_t serial, uint32_t w, uint32_t h) {
|
|
||||||
width = w;
|
|
||||||
height = h;
|
|
||||||
if (egl_window) {
|
|
||||||
wl_egl_window_resize(egl_window, width, height, 0, 0);
|
|
||||||
}
|
|
||||||
zwlr_layer_surface_v1_ack_configure(surface, serial);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void layer_surface_closed(void *data,
|
|
||||||
struct zwlr_layer_surface_v1 *surface) {
|
|
||||||
eglDestroySurface(egl_display, egl_surface);
|
|
||||||
wl_egl_window_destroy(egl_window);
|
|
||||||
zwlr_layer_surface_v1_destroy(surface);
|
|
||||||
wl_surface_destroy(wl_surface);
|
|
||||||
run_display = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct zwlr_layer_surface_v1_listener layer_surface_listener = {
|
|
||||||
.configure = layer_surface_configure,
|
|
||||||
.closed = layer_surface_closed,
|
|
||||||
};
|
|
||||||
|
|
||||||
static void wl_pointer_enter(void *data, struct wl_pointer *wl_pointer,
|
|
||||||
uint32_t serial, struct wl_surface *surface,
|
|
||||||
wl_fixed_t surface_x, wl_fixed_t surface_y) {
|
|
||||||
struct wl_cursor_image *image;
|
|
||||||
if (surface == popup_wl_surface) {
|
|
||||||
image = popup_cursor_image;
|
|
||||||
} else {
|
|
||||||
image = cursor_image;
|
|
||||||
}
|
|
||||||
wl_surface_attach(cursor_surface,
|
|
||||||
wl_cursor_image_get_buffer(image), 0, 0);
|
|
||||||
wl_surface_damage(cursor_surface, 1, 0,
|
|
||||||
image->width, image->height);
|
|
||||||
wl_surface_commit(cursor_surface);
|
|
||||||
wl_pointer_set_cursor(wl_pointer, serial, cursor_surface,
|
|
||||||
image->hotspot_x, image->hotspot_y);
|
|
||||||
input_surface = surface;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void wl_pointer_leave(void *data, struct wl_pointer *wl_pointer,
|
|
||||||
uint32_t serial, struct wl_surface *surface) {
|
|
||||||
cur_x = cur_y = -1;
|
|
||||||
buttons = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void wl_pointer_motion(void *data, struct wl_pointer *wl_pointer,
|
|
||||||
uint32_t time, wl_fixed_t surface_x, wl_fixed_t surface_y) {
|
|
||||||
cur_x = wl_fixed_to_int(surface_x);
|
|
||||||
cur_y = wl_fixed_to_int(surface_y);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void wl_pointer_button(void *data, struct wl_pointer *wl_pointer,
|
|
||||||
uint32_t serial, uint32_t time, uint32_t button, uint32_t state) {
|
|
||||||
if (input_surface == wl_surface) {
|
|
||||||
if (state == WL_POINTER_BUTTON_STATE_PRESSED) {
|
|
||||||
if (button == BTN_RIGHT) {
|
|
||||||
if (popup_wl_surface) {
|
|
||||||
popup_destroy();
|
|
||||||
} else {
|
|
||||||
create_popup(serial);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
buttons++;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (button != BTN_RIGHT) {
|
|
||||||
buttons--;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else if (input_surface == popup_wl_surface) {
|
|
||||||
if (state == WL_POINTER_BUTTON_STATE_PRESSED) {
|
|
||||||
if (button == BTN_LEFT && popup_red <= 0.9f) {
|
|
||||||
popup_red += 0.1;
|
|
||||||
} else if (button == BTN_RIGHT && popup_red >= 0.1f) {
|
|
||||||
popup_red -= 0.1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
assert(false && "Unknown surface");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void wl_pointer_axis(void *data, struct wl_pointer *wl_pointer,
|
|
||||||
uint32_t time, uint32_t axis, wl_fixed_t value) {
|
|
||||||
// Who cares
|
|
||||||
}
|
|
||||||
|
|
||||||
static void wl_pointer_frame(void *data, struct wl_pointer *wl_pointer) {
|
|
||||||
// Who cares
|
|
||||||
}
|
|
||||||
|
|
||||||
static void wl_pointer_axis_source(void *data, struct wl_pointer *wl_pointer,
|
|
||||||
uint32_t axis_source) {
|
|
||||||
// Who cares
|
|
||||||
}
|
|
||||||
|
|
||||||
static void wl_pointer_axis_stop(void *data, struct wl_pointer *wl_pointer,
|
|
||||||
uint32_t time, uint32_t axis) {
|
|
||||||
// Who cares
|
|
||||||
}
|
|
||||||
|
|
||||||
static void wl_pointer_axis_discrete(void *data, struct wl_pointer *wl_pointer,
|
|
||||||
uint32_t axis, int32_t discrete) {
|
|
||||||
// Who cares
|
|
||||||
}
|
|
||||||
|
|
||||||
struct wl_pointer_listener pointer_listener = {
|
|
||||||
.enter = wl_pointer_enter,
|
|
||||||
.leave = wl_pointer_leave,
|
|
||||||
.motion = wl_pointer_motion,
|
|
||||||
.button = wl_pointer_button,
|
|
||||||
.axis = wl_pointer_axis,
|
|
||||||
.frame = wl_pointer_frame,
|
|
||||||
.axis_source = wl_pointer_axis_source,
|
|
||||||
.axis_stop = wl_pointer_axis_stop,
|
|
||||||
.axis_discrete = wl_pointer_axis_discrete,
|
|
||||||
};
|
|
||||||
|
|
||||||
static void wl_keyboard_keymap(void *data, struct wl_keyboard *wl_keyboard,
|
|
||||||
uint32_t format, int32_t fd, uint32_t size) {
|
|
||||||
// Who cares
|
|
||||||
}
|
|
||||||
|
|
||||||
static void wl_keyboard_enter(void *data, struct wl_keyboard *wl_keyboard,
|
|
||||||
uint32_t serial, struct wl_surface *surface, struct wl_array *keys) {
|
|
||||||
fprintf(stderr, "Keyboard enter\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
static void wl_keyboard_leave(void *data, struct wl_keyboard *wl_keyboard,
|
|
||||||
uint32_t serial, struct wl_surface *surface) {
|
|
||||||
fprintf(stderr, "Keyboard leave\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
static void wl_keyboard_key(void *data, struct wl_keyboard *wl_keyboard,
|
|
||||||
uint32_t serial, uint32_t time, uint32_t key, uint32_t state) {
|
|
||||||
fprintf(stderr, "Key event: %d %d\n", key, state);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void wl_keyboard_modifiers(void *data, struct wl_keyboard *wl_keyboard,
|
|
||||||
uint32_t serial, uint32_t mods_depressed, uint32_t mods_latched,
|
|
||||||
uint32_t mods_locked, uint32_t group) {
|
|
||||||
// Who cares
|
|
||||||
}
|
|
||||||
|
|
||||||
static void wl_keyboard_repeat_info(void *data, struct wl_keyboard *wl_keyboard,
|
|
||||||
int32_t rate, int32_t delay) {
|
|
||||||
// Who cares
|
|
||||||
}
|
|
||||||
|
|
||||||
static struct wl_keyboard_listener keyboard_listener = {
|
|
||||||
.keymap = wl_keyboard_keymap,
|
|
||||||
.enter = wl_keyboard_enter,
|
|
||||||
.leave = wl_keyboard_leave,
|
|
||||||
.key = wl_keyboard_key,
|
|
||||||
.modifiers = wl_keyboard_modifiers,
|
|
||||||
.repeat_info = wl_keyboard_repeat_info,
|
|
||||||
};
|
|
||||||
|
|
||||||
static void seat_handle_capabilities(void *data, struct wl_seat *wl_seat,
|
|
||||||
enum wl_seat_capability caps) {
|
|
||||||
if ((caps & WL_SEAT_CAPABILITY_POINTER)) {
|
|
||||||
pointer = wl_seat_get_pointer(wl_seat);
|
|
||||||
wl_pointer_add_listener(pointer, &pointer_listener, NULL);
|
|
||||||
}
|
|
||||||
if ((caps & WL_SEAT_CAPABILITY_KEYBOARD)) {
|
|
||||||
keyboard = wl_seat_get_keyboard(wl_seat);
|
|
||||||
wl_keyboard_add_listener(keyboard, &keyboard_listener, NULL);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void seat_handle_name(void *data, struct wl_seat *wl_seat,
|
|
||||||
const char *name) {
|
|
||||||
// Who cares
|
|
||||||
}
|
|
||||||
|
|
||||||
const struct wl_seat_listener seat_listener = {
|
|
||||||
.capabilities = seat_handle_capabilities,
|
|
||||||
.name = seat_handle_name,
|
|
||||||
};
|
|
||||||
|
|
||||||
static void handle_global(void *data, struct wl_registry *registry,
|
|
||||||
uint32_t name, const char *interface, uint32_t version) {
|
|
||||||
if (strcmp(interface, wl_compositor_interface.name) == 0) {
|
|
||||||
compositor = wl_registry_bind(registry, name,
|
|
||||||
&wl_compositor_interface, 1);
|
|
||||||
} else if (strcmp(interface, wl_shm_interface.name) == 0) {
|
|
||||||
shm = wl_registry_bind(registry, name,
|
|
||||||
&wl_shm_interface, 1);
|
|
||||||
} else if (strcmp(interface, "wl_output") == 0) {
|
|
||||||
if (output != UINT32_MAX) {
|
|
||||||
if (!wl_output) {
|
|
||||||
wl_output = wl_registry_bind(registry, name,
|
|
||||||
&wl_output_interface, 1);
|
|
||||||
} else {
|
|
||||||
output--;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else if (strcmp(interface, wl_seat_interface.name) == 0) {
|
|
||||||
seat = wl_registry_bind(registry, name,
|
|
||||||
&wl_seat_interface, 1);
|
|
||||||
wl_seat_add_listener(seat, &seat_listener, NULL);
|
|
||||||
} else if (strcmp(interface, zwlr_layer_shell_v1_interface.name) == 0) {
|
|
||||||
layer_shell = wl_registry_bind(registry, name,
|
|
||||||
&zwlr_layer_shell_v1_interface, version < 4 ? version : 4);
|
|
||||||
} else if (strcmp(interface, xdg_wm_base_interface.name) == 0) {
|
|
||||||
xdg_wm_base = wl_registry_bind(
|
|
||||||
registry, name, &xdg_wm_base_interface, 1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void handle_global_remove(void *data, struct wl_registry *registry,
|
|
||||||
uint32_t name) {
|
|
||||||
// who cares
|
|
||||||
}
|
|
||||||
|
|
||||||
static const struct wl_registry_listener registry_listener = {
|
|
||||||
.global = handle_global,
|
|
||||||
.global_remove = handle_global_remove,
|
|
||||||
};
|
|
||||||
|
|
||||||
int main(int argc, char **argv) {
|
|
||||||
char *namespace = "wlroots";
|
|
||||||
int exclusive_zone = 0;
|
|
||||||
int32_t margin_right = 0, margin_bottom = 0, margin_left = 0;
|
|
||||||
bool found;
|
|
||||||
int c;
|
|
||||||
while ((c = getopt(argc, argv, "k:nw:h:o:l:a:x:m:t:")) != -1) {
|
|
||||||
switch (c) {
|
|
||||||
case 'o':
|
|
||||||
output = atoi(optarg);
|
|
||||||
break;
|
|
||||||
case 'w':
|
|
||||||
width = atoi(optarg);
|
|
||||||
break;
|
|
||||||
case 'h':
|
|
||||||
height = atoi(optarg);
|
|
||||||
break;
|
|
||||||
case 'x':
|
|
||||||
exclusive_zone = atoi(optarg);
|
|
||||||
break;
|
|
||||||
case 'l': {
|
|
||||||
struct {
|
|
||||||
char *name;
|
|
||||||
enum zwlr_layer_shell_v1_layer value;
|
|
||||||
} layers[] = {
|
|
||||||
{ "background", ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND },
|
|
||||||
{ "bottom", ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM },
|
|
||||||
{ "top", ZWLR_LAYER_SHELL_V1_LAYER_TOP },
|
|
||||||
{ "overlay", ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY },
|
|
||||||
};
|
|
||||||
found = false;
|
|
||||||
for (size_t i = 0; i < sizeof(layers) / sizeof(layers[0]); ++i) {
|
|
||||||
if (strcmp(optarg, layers[i].name) == 0) {
|
|
||||||
layer = layers[i].value;
|
|
||||||
found = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!found) {
|
|
||||||
fprintf(stderr, "invalid layer %s\n", optarg);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case 'a': {
|
|
||||||
struct {
|
|
||||||
char *name;
|
|
||||||
uint32_t value;
|
|
||||||
} anchors[] = {
|
|
||||||
{ "top", ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP },
|
|
||||||
{ "bottom", ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM },
|
|
||||||
{ "left", ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT },
|
|
||||||
{ "right", ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT },
|
|
||||||
};
|
|
||||||
found = false;
|
|
||||||
for (size_t i = 0; i < sizeof(anchors) / sizeof(anchors[0]); ++i) {
|
|
||||||
if (strcmp(optarg, anchors[i].name) == 0) {
|
|
||||||
anchor |= anchors[i].value;
|
|
||||||
found = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!found) {
|
|
||||||
fprintf(stderr, "invalid anchor %s\n", optarg);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case 't':
|
|
||||||
alpha = atof(optarg);
|
|
||||||
break;
|
|
||||||
case 'm': {
|
|
||||||
char *endptr = optarg;
|
|
||||||
margin_top = strtol(endptr, &endptr, 10);
|
|
||||||
assert(*endptr == ',');
|
|
||||||
margin_right = strtol(endptr + 1, &endptr, 10);
|
|
||||||
assert(*endptr == ',');
|
|
||||||
margin_bottom = strtol(endptr + 1, &endptr, 10);
|
|
||||||
assert(*endptr == ',');
|
|
||||||
margin_left = strtol(endptr + 1, &endptr, 10);
|
|
||||||
assert(!*endptr);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case 'n':
|
|
||||||
animate = true;
|
|
||||||
break;
|
|
||||||
case 'k': {
|
|
||||||
const struct {
|
|
||||||
const char *name;
|
|
||||||
enum zwlr_layer_surface_v1_keyboard_interactivity value;
|
|
||||||
} kb_int[] = {
|
|
||||||
{ "none", ZWLR_LAYER_SURFACE_V1_KEYBOARD_INTERACTIVITY_NONE },
|
|
||||||
{ "exclusive", ZWLR_LAYER_SURFACE_V1_KEYBOARD_INTERACTIVITY_EXCLUSIVE },
|
|
||||||
{ "on_demand", ZWLR_LAYER_SURFACE_V1_KEYBOARD_INTERACTIVITY_ON_DEMAND }
|
|
||||||
};
|
|
||||||
found = false;
|
|
||||||
for (size_t i = 0; i < sizeof(kb_int) / sizeof(kb_int[0]); ++i) {
|
|
||||||
if (strcmp(optarg, kb_int[i].name) == 0) {
|
|
||||||
keyboard_interactive = kb_int[i].value;
|
|
||||||
found = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!found) {
|
|
||||||
fprintf(stderr, "invalid keyboard interactivity setting %s\n", optarg);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
display = wl_display_connect(NULL);
|
|
||||||
if (display == NULL) {
|
|
||||||
fprintf(stderr, "Failed to create display\n");
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct wl_registry *registry = wl_display_get_registry(display);
|
|
||||||
wl_registry_add_listener(registry, ®istry_listener, NULL);
|
|
||||||
wl_display_roundtrip(display);
|
|
||||||
|
|
||||||
if (compositor == NULL) {
|
|
||||||
fprintf(stderr, "wl_compositor not available\n");
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
if (shm == NULL) {
|
|
||||||
fprintf(stderr, "wl_shm not available\n");
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
if (layer_shell == NULL) {
|
|
||||||
fprintf(stderr, "layer_shell not available\n");
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct wl_cursor_theme *cursor_theme =
|
|
||||||
wl_cursor_theme_load(NULL, 16, shm);
|
|
||||||
assert(cursor_theme);
|
|
||||||
struct wl_cursor *cursor =
|
|
||||||
wl_cursor_theme_get_cursor(cursor_theme, "crosshair");
|
|
||||||
if (cursor == NULL) {
|
|
||||||
cursor = wl_cursor_theme_get_cursor(cursor_theme, "default");
|
|
||||||
}
|
|
||||||
assert(cursor);
|
|
||||||
cursor_image = cursor->images[0];
|
|
||||||
|
|
||||||
cursor = wl_cursor_theme_get_cursor(cursor_theme, "tcross");
|
|
||||||
if (cursor == NULL) {
|
|
||||||
cursor = wl_cursor_theme_get_cursor(cursor_theme, "default");
|
|
||||||
}
|
|
||||||
assert(cursor);
|
|
||||||
popup_cursor_image = cursor->images[0];
|
|
||||||
|
|
||||||
cursor_surface = wl_compositor_create_surface(compositor);
|
|
||||||
assert(cursor_surface);
|
|
||||||
|
|
||||||
egl_init(display);
|
|
||||||
|
|
||||||
wl_surface = wl_compositor_create_surface(compositor);
|
|
||||||
assert(wl_surface);
|
|
||||||
|
|
||||||
layer_surface = zwlr_layer_shell_v1_get_layer_surface(layer_shell,
|
|
||||||
wl_surface, wl_output, layer, namespace);
|
|
||||||
assert(layer_surface);
|
|
||||||
zwlr_layer_surface_v1_set_size(layer_surface, width, height);
|
|
||||||
zwlr_layer_surface_v1_set_anchor(layer_surface, anchor);
|
|
||||||
zwlr_layer_surface_v1_set_exclusive_zone(layer_surface, exclusive_zone);
|
|
||||||
zwlr_layer_surface_v1_set_margin(layer_surface,
|
|
||||||
margin_top, margin_right, margin_bottom, margin_left);
|
|
||||||
zwlr_layer_surface_v1_set_keyboard_interactivity(
|
|
||||||
layer_surface, keyboard_interactive);
|
|
||||||
zwlr_layer_surface_v1_add_listener(layer_surface,
|
|
||||||
&layer_surface_listener, layer_surface);
|
|
||||||
wl_surface_commit(wl_surface);
|
|
||||||
wl_display_roundtrip(display);
|
|
||||||
|
|
||||||
egl_window = wl_egl_window_create(wl_surface, width, height);
|
|
||||||
assert(egl_window);
|
|
||||||
egl_surface = eglCreatePlatformWindowSurfaceEXT(
|
|
||||||
egl_display, egl_config, egl_window, NULL);
|
|
||||||
assert(egl_surface != EGL_NO_SURFACE);
|
|
||||||
|
|
||||||
wl_display_roundtrip(display);
|
|
||||||
draw();
|
|
||||||
|
|
||||||
while (wl_display_dispatch(display) != -1 && run_display) {
|
|
||||||
// This space intentionally left blank
|
|
||||||
}
|
|
||||||
|
|
||||||
wl_cursor_theme_destroy(cursor_theme);
|
|
||||||
return 0;
|
|
||||||
}
|
|
@ -1,29 +1,10 @@
|
|||||||
threads = dependency('threads')
|
|
||||||
wayland_egl = dependency('wayland-egl')
|
|
||||||
wayland_cursor = dependency('wayland-cursor')
|
|
||||||
wayland_client = dependency('wayland-client')
|
|
||||||
libpng = dependency('libpng', required: false, disabler: true)
|
|
||||||
egl = dependency('egl', required: false, disabler: true)
|
|
||||||
glesv2 = dependency('glesv2', required: false, disabler: true)
|
|
||||||
gbm = dependency('gbm', required: false, disabler: true)
|
|
||||||
cairo = dependency('cairo', required: false, disabler: true)
|
cairo = dependency('cairo', required: false, disabler: true)
|
||||||
# These versions correspond to ffmpeg 4.0
|
|
||||||
libavutil = dependency('libavutil', version: '>=56.14.100', required: false, disabler: true)
|
|
||||||
libavcodec = dependency('libavcodec', version: '>=58.18.100', required: false, disabler: true)
|
|
||||||
libavformat = dependency('libavformat', version: '>=58.12.100', required: false, disabler: true)
|
|
||||||
# Only needed for drm_fourcc.h
|
# Only needed for drm_fourcc.h
|
||||||
libdrm_header = dependency('libdrm').partial_dependency(compile_args: true, includes: true)
|
libdrm_header = dependency('libdrm').partial_dependency(compile_args: true, includes: true)
|
||||||
|
wayland_client = dependency('wayland-client', required: false, disabler: true)
|
||||||
# epoll is a separate library in FreeBSD
|
wayland_egl = dependency('wayland-egl', required: false, disabler: true)
|
||||||
if host_machine.system() == 'freebsd'
|
egl = dependency('egl', version: '>= 1.5', required: false, disabler: true)
|
||||||
libepoll = dependency('epoll-shim')
|
glesv2 = dependency('glesv2', required: false, disabler: true)
|
||||||
else
|
|
||||||
libepoll = dependency('', required: false)
|
|
||||||
endif
|
|
||||||
|
|
||||||
if not cc.has_header('libavutil/hwcontext_drm.h', dependencies: libavutil)
|
|
||||||
libavutil = disabler()
|
|
||||||
endif
|
|
||||||
|
|
||||||
compositors = {
|
compositors = {
|
||||||
'simple': {
|
'simple': {
|
||||||
@ -62,131 +43,13 @@ compositors = {
|
|||||||
'src': 'cairo-buffer.c',
|
'src': 'cairo-buffer.c',
|
||||||
'dep': cairo,
|
'dep': cairo,
|
||||||
},
|
},
|
||||||
}
|
'embedded': {
|
||||||
|
'src': [
|
||||||
clients = {
|
'embedded.c',
|
||||||
'idle-inhibit': {
|
protocols_code['xdg-shell'],
|
||||||
'src': ['idle-inhibit.c', 'egl_common.c'],
|
protocols_client_header['xdg-shell'],
|
||||||
'dep': [wayland_egl, egl, glesv2],
|
|
||||||
'proto': [
|
|
||||||
'idle-inhibit-unstable-v1',
|
|
||||||
'xdg-shell',
|
|
||||||
],
|
|
||||||
},
|
|
||||||
'keyboard-shortcuts-inhibit': {
|
|
||||||
'src': ['keyboard-shortcuts-inhibit.c', 'egl_common.c'],
|
|
||||||
'dep': [wayland_egl, wayland_cursor, egl, glesv2],
|
|
||||||
'proto': [
|
|
||||||
'keyboard-shortcuts-inhibit-unstable-v1',
|
|
||||||
'xdg-shell',
|
|
||||||
],
|
|
||||||
},
|
|
||||||
'layer-shell': {
|
|
||||||
'src': ['layer-shell.c', 'egl_common.c'],
|
|
||||||
'dep': [wayland_egl, wayland_cursor, egl, glesv2],
|
|
||||||
'proto': [
|
|
||||||
'wlr-layer-shell-unstable-v1',
|
|
||||||
'xdg-shell',
|
|
||||||
],
|
|
||||||
},
|
|
||||||
'input-inhibitor': {
|
|
||||||
'src': ['input-inhibitor.c', 'egl_common.c'],
|
|
||||||
'dep': [wayland_egl, wayland_cursor, egl, glesv2],
|
|
||||||
'proto': [
|
|
||||||
'wlr-input-inhibitor-unstable-v1',
|
|
||||||
'xdg-shell',
|
|
||||||
],
|
|
||||||
},
|
|
||||||
'gamma-control': {
|
|
||||||
'src': 'gamma-control.c',
|
|
||||||
'dep': [wayland_cursor, math],
|
|
||||||
'proto': ['wlr-gamma-control-unstable-v1'],
|
|
||||||
},
|
|
||||||
'output-power-management': {
|
|
||||||
'src': 'output-power-management.c',
|
|
||||||
'dep': [wayland_client],
|
|
||||||
'proto': ['wlr-output-power-management-unstable-v1'],
|
|
||||||
},
|
|
||||||
'pointer-constraints': {
|
|
||||||
'src': ['pointer-constraints.c', 'egl_common.c'],
|
|
||||||
'dep': [wayland_egl, egl, glesv2],
|
|
||||||
'proto': [
|
|
||||||
'pointer-constraints-unstable-v1',
|
|
||||||
'xdg-shell',
|
|
||||||
],
|
|
||||||
},
|
|
||||||
'relative-pointer': {
|
|
||||||
'src': ['relative-pointer-unstable-v1.c', 'egl_common.c'],
|
|
||||||
'dep': [wayland_egl, egl, glesv2],
|
|
||||||
'proto': [
|
|
||||||
'pointer-constraints-unstable-v1',
|
|
||||||
'relative-pointer-unstable-v1',
|
|
||||||
'xdg-shell',
|
|
||||||
],
|
|
||||||
},
|
|
||||||
'dmabuf-capture': {
|
|
||||||
'src': 'dmabuf-capture.c',
|
|
||||||
'dep': [
|
|
||||||
libavcodec,
|
|
||||||
libavformat,
|
|
||||||
libavutil,
|
|
||||||
drm,
|
|
||||||
threads,
|
|
||||||
],
|
|
||||||
'proto': ['wlr-export-dmabuf-unstable-v1'],
|
|
||||||
},
|
|
||||||
'screencopy': {
|
|
||||||
'src': 'screencopy.c',
|
|
||||||
'dep': [libpng, rt],
|
|
||||||
'proto': ['wlr-screencopy-unstable-v1'],
|
|
||||||
},
|
|
||||||
'screencopy-dmabuf': {
|
|
||||||
'src': 'screencopy-dmabuf.c',
|
|
||||||
'dep': [libpng, rt, gbm, drm],
|
|
||||||
'proto': [
|
|
||||||
'wlr-screencopy-unstable-v1',
|
|
||||||
'linux-dmabuf-unstable-v1',
|
|
||||||
],
|
|
||||||
},
|
|
||||||
'toplevel-decoration': {
|
|
||||||
'src': ['toplevel-decoration.c', 'egl_common.c'],
|
|
||||||
'dep': [wayland_egl, egl, glesv2],
|
|
||||||
'proto': [
|
|
||||||
'xdg-decoration-unstable-v1',
|
|
||||||
'xdg-shell',
|
|
||||||
],
|
|
||||||
},
|
|
||||||
'input-method': {
|
|
||||||
'src': 'input-method.c',
|
|
||||||
'dep': [wayland_egl, libepoll],
|
|
||||||
'proto': [
|
|
||||||
'input-method-unstable-v2',
|
|
||||||
'text-input-unstable-v3',
|
|
||||||
'xdg-shell',
|
|
||||||
],
|
|
||||||
},
|
|
||||||
'text-input': {
|
|
||||||
'src': ['text-input.c', 'egl_common.c'],
|
|
||||||
'dep': [wayland_egl, wayland_cursor, egl, glesv2],
|
|
||||||
'proto': [
|
|
||||||
'text-input-unstable-v3',
|
|
||||||
'xdg-shell',
|
|
||||||
],
|
|
||||||
},
|
|
||||||
'foreign-toplevel': {
|
|
||||||
'src': 'foreign-toplevel.c',
|
|
||||||
'proto': ['wlr-foreign-toplevel-management-unstable-v1'],
|
|
||||||
},
|
|
||||||
'virtual-pointer': {
|
|
||||||
'src': 'virtual-pointer.c',
|
|
||||||
'proto': ['wlr-virtual-pointer-unstable-v1'],
|
|
||||||
},
|
|
||||||
'input-method-keyboard-grab': {
|
|
||||||
'src': 'input-method-keyboard-grab.c',
|
|
||||||
'dep': xkbcommon,
|
|
||||||
'proto': [
|
|
||||||
'input-method-unstable-v2',
|
|
||||||
],
|
],
|
||||||
|
'dep': [wayland_client, wayland_egl, egl, glesv2],
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -203,29 +66,3 @@ foreach name, info : compositors
|
|||||||
build_by_default: get_option('examples'),
|
build_by_default: get_option('examples'),
|
||||||
)
|
)
|
||||||
endforeach
|
endforeach
|
||||||
|
|
||||||
foreach name, info : clients
|
|
||||||
extra_src = []
|
|
||||||
foreach p : info.get('proto')
|
|
||||||
extra_src += protocols_code[p]
|
|
||||||
extra_src += protocols_client_header[p]
|
|
||||||
endforeach
|
|
||||||
|
|
||||||
executable(
|
|
||||||
name,
|
|
||||||
[info.get('src'), extra_src],
|
|
||||||
dependencies: [wayland_client, info.get('dep', [])],
|
|
||||||
build_by_default: get_option('examples'),
|
|
||||||
)
|
|
||||||
endforeach
|
|
||||||
|
|
||||||
executable(
|
|
||||||
'embedded',
|
|
||||||
[
|
|
||||||
'embedded.c',
|
|
||||||
'egl_common.c',
|
|
||||||
protocols_code['xdg-shell'],
|
|
||||||
protocols_client_header['xdg-shell'],
|
|
||||||
],
|
|
||||||
dependencies: [wlroots, wayland_client, wayland_egl, egl, glesv2],
|
|
||||||
)
|
|
||||||
|
@ -1,144 +0,0 @@
|
|||||||
#define _POSIX_C_SOURCE 200809L
|
|
||||||
#include <errno.h>
|
|
||||||
#include <stdbool.h>
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include <sys/mman.h>
|
|
||||||
#include <sys/types.h>
|
|
||||||
#include <unistd.h>
|
|
||||||
#include <wayland-client-protocol.h>
|
|
||||||
#include <wayland-client.h>
|
|
||||||
#include "wlr-output-power-management-unstable-v1-client-protocol.h"
|
|
||||||
|
|
||||||
struct output {
|
|
||||||
struct wl_output *wl_output;
|
|
||||||
struct zwlr_output_power_v1 *output_power;
|
|
||||||
struct wl_list link;
|
|
||||||
};
|
|
||||||
|
|
||||||
static struct wl_list outputs;
|
|
||||||
static struct zwlr_output_power_manager_v1 *output_power_manager = NULL;
|
|
||||||
|
|
||||||
static void output_power_handle_mode(void *data,
|
|
||||||
struct zwlr_output_power_v1 *output_power,
|
|
||||||
enum zwlr_output_power_v1_mode mode) {
|
|
||||||
struct output *output = data;
|
|
||||||
|
|
||||||
switch (mode) {
|
|
||||||
case ZWLR_OUTPUT_POWER_V1_MODE_OFF:
|
|
||||||
printf("Output %p disabled\n", output);
|
|
||||||
break;
|
|
||||||
case ZWLR_OUTPUT_POWER_V1_MODE_ON:
|
|
||||||
printf("Output %p enabled\n", output);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void output_power_handle_failed(void *data,
|
|
||||||
struct zwlr_output_power_v1 *output_power) {
|
|
||||||
fprintf(stderr, "failed to set output power mode\n");
|
|
||||||
exit(EXIT_FAILURE);
|
|
||||||
}
|
|
||||||
|
|
||||||
static const struct zwlr_output_power_v1_listener output_power_listener = {
|
|
||||||
.mode = output_power_handle_mode,
|
|
||||||
.failed = output_power_handle_failed,
|
|
||||||
};
|
|
||||||
|
|
||||||
static void registry_handle_global(void *data, struct wl_registry *registry,
|
|
||||||
uint32_t name, const char *interface, uint32_t version) {
|
|
||||||
if (strcmp(interface, wl_output_interface.name) == 0) {
|
|
||||||
struct output *output = calloc(1, sizeof(*output));
|
|
||||||
output->wl_output = wl_registry_bind(registry, name,
|
|
||||||
&wl_output_interface, 1);
|
|
||||||
wl_list_insert(&outputs, &output->link);
|
|
||||||
} else if (strcmp(interface,
|
|
||||||
zwlr_output_power_manager_v1_interface.name) == 0) {
|
|
||||||
output_power_manager = wl_registry_bind(registry, name,
|
|
||||||
&zwlr_output_power_manager_v1_interface, 1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void registry_handle_global_remove(void *data,
|
|
||||||
struct wl_registry *registry, uint32_t name) {
|
|
||||||
// Who cares?
|
|
||||||
}
|
|
||||||
|
|
||||||
static const struct wl_registry_listener registry_listener = {
|
|
||||||
.global = registry_handle_global,
|
|
||||||
.global_remove = registry_handle_global_remove,
|
|
||||||
};
|
|
||||||
|
|
||||||
static const char usage[] = "usage: output-power-management [options...]\n"
|
|
||||||
" -h: show this help message\n"
|
|
||||||
" -e: turn outputs on\n"
|
|
||||||
" -d: turn outputs off\n"
|
|
||||||
" -w: continuously watch for power mode changes\n";
|
|
||||||
|
|
||||||
int main(int argc, char *argv[]) {
|
|
||||||
wl_list_init(&outputs);
|
|
||||||
|
|
||||||
int opt;
|
|
||||||
enum zwlr_output_power_v1_mode mode =
|
|
||||||
ZWLR_OUTPUT_POWER_V1_MODE_ON;
|
|
||||||
bool watch_mode = false;
|
|
||||||
while ((opt = getopt(argc, argv, "edhw")) != -1) {
|
|
||||||
switch (opt) {
|
|
||||||
case 'e':
|
|
||||||
mode = ZWLR_OUTPUT_POWER_V1_MODE_ON;
|
|
||||||
break;
|
|
||||||
case 'd':
|
|
||||||
mode = ZWLR_OUTPUT_POWER_V1_MODE_OFF;
|
|
||||||
break;
|
|
||||||
case 'w':
|
|
||||||
watch_mode = true;
|
|
||||||
break;
|
|
||||||
case 'h':
|
|
||||||
default:
|
|
||||||
fprintf(stderr, usage);
|
|
||||||
return opt == 'h' ? EXIT_SUCCESS : EXIT_FAILURE;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
struct wl_display *display = wl_display_connect(NULL);
|
|
||||||
if (display == NULL) {
|
|
||||||
fprintf(stderr, "failed to create display\n");
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct wl_registry *registry = wl_display_get_registry(display);
|
|
||||||
wl_registry_add_listener(registry, ®istry_listener, NULL);
|
|
||||||
wl_display_roundtrip(display);
|
|
||||||
|
|
||||||
if (output_power_manager == NULL) {
|
|
||||||
fprintf(stderr,
|
|
||||||
"compositor doesn't support wlr-output-power-management-unstable-v1\n");
|
|
||||||
return EXIT_FAILURE;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct output *output;
|
|
||||||
wl_list_for_each(output, &outputs, link) {
|
|
||||||
output->output_power = zwlr_output_power_manager_v1_get_output_power(
|
|
||||||
output_power_manager, output->wl_output);
|
|
||||||
zwlr_output_power_v1_add_listener(output->output_power,
|
|
||||||
&output_power_listener, output);
|
|
||||||
}
|
|
||||||
wl_display_roundtrip(display);
|
|
||||||
|
|
||||||
wl_list_for_each(output, &outputs, link) {
|
|
||||||
zwlr_output_power_v1_set_mode(output->output_power,
|
|
||||||
mode);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!watch_mode) {
|
|
||||||
wl_display_roundtrip(display);
|
|
||||||
return EXIT_SUCCESS;
|
|
||||||
}
|
|
||||||
|
|
||||||
while (wl_display_dispatch(display) != -1) {
|
|
||||||
// nothing to see here, please move along
|
|
||||||
}
|
|
||||||
|
|
||||||
return EXIT_SUCCESS;
|
|
||||||
}
|
|
@ -1,258 +0,0 @@
|
|||||||
#include <GLES2/gl2.h>
|
|
||||||
#include <linux/input-event-codes.h>
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include <wayland-client.h>
|
|
||||||
#include <wayland-egl.h>
|
|
||||||
#include "egl_common.h"
|
|
||||||
#include "xdg-shell-client-protocol.h"
|
|
||||||
#include "pointer-constraints-unstable-v1-client-protocol.h"
|
|
||||||
|
|
||||||
static int width = 512, height = 512;
|
|
||||||
|
|
||||||
static struct wl_compositor *compositor = NULL;
|
|
||||||
static struct wl_seat *seat = NULL;
|
|
||||||
static struct xdg_wm_base *wm_base = NULL;
|
|
||||||
static struct zwp_pointer_constraints_v1 *pointer_constraints = NULL;
|
|
||||||
|
|
||||||
struct wl_egl_window *egl_window;
|
|
||||||
struct wlr_egl_surface *egl_surface;
|
|
||||||
struct zwp_locked_pointer_v1* locked_pointer;
|
|
||||||
struct zwp_confined_pointer_v1* confined_pointer;
|
|
||||||
|
|
||||||
enum {
|
|
||||||
REGION_TYPE_NONE,
|
|
||||||
REGION_TYPE_DISJOINT,
|
|
||||||
REGION_TYPE_JOINT,
|
|
||||||
REGION_TYPE_MAX
|
|
||||||
} region_type = REGION_TYPE_NONE;
|
|
||||||
|
|
||||||
struct wl_region *regions[3];
|
|
||||||
|
|
||||||
static void draw(void) {
|
|
||||||
eglMakeCurrent(egl_display, egl_surface, egl_surface, egl_context);
|
|
||||||
|
|
||||||
float color[] = {1.0, 1.0, 0.0, 1.0};
|
|
||||||
|
|
||||||
glViewport(0, 0, width, height);
|
|
||||||
glClearColor(color[0], color[1], color[2], 1.0);
|
|
||||||
glClear(GL_COLOR_BUFFER_BIT);
|
|
||||||
|
|
||||||
eglSwapBuffers(egl_display, egl_surface);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void pointer_handle_button(void *data, struct wl_pointer *pointer,
|
|
||||||
uint32_t serial, uint32_t time, uint32_t button, uint32_t state_w) {
|
|
||||||
struct wl_surface *surface = data;
|
|
||||||
|
|
||||||
if (button == BTN_LEFT && state_w == WL_POINTER_BUTTON_STATE_PRESSED) {
|
|
||||||
region_type = (region_type + 1) % REGION_TYPE_MAX;
|
|
||||||
|
|
||||||
if (locked_pointer) {
|
|
||||||
zwp_locked_pointer_v1_set_region(locked_pointer,
|
|
||||||
regions[region_type]);
|
|
||||||
} else if (confined_pointer) {
|
|
||||||
zwp_confined_pointer_v1_set_region(confined_pointer,
|
|
||||||
regions[region_type]);
|
|
||||||
}
|
|
||||||
|
|
||||||
wl_surface_commit(surface);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void pointer_handle_enter(void *data, struct wl_pointer *wl_pointer,
|
|
||||||
uint32_t serial, struct wl_surface *surface,
|
|
||||||
wl_fixed_t surface_x, wl_fixed_t surface_y) {
|
|
||||||
// This space intentionally left blank
|
|
||||||
}
|
|
||||||
|
|
||||||
static void pointer_handle_leave(void *data, struct wl_pointer *wl_pointer,
|
|
||||||
uint32_t serial, struct wl_surface *surface) {
|
|
||||||
// This space intentionally left blank
|
|
||||||
}
|
|
||||||
|
|
||||||
static void pointer_handle_motion(void *data, struct wl_pointer *wl_pointer,
|
|
||||||
uint32_t time, wl_fixed_t surface_x, wl_fixed_t surface_y) {
|
|
||||||
// This space intentionally left blank
|
|
||||||
}
|
|
||||||
|
|
||||||
static void pointer_handle_axis(void *data, struct wl_pointer *wl_pointer,
|
|
||||||
uint32_t time, uint32_t axis, wl_fixed_t value) {
|
|
||||||
// This space intentionally left blank
|
|
||||||
}
|
|
||||||
|
|
||||||
static void pointer_handle_frame(void *data, struct wl_pointer *wl_pointer) {
|
|
||||||
// This space intentionally left blank
|
|
||||||
}
|
|
||||||
|
|
||||||
static void pointer_handle_axis_source(void *data,
|
|
||||||
struct wl_pointer *wl_pointer, uint32_t axis_source) {
|
|
||||||
// This space intentionally left blank
|
|
||||||
}
|
|
||||||
|
|
||||||
static void pointer_handle_axis_stop(void *data,
|
|
||||||
struct wl_pointer *wl_pointer, uint32_t time, uint32_t axis) {
|
|
||||||
// This space intentionally left blank
|
|
||||||
}
|
|
||||||
|
|
||||||
static void pointer_handle_axis_discrete(void *data,
|
|
||||||
struct wl_pointer *wl_pointer, uint32_t axis, int32_t discrete) {
|
|
||||||
// This space intentionally left blank
|
|
||||||
}
|
|
||||||
|
|
||||||
static const struct wl_pointer_listener pointer_listener = {
|
|
||||||
.enter = pointer_handle_enter,
|
|
||||||
.leave = pointer_handle_leave,
|
|
||||||
.motion = pointer_handle_motion,
|
|
||||||
.button = pointer_handle_button,
|
|
||||||
.axis = pointer_handle_axis,
|
|
||||||
.frame = pointer_handle_frame,
|
|
||||||
.axis_source = pointer_handle_axis_source,
|
|
||||||
.axis_stop = pointer_handle_axis_stop,
|
|
||||||
.axis_discrete = pointer_handle_axis_discrete,
|
|
||||||
};
|
|
||||||
|
|
||||||
static void xdg_surface_handle_configure(void *data,
|
|
||||||
struct xdg_surface *xdg_surface, uint32_t serial) {
|
|
||||||
xdg_surface_ack_configure(xdg_surface, serial);
|
|
||||||
wl_egl_window_resize(egl_window, width, height, 0, 0);
|
|
||||||
draw();
|
|
||||||
}
|
|
||||||
|
|
||||||
static const struct xdg_surface_listener xdg_surface_listener = {
|
|
||||||
.configure = xdg_surface_handle_configure,
|
|
||||||
};
|
|
||||||
|
|
||||||
static void xdg_toplevel_handle_configure(void *data,
|
|
||||||
struct xdg_toplevel *xdg_toplevel, int32_t w, int32_t h,
|
|
||||||
struct wl_array *states) {
|
|
||||||
width = w;
|
|
||||||
height = h;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void xdg_toplevel_handle_close(void *data,
|
|
||||||
struct xdg_toplevel *xdg_toplevel) {
|
|
||||||
exit(EXIT_SUCCESS);
|
|
||||||
}
|
|
||||||
|
|
||||||
static const struct xdg_toplevel_listener xdg_toplevel_listener = {
|
|
||||||
.configure = xdg_toplevel_handle_configure,
|
|
||||||
.close = xdg_toplevel_handle_close,
|
|
||||||
};
|
|
||||||
|
|
||||||
static void handle_global(void *data, struct wl_registry *registry,
|
|
||||||
uint32_t name, const char *interface, uint32_t version) {
|
|
||||||
if (strcmp(interface, wl_compositor_interface.name) == 0) {
|
|
||||||
compositor = wl_registry_bind(registry, name,
|
|
||||||
&wl_compositor_interface, 1);
|
|
||||||
} else if (strcmp(interface, xdg_wm_base_interface.name) == 0) {
|
|
||||||
wm_base = wl_registry_bind(registry, name, &xdg_wm_base_interface, 1);
|
|
||||||
} else if (strcmp(interface, wl_seat_interface.name) == 0) {
|
|
||||||
seat = wl_registry_bind(registry, name, &wl_seat_interface, version);
|
|
||||||
} else if (strcmp(interface,
|
|
||||||
zwp_pointer_constraints_v1_interface.name) == 0) {
|
|
||||||
pointer_constraints = wl_registry_bind(registry, name,
|
|
||||||
&zwp_pointer_constraints_v1_interface, version);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static const struct wl_registry_listener registry_listener = {
|
|
||||||
.global = handle_global,
|
|
||||||
.global_remove = NULL,
|
|
||||||
};
|
|
||||||
|
|
||||||
int main(int argc, char **argv) {
|
|
||||||
if (argc != 4) {
|
|
||||||
goto invalid_args;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool lock;
|
|
||||||
if (strcmp(argv[1], "lock") == 0) {
|
|
||||||
lock = true;
|
|
||||||
} else if (strcmp(argv[1], "confine") == 0) {
|
|
||||||
lock = false;
|
|
||||||
} else {
|
|
||||||
goto invalid_args;
|
|
||||||
}
|
|
||||||
|
|
||||||
enum zwp_pointer_constraints_v1_lifetime lifetime;
|
|
||||||
if (strcmp(argv[2], "oneshot") == 0) {
|
|
||||||
lifetime = ZWP_POINTER_CONSTRAINTS_V1_LIFETIME_ONESHOT;
|
|
||||||
} else if (strcmp(argv[2], "persistent") == 0) {
|
|
||||||
lifetime = ZWP_POINTER_CONSTRAINTS_V1_LIFETIME_PERSISTENT;
|
|
||||||
} else {
|
|
||||||
goto invalid_args;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (strcmp(argv[3], "no-region") == 0) {
|
|
||||||
region_type = REGION_TYPE_NONE;
|
|
||||||
} else if (strcmp(argv[3], "disjoint-region") == 0) {
|
|
||||||
region_type = REGION_TYPE_DISJOINT;
|
|
||||||
} else if (strcmp(argv[3], "joint-region") == 0) {
|
|
||||||
region_type = REGION_TYPE_JOINT;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct wl_display *display = wl_display_connect(NULL);
|
|
||||||
|
|
||||||
struct wl_registry *registry = wl_display_get_registry(display);
|
|
||||||
wl_registry_add_listener(registry, ®istry_listener, NULL);
|
|
||||||
wl_display_roundtrip(display);
|
|
||||||
|
|
||||||
struct wl_region *disjoint_region = wl_compositor_create_region(compositor);
|
|
||||||
wl_region_add(disjoint_region, 0, 0, 255, 256);
|
|
||||||
wl_region_add(disjoint_region, 257, 0, 255, 256);
|
|
||||||
regions[REGION_TYPE_DISJOINT] = disjoint_region;
|
|
||||||
|
|
||||||
struct wl_region *joint_region = wl_compositor_create_region(compositor);
|
|
||||||
wl_region_add(joint_region, 0, 0, 256, 256);
|
|
||||||
wl_region_add(joint_region, 256, 0, 256, 256);
|
|
||||||
wl_region_add(joint_region, 256, 256, 256, 256);
|
|
||||||
regions[REGION_TYPE_JOINT] = joint_region;
|
|
||||||
|
|
||||||
egl_init(display);
|
|
||||||
|
|
||||||
struct wl_surface *surface = wl_compositor_create_surface(compositor);
|
|
||||||
struct xdg_surface *xdg_surface =
|
|
||||||
xdg_wm_base_get_xdg_surface(wm_base, 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);
|
|
||||||
|
|
||||||
struct wl_pointer *pointer = wl_seat_get_pointer(seat);
|
|
||||||
wl_pointer_add_listener(pointer, &pointer_listener, surface);
|
|
||||||
|
|
||||||
if (lock) {
|
|
||||||
locked_pointer = zwp_pointer_constraints_v1_lock_pointer(
|
|
||||||
pointer_constraints, surface, pointer,
|
|
||||||
regions[region_type], lifetime);
|
|
||||||
|
|
||||||
zwp_locked_pointer_v1_set_cursor_position_hint(locked_pointer,
|
|
||||||
wl_fixed_from_int(128), wl_fixed_from_int(128));
|
|
||||||
} else {
|
|
||||||
confined_pointer = zwp_pointer_constraints_v1_confine_pointer(
|
|
||||||
pointer_constraints, surface, pointer,
|
|
||||||
regions[region_type], lifetime);
|
|
||||||
}
|
|
||||||
|
|
||||||
wl_surface_commit(surface);
|
|
||||||
|
|
||||||
egl_window = wl_egl_window_create(surface, width, height);
|
|
||||||
egl_surface = eglCreatePlatformWindowSurfaceEXT(
|
|
||||||
egl_display, egl_config, egl_window, NULL);
|
|
||||||
|
|
||||||
wl_display_roundtrip(display);
|
|
||||||
|
|
||||||
draw();
|
|
||||||
|
|
||||||
while (wl_display_dispatch(display) != -1) {}
|
|
||||||
|
|
||||||
return EXIT_SUCCESS;
|
|
||||||
|
|
||||||
invalid_args:
|
|
||||||
fprintf(stderr, "pointer-constraints <lock | confine> "
|
|
||||||
"<oneshot | persistent> "
|
|
||||||
"<no-region | disjoint-rejoin | joint-region>\n");
|
|
||||||
return EXIT_FAILURE;
|
|
||||||
}
|
|
@ -1,360 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright © 2008 Kristian Høgsberg
|
|
||||||
* Copyright © 2020 Andri Yngvason
|
|
||||||
*
|
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a
|
|
||||||
* copy of this software and associated documentation files (the "Software"),
|
|
||||||
* to deal in the Software without restriction, including without limitation
|
|
||||||
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
|
||||||
* and/or sell copies of the Software, and to permit persons to whom the
|
|
||||||
* Software is furnished to do so, subject to the following conditions:
|
|
||||||
*
|
|
||||||
* The above copyright notice and this permission notice (including the next
|
|
||||||
* paragraph) shall be included in all copies or substantial portions of the
|
|
||||||
* Software.
|
|
||||||
*
|
|
||||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
|
||||||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
|
||||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
|
||||||
* DEALINGS IN THE SOFTWARE.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#define _POSIX_C_SOURCE 200112L
|
|
||||||
#include <errno.h>
|
|
||||||
#include <fcntl.h>
|
|
||||||
#include <limits.h>
|
|
||||||
#include <png.h>
|
|
||||||
#include <stdbool.h>
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include <sys/mman.h>
|
|
||||||
#include <sys/param.h>
|
|
||||||
#include <sys/stat.h>
|
|
||||||
#include <sys/wait.h>
|
|
||||||
#include <unistd.h>
|
|
||||||
#include <assert.h>
|
|
||||||
#include <gbm.h>
|
|
||||||
#include <xf86drm.h>
|
|
||||||
#include <drm_fourcc.h>
|
|
||||||
#include <wayland-client-protocol.h>
|
|
||||||
#include "wlr-screencopy-unstable-v1-client-protocol.h"
|
|
||||||
#include "linux-dmabuf-unstable-v1-client-protocol.h"
|
|
||||||
|
|
||||||
struct format {
|
|
||||||
uint32_t format;
|
|
||||||
bool is_bgr;
|
|
||||||
};
|
|
||||||
|
|
||||||
static int drm_fd = -1;
|
|
||||||
static struct gbm_device *gbm_device = NULL;
|
|
||||||
|
|
||||||
static struct zwp_linux_dmabuf_v1 *dmabuf = NULL;
|
|
||||||
static struct zwlr_screencopy_manager_v1 *screencopy_manager = NULL;
|
|
||||||
static struct wl_output *output = NULL;
|
|
||||||
|
|
||||||
static struct {
|
|
||||||
struct gbm_bo *bo;
|
|
||||||
struct wl_buffer *wl_buffer;
|
|
||||||
int width, height;
|
|
||||||
uint32_t format;
|
|
||||||
bool y_invert;
|
|
||||||
} buffer;
|
|
||||||
|
|
||||||
bool buffer_copy_done = false;
|
|
||||||
static bool have_linux_dmabuf = false;
|
|
||||||
|
|
||||||
// wl_shm_format describes little-endian formats, libpng uses big-endian
|
|
||||||
// formats (so Wayland's ABGR is libpng's RGBA).
|
|
||||||
static const struct format formats[] = {
|
|
||||||
{DRM_FORMAT_XRGB8888, true},
|
|
||||||
{DRM_FORMAT_ARGB8888, true},
|
|
||||||
{DRM_FORMAT_XBGR8888, false},
|
|
||||||
{DRM_FORMAT_ABGR8888, false},
|
|
||||||
};
|
|
||||||
|
|
||||||
static bool find_render_node(char *node, size_t node_size) {
|
|
||||||
bool r = false;
|
|
||||||
drmDevice *devices[64];
|
|
||||||
|
|
||||||
int n = drmGetDevices2(0, devices, sizeof(devices) / sizeof(devices[0]));
|
|
||||||
for (int i = 0; i < n; ++i) {
|
|
||||||
drmDevice *dev = devices[i];
|
|
||||||
if (!(dev->available_nodes & (1 << DRM_NODE_RENDER))) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
snprintf(node, node_size, "%s", dev->nodes[DRM_NODE_RENDER]);
|
|
||||||
r = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
drmFreeDevices(devices, n);
|
|
||||||
return r;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void dmabuf_created(void *data,
|
|
||||||
struct zwp_linux_buffer_params_v1 *params,
|
|
||||||
struct wl_buffer *wl_buffer) {
|
|
||||||
buffer.wl_buffer = wl_buffer;
|
|
||||||
|
|
||||||
zwlr_screencopy_frame_v1_copy(data, buffer.wl_buffer);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void dmabuf_failed(void *data,
|
|
||||||
struct zwp_linux_buffer_params_v1 *params) {
|
|
||||||
fprintf(stderr, "Failed to create dmabuf\n");
|
|
||||||
exit(EXIT_FAILURE);
|
|
||||||
}
|
|
||||||
|
|
||||||
static const struct zwp_linux_buffer_params_v1_listener params_listener = {
|
|
||||||
.created = dmabuf_created,
|
|
||||||
.failed = dmabuf_failed,
|
|
||||||
};
|
|
||||||
|
|
||||||
static void frame_handle_buffer(void *data,
|
|
||||||
struct zwlr_screencopy_frame_v1 *frame, uint32_t wl_format,
|
|
||||||
uint32_t width, uint32_t height, uint32_t stride) {
|
|
||||||
// Not implemented
|
|
||||||
}
|
|
||||||
|
|
||||||
static void frame_handle_linux_dmabuf(void *data,
|
|
||||||
struct zwlr_screencopy_frame_v1 *frame, uint32_t fourcc,
|
|
||||||
uint32_t width, uint32_t height) {
|
|
||||||
buffer.width = width;
|
|
||||||
buffer.height = height;
|
|
||||||
buffer.format = fourcc;
|
|
||||||
have_linux_dmabuf = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void frame_handle_buffer_done(void *data,
|
|
||||||
struct zwlr_screencopy_frame_v1 *frame) {
|
|
||||||
assert(!buffer.bo);
|
|
||||||
|
|
||||||
if (!have_linux_dmabuf) {
|
|
||||||
fprintf(stderr, "linux-dmabuf is not supported\n");
|
|
||||||
exit(EXIT_FAILURE);
|
|
||||||
}
|
|
||||||
|
|
||||||
buffer.bo = gbm_bo_create(gbm_device, buffer.width, buffer.height,
|
|
||||||
buffer.format,
|
|
||||||
GBM_BO_USE_LINEAR | GBM_BO_USE_RENDERING);
|
|
||||||
if (buffer.bo == NULL) {
|
|
||||||
fprintf(stderr, "failed to create GBM buffer object\n");
|
|
||||||
exit(EXIT_FAILURE);
|
|
||||||
}
|
|
||||||
|
|
||||||
struct zwp_linux_buffer_params_v1 *params;
|
|
||||||
params = zwp_linux_dmabuf_v1_create_params(dmabuf);
|
|
||||||
assert(params);
|
|
||||||
|
|
||||||
int fd = gbm_bo_get_fd(buffer.bo);
|
|
||||||
uint32_t off = gbm_bo_get_offset(buffer.bo, 0);
|
|
||||||
uint32_t bo_stride = gbm_bo_get_stride(buffer.bo);
|
|
||||||
uint64_t mod = gbm_bo_get_modifier(buffer.bo);
|
|
||||||
zwp_linux_buffer_params_v1_add(params, fd, 0, off, bo_stride, mod >> 32,
|
|
||||||
mod & 0xffffffff);
|
|
||||||
|
|
||||||
zwp_linux_buffer_params_v1_add_listener(params, ¶ms_listener, frame);
|
|
||||||
|
|
||||||
zwp_linux_buffer_params_v1_create(params, buffer.width, buffer.height,
|
|
||||||
buffer.format, /* flags */ 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void frame_handle_flags(void *data,
|
|
||||||
struct zwlr_screencopy_frame_v1 *frame, uint32_t flags) {
|
|
||||||
buffer.y_invert = flags & ZWLR_SCREENCOPY_FRAME_V1_FLAGS_Y_INVERT;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void frame_handle_ready(void *data,
|
|
||||||
struct zwlr_screencopy_frame_v1 *frame, uint32_t tv_sec_hi,
|
|
||||||
uint32_t tv_sec_lo, uint32_t tv_nsec) {
|
|
||||||
buffer_copy_done = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void frame_handle_failed(void *data,
|
|
||||||
struct zwlr_screencopy_frame_v1 *frame) {
|
|
||||||
fprintf(stderr, "failed to copy frame\n");
|
|
||||||
exit(EXIT_FAILURE);
|
|
||||||
}
|
|
||||||
|
|
||||||
static const struct zwlr_screencopy_frame_v1_listener frame_listener = {
|
|
||||||
.buffer = frame_handle_buffer,
|
|
||||||
.linux_dmabuf = frame_handle_linux_dmabuf,
|
|
||||||
.buffer_done = frame_handle_buffer_done,
|
|
||||||
.flags = frame_handle_flags,
|
|
||||||
.ready = frame_handle_ready,
|
|
||||||
.failed = frame_handle_failed,
|
|
||||||
};
|
|
||||||
|
|
||||||
static void dmabuf_format(void *data,
|
|
||||||
struct zwp_linux_dmabuf_v1 *zwp_linux_dmabuf, uint32_t format) {
|
|
||||||
// deprecated
|
|
||||||
}
|
|
||||||
|
|
||||||
static void dmabuf_modifier(void *data,
|
|
||||||
struct zwp_linux_dmabuf_v1 *zwp_linux_dmabuf, uint32_t format,
|
|
||||||
uint32_t modifier_hi, uint32_t modifier_lo) {
|
|
||||||
// TODO?
|
|
||||||
}
|
|
||||||
|
|
||||||
static const struct zwp_linux_dmabuf_v1_listener dmabuf_listener = {
|
|
||||||
.format = dmabuf_format,
|
|
||||||
.modifier = dmabuf_modifier,
|
|
||||||
};
|
|
||||||
|
|
||||||
static void handle_global(void *data, struct wl_registry *registry,
|
|
||||||
uint32_t name, const char *interface, uint32_t version) {
|
|
||||||
if (strcmp(interface, wl_output_interface.name) == 0 && output == NULL) {
|
|
||||||
output = wl_registry_bind(registry, name, &wl_output_interface, 1);
|
|
||||||
} else if (strcmp(interface, zwp_linux_dmabuf_v1_interface.name) == 0) {
|
|
||||||
dmabuf = wl_registry_bind(registry, name,
|
|
||||||
&zwp_linux_dmabuf_v1_interface, 3);
|
|
||||||
zwp_linux_dmabuf_v1_add_listener(dmabuf, &dmabuf_listener, data);
|
|
||||||
} else if (strcmp(interface,
|
|
||||||
zwlr_screencopy_manager_v1_interface.name) == 0) {
|
|
||||||
screencopy_manager = wl_registry_bind(registry, name,
|
|
||||||
&zwlr_screencopy_manager_v1_interface, 3);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void handle_global_remove(void *data, struct wl_registry *registry,
|
|
||||||
uint32_t name) {
|
|
||||||
// Who cares?
|
|
||||||
}
|
|
||||||
|
|
||||||
static const struct wl_registry_listener registry_listener = {
|
|
||||||
.global = handle_global,
|
|
||||||
.global_remove = handle_global_remove,
|
|
||||||
};
|
|
||||||
|
|
||||||
static void write_image(char *filename, uint32_t format, int width,
|
|
||||||
int height, int stride, bool y_invert, png_bytep data) {
|
|
||||||
const struct format *fmt = NULL;
|
|
||||||
for (size_t i = 0; i < sizeof(formats) / sizeof(formats[0]); ++i) {
|
|
||||||
if (formats[i].format == format) {
|
|
||||||
fmt = &formats[i];
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (fmt == NULL) {
|
|
||||||
fprintf(stderr, "unsupported format %"PRIu32"\n", format);
|
|
||||||
exit(EXIT_FAILURE);
|
|
||||||
}
|
|
||||||
|
|
||||||
FILE *f = fopen(filename, "wb");
|
|
||||||
if (f == NULL) {
|
|
||||||
fprintf(stderr, "failed to open output file\n");
|
|
||||||
exit(EXIT_FAILURE);
|
|
||||||
}
|
|
||||||
|
|
||||||
png_structp png =
|
|
||||||
png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
|
|
||||||
png_infop info = png_create_info_struct(png);
|
|
||||||
|
|
||||||
png_init_io(png, f);
|
|
||||||
|
|
||||||
png_set_IHDR(png, info, width, height, 8, PNG_COLOR_TYPE_RGBA,
|
|
||||||
PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT,
|
|
||||||
PNG_FILTER_TYPE_DEFAULT);
|
|
||||||
|
|
||||||
if (fmt->is_bgr) {
|
|
||||||
png_set_bgr(png);
|
|
||||||
}
|
|
||||||
|
|
||||||
png_write_info(png, info);
|
|
||||||
|
|
||||||
for (size_t i = 0; i < (size_t)height; ++i) {
|
|
||||||
png_bytep row;
|
|
||||||
if (y_invert) {
|
|
||||||
row = data + (height - i - 1) * stride;
|
|
||||||
} else {
|
|
||||||
row = data + i * stride;
|
|
||||||
}
|
|
||||||
png_write_row(png, row);
|
|
||||||
}
|
|
||||||
|
|
||||||
png_write_end(png, NULL);
|
|
||||||
|
|
||||||
png_destroy_write_struct(&png, &info);
|
|
||||||
|
|
||||||
fclose(f);
|
|
||||||
}
|
|
||||||
|
|
||||||
int main(int argc, char *argv[]) {
|
|
||||||
char render_node[256];
|
|
||||||
if (!find_render_node(render_node, sizeof(render_node))) {
|
|
||||||
fprintf(stderr, "Failed to find a DRM render node\n");
|
|
||||||
return EXIT_FAILURE;
|
|
||||||
}
|
|
||||||
|
|
||||||
printf("Using render node: %s\n", render_node);
|
|
||||||
|
|
||||||
drm_fd = open(render_node, O_RDWR);
|
|
||||||
if (drm_fd < 0) {
|
|
||||||
perror("Failed to open drm render node");
|
|
||||||
return EXIT_FAILURE;
|
|
||||||
}
|
|
||||||
|
|
||||||
gbm_device = gbm_create_device(drm_fd);
|
|
||||||
if (!gbm_device) {
|
|
||||||
fprintf(stderr, "Failed to create gbm device\n");
|
|
||||||
return EXIT_FAILURE;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct wl_display *display = wl_display_connect(NULL);
|
|
||||||
if (display == NULL) {
|
|
||||||
perror("failed to create display");
|
|
||||||
return EXIT_FAILURE;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct wl_registry *registry = wl_display_get_registry(display);
|
|
||||||
wl_registry_add_listener(registry, ®istry_listener, NULL);
|
|
||||||
wl_display_roundtrip(display);
|
|
||||||
|
|
||||||
if (dmabuf == NULL) {
|
|
||||||
fprintf(stderr, "compositor is missing linux-dmabuf-unstable-v1\n");
|
|
||||||
return EXIT_FAILURE;
|
|
||||||
}
|
|
||||||
if (screencopy_manager == NULL) {
|
|
||||||
fprintf(stderr, "compositor doesn't support wlr-screencopy-unstable-v1\n");
|
|
||||||
return EXIT_FAILURE;
|
|
||||||
}
|
|
||||||
if (output == NULL) {
|
|
||||||
fprintf(stderr, "no output available\n");
|
|
||||||
return EXIT_FAILURE;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct zwlr_screencopy_frame_v1 *frame =
|
|
||||||
zwlr_screencopy_manager_v1_capture_output(screencopy_manager, 0, output);
|
|
||||||
zwlr_screencopy_frame_v1_add_listener(frame, &frame_listener, NULL);
|
|
||||||
|
|
||||||
while (!buffer_copy_done && wl_display_dispatch(display) != -1) {
|
|
||||||
// This space is intentionally left blank
|
|
||||||
}
|
|
||||||
|
|
||||||
uint32_t stride = 0;
|
|
||||||
void *map_data = NULL;
|
|
||||||
void *data = gbm_bo_map(buffer.bo, 0, 0, buffer.width, buffer.height,
|
|
||||||
GBM_BO_TRANSFER_READ, &stride, &map_data);
|
|
||||||
if (!data) {
|
|
||||||
perror("Failed to map gbm bo");
|
|
||||||
return EXIT_FAILURE;
|
|
||||||
}
|
|
||||||
|
|
||||||
write_image("wayland-screenshot.png", buffer.format, buffer.width,
|
|
||||||
buffer.height, stride, buffer.y_invert, data);
|
|
||||||
|
|
||||||
gbm_bo_unmap(buffer.bo, map_data);
|
|
||||||
gbm_bo_destroy(buffer.bo);
|
|
||||||
wl_buffer_destroy(buffer.wl_buffer);
|
|
||||||
|
|
||||||
gbm_device_destroy(gbm_device);
|
|
||||||
close(drm_fd);
|
|
||||||
|
|
||||||
return EXIT_SUCCESS;
|
|
||||||
}
|
|
@ -1,266 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright © 2008 Kristian Høgsberg
|
|
||||||
*
|
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a
|
|
||||||
* copy of this software and associated documentation files (the "Software"),
|
|
||||||
* to deal in the Software without restriction, including without limitation
|
|
||||||
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
|
||||||
* and/or sell copies of the Software, and to permit persons to whom the
|
|
||||||
* Software is furnished to do so, subject to the following conditions:
|
|
||||||
*
|
|
||||||
* The above copyright notice and this permission notice (including the next
|
|
||||||
* paragraph) shall be included in all copies or substantial portions of the
|
|
||||||
* Software.
|
|
||||||
*
|
|
||||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
|
||||||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
|
||||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
|
||||||
* DEALINGS IN THE SOFTWARE.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#define _POSIX_C_SOURCE 200112L
|
|
||||||
#include <errno.h>
|
|
||||||
#include <fcntl.h>
|
|
||||||
#include <limits.h>
|
|
||||||
#include <png.h>
|
|
||||||
#include <stdbool.h>
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include <sys/mman.h>
|
|
||||||
#include <sys/param.h>
|
|
||||||
#include <sys/stat.h>
|
|
||||||
#include <sys/wait.h>
|
|
||||||
#include <unistd.h>
|
|
||||||
#include <assert.h>
|
|
||||||
#include <wayland-client-protocol.h>
|
|
||||||
#include "wlr-screencopy-unstable-v1-client-protocol.h"
|
|
||||||
|
|
||||||
struct format {
|
|
||||||
enum wl_shm_format wl_format;
|
|
||||||
bool is_bgr;
|
|
||||||
};
|
|
||||||
|
|
||||||
static struct wl_shm *shm = NULL;
|
|
||||||
static struct zwlr_screencopy_manager_v1 *screencopy_manager = NULL;
|
|
||||||
static struct wl_output *output = NULL;
|
|
||||||
|
|
||||||
static struct {
|
|
||||||
struct wl_buffer *wl_buffer;
|
|
||||||
void *data;
|
|
||||||
enum wl_shm_format format;
|
|
||||||
int width, height, stride;
|
|
||||||
bool y_invert;
|
|
||||||
} buffer;
|
|
||||||
bool buffer_copy_done = false;
|
|
||||||
|
|
||||||
// wl_shm_format describes little-endian formats, libpng uses big-endian
|
|
||||||
// formats (so Wayland's ABGR is libpng's RGBA).
|
|
||||||
static const struct format formats[] = {
|
|
||||||
{WL_SHM_FORMAT_XRGB8888, true},
|
|
||||||
{WL_SHM_FORMAT_ARGB8888, true},
|
|
||||||
{WL_SHM_FORMAT_XBGR8888, false},
|
|
||||||
{WL_SHM_FORMAT_ABGR8888, false},
|
|
||||||
};
|
|
||||||
|
|
||||||
static struct wl_buffer *create_shm_buffer(enum wl_shm_format fmt,
|
|
||||||
int width, int height, int stride, void **data_out) {
|
|
||||||
int size = stride * height;
|
|
||||||
|
|
||||||
const char shm_name[] = "/wlroots-screencopy";
|
|
||||||
int fd = shm_open(shm_name, O_RDWR | O_CREAT | O_EXCL, S_IRUSR | S_IWUSR);
|
|
||||||
if (fd < 0) {
|
|
||||||
fprintf(stderr, "shm_open failed\n");
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
shm_unlink(shm_name);
|
|
||||||
|
|
||||||
int ret;
|
|
||||||
while ((ret = ftruncate(fd, size)) == EINTR) {
|
|
||||||
// No-op
|
|
||||||
}
|
|
||||||
if (ret < 0) {
|
|
||||||
close(fd);
|
|
||||||
fprintf(stderr, "ftruncate failed\n");
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
void *data = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
|
|
||||||
if (data == MAP_FAILED) {
|
|
||||||
perror("mmap failed");
|
|
||||||
close(fd);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct wl_shm_pool *pool = wl_shm_create_pool(shm, fd, size);
|
|
||||||
close(fd);
|
|
||||||
struct wl_buffer *buffer = wl_shm_pool_create_buffer(pool, 0, width, height,
|
|
||||||
stride, fmt);
|
|
||||||
wl_shm_pool_destroy(pool);
|
|
||||||
|
|
||||||
*data_out = data;
|
|
||||||
return buffer;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void frame_handle_buffer(void *data,
|
|
||||||
struct zwlr_screencopy_frame_v1 *frame, uint32_t format,
|
|
||||||
uint32_t width, uint32_t height, uint32_t stride) {
|
|
||||||
buffer.format = format;
|
|
||||||
buffer.width = width;
|
|
||||||
buffer.height = height;
|
|
||||||
buffer.stride = stride;
|
|
||||||
|
|
||||||
// Make sure the buffer is not allocated
|
|
||||||
assert(!buffer.wl_buffer);
|
|
||||||
buffer.wl_buffer =
|
|
||||||
create_shm_buffer(format, width, height, stride, &buffer.data);
|
|
||||||
if (buffer.wl_buffer == NULL) {
|
|
||||||
fprintf(stderr, "failed to create buffer\n");
|
|
||||||
exit(EXIT_FAILURE);
|
|
||||||
}
|
|
||||||
|
|
||||||
zwlr_screencopy_frame_v1_copy(frame, buffer.wl_buffer);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void frame_handle_flags(void *data,
|
|
||||||
struct zwlr_screencopy_frame_v1 *frame, uint32_t flags) {
|
|
||||||
buffer.y_invert = flags & ZWLR_SCREENCOPY_FRAME_V1_FLAGS_Y_INVERT;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void frame_handle_ready(void *data,
|
|
||||||
struct zwlr_screencopy_frame_v1 *frame, uint32_t tv_sec_hi,
|
|
||||||
uint32_t tv_sec_lo, uint32_t tv_nsec) {
|
|
||||||
buffer_copy_done = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void frame_handle_failed(void *data,
|
|
||||||
struct zwlr_screencopy_frame_v1 *frame) {
|
|
||||||
fprintf(stderr, "failed to copy frame\n");
|
|
||||||
exit(EXIT_FAILURE);
|
|
||||||
}
|
|
||||||
|
|
||||||
static const struct zwlr_screencopy_frame_v1_listener frame_listener = {
|
|
||||||
.buffer = frame_handle_buffer,
|
|
||||||
.flags = frame_handle_flags,
|
|
||||||
.ready = frame_handle_ready,
|
|
||||||
.failed = frame_handle_failed,
|
|
||||||
};
|
|
||||||
|
|
||||||
static void handle_global(void *data, struct wl_registry *registry,
|
|
||||||
uint32_t name, const char *interface, uint32_t version) {
|
|
||||||
if (strcmp(interface, wl_output_interface.name) == 0 && output == NULL) {
|
|
||||||
output = wl_registry_bind(registry, name, &wl_output_interface, 1);
|
|
||||||
} else if (strcmp(interface, wl_shm_interface.name) == 0) {
|
|
||||||
shm = wl_registry_bind(registry, name, &wl_shm_interface, 1);
|
|
||||||
} else if (strcmp(interface,
|
|
||||||
zwlr_screencopy_manager_v1_interface.name) == 0) {
|
|
||||||
screencopy_manager = wl_registry_bind(registry, name,
|
|
||||||
&zwlr_screencopy_manager_v1_interface, 1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void handle_global_remove(void *data, struct wl_registry *registry,
|
|
||||||
uint32_t name) {
|
|
||||||
// Who cares?
|
|
||||||
}
|
|
||||||
|
|
||||||
static const struct wl_registry_listener registry_listener = {
|
|
||||||
.global = handle_global,
|
|
||||||
.global_remove = handle_global_remove,
|
|
||||||
};
|
|
||||||
|
|
||||||
static void write_image(char *filename, enum wl_shm_format wl_fmt, int width,
|
|
||||||
int height, int stride, bool y_invert, png_bytep data) {
|
|
||||||
const struct format *fmt = NULL;
|
|
||||||
for (size_t i = 0; i < sizeof(formats) / sizeof(formats[0]); ++i) {
|
|
||||||
if (formats[i].wl_format == wl_fmt) {
|
|
||||||
fmt = &formats[i];
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (fmt == NULL) {
|
|
||||||
fprintf(stderr, "unsupported format %"PRIu32"\n", wl_fmt);
|
|
||||||
exit(EXIT_FAILURE);
|
|
||||||
}
|
|
||||||
|
|
||||||
FILE *f = fopen(filename, "wb");
|
|
||||||
if (f == NULL) {
|
|
||||||
fprintf(stderr, "failed to open output file\n");
|
|
||||||
exit(EXIT_FAILURE);
|
|
||||||
}
|
|
||||||
|
|
||||||
png_structp png =
|
|
||||||
png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
|
|
||||||
png_infop info = png_create_info_struct(png);
|
|
||||||
|
|
||||||
png_init_io(png, f);
|
|
||||||
|
|
||||||
png_set_IHDR(png, info, width, height, 8, PNG_COLOR_TYPE_RGBA,
|
|
||||||
PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT,
|
|
||||||
PNG_FILTER_TYPE_DEFAULT);
|
|
||||||
|
|
||||||
if (fmt->is_bgr) {
|
|
||||||
png_set_bgr(png);
|
|
||||||
}
|
|
||||||
|
|
||||||
png_write_info(png, info);
|
|
||||||
|
|
||||||
for (size_t i = 0; i < (size_t)height; ++i) {
|
|
||||||
png_bytep row;
|
|
||||||
if (y_invert) {
|
|
||||||
row = data + (height - i - 1) * stride;
|
|
||||||
} else {
|
|
||||||
row = data + i * stride;
|
|
||||||
}
|
|
||||||
png_write_row(png, row);
|
|
||||||
}
|
|
||||||
|
|
||||||
png_write_end(png, NULL);
|
|
||||||
|
|
||||||
png_destroy_write_struct(&png, &info);
|
|
||||||
|
|
||||||
fclose(f);
|
|
||||||
}
|
|
||||||
|
|
||||||
int main(int argc, char *argv[]) {
|
|
||||||
struct wl_display * display = wl_display_connect(NULL);
|
|
||||||
if (display == NULL) {
|
|
||||||
perror("failed to create display");
|
|
||||||
return EXIT_FAILURE;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct wl_registry *registry = wl_display_get_registry(display);
|
|
||||||
wl_registry_add_listener(registry, ®istry_listener, NULL);
|
|
||||||
wl_display_roundtrip(display);
|
|
||||||
|
|
||||||
if (shm == NULL) {
|
|
||||||
fprintf(stderr, "compositor is missing wl_shm\n");
|
|
||||||
return EXIT_FAILURE;
|
|
||||||
}
|
|
||||||
if (screencopy_manager == NULL) {
|
|
||||||
fprintf(stderr, "compositor doesn't support wlr-screencopy-unstable-v1\n");
|
|
||||||
return EXIT_FAILURE;
|
|
||||||
}
|
|
||||||
if (output == NULL) {
|
|
||||||
fprintf(stderr, "no output available\n");
|
|
||||||
return EXIT_FAILURE;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct zwlr_screencopy_frame_v1 *frame =
|
|
||||||
zwlr_screencopy_manager_v1_capture_output(screencopy_manager, 0, output);
|
|
||||||
zwlr_screencopy_frame_v1_add_listener(frame, &frame_listener, NULL);
|
|
||||||
|
|
||||||
while (!buffer_copy_done && wl_display_dispatch(display) != -1) {
|
|
||||||
// This space is intentionally left blank
|
|
||||||
}
|
|
||||||
|
|
||||||
write_image("wayland-screenshot.png", buffer.format, buffer.width,
|
|
||||||
buffer.height, buffer.stride, buffer.y_invert, buffer.data);
|
|
||||||
wl_buffer_destroy(buffer.wl_buffer);
|
|
||||||
munmap(buffer.data, buffer.stride * buffer.height);
|
|
||||||
|
|
||||||
return EXIT_SUCCESS;
|
|
||||||
}
|
|
@ -1,390 +0,0 @@
|
|||||||
#define _POSIX_C_SOURCE 200809L
|
|
||||||
#include <GLES2/gl2.h>
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include <unistd.h>
|
|
||||||
#include <wayland-client.h>
|
|
||||||
#include <wayland-egl.h>
|
|
||||||
#include "egl_common.h"
|
|
||||||
#include "text-input-unstable-v3-client-protocol.h"
|
|
||||||
#include "xdg-shell-client-protocol.h"
|
|
||||||
|
|
||||||
const char usage[] = "Usage: text-input [seconds [width height]]\n\
|
|
||||||
\n\
|
|
||||||
Creates a xdg-toplevel using the text-input protocol.\n\
|
|
||||||
It will be solid black when it has no text input focus, yellow when it\n\
|
|
||||||
has focus, and red when it was notified that the focus moved away\n\
|
|
||||||
but still didn't give up the text input ability.\n\
|
|
||||||
\n\
|
|
||||||
The \"seconds\" argument is optional and defines the delay between getting\n\
|
|
||||||
notified of lost focus and releasing text input.\n\
|
|
||||||
\n\
|
|
||||||
The \"width\" and \"height\" arguments define the window shape.\n\
|
|
||||||
\n\
|
|
||||||
The console will print the internal state of the text field:\n\
|
|
||||||
- the text in the 1st line\n\
|
|
||||||
- \".\" under each preedit character\n\
|
|
||||||
- \"_\" under each selected preedit character\n\
|
|
||||||
- \"|\" at the cursor position if there are no selected characters in the\n\
|
|
||||||
preedit.\n\
|
|
||||||
\n\
|
|
||||||
The cursor positions may be inaccurate, especially in presence of zero-width\n\
|
|
||||||
characters or non-monospaced fonts.\n";
|
|
||||||
|
|
||||||
struct text_input_state {
|
|
||||||
char *commit;
|
|
||||||
struct {
|
|
||||||
char *text;
|
|
||||||
int32_t cursor_begin;
|
|
||||||
int32_t cursor_end;
|
|
||||||
} preedit;
|
|
||||||
struct {
|
|
||||||
uint32_t after_length;
|
|
||||||
uint32_t before_length;
|
|
||||||
} delete_surrounding;
|
|
||||||
};
|
|
||||||
|
|
||||||
static struct text_input_state pending = {0};
|
|
||||||
static struct text_input_state current = {0};
|
|
||||||
static bool entered = false;
|
|
||||||
static uint32_t serial;
|
|
||||||
static char *buffer; // text buffer
|
|
||||||
// cursor is not present, there's no way to move it outside of preedit
|
|
||||||
|
|
||||||
static int sleeptime = 0;
|
|
||||||
static int width = 100, height = 200;
|
|
||||||
static int enabled = 0;
|
|
||||||
|
|
||||||
static struct wl_display *display = NULL;
|
|
||||||
static struct wl_compositor *compositor = NULL;
|
|
||||||
static struct wl_seat *seat = NULL;
|
|
||||||
static struct xdg_wm_base *wm_base = NULL;
|
|
||||||
static struct zwp_text_input_manager_v3 *text_input_manager = NULL;
|
|
||||||
static struct zwp_text_input_v3 *text_input = NULL;
|
|
||||||
|
|
||||||
struct wl_egl_window *egl_window;
|
|
||||||
struct wlr_egl_surface *egl_surface;
|
|
||||||
|
|
||||||
static void draw(void) {
|
|
||||||
eglMakeCurrent(egl_display, egl_surface, egl_surface, egl_context);
|
|
||||||
|
|
||||||
float color[] = {1.0, 1.0, 0.0, 1.0};
|
|
||||||
color[0] = enabled * 1.0;
|
|
||||||
color[1] = entered * 1.0;
|
|
||||||
|
|
||||||
glViewport(0, 0, width, height);
|
|
||||||
glClearColor(color[0], color[1], color[2], 1.0);
|
|
||||||
glClear(GL_COLOR_BUFFER_BIT);
|
|
||||||
|
|
||||||
eglSwapBuffers(egl_display, egl_surface);
|
|
||||||
}
|
|
||||||
|
|
||||||
static size_t utf8_strlen(char *str) {
|
|
||||||
size_t cp_count = 0;
|
|
||||||
for (; *str != '\0'; str++) {
|
|
||||||
if ((*str & 0xc0) != 0x80) {
|
|
||||||
cp_count++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return cp_count;
|
|
||||||
}
|
|
||||||
|
|
||||||
static size_t utf8_offset(char *utf8_str, size_t byte_offset) {
|
|
||||||
size_t cp_count = 0;
|
|
||||||
for (char *c = utf8_str; c < utf8_str + byte_offset; c++) {
|
|
||||||
if ((*c & 0xc0) != 0x80) {
|
|
||||||
cp_count++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return cp_count;
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: would be nicer to have this text display inside the window
|
|
||||||
static void show_status(void) {
|
|
||||||
printf("State %d:", serial);
|
|
||||||
if (!enabled) {
|
|
||||||
printf(" disabled");
|
|
||||||
}
|
|
||||||
|
|
||||||
char *preedit_text = current.preedit.text;
|
|
||||||
if (!preedit_text) {
|
|
||||||
preedit_text = "";
|
|
||||||
}
|
|
||||||
|
|
||||||
printf("\n");
|
|
||||||
printf("%s", buffer);
|
|
||||||
printf("%s\n", preedit_text);
|
|
||||||
|
|
||||||
// Positioning of the cursor requires UTF8 offsets to match monospaced
|
|
||||||
// glyphs
|
|
||||||
for (unsigned i = 0; i < utf8_strlen(buffer); i++) {
|
|
||||||
printf(" ");
|
|
||||||
}
|
|
||||||
char *cursor_mark = calloc(utf8_strlen(preedit_text) + 2, sizeof(*cursor_mark));
|
|
||||||
for (unsigned i = 0; i < utf8_strlen(preedit_text); i++) {
|
|
||||||
cursor_mark[i] = '.';
|
|
||||||
}
|
|
||||||
if (current.preedit.cursor_begin == -1
|
|
||||||
&& current.preedit.cursor_end == -1) {
|
|
||||||
goto end;
|
|
||||||
}
|
|
||||||
if (current.preedit.cursor_begin == -1
|
|
||||||
|| current.preedit.cursor_end == -1) {
|
|
||||||
printf("Only one cursor side is defined: %d to %d\n",
|
|
||||||
current.preedit.cursor_begin, current.preedit.cursor_end);
|
|
||||||
goto end;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((unsigned)current.preedit.cursor_begin > strlen(preedit_text)) {
|
|
||||||
printf("Cursor out of bounds\n");
|
|
||||||
goto end;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (current.preedit.cursor_begin == current.preedit.cursor_end) {
|
|
||||||
cursor_mark[utf8_offset(preedit_text, current.preedit.cursor_begin)]
|
|
||||||
= '|';
|
|
||||||
goto print;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (current.preedit.cursor_begin > current.preedit.cursor_end) {
|
|
||||||
printf("End cursor is before start cursor\n");
|
|
||||||
goto end;
|
|
||||||
}
|
|
||||||
|
|
||||||
// negative offsets already checked before
|
|
||||||
for (unsigned i = utf8_offset(preedit_text, current.preedit.cursor_begin);
|
|
||||||
i < utf8_offset(preedit_text, current.preedit.cursor_end); i++) {
|
|
||||||
cursor_mark[i] = '_';
|
|
||||||
}
|
|
||||||
print:
|
|
||||||
printf("%s\n", cursor_mark);
|
|
||||||
end:
|
|
||||||
free(cursor_mark);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void commit(struct zwp_text_input_v3 *text_input) {
|
|
||||||
zwp_text_input_v3_commit(text_input);
|
|
||||||
serial++;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void send_status_update(struct zwp_text_input_v3 *text_input) {
|
|
||||||
zwp_text_input_v3_set_surrounding_text(text_input, buffer, strlen(buffer), strlen(buffer));
|
|
||||||
zwp_text_input_v3_set_text_change_cause(text_input, ZWP_TEXT_INPUT_V3_CHANGE_CAUSE_INPUT_METHOD);
|
|
||||||
commit(text_input);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void text_input_handle_enter(void *data,
|
|
||||||
struct zwp_text_input_v3 *zwp_text_input_v3,
|
|
||||||
struct wl_surface *surface) {
|
|
||||||
entered = true;
|
|
||||||
zwp_text_input_v3_enable(zwp_text_input_v3);
|
|
||||||
commit(zwp_text_input_v3);
|
|
||||||
enabled = true;
|
|
||||||
draw();
|
|
||||||
show_status();
|
|
||||||
}
|
|
||||||
|
|
||||||
static void text_input_handle_leave(void *data,
|
|
||||||
struct zwp_text_input_v3 *zwp_text_input_v3,
|
|
||||||
struct wl_surface *surface) {
|
|
||||||
entered = false;
|
|
||||||
draw();
|
|
||||||
wl_display_roundtrip(display);
|
|
||||||
sleep(sleeptime);
|
|
||||||
zwp_text_input_v3_disable(zwp_text_input_v3);
|
|
||||||
commit(zwp_text_input_v3);
|
|
||||||
enabled = false;
|
|
||||||
draw();
|
|
||||||
show_status();
|
|
||||||
}
|
|
||||||
|
|
||||||
static void text_input_commit_string(void *data,
|
|
||||||
struct zwp_text_input_v3 *zwp_text_input_v3,
|
|
||||||
const char *text) {
|
|
||||||
free(pending.commit);
|
|
||||||
pending.commit = strdup(text);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void text_input_delete_surrounding_text(void *data,
|
|
||||||
struct zwp_text_input_v3 *zwp_text_input_v3,
|
|
||||||
uint32_t before_length, uint32_t after_length) {
|
|
||||||
pending.delete_surrounding.before_length = before_length;
|
|
||||||
pending.delete_surrounding.after_length = after_length;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void text_input_preedit_string(void *data,
|
|
||||||
struct zwp_text_input_v3 *zwp_text_input_v3,
|
|
||||||
const char *text, int32_t cursor_begin, int32_t cursor_end) {
|
|
||||||
free(pending.preedit.text);
|
|
||||||
pending.preedit.text = strdup(text);
|
|
||||||
pending.preedit.cursor_begin = cursor_begin;
|
|
||||||
pending.preedit.cursor_end = cursor_end;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void text_input_handle_done(void *data,
|
|
||||||
struct zwp_text_input_v3 *zwp_text_input_v3,
|
|
||||||
uint32_t incoming_serial) {
|
|
||||||
if (serial != incoming_serial) {
|
|
||||||
fprintf(stderr, "Received serial %d while expecting %d\n", incoming_serial, serial);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
free(current.preedit.text);
|
|
||||||
free(current.commit);
|
|
||||||
current = pending;
|
|
||||||
struct text_input_state empty = {0};
|
|
||||||
pending = empty;
|
|
||||||
|
|
||||||
if (current.delete_surrounding.after_length + current.delete_surrounding.before_length > 0) {
|
|
||||||
// cursor is always after committed text, after_length != 0 will never happen
|
|
||||||
unsigned delete_before = current.delete_surrounding.before_length;
|
|
||||||
if (delete_before > strlen(buffer)) {
|
|
||||||
delete_before = strlen(buffer);
|
|
||||||
}
|
|
||||||
buffer[strlen(buffer) - delete_before] = '\0';
|
|
||||||
}
|
|
||||||
|
|
||||||
const char *commit_string = current.commit;
|
|
||||||
if (!commit_string) {
|
|
||||||
commit_string = "";
|
|
||||||
}
|
|
||||||
size_t new_size = strlen(buffer) + strlen(commit_string) + 1;
|
|
||||||
char *new_buffer = calloc(new_size, sizeof(*new_buffer)); // realloc may fail anyway
|
|
||||||
snprintf(new_buffer, new_size, "%s%s", buffer, commit_string);
|
|
||||||
free(buffer);
|
|
||||||
buffer = new_buffer;
|
|
||||||
|
|
||||||
send_status_update(zwp_text_input_v3);
|
|
||||||
show_status();
|
|
||||||
}
|
|
||||||
|
|
||||||
static const struct zwp_text_input_v3_listener text_input_listener = {
|
|
||||||
.enter = text_input_handle_enter,
|
|
||||||
.leave = text_input_handle_leave,
|
|
||||||
.commit_string = text_input_commit_string,
|
|
||||||
.delete_surrounding_text = text_input_delete_surrounding_text,
|
|
||||||
.preedit_string = text_input_preedit_string,
|
|
||||||
.done = text_input_handle_done,
|
|
||||||
};
|
|
||||||
|
|
||||||
static void xdg_surface_handle_configure(void *data,
|
|
||||||
struct xdg_surface *xdg_surface, uint32_t serial) {
|
|
||||||
xdg_surface_ack_configure(xdg_surface, serial);
|
|
||||||
wl_egl_window_resize(egl_window, width, height, 0, 0);
|
|
||||||
draw();
|
|
||||||
}
|
|
||||||
|
|
||||||
static const struct xdg_surface_listener xdg_surface_listener = {
|
|
||||||
.configure = xdg_surface_handle_configure,
|
|
||||||
};
|
|
||||||
|
|
||||||
static void xdg_toplevel_handle_configure(void *data,
|
|
||||||
struct xdg_toplevel *xdg_toplevel, int32_t w, int32_t h,
|
|
||||||
struct wl_array *states) {
|
|
||||||
width = w;
|
|
||||||
height = h;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void xdg_toplevel_handle_close(void *data,
|
|
||||||
struct xdg_toplevel *xdg_toplevel) {
|
|
||||||
exit(EXIT_SUCCESS);
|
|
||||||
}
|
|
||||||
|
|
||||||
static const struct xdg_toplevel_listener xdg_toplevel_listener = {
|
|
||||||
.configure = xdg_toplevel_handle_configure,
|
|
||||||
.close = xdg_toplevel_handle_close,
|
|
||||||
};
|
|
||||||
|
|
||||||
static void handle_global(void *data, struct wl_registry *registry,
|
|
||||||
uint32_t name, const char *interface, uint32_t version) {
|
|
||||||
if (strcmp(interface, "wl_compositor") == 0) {
|
|
||||||
compositor = wl_registry_bind(registry, name,
|
|
||||||
&wl_compositor_interface, 1);
|
|
||||||
} else if (strcmp(interface, xdg_wm_base_interface.name) == 0) {
|
|
||||||
wm_base = wl_registry_bind(registry, name, &xdg_wm_base_interface, 1);
|
|
||||||
} else if (strcmp(interface, zwp_text_input_manager_v3_interface.name) == 0) {
|
|
||||||
text_input_manager = wl_registry_bind(registry, name,
|
|
||||||
&zwp_text_input_manager_v3_interface, 1);
|
|
||||||
} else if (strcmp(interface, wl_seat_interface.name) == 0) {
|
|
||||||
seat = wl_registry_bind(registry, name, &wl_seat_interface, version);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void handle_global_remove(void *data, struct wl_registry *registry,
|
|
||||||
uint32_t name) {
|
|
||||||
// who cares
|
|
||||||
}
|
|
||||||
|
|
||||||
static const struct wl_registry_listener registry_listener = {
|
|
||||||
.global = handle_global,
|
|
||||||
.global_remove = handle_global_remove,
|
|
||||||
};
|
|
||||||
|
|
||||||
int main(int argc, char **argv) {
|
|
||||||
if (argc > 1) {
|
|
||||||
if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-h") == 0) {
|
|
||||||
printf(usage);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
sleeptime = atoi(argv[1]);
|
|
||||||
if (argc > 3) {
|
|
||||||
width = atoi(argv[2]);
|
|
||||||
height = atoi(argv[3]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
buffer = calloc(1, sizeof(*buffer));
|
|
||||||
|
|
||||||
display = wl_display_connect(NULL);
|
|
||||||
if (display == NULL) {
|
|
||||||
fprintf(stderr, "Failed to create display\n");
|
|
||||||
return EXIT_FAILURE;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct wl_registry *registry = wl_display_get_registry(display);
|
|
||||||
wl_registry_add_listener(registry, ®istry_listener, NULL);
|
|
||||||
wl_display_roundtrip(display);
|
|
||||||
|
|
||||||
if (compositor == NULL) {
|
|
||||||
fprintf(stderr, "wl-compositor not available\n");
|
|
||||||
return EXIT_FAILURE;
|
|
||||||
}
|
|
||||||
if (wm_base == NULL) {
|
|
||||||
fprintf(stderr, "xdg-shell not available\n");
|
|
||||||
return EXIT_FAILURE;
|
|
||||||
}
|
|
||||||
if (text_input_manager == NULL) {
|
|
||||||
fprintf(stderr, "text-input not available\n");
|
|
||||||
return EXIT_FAILURE;
|
|
||||||
}
|
|
||||||
|
|
||||||
text_input = zwp_text_input_manager_v3_get_text_input(text_input_manager, seat);
|
|
||||||
|
|
||||||
zwp_text_input_v3_add_listener(text_input, &text_input_listener, NULL);
|
|
||||||
|
|
||||||
egl_init(display);
|
|
||||||
|
|
||||||
struct wl_surface *surface = wl_compositor_create_surface(compositor);
|
|
||||||
struct xdg_surface *xdg_surface =
|
|
||||||
xdg_wm_base_get_xdg_surface(wm_base, 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);
|
|
||||||
|
|
||||||
wl_surface_commit(surface);
|
|
||||||
|
|
||||||
egl_window = wl_egl_window_create(surface, width, height);
|
|
||||||
egl_surface = eglCreatePlatformWindowSurfaceEXT(
|
|
||||||
egl_display, egl_config, egl_window, NULL);
|
|
||||||
|
|
||||||
wl_display_roundtrip(display);
|
|
||||||
|
|
||||||
draw();
|
|
||||||
|
|
||||||
while (wl_display_dispatch(display) != -1) {
|
|
||||||
// This space intentionally left blank
|
|
||||||
}
|
|
||||||
|
|
||||||
return EXIT_SUCCESS;
|
|
||||||
}
|
|
@ -1,251 +0,0 @@
|
|||||||
#include <GLES2/gl2.h>
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include <wayland-client.h>
|
|
||||||
#include <wayland-egl.h>
|
|
||||||
#include "egl_common.h"
|
|
||||||
#include "xdg-shell-client-protocol.h"
|
|
||||||
#include "xdg-decoration-unstable-v1-client-protocol.h"
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Usage: toplevel-decoration [mode]
|
|
||||||
* Creates an xdg-toplevel supporting decoration negotiation. If `mode` is
|
|
||||||
* specified, the client will prefer this decoration mode.
|
|
||||||
*/
|
|
||||||
|
|
||||||
static int width = 500, height = 300;
|
|
||||||
|
|
||||||
static struct wl_compositor *compositor = NULL;
|
|
||||||
static struct xdg_wm_base *wm_base = NULL;
|
|
||||||
static struct zxdg_decoration_manager_v1 *decoration_manager = NULL;
|
|
||||||
|
|
||||||
struct wl_egl_window *egl_window;
|
|
||||||
struct wlr_egl_surface *egl_surface;
|
|
||||||
|
|
||||||
struct zxdg_toplevel_decoration_v1 *decoration;
|
|
||||||
enum zxdg_toplevel_decoration_v1_mode client_preferred_mode, current_mode;
|
|
||||||
|
|
||||||
static const char *get_mode_name(enum zxdg_toplevel_decoration_v1_mode mode) {
|
|
||||||
switch (mode) {
|
|
||||||
case ZXDG_TOPLEVEL_DECORATION_V1_MODE_CLIENT_SIDE:
|
|
||||||
return "client-side decorations";
|
|
||||||
case ZXDG_TOPLEVEL_DECORATION_V1_MODE_SERVER_SIDE:
|
|
||||||
return "server-side decorations";
|
|
||||||
}
|
|
||||||
abort();
|
|
||||||
}
|
|
||||||
|
|
||||||
static void request_preferred_mode(void) {
|
|
||||||
enum zxdg_toplevel_decoration_v1_mode mode = client_preferred_mode;
|
|
||||||
if (mode == 0) {
|
|
||||||
printf("Requesting compositor preferred mode\n");
|
|
||||||
zxdg_toplevel_decoration_v1_unset_mode(decoration);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (mode == current_mode) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
printf("Requesting %s\n", get_mode_name(mode));
|
|
||||||
zxdg_toplevel_decoration_v1_set_mode(decoration, mode);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void draw(void) {
|
|
||||||
eglMakeCurrent(egl_display, egl_surface, egl_surface, egl_context);
|
|
||||||
|
|
||||||
float color[] = {1.0, 1.0, 0.0, 1.0};
|
|
||||||
if (current_mode == ZXDG_TOPLEVEL_DECORATION_V1_MODE_CLIENT_SIDE) {
|
|
||||||
color[0] = 0.0;
|
|
||||||
}
|
|
||||||
|
|
||||||
glViewport(0, 0, width, height);
|
|
||||||
glClearColor(color[0], color[1], color[2], 1.0);
|
|
||||||
glClear(GL_COLOR_BUFFER_BIT);
|
|
||||||
|
|
||||||
eglSwapBuffers(egl_display, egl_surface);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void xdg_surface_handle_configure(void *data,
|
|
||||||
struct xdg_surface *xdg_surface, uint32_t serial) {
|
|
||||||
xdg_surface_ack_configure(xdg_surface, serial);
|
|
||||||
wl_egl_window_resize(egl_window, width, height, 0, 0);
|
|
||||||
draw();
|
|
||||||
}
|
|
||||||
|
|
||||||
static const struct xdg_surface_listener xdg_surface_listener = {
|
|
||||||
.configure = xdg_surface_handle_configure,
|
|
||||||
};
|
|
||||||
|
|
||||||
static void xdg_toplevel_handle_configure(void *data,
|
|
||||||
struct xdg_toplevel *xdg_toplevel, int32_t w, int32_t h,
|
|
||||||
struct wl_array *states) {
|
|
||||||
width = w;
|
|
||||||
height = h;
|
|
||||||
}
|
|
||||||
|
|
||||||
static const struct xdg_toplevel_listener xdg_toplevel_listener = {
|
|
||||||
.configure = xdg_toplevel_handle_configure,
|
|
||||||
};
|
|
||||||
|
|
||||||
static void decoration_handle_configure(void *data,
|
|
||||||
struct zxdg_toplevel_decoration_v1 *decoration,
|
|
||||||
enum zxdg_toplevel_decoration_v1_mode mode) {
|
|
||||||
printf("Using %s\n", get_mode_name(mode));
|
|
||||||
current_mode = mode;
|
|
||||||
}
|
|
||||||
|
|
||||||
static const struct zxdg_toplevel_decoration_v1_listener decoration_listener = {
|
|
||||||
.configure = decoration_handle_configure,
|
|
||||||
};
|
|
||||||
|
|
||||||
static void pointer_handle_enter(void *data, struct wl_pointer *pointer,
|
|
||||||
uint32_t serial, struct wl_surface *surface,
|
|
||||||
wl_fixed_t sx, wl_fixed_t sy) {
|
|
||||||
// No-op
|
|
||||||
}
|
|
||||||
|
|
||||||
static void pointer_handle_leave(void *data, struct wl_pointer *pointer,
|
|
||||||
uint32_t serial, struct wl_surface *surface) {
|
|
||||||
// No-op
|
|
||||||
}
|
|
||||||
|
|
||||||
static void pointer_handle_motion(void *data, struct wl_pointer *pointer,
|
|
||||||
uint32_t time, wl_fixed_t sx, wl_fixed_t sy) {
|
|
||||||
// No-op
|
|
||||||
}
|
|
||||||
|
|
||||||
static void pointer_handle_button(void *data, struct wl_pointer *wl_pointer,
|
|
||||||
uint32_t serial, uint32_t time, uint32_t button,
|
|
||||||
enum wl_pointer_button_state state) {
|
|
||||||
if (state == WL_POINTER_BUTTON_STATE_PRESSED) {
|
|
||||||
// Toggle mode
|
|
||||||
if (client_preferred_mode == ZXDG_TOPLEVEL_DECORATION_V1_MODE_SERVER_SIDE) {
|
|
||||||
client_preferred_mode = ZXDG_TOPLEVEL_DECORATION_V1_MODE_CLIENT_SIDE;
|
|
||||||
} else {
|
|
||||||
client_preferred_mode = ZXDG_TOPLEVEL_DECORATION_V1_MODE_SERVER_SIDE;
|
|
||||||
}
|
|
||||||
request_preferred_mode();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void pointer_handle_axis(void *data, struct wl_pointer *wl_pointer,
|
|
||||||
uint32_t time, enum wl_pointer_axis axis, wl_fixed_t value) {
|
|
||||||
// No-op
|
|
||||||
}
|
|
||||||
|
|
||||||
static const struct wl_pointer_listener pointer_listener = {
|
|
||||||
.enter = pointer_handle_enter,
|
|
||||||
.leave = pointer_handle_leave,
|
|
||||||
.motion = pointer_handle_motion,
|
|
||||||
.button = pointer_handle_button,
|
|
||||||
.axis = pointer_handle_axis,
|
|
||||||
};
|
|
||||||
|
|
||||||
static void seat_handle_capabilities(void *data, struct wl_seat *seat,
|
|
||||||
enum wl_seat_capability caps) {
|
|
||||||
if (caps & WL_SEAT_CAPABILITY_POINTER) {
|
|
||||||
struct wl_pointer *pointer = wl_seat_get_pointer(seat);
|
|
||||||
wl_pointer_add_listener(pointer, &pointer_listener, NULL);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static const struct wl_seat_listener seat_listener = {
|
|
||||||
.capabilities = seat_handle_capabilities,
|
|
||||||
};
|
|
||||||
|
|
||||||
static void handle_global(void *data, struct wl_registry *registry,
|
|
||||||
uint32_t name, const char *interface, uint32_t version) {
|
|
||||||
if (strcmp(interface, wl_compositor_interface.name) == 0) {
|
|
||||||
compositor = wl_registry_bind(registry, name, &wl_compositor_interface,
|
|
||||||
1);
|
|
||||||
} else if (strcmp(interface, xdg_wm_base_interface.name) == 0) {
|
|
||||||
wm_base = wl_registry_bind(registry, name, &xdg_wm_base_interface, 1);
|
|
||||||
} else if (strcmp(interface, zxdg_decoration_manager_v1_interface.name) == 0) {
|
|
||||||
decoration_manager = wl_registry_bind(registry, name,
|
|
||||||
&zxdg_decoration_manager_v1_interface, 1);
|
|
||||||
} else if (strcmp(interface, wl_seat_interface.name) == 0) {
|
|
||||||
struct wl_seat *seat =
|
|
||||||
wl_registry_bind(registry, name, &wl_seat_interface, 1);
|
|
||||||
wl_seat_add_listener(seat, &seat_listener, NULL);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void handle_global_remove(void *data, struct wl_registry *registry,
|
|
||||||
uint32_t name) {
|
|
||||||
// Who cares?
|
|
||||||
}
|
|
||||||
|
|
||||||
static const struct wl_registry_listener registry_listener = {
|
|
||||||
.global = handle_global,
|
|
||||||
.global_remove = handle_global_remove,
|
|
||||||
};
|
|
||||||
|
|
||||||
int main(int argc, char **argv) {
|
|
||||||
if (argc == 2) {
|
|
||||||
char *mode = argv[1];
|
|
||||||
if (strcmp(mode, "client") == 0) {
|
|
||||||
client_preferred_mode = ZXDG_TOPLEVEL_DECORATION_V1_MODE_CLIENT_SIDE;
|
|
||||||
} else if (strcmp(mode, "server") == 0) {
|
|
||||||
client_preferred_mode = ZXDG_TOPLEVEL_DECORATION_V1_MODE_SERVER_SIDE;
|
|
||||||
} else {
|
|
||||||
fprintf(stderr, "Invalid decoration mode\n");
|
|
||||||
return EXIT_FAILURE;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
struct wl_display *display = wl_display_connect(NULL);
|
|
||||||
if (display == NULL) {
|
|
||||||
fprintf(stderr, "Failed to create display\n");
|
|
||||||
return EXIT_FAILURE;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct wl_registry *registry = wl_display_get_registry(display);
|
|
||||||
wl_registry_add_listener(registry, ®istry_listener, NULL);
|
|
||||||
wl_display_roundtrip(display);
|
|
||||||
|
|
||||||
if (compositor == NULL) {
|
|
||||||
fprintf(stderr, "wl-compositor not available\n");
|
|
||||||
return EXIT_FAILURE;
|
|
||||||
}
|
|
||||||
if (wm_base == NULL) {
|
|
||||||
fprintf(stderr, "xdg-shell not available\n");
|
|
||||||
return EXIT_FAILURE;
|
|
||||||
}
|
|
||||||
if (decoration_manager == NULL) {
|
|
||||||
fprintf(stderr, "xdg-decoration not available\n");
|
|
||||||
return EXIT_FAILURE;
|
|
||||||
}
|
|
||||||
|
|
||||||
egl_init(display);
|
|
||||||
|
|
||||||
struct wl_surface *surface = wl_compositor_create_surface(compositor);
|
|
||||||
struct xdg_surface *xdg_surface =
|
|
||||||
xdg_wm_base_get_xdg_surface(wm_base, surface);
|
|
||||||
struct xdg_toplevel *xdg_toplevel = xdg_surface_get_toplevel(xdg_surface);
|
|
||||||
decoration = zxdg_decoration_manager_v1_get_toplevel_decoration(
|
|
||||||
decoration_manager, xdg_toplevel);
|
|
||||||
|
|
||||||
wl_display_roundtrip(display);
|
|
||||||
request_preferred_mode();
|
|
||||||
|
|
||||||
xdg_surface_add_listener(xdg_surface, &xdg_surface_listener, NULL);
|
|
||||||
xdg_toplevel_add_listener(xdg_toplevel, &xdg_toplevel_listener, NULL);
|
|
||||||
zxdg_toplevel_decoration_v1_add_listener(decoration, &decoration_listener,
|
|
||||||
NULL);
|
|
||||||
wl_surface_commit(surface);
|
|
||||||
|
|
||||||
egl_window = wl_egl_window_create(surface, width, height);
|
|
||||||
egl_surface = eglCreatePlatformWindowSurfaceEXT(
|
|
||||||
egl_display, egl_config, egl_window, NULL);
|
|
||||||
|
|
||||||
wl_display_roundtrip(display);
|
|
||||||
|
|
||||||
draw();
|
|
||||||
|
|
||||||
while (wl_display_dispatch(display) != -1) {
|
|
||||||
// This space is intentionally left blank
|
|
||||||
}
|
|
||||||
|
|
||||||
return EXIT_SUCCESS;
|
|
||||||
}
|
|
@ -1,138 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright © 2019 Josef Gajdusek
|
|
||||||
*
|
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a
|
|
||||||
* copy of this software and associated documentation files (the "Software"),
|
|
||||||
* to deal in the Software without restriction, including without limitation
|
|
||||||
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
|
||||||
* and/or sell copies of the Software, and to permit persons to whom the
|
|
||||||
* Software is furnished to do so, subject to the following conditions:
|
|
||||||
*
|
|
||||||
* The above copyright notice and this permission notice (including the next
|
|
||||||
* paragraph) shall be included in all copies or substantial portions of the
|
|
||||||
* Software.
|
|
||||||
*
|
|
||||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
|
||||||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
|
||||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
|
||||||
* DEALINGS IN THE SOFTWARE.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#define _POSIX_C_SOURCE 200112L
|
|
||||||
#include <stdbool.h>
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include <wayland-client-protocol.h>
|
|
||||||
#include "wlr-virtual-pointer-unstable-v1-client-protocol.h"
|
|
||||||
|
|
||||||
static struct wl_seat *seat = NULL;
|
|
||||||
static struct zwlr_virtual_pointer_manager_v1 *pointer_manager = NULL;
|
|
||||||
|
|
||||||
static void handle_global(void *data, struct wl_registry *registry,
|
|
||||||
uint32_t name, const char *interface, uint32_t version) {
|
|
||||||
if (strcmp(interface, wl_seat_interface.name) == 0) {
|
|
||||||
seat = wl_registry_bind(registry, name,
|
|
||||||
&wl_seat_interface, version);
|
|
||||||
} else if (strcmp(interface,
|
|
||||||
zwlr_virtual_pointer_manager_v1_interface.name) == 0) {
|
|
||||||
pointer_manager = wl_registry_bind(registry, name,
|
|
||||||
&zwlr_virtual_pointer_manager_v1_interface, 1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void handle_global_remove(void *data, struct wl_registry *registry,
|
|
||||||
uint32_t name) {
|
|
||||||
// Who cares?
|
|
||||||
}
|
|
||||||
|
|
||||||
static const struct wl_registry_listener registry_listener = {
|
|
||||||
.global = handle_global,
|
|
||||||
.global_remove = handle_global_remove,
|
|
||||||
};
|
|
||||||
|
|
||||||
int main(int argc, char *argv[]) {
|
|
||||||
if (argc < 2) {
|
|
||||||
fprintf(stderr, "Usage: ./virtual-pointer <subcommand>\n");
|
|
||||||
return EXIT_FAILURE;
|
|
||||||
}
|
|
||||||
struct wl_display * display = wl_display_connect(NULL);
|
|
||||||
if (display == NULL) {
|
|
||||||
perror("failed to create display");
|
|
||||||
return EXIT_FAILURE;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct wl_registry *registry = wl_display_get_registry(display);
|
|
||||||
wl_registry_add_listener(registry, ®istry_listener, NULL);
|
|
||||||
wl_display_roundtrip(display);
|
|
||||||
|
|
||||||
if (pointer_manager == NULL) {
|
|
||||||
fprintf(stderr, "compositor does not support wlr-virtual-pointer-unstable-v1\n");
|
|
||||||
return EXIT_FAILURE;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct zwlr_virtual_pointer_v1 *pointer =
|
|
||||||
zwlr_virtual_pointer_manager_v1_create_virtual_pointer(
|
|
||||||
pointer_manager, seat);
|
|
||||||
|
|
||||||
const char *cmd = argv[1];
|
|
||||||
if (strcmp(cmd, "motion") == 0) {
|
|
||||||
if (argc < 4) {
|
|
||||||
fprintf(stderr, "Usage: ./virtual-pointer motion <dx> <dy>\n");
|
|
||||||
return EXIT_FAILURE;
|
|
||||||
}
|
|
||||||
wl_fixed_t dx = wl_fixed_from_double(atof(argv[2]));
|
|
||||||
wl_fixed_t dy = wl_fixed_from_double(atof(argv[3]));
|
|
||||||
zwlr_virtual_pointer_v1_motion(pointer, 0, dx, dy);
|
|
||||||
} else if (strcmp(cmd, "absolute") == 0) {
|
|
||||||
if (argc < 6) {
|
|
||||||
fprintf(stderr, "Usage: ./virtual-pointer absolute <x> <y> <x_extent> <y_extent>\n");
|
|
||||||
return EXIT_FAILURE;
|
|
||||||
}
|
|
||||||
uint32_t x = atoi(argv[2]);
|
|
||||||
uint32_t y = atoi(argv[3]);
|
|
||||||
uint32_t x_extent = atoi(argv[4]);
|
|
||||||
uint32_t y_extent = atoi(argv[5]);
|
|
||||||
zwlr_virtual_pointer_v1_motion_absolute(pointer, 0, x, y, x_extent, y_extent);
|
|
||||||
} else if (strcmp(cmd, "button") == 0) {
|
|
||||||
if (argc < 4) {
|
|
||||||
fprintf(stderr, "Usage: ./virtual-pointer button <button> press|release\n");
|
|
||||||
return EXIT_FAILURE;
|
|
||||||
}
|
|
||||||
uint32_t button = atoi(argv[2]);
|
|
||||||
bool press = !!strcmp(argv[3], "release");
|
|
||||||
zwlr_virtual_pointer_v1_button(pointer, 0, button, press);
|
|
||||||
} else if (strcmp(cmd, "axis") == 0) {
|
|
||||||
if (argc < 4) {
|
|
||||||
fprintf(stderr, "Usage: ./virtual-pointer axis <axis> <value>\n");
|
|
||||||
return EXIT_FAILURE;
|
|
||||||
}
|
|
||||||
uint32_t axis = atoi(argv[2]);
|
|
||||||
wl_fixed_t value = wl_fixed_from_double(atof(argv[3]));
|
|
||||||
zwlr_virtual_pointer_v1_axis(pointer, 0, axis, value);
|
|
||||||
zwlr_virtual_pointer_v1_axis_stop(pointer, 0, axis);
|
|
||||||
} else if (strcmp(cmd, "axis_discrete") == 0) {
|
|
||||||
if (argc < 5) {
|
|
||||||
fprintf(stderr, "Usage: ./virtual-pointer axis <axis> <value> <value_discrete>\n");
|
|
||||||
return EXIT_FAILURE;
|
|
||||||
}
|
|
||||||
uint32_t axis = atoi(argv[2]);
|
|
||||||
wl_fixed_t value = wl_fixed_from_double(atof(argv[3]));
|
|
||||||
uint32_t discrete = atoi(argv[4]);
|
|
||||||
zwlr_virtual_pointer_v1_axis_discrete(pointer, 0, axis, value, discrete);
|
|
||||||
zwlr_virtual_pointer_v1_axis_stop(pointer, 0, axis);
|
|
||||||
} else {
|
|
||||||
fprintf(stderr, "Invalid subcommand\n");
|
|
||||||
return EXIT_FAILURE;
|
|
||||||
}
|
|
||||||
|
|
||||||
zwlr_virtual_pointer_v1_frame(pointer);
|
|
||||||
zwlr_virtual_pointer_v1_destroy(pointer);
|
|
||||||
|
|
||||||
wl_display_flush(display);
|
|
||||||
|
|
||||||
return EXIT_SUCCESS;
|
|
||||||
}
|
|
Loading…
Reference in New Issue
Block a user