Merge branch 'rootston'

This commit is contained in:
Drew DeVault 2017-09-28 19:06:41 -04:00
commit 033036712a
47 changed files with 2754 additions and 1116 deletions

View file

@ -68,5 +68,5 @@ void handle_keyboard_key(struct libinput_event *event,
wlr_event.state = WLR_KEY_PRESSED;
break;
}
wl_signal_emit(&wlr_dev->keyboard->events.key, &wlr_event);
wlr_keyboard_update_state(wlr_dev->keyboard, &wlr_event);
}

View file

@ -35,26 +35,13 @@ static bool wlr_wl_backend_start(struct wlr_backend *_backend) {
struct wlr_wl_backend *backend = (struct wlr_wl_backend *)_backend;
wlr_log(L_INFO, "Initializating wayland backend");
backend->remote_display = wl_display_connect(NULL);
if (!backend->remote_display) {
wlr_log_errno(L_ERROR, "Could not connect to remote display");
return false;
}
if (!(backend->registry = wl_display_get_registry(backend->remote_display))) {
wlr_log_errno(L_ERROR, "Could not obtain reference to remote registry");
return false;
}
wlr_wl_registry_poll(backend);
if (!(backend->compositor) || (!(backend->shell))) {
wlr_log_errno(L_ERROR, "Could not obtain retrieve required globals");
return false;
}
wlr_egl_init(&backend->egl, EGL_PLATFORM_WAYLAND_EXT, backend->remote_display);
wlr_egl_bind_display(&backend->egl, backend->local_display);
backend->started = true;
for (size_t i = 0; i < backend->requested_outputs; ++i) {
wlr_wl_output_create(&backend->backend);
}
@ -146,6 +133,21 @@ struct wlr_backend *wlr_wl_backend_create(struct wl_display *display) {
}
backend->local_display = display;
backend->remote_display = wl_display_connect(NULL);
if (!backend->remote_display) {
wlr_log_errno(L_ERROR, "Could not connect to remote display");
return false;
}
if (!(backend->registry = wl_display_get_registry(backend->remote_display))) {
wlr_log_errno(L_ERROR, "Could not obtain reference to remote registry");
return false;
}
wlr_egl_init(&backend->egl, EGL_PLATFORM_WAYLAND_EXT, backend->remote_display);
wlr_egl_bind_display(&backend->egl, backend->local_display);
return &backend->backend;
error:

View file

@ -209,7 +209,7 @@ static struct zxdg_toplevel_v6_listener xdg_toplevel_listener = {
struct wlr_output *wlr_wl_output_create(struct wlr_backend *_backend) {
assert(wlr_backend_is_wl(_backend));
struct wlr_wl_backend *backend = (struct wlr_wl_backend *)_backend;
if (!backend->remote_display) {
if (!backend->started) {
++backend->requested_outputs;
return NULL;
}

View file

@ -128,17 +128,15 @@ static const struct wl_pointer_listener pointer_listener = {
static void keyboard_handle_keymap(void *data, struct wl_keyboard *wl_keyboard,
uint32_t format, int32_t fd, uint32_t size) {
// TODO: set keymap
}
static void keyboard_handle_enter(void *data, struct wl_keyboard *wl_keyboard,
uint32_t serial, struct wl_surface *surface, struct wl_array *keys) {
}
static void keyboard_handle_leave(void *data, struct wl_keyboard *wl_keyboard,
uint32_t serial, struct wl_surface *surface) {
}
static void keyboard_handle_key(void *data, struct wl_keyboard *wl_keyboard,
@ -151,7 +149,7 @@ static void keyboard_handle_key(void *data, struct wl_keyboard *wl_keyboard,
wlr_event.state = state;
wlr_event.time_sec = time / 1000;
wlr_event.time_usec = time * 1000;
wl_signal_emit(&dev->keyboard->events.key, &wlr_event);
wlr_keyboard_update_state(dev->keyboard, &wlr_event);
}
static void keyboard_handle_modifiers(void *data, struct wl_keyboard *wl_keyboard,

View file

@ -1,686 +0,0 @@
#define _POSIX_C_SOURCE 199309L
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <inttypes.h>
#include <unistd.h>
#include <sys/mman.h>
#include <wayland-server.h>
// TODO: BSD et al
#include <linux/input-event-codes.h>
#include <wlr/backend.h>
#include <wlr/backend/session.h>
#include <wlr/render.h>
#include <wlr/render/matrix.h>
#include <wlr/render/gles2.h>
#include <wlr/types/wlr_output.h>
#include <wlr/types/wlr_surface.h>
#include <wlr/types/wlr_wl_shell.h>
#include <wlr/types/wlr_xdg_shell_v6.h>
#include <wlr/types/wlr_seat.h>
#include <wlr/types/wlr_input_device.h>
#include <wlr/types/wlr_data_device_manager.h>
#include <wlr/types/wlr_gamma_control.h>
#include "wlr/types/wlr_compositor.h"
#include <wlr/types/wlr_cursor.h>
#include <wlr/types/wlr_output_layout.h>
#include <wlr/xwayland.h>
#include <xkbcommon/xkbcommon.h>
#include <wlr/util/log.h>
#include "config.h"
#include "shared.h"
#include <assert.h>
// TODO: move to common header?
int os_create_anonymous_file(off_t size);
struct sample_state;
struct example_xdg_surface_v6 {
struct wlr_xdg_surface_v6 *surface;
struct sample_state *sample;
// position of the wlr_surface in the layout
struct {
int lx;
int ly;
} position;
struct wl_listener destroy_listener;
struct wl_listener ping_timeout_listener;
struct wl_listener request_minimize_listener;
struct wl_listener request_move_listener;
struct wl_listener request_resize_listener;
struct wl_listener request_show_window_menu_listener;
};
struct input_event_cache {
uint32_t serial;
struct wlr_cursor *cursor;
struct wlr_input_device *device;
};
struct motion_context {
struct example_xdg_surface_v6 *surface;
int off_x, off_y;
};
struct sample_state {
struct wlr_renderer *renderer;
struct compositor_state *compositor;
struct wlr_compositor *wlr_compositor;
struct wlr_wl_shell *wl_shell;
struct wlr_seat *wl_seat;
struct wlr_xdg_shell_v6 *xdg_shell;
struct wlr_data_device_manager *data_device_manager;
struct wl_resource *focus;
struct wl_listener keyboard_bound;
struct wlr_xwayland *xwayland;
struct wlr_gamma_control_manager *gamma_control_manager;
bool mod_down;
int keymap_fd;
size_t keymap_size;
uint32_t serial;
struct motion_context motion_context;
struct example_config *config;
struct wlr_output_layout *layout;
struct wlr_cursor *cursor;
struct wlr_xcursor *xcursor;
// Ring buffer
int input_cache_idx;
struct input_event_cache input_cache[16];
struct wl_listener cursor_motion;
struct wl_listener cursor_motion_absolute;
struct wl_listener cursor_button;
struct wl_listener cursor_axis;
struct wl_listener tool_axis;
struct wl_listener tool_tip;
struct wl_listener tool_button;
struct wl_listener new_xdg_surface_v6;
struct wlr_xdg_surface_v6 *focused_surface;
};
static void example_set_focused_surface(struct sample_state *sample,
struct wlr_xdg_surface_v6 *surface) {
if (sample->focused_surface == surface) {
return;
}
// set activated state of the xdg surfaces
struct wlr_xdg_surface_v6 *xdg_surface;
struct wlr_xdg_client_v6 *xdg_client;
wl_list_for_each(xdg_client, &sample->xdg_shell->clients, link) {
wl_list_for_each(xdg_surface, &xdg_client->surfaces, link) {
if (!xdg_surface->configured ||
xdg_surface->role != WLR_XDG_SURFACE_V6_ROLE_TOPLEVEL) {
continue;
}
wlr_xdg_toplevel_v6_set_activated(xdg_surface,
xdg_surface == surface);
}
}
if (surface) {
// TODO: send array of currently pressed keys
struct wl_array keys;
wl_array_init(&keys);
wlr_seat_keyboard_enter(sample->wl_seat, surface->surface, keys);
} else {
wlr_seat_keyboard_clear_focus(sample->wl_seat);
}
sample->focused_surface = surface;
}
/*
* Convert timespec to milliseconds
*/
static inline int64_t timespec_to_msec(const struct timespec *a) {
return (int64_t)a->tv_sec * 1000 + a->tv_nsec / 1000000;
}
static void output_frame_handle_surface(struct sample_state *sample,
struct wlr_output *wlr_output, struct timespec *ts,
struct wl_resource *_res, int ox, int oy) {
struct wlr_surface *surface = wl_resource_get_user_data(_res);
float matrix[16];
float transform[16];
wlr_surface_flush_damage(surface);
if (surface->texture->valid) {
wlr_matrix_translate(&transform, ox, oy, 0);
wlr_surface_get_matrix(surface, &matrix,
&wlr_output->transform_matrix, &transform);
wlr_render_with_matrix(sample->renderer, surface->texture, &matrix);
struct wlr_frame_callback *cb, *cnext;
wl_list_for_each_safe(cb, cnext, &surface->frame_callback_list, link) {
wl_callback_send_done(cb->resource, timespec_to_msec(ts));
wl_resource_destroy(cb->resource);
}
}
}
static void handle_xdg_surface_v6_ping_timeout(struct wl_listener *listener,
void *data) {
struct wlr_xdg_surface_v6 *surface = data;
wlr_log(L_DEBUG, "got ping timeout for surface: %s", surface->title);
}
static void handle_xdg_surface_v6_destroy(struct wl_listener *listener,
void *data) {
struct example_xdg_surface_v6 *example_surface =
wl_container_of(listener, example_surface, destroy_listener);
wl_list_remove(&example_surface->destroy_listener.link);
wl_list_remove(&example_surface->ping_timeout_listener.link);
wl_list_remove(&example_surface->request_move_listener.link);
wl_list_remove(&example_surface->request_resize_listener.link);
wl_list_remove(&example_surface->request_show_window_menu_listener.link);
wl_list_remove(&example_surface->request_minimize_listener.link);
free(example_surface);
}
static void handle_xdg_surface_v6_request_move(struct wl_listener *listener,
void *data) {
struct example_xdg_surface_v6 *esurface =
wl_container_of(listener, esurface, request_move_listener);
struct wlr_xdg_toplevel_v6_move_event *e = data;
struct sample_state *sample = esurface->sample;
struct input_event_cache *event = NULL;
for (size_t i = 0;
i < sizeof(sample->input_cache) / sizeof(sample->input_cache[0]);
++i) {
if (sample->input_cache[i].cursor
&& sample->input_cache[i].serial == e->serial) {
event = &sample->input_cache[i];
break;
}
}
if (!event || sample->motion_context.surface) {
return;
}
sample->motion_context.surface = esurface;
sample->motion_context.off_x = sample->cursor->x - esurface->position.lx;
sample->motion_context.off_y = sample->cursor->y - esurface->position.ly;
wlr_seat_pointer_clear_focus(sample->wl_seat);
}
static void handle_xdg_surface_v6_request_resize(struct wl_listener *listener,
void *data) {
struct example_xdg_surface_v6 *example_surface =
wl_container_of(listener, example_surface, request_resize_listener);
struct wlr_xdg_toplevel_v6_resize_event *e = data;
wlr_log(L_DEBUG, "TODO: surface requested resize: %s", e->surface->title);
}
static void handle_xdg_surface_v6_request_show_window_menu(
struct wl_listener *listener, void *data) {
struct example_xdg_surface_v6 *example_surface =
wl_container_of(listener, example_surface,
request_show_window_menu_listener);
struct wlr_xdg_toplevel_v6_show_window_menu_event *e = data;
wlr_log(L_DEBUG, "TODO: surface requested to show window menu: %s",
e->surface->title);
}
static void handle_xdg_surface_v6_request_minimize(
struct wl_listener *listener, void *data) {
struct example_xdg_surface_v6 *example_surface =
wl_container_of(listener, example_surface, request_minimize_listener);
wlr_log(L_DEBUG, "TODO: surface requested to be minimized: %s",
example_surface->surface->title);
}
static void handle_new_xdg_surface_v6(struct wl_listener *listener,
void *data) {
struct sample_state *sample_state =
wl_container_of(listener, sample_state, new_xdg_surface_v6);
struct wlr_xdg_surface_v6 *surface = data;
wlr_log(L_DEBUG, "new xdg surface: title=%s, app_id=%s",
surface->title, surface->app_id);
wlr_xdg_surface_v6_ping(surface);
struct example_xdg_surface_v6 *esurface =
calloc(1, sizeof(struct example_xdg_surface_v6));
if (esurface == NULL) {
return;
}
esurface->sample = sample_state;
esurface->surface = surface;
// TODO sensible default position
esurface->position.lx = 300;
esurface->position.ly = 300;
surface->data = esurface;
wl_signal_add(&surface->events.destroy, &esurface->destroy_listener);
esurface->destroy_listener.notify = handle_xdg_surface_v6_destroy;
wl_signal_add(&surface->events.ping_timeout,
&esurface->ping_timeout_listener);
esurface->ping_timeout_listener.notify = handle_xdg_surface_v6_ping_timeout;
wl_signal_add(&surface->events.request_move,
&esurface->request_move_listener);
esurface->request_move_listener.notify = handle_xdg_surface_v6_request_move;
wl_signal_add(&surface->events.request_resize,
&esurface->request_resize_listener);
esurface->request_resize_listener.notify =
handle_xdg_surface_v6_request_resize;
wl_signal_add(&surface->events.request_show_window_menu,
&esurface->request_show_window_menu_listener);
esurface->request_show_window_menu_listener.notify =
handle_xdg_surface_v6_request_show_window_menu;
wl_signal_add(&surface->events.request_minimize,
&esurface->request_minimize_listener);
esurface->request_minimize_listener.notify =
handle_xdg_surface_v6_request_minimize;
}
static void handle_output_frame(struct output_state *output,
struct timespec *ts) {
struct compositor_state *state = output->compositor;
struct sample_state *sample = state->data;
struct wlr_output *wlr_output = output->output;
wlr_output_make_current(wlr_output);
wlr_renderer_begin(sample->renderer, wlr_output);
struct wlr_wl_shell_surface *wl_shell_surface;
wl_list_for_each(wl_shell_surface, &sample->wl_shell->surfaces, link) {
output_frame_handle_surface(sample, wlr_output, ts,
wl_shell_surface->surface, 200, 200);
}
struct wlr_xdg_surface_v6 *xdg_surface;
struct wlr_xdg_client_v6 *xdg_client;
wl_list_for_each(xdg_client, &sample->xdg_shell->clients, link) {
wl_list_for_each(xdg_surface, &xdg_client->surfaces, link) {
if (!xdg_surface->configured) {
continue;
}
struct example_xdg_surface_v6 *esurface = xdg_surface->data;
assert(esurface);
int width = xdg_surface->surface->current.buffer_width;
int height = xdg_surface->surface->current.buffer_height;
bool intersects_output = wlr_output_layout_intersects(
sample->layout, wlr_output,
esurface->position.lx, esurface->position.ly,
esurface->position.lx + width, esurface->position.ly + height);
if (intersects_output) {
double ox = esurface->position.lx, oy = esurface->position.ly;
wlr_output_layout_output_coords(sample->layout, wlr_output,
&ox, &oy);
output_frame_handle_surface(sample, wlr_output, ts,
xdg_surface->surface->resource, ox, oy);
}
}
}
struct wlr_x11_window *x11_window;
wl_list_for_each(x11_window, &sample->xwayland->displayable_windows, link) {
output_frame_handle_surface(sample, wlr_output, ts,
x11_window->surface, 200, 200);
}
wlr_renderer_end(sample->renderer);
wlr_output_swap_buffers(wlr_output);
}
static void handle_keyboard_key(struct keyboard_state *keyboard,
uint32_t keycode, xkb_keysym_t sym, enum wlr_key_state key_state,
uint64_t time_usec) {
struct compositor_state *state = keyboard->compositor;
struct sample_state *sample = state->data;
uint32_t depressed = xkb_state_serialize_mods(keyboard->xkb_state,
XKB_STATE_MODS_DEPRESSED);
uint32_t latched = xkb_state_serialize_mods(keyboard->xkb_state,
XKB_STATE_MODS_LATCHED);
uint32_t locked = xkb_state_serialize_mods(keyboard->xkb_state,
XKB_STATE_MODS_LOCKED);
uint32_t group = xkb_state_serialize_layout(keyboard->xkb_state,
XKB_STATE_LAYOUT_EFFECTIVE);
wlr_seat_keyboard_send_modifiers(sample->wl_seat, depressed, latched,
locked, group);
wlr_seat_keyboard_send_key(sample->wl_seat, (uint32_t)time_usec, keycode,
key_state);
if (sym == XKB_KEY_Super_L || sym == XKB_KEY_Super_R) {
sample->mod_down = key_state == WLR_KEY_PRESSED;
}
}
static struct wlr_xdg_surface_v6 *example_xdg_surface_at(
struct sample_state *sample, int lx, int ly) {
struct wlr_xdg_surface_v6 *xdg_surface;
struct wlr_xdg_client_v6 *xdg_client;
wl_list_for_each(xdg_client, &sample->xdg_shell->clients, link) {
wl_list_for_each(xdg_surface, &xdg_client->surfaces, link) {
if (!xdg_surface->configured) {
continue;
}
struct example_xdg_surface_v6 *esurface = xdg_surface->data;
double window_x = esurface->position.lx + xdg_surface->geometry->x;
double window_y = esurface->position.ly + xdg_surface->geometry->y;
if (sample->cursor->x >= window_x &&
sample->cursor->y >= window_y &&
sample->cursor->x <= window_x +
xdg_surface->geometry->width &&
sample->cursor->y <= window_y +
xdg_surface->geometry->height) {
return xdg_surface;
}
}
}
return NULL;
}
static void update_pointer_position(struct sample_state *sample, uint32_t time) {
if (sample->motion_context.surface) {
struct example_xdg_surface_v6 *surface;
surface = sample->motion_context.surface;
surface->position.lx = sample->cursor->x - sample->motion_context.off_x;
surface->position.ly = sample->cursor->y - sample->motion_context.off_y;
return;
}
struct wlr_xdg_surface_v6 *surface = example_xdg_surface_at(sample,
sample->cursor->x, sample->cursor->y);
if (surface) {
struct example_xdg_surface_v6 *esurface = surface->data;
double sx = sample->cursor->x - esurface->position.lx;
double sy = sample->cursor->y - esurface->position.ly;
// TODO z-order
wlr_seat_pointer_enter(sample->wl_seat, surface->surface, sx, sy);
wlr_seat_pointer_send_motion(sample->wl_seat, time, sx, sy);
} else {
wlr_seat_pointer_clear_focus(sample->wl_seat);
}
}
static void handle_cursor_motion(struct wl_listener *listener, void *data) {
struct sample_state *sample =
wl_container_of(listener, sample, cursor_motion);
struct wlr_event_pointer_motion *event = data;
wlr_cursor_move(sample->cursor, event->device, event->delta_x,
event->delta_y);
update_pointer_position(sample, (uint32_t)event->time_usec);
}
static void handle_cursor_motion_absolute(struct wl_listener *listener,
void *data) {
struct sample_state *sample =
wl_container_of(listener, sample, cursor_motion_absolute);
struct wlr_event_pointer_motion_absolute *event = data;
wlr_cursor_warp_absolute(sample->cursor, event->device,
event->x_mm / event->width_mm, event->y_mm / event->height_mm);
update_pointer_position(sample, (uint32_t)event->time_usec);
}
static void handle_cursor_axis(struct wl_listener *listener, void *data) {
struct sample_state *sample =
wl_container_of(listener, sample, cursor_axis);
struct wlr_event_pointer_axis *event = data;
wlr_seat_pointer_send_axis(sample->wl_seat, event->time_sec,
event->orientation, event->delta);
}
static void handle_cursor_button(struct wl_listener *listener, void *data) {
struct sample_state *sample =
wl_container_of(listener, sample, cursor_button);
struct wlr_event_pointer_button *event = data;
struct wlr_xdg_surface_v6 *surface =
example_xdg_surface_at(sample, sample->cursor->x, sample->cursor->y);
uint32_t serial = wlr_seat_pointer_send_button(sample->wl_seat,
(uint32_t)event->time_usec, event->button, event->state);
int i;
switch (event->state) {
case WLR_BUTTON_RELEASED:
if (sample->motion_context.surface) {
sample->motion_context.surface = NULL;
}
break;
case WLR_BUTTON_PRESSED:
i = sample->input_cache_idx;
sample->input_cache[i].serial = serial;
sample->input_cache[i].cursor = sample->cursor;
sample->input_cache[i].device = event->device;
sample->input_cache_idx = (i + 1)
% (sizeof(sample->input_cache) / sizeof(sample->input_cache[0]));
example_set_focused_surface(sample, surface);
wlr_log(L_DEBUG, "Stored event %d at %d", serial, i);
if (sample->mod_down && event->button == BTN_LEFT) {
struct example_xdg_surface_v6 *esurface = surface->data;
sample->motion_context.surface = esurface;
sample->motion_context.off_x = sample->cursor->x - esurface->position.lx;
sample->motion_context.off_y = sample->cursor->y - esurface->position.ly;
wlr_seat_pointer_clear_focus(sample->wl_seat);
}
break;
}
}
static void handle_tool_axis(struct wl_listener *listener, void *data) {
struct sample_state *sample =
wl_container_of(listener, sample, tool_axis);
struct wlr_event_tablet_tool_axis *event = data;
if ((event->updated_axes & WLR_TABLET_TOOL_AXIS_X) &&
(event->updated_axes & WLR_TABLET_TOOL_AXIS_Y)) {
wlr_cursor_warp_absolute(sample->cursor, event->device,
event->x_mm / event->width_mm, event->y_mm / event->height_mm);
update_pointer_position(sample, (uint32_t)event->time_usec);
}
}
static void handle_tool_tip(struct wl_listener *listener, void *data) {
struct sample_state *sample =
wl_container_of(listener, sample, tool_tip);
struct wlr_event_tablet_tool_tip *event = data;
struct wlr_xdg_surface_v6 *surface =
example_xdg_surface_at(sample, sample->cursor->x, sample->cursor->y);
example_set_focused_surface(sample, surface);
wlr_seat_pointer_send_button(sample->wl_seat, (uint32_t)event->time_usec,
BTN_LEFT, event->state);
}
static void handle_input_add(struct compositor_state *state,
struct wlr_input_device *device) {
struct sample_state *sample = state->data;
if (device->type == WLR_INPUT_DEVICE_POINTER ||
device->type == WLR_INPUT_DEVICE_TOUCH ||
device->type == WLR_INPUT_DEVICE_TABLET_TOOL) {
wlr_cursor_attach_input_device(sample->cursor, device);
example_config_configure_cursor(sample->config, sample->cursor,
sample->compositor);
}
}
static void handle_output_add(struct output_state *ostate) {
struct sample_state *sample = ostate->compositor->data;
struct wlr_output *wlr_output = ostate->output;
struct wlr_xcursor_image *image = sample->xcursor->images[0];
struct output_config *o_config =
example_config_get_output(sample->config, ostate->output);
if (o_config) {
wlr_output_transform(ostate->output, o_config->transform);
wlr_output_layout_add(sample->layout, ostate->output, o_config->x,
o_config->y);
} else {
wlr_output_layout_add_auto(sample->layout, ostate->output);
}
example_config_configure_cursor(sample->config, sample->cursor,
sample->compositor);
// TODO the cursor must be set depending on which surface it is displayed
// over which should happen in the compositor.
if (!wlr_output_set_cursor(wlr_output, image->buffer,
image->width, image->width, image->height)) {
wlr_log(L_DEBUG, "Failed to set hardware cursor");
return;
}
wlr_cursor_warp(sample->cursor, NULL, sample->cursor->x, sample->cursor->y);
}
static void handle_output_remove(struct output_state *ostate) {
struct sample_state *sample = ostate->compositor->data;
wlr_output_layout_remove(sample->layout, ostate->output);
example_config_configure_cursor(sample->config, sample->cursor,
sample->compositor);
}
int main(int argc, char *argv[]) {
struct sample_state state = { 0 };
struct compositor_state compositor = { 0,
.data = &state,
.output_frame_cb = handle_output_frame,
};
compositor.input_add_cb = handle_input_add;
compositor.output_add_cb = handle_output_add;
compositor.output_remove_cb = handle_output_remove;
state.compositor = &compositor;
state.config = parse_args(argc, argv);
state.cursor = wlr_cursor_create();
state.layout = wlr_output_layout_create();
wlr_cursor_attach_output_layout(state.cursor, state.layout);
wlr_cursor_map_to_region(state.cursor, state.config->cursor.mapped_box);
struct wlr_xcursor_theme *theme = wlr_xcursor_theme_load("default", 16);
if (!theme) {
wlr_log(L_ERROR, "Failed to load cursor theme");
return 1;
}
state.xcursor = wlr_xcursor_theme_get_cursor(theme, "left_ptr");
if (!state.xcursor) {
wlr_log(L_ERROR, "Failed to load left_ptr cursor");
return 1;
}
wlr_cursor_set_xcursor(state.cursor, state.xcursor);
// pointer events
wl_signal_add(&state.cursor->events.motion, &state.cursor_motion);
state.cursor_motion.notify = handle_cursor_motion;
wl_signal_add(&state.cursor->events.motion_absolute,
&state.cursor_motion_absolute);
state.cursor_motion_absolute.notify = handle_cursor_motion_absolute;
wl_signal_add(&state.cursor->events.button, &state.cursor_button);
state.cursor_button.notify = handle_cursor_button;
wl_signal_add(&state.cursor->events.axis, &state.cursor_axis);
state.cursor_axis.notify = handle_cursor_axis;
wl_signal_add(&state.cursor->events.tablet_tool_axis, &state.tool_axis);
state.tool_axis.notify = handle_tool_axis;
wl_signal_add(&state.cursor->events.tablet_tool_tip, &state.tool_tip);
state.tool_tip.notify = handle_tool_tip;
compositor_init(&compositor);
state.renderer = wlr_gles2_renderer_create(compositor.backend);
if (!state.renderer) {
wlr_log(L_ERROR, "Could not start compositor, OOM");
exit(EXIT_FAILURE);
}
wl_display_init_shm(compositor.display);
state.wlr_compositor =
wlr_compositor_create(compositor.display, state.renderer);
state.wl_shell = wlr_wl_shell_create(compositor.display);
state.xdg_shell = wlr_xdg_shell_v6_create(compositor.display);
// shell events
wl_signal_add(&state.xdg_shell->events.new_surface,
&state.new_xdg_surface_v6);
state.new_xdg_surface_v6.notify = handle_new_xdg_surface_v6;
state.data_device_manager =
wlr_data_device_manager_create(compositor.display);
state.gamma_control_manager =
wlr_gamma_control_manager_create(compositor.display);
state.wl_seat = wlr_seat_create(compositor.display, "seat0");
assert(state.wl_seat);
wlr_seat_set_capabilities(state.wl_seat, WL_SEAT_CAPABILITY_KEYBOARD
| WL_SEAT_CAPABILITY_POINTER | WL_SEAT_CAPABILITY_TOUCH);
struct keyboard_state *kbstate;
wl_list_for_each(kbstate, &compositor.keyboards, link) {
char *keymap = xkb_keymap_get_as_string(kbstate->keymap,
XKB_KEYMAP_FORMAT_TEXT_V1);
state.keymap_size = strlen(keymap);
state.keymap_fd = os_create_anonymous_file(state.keymap_size);
void *ptr =
mmap(NULL, state.keymap_size, PROT_READ | PROT_WRITE, MAP_SHARED,
state.keymap_fd, 0);
strcpy(ptr, keymap);
free(keymap);
break;
}
wlr_seat_keyboard_set_keymap(state.wl_seat, state.keymap_fd,
state.keymap_size);
state.xwayland = wlr_xwayland_create(compositor.display,
state.wlr_compositor);
compositor.keyboard_key_cb = handle_keyboard_key;
wl_display_run(compositor.display);
wl_list_remove(&state.new_xdg_surface_v6.link);
wlr_xwayland_destroy(state.xwayland);
close(state.keymap_fd);
wlr_seat_destroy(state.wl_seat);
wlr_gamma_control_manager_destroy(state.gamma_control_manager);
wlr_data_device_manager_destroy(state.data_device_manager);
wlr_xdg_shell_v6_destroy(state.xdg_shell);
wlr_wl_shell_destroy(state.wl_shell);
wlr_compositor_destroy(state.wlr_compositor);
wlr_renderer_destroy(state.renderer);
compositor_fini(&compositor);
}

View file

@ -22,10 +22,3 @@ executable(
dependencies: wlroots,
link_with: lib_shared,
)
executable(
'compositor',
'compositor.c',
dependencies: wlroots,
link_with: lib_shared,
)

View file

@ -204,6 +204,11 @@ int main(int argc, char *argv[]) {
wlr_texture_upload_pixels(state.cat_texture, WL_SHM_FORMAT_ABGR8888,
cat_tex.width, cat_tex.width, cat_tex.height, cat_tex.pixel_data);
if (!wlr_backend_start(compositor.backend)) {
wlr_log(L_ERROR, "Failed to start backend");
wlr_backend_destroy(compositor.backend);
exit(1);
}
wl_display_run(compositor.display);
wlr_texture_destroy(state.cat_texture);

View file

@ -333,6 +333,11 @@ int main(int argc, char *argv[]) {
wlr_cursor_set_xcursor(state.cursor, state.xcursor);
compositor_init(&compositor);
if (!wlr_backend_start(compositor.backend)) {
wlr_log(L_ERROR, "Failed to start backend");
wlr_backend_destroy(compositor.backend);
exit(1);
}
wl_display_run(compositor.display);
compositor_fini(&compositor);

View file

@ -147,6 +147,11 @@ int main(int argc, char *argv[]) {
wlr_texture_upload_pixels(state.cat_texture, WL_SHM_FORMAT_ABGR8888,
cat_tex.width, cat_tex.width, cat_tex.height, cat_tex.pixel_data);
if (!wlr_backend_start(compositor.backend)) {
wlr_log(L_ERROR, "Failed to start backend");
wlr_backend_destroy(compositor.backend);
exit(1);
}
wl_display_run(compositor.display);
wlr_texture_destroy(state.cat_texture);

View file

@ -538,11 +538,6 @@ void compositor_init(struct compositor_state *state) {
wlr_log(L_INFO, "Running compositor on wayland display '%s'", socket);
setenv("_WAYLAND_DISPLAY", socket, true);
if (!wlr_backend_start(state->backend)) {
wlr_log(L_ERROR, "Failed to start backend");
wlr_backend_destroy(wlr);
exit(1);
}
}
void compositor_fini(struct compositor_state *state) {

View file

@ -9,6 +9,7 @@
#include <wlr/backend.h>
#include <wlr/backend/session.h>
#include <wlr/types/wlr_output.h>
#include <wlr/util/log.h>
#include <xkbcommon/xkbcommon.h>
#include "shared.h"
@ -52,6 +53,11 @@ int main() {
.output_frame_cb = handle_output_frame,
};
compositor_init(&compositor);
if (!wlr_backend_start(compositor.backend)) {
wlr_log(L_ERROR, "Failed to start backend");
wlr_backend_destroy(compositor.backend);
exit(1);
}
wl_display_run(compositor.display);
compositor_fini(&compositor);
}

View file

@ -158,6 +158,11 @@ int main(int argc, char *argv[]) {
wlr_log(L_ERROR, "Could not start compositor, OOM");
exit(EXIT_FAILURE);
}
if (!wlr_backend_start(compositor.backend)) {
wlr_log(L_ERROR, "Failed to start backend");
wlr_backend_destroy(compositor.backend);
exit(1);
}
wl_display_run(compositor.display);
wlr_renderer_destroy(state.renderer);

View file

@ -120,6 +120,11 @@ int main(int argc, char *argv[]) {
wlr_texture_upload_pixels(state.cat_texture, WL_SHM_FORMAT_ARGB8888,
cat_tex.width, cat_tex.width, cat_tex.height, cat_tex.pixel_data);
if (!wlr_backend_start(compositor.backend)) {
wlr_log(L_ERROR, "Failed to start backend");
wlr_backend_destroy(compositor.backend);
exit(1);
}
wl_display_run(compositor.display);
wlr_texture_destroy(state.cat_texture);

View file

@ -1,57 +0,0 @@
# Configuration
# -------------
# Some examples will read a configuration file. Not all examples will use all of
# the configuration options. The configuration file will be loaded from
# `wlr-example.ini` from the current directory or the path can be specified by the
# `-C` option given on the command line.
#
# Output configuration
# ~~~~~~~~~~~~~~~~~~~~
# Each output is specified in a section named [output:{NAME}] where NAME is the
# drm name for this output.
#
# Value "x" specifies the x-coordinate in the output layout.
#
# Value "y" specifies the y-coordinate in the output layout.
#
# Value "rotate" specifies output rotation and can be 90, 180, 270, flipped,
# flipped-90, flipped-180, or flipped-270
[output:HDMI-A-1]
x=3000
y=0
rotate=90
[output:DP-1]
x=0
y=0
rotate=270
[output:DVI-D-1]
x=1080
y=232
# Cursor Configuration
# ~~~~~~~~~~~~~~~~~~~~
# Value "map-to-output" specifies the output to which the cursor is
# constrained.
#
# Value "geometry" specifies the geometry (widthxheight+x+y) to which the cursor
# is constrained.
[cursor]
map-to-output=HDMI-A-1
geometry=500x700+50+50
# Device Configuration
# ~~~~~~~~~~~~~~~~~~~~
# Each device is specified in a section named [device:{NAME}] where NAME is the
# name given to this device. See a log file for device names.
#
# Value "map-to-output" specifies the output to which the device is constrained.
#
# Value "geometry" specifies the geometry (widthxheight+x+y) to which the device
# is constrained.
[device:Razer Razer DeathAdder 2013]
map-to-output=DP-1
geometry=500x700+50+50
# vim:filetype=dosini

View file

@ -1,6 +1,7 @@
#ifndef BACKEND_WAYLAND_H
#define BACKEND_WAYLAND_H
#include <stdbool.h>
#include <wayland-client.h>
#include <wayland-server.h>
#include <wayland-egl.h>
@ -14,6 +15,7 @@ struct wlr_wl_backend {
struct wlr_backend backend;
/* local state */
bool started;
struct wl_display *local_display;
list_t *devices;
list_t *outputs;

51
include/rootston/config.h Normal file
View file

@ -0,0 +1,51 @@
#ifndef _ROOTSTON_CONFIG_H
#define _ROOTSTON_CONFIG_H
#include <wlr/types/wlr_output_layout.h>
#include <wlr/types/wlr_input_device.h>
#include <wlr/types/wlr_cursor.h>
struct output_config {
char *name;
enum wl_output_transform transform;
int x, y;
struct wl_list link;
};
struct device_config {
char *name;
char *mapped_output;
struct wlr_box *mapped_box;
struct wl_list link;
};
struct roots_config {
// TODO: Multiple cursors, multiseat
struct {
char *mapped_output;
struct wlr_box *mapped_box;
} cursor;
struct wl_list outputs;
struct wl_list devices;
char *config_path;
};
struct roots_config *parse_args(int argc, char *argv[]);
void roots_config_destroy(struct roots_config *config);
/**
* Get configuration for the output. If the output is not configured, returns
* NULL.
*/
struct output_config *config_get_output(struct roots_config *config,
struct wlr_output *output);
/**
* Get configuration for the device. If the device is not configured, returns
* NULL.
*/
struct device_config *config_get_device(struct roots_config *config,
struct wlr_input_device *device);
#endif

View file

@ -0,0 +1,64 @@
#ifndef _ROOTSTON_DESKTOP_H
#define _ROOTSTON_DESKTOP_H
#include <time.h>
#include <wayland-server.h>
#include <wlr/types/wlr_output.h>
#include <wlr/types/wlr_output_layout.h>
#include <wlr/types/wlr_compositor.h>
#include <wlr/types/wlr_wl_shell.h>
#include <wlr/types/wlr_xdg_shell_v6.h>
#include <wlr/types/wlr_gamma_control.h>
#include "rootston/view.h"
#include "rootston/config.h"
struct roots_output {
struct roots_desktop *desktop;
struct wlr_output *wlr_output;
struct wl_listener frame;
struct wl_listener resolution;
struct timespec last_frame;
struct wl_list link;
};
struct roots_desktop {
struct wl_list views;
struct wl_list outputs;
struct timespec last_frame;
struct roots_server *server;
struct roots_config *config;
struct wlr_output_layout *layout;
struct wlr_compositor *compositor;
struct wlr_wl_shell *wl_shell;
struct wlr_xdg_shell_v6 *xdg_shell_v6;
struct wlr_xwayland *xwayland;
struct wlr_gamma_control_manager *gamma_control_manager;
struct wl_listener output_add;
struct wl_listener output_remove;
struct wl_listener xdg_shell_v6_surface;
struct wl_listener xwayland_surface;
struct wl_listener wl_shell_surface;
};
struct roots_server;
struct roots_desktop *desktop_create(struct roots_server *server,
struct roots_config *config);
void desktop_destroy(struct roots_desktop *desktop);
void view_destroy(struct roots_view *view);
struct roots_view *view_at(struct roots_desktop *desktop, int x, int y);
void view_activate(struct roots_view *view, bool activate);
void output_add_notify(struct wl_listener *listener, void *data);
void output_remove_notify(struct wl_listener *listener, void *data);
void handle_xdg_shell_v6_surface(struct wl_listener *listener, void *data);
void handle_wl_shell_surface(struct wl_listener *listener, void *data);
void handle_xwayland_surface(struct wl_listener *listener, void *data);
#endif

93
include/rootston/ini.h Normal file
View file

@ -0,0 +1,93 @@
/* inih -- simple .INI file parser
inih is released under the New BSD license (see LICENSE.txt). Go to the project
home page for more info:
https://github.com/benhoyt/inih
*/
#ifndef __INI_H__
#define __INI_H__
/* Make this header file easier to include in C++ code */
#ifdef __cplusplus
extern "C" {
#endif
#include <stdio.h>
/* Typedef for prototype of handler function. */
typedef int (*ini_handler)(void* user, const char* section,
const char* name, const char* value);
/* Typedef for prototype of fgets-style reader function. */
typedef char* (*ini_reader)(char* str, int num, void* stream);
/* Parse given INI-style file. May have [section]s, name=value pairs
(whitespace stripped), and comments starting with ';' (semicolon). Section
is "" if name=value pair parsed before any section heading. name:value
pairs are also supported as a concession to Python's configparser.
For each name=value pair parsed, call handler function with given user
pointer as well as section, name, and value (data only valid for duration
of handler call). Handler should return nonzero on success, zero on error.
Returns 0 on success, line number of first error on parse error (doesn't
stop on first error), -1 on file open error, or -2 on memory allocation
error (only when INI_USE_STACK is zero).
*/
int ini_parse(const char* filename, ini_handler handler, void* user);
/* Same as ini_parse(), but takes a FILE* instead of filename. This doesn't
close the file when it's finished -- the caller must do that. */
int ini_parse_file(FILE* file, ini_handler handler, void* user);
/* Same as ini_parse(), but takes an ini_reader function pointer instead of
filename. Used for implementing custom or string-based I/O. */
int ini_parse_stream(ini_reader reader, void* stream, ini_handler handler,
void* user);
/* Nonzero to allow multi-line value parsing, in the style of Python's
configparser. If allowed, ini_parse() will call the handler with the same
name for each subsequent line parsed. */
#ifndef INI_ALLOW_MULTILINE
#define INI_ALLOW_MULTILINE 1
#endif
/* Nonzero to allow a UTF-8 BOM sequence (0xEF 0xBB 0xBF) at the start of
the file. See http://code.google.com/p/inih/issues/detail?id=21 */
#ifndef INI_ALLOW_BOM
#define INI_ALLOW_BOM 1
#endif
/* Nonzero to allow inline comments (with valid inline comment characters
specified by INI_INLINE_COMMENT_PREFIXES). Set to 0 to turn off and match
Python 3.2+ configparser behaviour. */
#ifndef INI_ALLOW_INLINE_COMMENTS
#define INI_ALLOW_INLINE_COMMENTS 1
#endif
#ifndef INI_INLINE_COMMENT_PREFIXES
#define INI_INLINE_COMMENT_PREFIXES ";"
#endif
/* Nonzero to use stack, zero to use heap (malloc/free). */
#ifndef INI_USE_STACK
#define INI_USE_STACK 1
#endif
/* Stop parsing on first error (default is to keep parsing). */
#ifndef INI_STOP_ON_FIRST_ERROR
#define INI_STOP_ON_FIRST_ERROR 0
#endif
/* Maximum line length for any line in INI file. */
#ifndef INI_MAX_LINE
#define INI_MAX_LINE 2000
#endif
#ifdef __cplusplus
}
#endif
#endif /* __INI_H__ */

110
include/rootston/input.h Normal file
View file

@ -0,0 +1,110 @@
#ifndef _ROOTSTON_INPUT_H
#define _ROOTSTON_INPUT_H
#include <xkbcommon/xkbcommon.h>
#include <wayland-server.h>
#include <wlr/types/wlr_input_device.h>
#include <wlr/types/wlr_cursor.h>
#include <wlr/types/wlr_seat.h>
#include <wlr/xcursor.h>
#include "rootston/config.h"
#include "rootston/view.h"
#include "rootston/server.h"
struct roots_keyboard {
struct roots_input *input;
struct wlr_input_device *device;
struct wl_listener key;
struct wl_list link;
};
struct roots_pointer {
struct roots_input *input;
struct wlr_input_device *device;
// We don't listen to any pointer events directly - they go through
// wlr_cursor
struct wl_list link;
};
struct roots_touch {
struct roots_input *input;
struct wlr_input_device *device;
struct wl_listener down;
struct wl_listener up;
struct wl_listener motion;
struct wl_listener cancel;
struct wl_list link;
};
// TODO: tablet pad
struct roots_tablet_tool {
struct roots_input *input;
struct wlr_input_device *device;
struct wl_listener axis;
struct wl_listener proximity;
struct wl_listener tip;
struct wl_listener button;
struct wl_list link;
};
enum roots_cursor_mode {
ROOTS_CURSOR_PASSTHROUGH = 0,
ROOTS_CURSOR_MOVE = 1,
ROOTS_CURSOR_RESIZE = 2,
ROOTS_CURSOR_ROTATE = 3,
};
struct roots_input_event {
uint32_t serial;
struct wlr_cursor *cursor;
struct wlr_input_device *device;
};
struct roots_input {
struct roots_config *config;
struct roots_server *server;
// TODO: multiseat, multicursor
struct wlr_cursor *cursor;
struct wlr_xcursor *xcursor;
struct wlr_seat *wl_seat;
enum roots_cursor_mode mode;
struct roots_view *active_view;
int offs_x, offs_y;
// Ring buffer of input events that could trigger move/resize/rotate
int input_events_idx;
struct roots_input_event input_events[16];
struct wl_list keyboards;
struct wl_list pointers;
struct wl_list touch;
struct wl_list tablet_tools;
struct wl_listener input_add;
struct wl_listener input_remove;
struct wl_listener cursor_motion;
struct wl_listener cursor_motion_absolute;
struct wl_listener cursor_button;
struct wl_listener cursor_axis;
struct wl_listener cursor_tool_axis;
struct wl_listener cursor_tool_tip;
};
struct roots_input *input_create(struct roots_server *server,
struct roots_config *config);
void input_destroy(struct roots_input *input);
void pointer_add(struct wlr_input_device *device, struct roots_input *input);
void pointer_remove(struct wlr_input_device *device, struct roots_input *input);
void keyboard_add(struct wlr_input_device *device, struct roots_input *input);
void keyboard_remove(struct wlr_input_device *device, struct roots_input *input);
void cursor_initialize(struct roots_input *input);
void cursor_load_config(struct roots_config *config,
struct wlr_cursor *cursor,
struct roots_input *input,
struct roots_desktop *desktop);
#endif

33
include/rootston/server.h Normal file
View file

@ -0,0 +1,33 @@
#ifndef _ROOTSTON_SERVER_H
#define _ROOTSTON_SERVER_H
#include <wayland-server.h>
#include <wlr/backend.h>
#include <wlr/backend/session.h>
#include <wlr/types/wlr_data_device_manager.h>
#include <wlr/render.h>
#include <wlr/xwayland.h>
#include "rootston/config.h"
#include "rootston/desktop.h"
#include "rootston/input.h"
struct roots_server {
/* Rootston resources */
struct roots_config *config;
struct roots_desktop *desktop;
struct roots_input *input;
/* Wayland resources */
struct wl_display *wl_display;
struct wl_event_loop *wl_event_loop;
/* WLR tools */
struct wlr_backend *backend;
struct wlr_renderer *renderer;
/* Global resources */
struct wlr_data_device_manager *data_device_manager;
};
extern struct roots_server server;
#endif

71
include/rootston/view.h Normal file
View file

@ -0,0 +1,71 @@
#ifndef _ROOTSTON_VIEW_H
#define _ROOTSTON_VIEW_H
#include <stdbool.h>
#include <wlr/types/wlr_box.h>
#include <wlr/types/wlr_surface.h>
#include <wlr/types/wlr_xdg_shell_v6.h>
struct roots_wl_shell_surface {
struct roots_view *view;
// TODO: Maybe destroy listener should go in roots_view
struct wl_listener destroy;
struct wl_listener ping_timeout;
struct wl_listener request_move;
struct wl_listener request_resize;
struct wl_listener request_set_fullscreen;
struct wl_listener request_set_maximized;
};
struct roots_xdg_surface_v6 {
struct roots_view *view;
// TODO: Maybe destroy listener should go in roots_view
struct wl_listener destroy;
struct wl_listener ping_timeout;
struct wl_listener request_minimize;
struct wl_listener request_move;
struct wl_listener request_resize;
struct wl_listener request_show_window_menu;
};
struct roots_xwayland_surface {
struct roots_view *view;
// TODO: Maybe destroy listener should go in roots_view
struct wl_listener destroy;
};
enum roots_view_type {
ROOTS_WL_SHELL_VIEW,
ROOTS_XDG_SHELL_V6_VIEW,
ROOTS_XWAYLAND_VIEW,
};
struct roots_view {
struct roots_desktop *desktop;
double x, y;
float rotation;
// TODO: Something for roots-enforced width/height
enum roots_view_type type;
union {
struct wlr_wl_shell_surface *wl_shell_surface;
struct wlr_xdg_surface_v6 *xdg_surface_v6;
struct wlr_xwayland_surface *xwayland_surface;
};
union {
struct roots_wl_shell_surface *roots_wl_shell_surface;
struct roots_xdg_surface_v6 *roots_xdg_surface_v6;
struct roots_xwayland_surface *roots_xwayland_surface;
};
struct wlr_surface *wlr_surface;
struct wl_list link;
// TODO: This would probably be better as a field that's updated on a
// configure event from the xdg_shell
// If not then this should follow the typical type/impl pattern we use
// elsewhere
void (*get_input_bounds)(struct roots_view *view, struct wlr_box *box);
void (*activate)(struct roots_view *view, bool active);
};
void view_get_input_bounds(struct roots_view *view, struct wlr_box *box);
void view_activate(struct roots_view *view, bool active);
#endif

View file

@ -11,5 +11,7 @@ struct wlr_keyboard_impl {
void wlr_keyboard_init(struct wlr_keyboard *keyboard, struct wlr_keyboard_impl *impl);
void wlr_keyboard_destroy(struct wlr_keyboard *keyboard);
void wlr_keyboard_update_state(struct wlr_keyboard *keyboard,
struct wlr_event_keyboard_key *event);
#endif

View file

@ -3,6 +3,8 @@
#include <wayland-server.h>
#include <stdint.h>
#include <wayland-server.h>
#include <xkbcommon/xkbcommon.h>
enum WLR_KEYBOARD_LED {
WLR_LED_NUM_LOCK = 1,
@ -15,9 +17,17 @@ struct wlr_keyboard_impl;
struct wlr_keyboard {
struct wlr_keyboard_impl *impl;
// TODO: Should this store key repeat info too?
int keymap_fd;
size_t keymap_size;
struct xkb_keymap *keymap;
struct xkb_state *xkb_state;
xkb_led_index_t leds[WLR_LED_LAST];
struct {
struct wl_signal key;
struct wl_signal keymap;
} events;
void *data;
@ -37,4 +47,7 @@ struct wlr_event_keyboard_key {
enum wlr_key_state state;
};
void wlr_keyboard_set_keymap(struct wlr_keyboard *kb,
struct xkb_keymap *keymap);
#endif

View file

@ -3,6 +3,7 @@
#include <wlr/types/wlr_surface.h>
#include <wlr/types/wlr_input_device.h>
#include <wlr/types/wlr_keyboard.h>
#include <wayland-server.h>
/**
@ -13,6 +14,7 @@
struct wlr_seat_handle {
struct wl_resource *wl_resource;
struct wlr_seat *wlr_seat;
struct wlr_seat_keyboard *seat_keyboard;
struct wl_resource *pointer;
struct wl_resource *keyboard;
@ -27,8 +29,17 @@ struct wlr_seat_pointer_state {
struct wlr_seat_handle *focused_handle;
struct wlr_surface *focused_surface;
struct wl_listener focus_surface_destroy_listener;
struct wl_listener focus_resource_destroy_listener;
struct wl_listener surface_destroy;
struct wl_listener resource_destroy;
};
struct wlr_seat_keyboard {
struct wlr_seat *seat;
struct wlr_keyboard *keyboard;
struct wl_listener key;
struct wl_listener keymap;
struct wl_listener destroy;
struct wl_list link;
};
struct wlr_seat_keyboard_state {
@ -36,17 +47,15 @@ struct wlr_seat_keyboard_state {
struct wlr_seat_handle *focused_handle;
struct wlr_surface *focused_surface;
int keymap_fd;
size_t keymap_size;
struct wl_listener focus_surface_destroy_listener;
struct wl_listener focus_resource_destroy_listener;
struct wl_listener surface_destroy;
struct wl_listener resource_destroy;
};
struct wlr_seat {
struct wl_global *wl_global;
struct wl_display *display;
struct wl_list handles;
struct wl_list keyboards;
char *name;
uint32_t capabilities;
struct wlr_data_device *data_device;
@ -57,7 +66,6 @@ struct wlr_seat {
struct {
struct wl_signal client_bound;
struct wl_signal client_unbound;
struct wl_signal keyboard_bound;
} events;
void *data;
@ -126,38 +134,33 @@ uint32_t wlr_seat_pointer_send_button(struct wlr_seat *wlr_seat, uint32_t time,
void wlr_seat_pointer_send_axis(struct wlr_seat *wlr_seat, uint32_t time,
enum wlr_axis_orientation orientation, double value);
/**
* Attaches this keyboard to the seat. Key events from this keyboard will be
* propegated to the focused client.
*/
void wlr_seat_attach_keyboard(struct wlr_seat *seat,
struct wlr_input_device *dev);
/**
* Detaches this keyboard from the seat. This is done automatically when the
* keyboard is destroyed; you only need to use this if you want to remove it for
* some other reason.
*/
void wlr_seat_detach_keyboard(struct wlr_seat *seat, struct wlr_keyboard *kb);
/**
* Send a keyboard enter event to the given surface and consider it to be the
* focused surface for the keyboard. This will send a leave event to the last
* surface that was entered. Pass an array of currently pressed keys.
*/
void wlr_seat_keyboard_enter(struct wlr_seat *wlr_seat,
struct wlr_surface *surface, struct wl_array keys);
struct wlr_surface *surface);
/**
* Clear the focused surface for the keyboard and leave all entered surfaces.
*/
void wlr_seat_keyboard_clear_focus(struct wlr_seat *wlr_seat);
/**
* Send a key event to the surface with keyboard focus. Returns the event
* serial.
*/
uint32_t wlr_seat_keyboard_send_key(struct wlr_seat *wlr_seat, uint32_t time,
uint32_t key, uint32_t state);
/**
* Send the modifiers event to the surface with keyboard focus. Also sends the
* event to the surface with pointer focus.
*/
void wlr_seat_keyboard_send_modifiers(struct wlr_seat *wlr_seat,
uint32_t mods_depressed, uint32_t mods_latched, uint32_t mods_locked,
uint32_t group);
/**
* Set the keymap and send it to seat keyboard resources.
*/
void wlr_seat_keyboard_set_keymap(struct wlr_seat *wlr_seat, int keymap_fd,
size_t keymap_size);
// TODO: May be useful to be able to simulate keyboard input events
#endif

View file

@ -1,26 +1,112 @@
#ifndef WLR_TYPES_WLR_WL_SHELL_H
#define WLR_TYPES_WLR_WL_SHELL_H
#include <stdbool.h>
#include <wayland-server.h>
struct wlr_wl_shell {
struct wl_global *wl_global;
struct wl_list wl_resources;
struct wl_list surfaces;
uint32_t ping_timeout;
struct {
struct wl_signal new_surface;
} events;
void *data;
};
struct wlr_wl_shell_surface_transient_state {
struct wlr_wl_shell_surface *parent;
int32_t x;
int32_t y;
enum wl_shell_surface_transient flags;
};
struct wlr_wl_shell_surface_popup_state {
struct wlr_seat_handle *seat_handle;
uint32_t serial;
};
enum wlr_wl_shell_surface_state {
WLR_WL_SHELL_SURFACE_STATE_NONE,
WLR_WL_SHELL_SURFACE_STATE_TOPLEVEL,
WLR_WL_SHELL_SURFACE_STATE_TRANSIENT,
WLR_WL_SHELL_SURFACE_STATE_POPUP,
};
struct wlr_wl_shell_surface {
struct wl_resource *surface;
struct wlr_texture *wlr_texture;
struct wlr_wl_shell *shell;
struct wl_client *client;
struct wl_resource *resource;
struct wlr_surface *surface;
struct wl_list link;
uint32_t ping_serial;
struct wl_event_source *ping_timer;
enum wlr_wl_shell_surface_state state;
struct wlr_wl_shell_surface_transient_state *transient_state;
struct wlr_wl_shell_surface_popup_state *popup_state;
char *title;
char *class;
struct wl_listener surface_destroy_listener;
struct {
struct wl_signal destroy;
struct wl_signal ping_timeout;
struct wl_signal request_move;
struct wl_signal request_resize;
struct wl_signal request_set_fullscreen;
struct wl_signal request_set_maximized;
struct wl_signal set_state;
struct wl_signal set_title;
struct wl_signal set_class;
} events;
void *data;
};
struct wlr_wl_shell_surface_move_event {
struct wl_client *client;
struct wlr_wl_shell_surface *surface;
struct wlr_seat_handle *seat_handle;
uint32_t serial;
};
struct wlr_wl_shell_surface_resize_event {
struct wl_client *client;
struct wlr_wl_shell_surface *surface;
struct wlr_seat_handle *seat_handle;
uint32_t serial;
enum wl_shell_surface_resize edges;
};
struct wlr_wl_shell_surface_set_fullscreen_event {
struct wl_client *client;
struct wlr_wl_shell_surface *surface;
enum wl_shell_surface_fullscreen_method method;
uint32_t framerate;
struct wlr_output *output;
};
struct wlr_wl_shell_surface_set_maximized_event {
struct wl_client *client;
struct wlr_wl_shell_surface *surface;
struct wlr_output *output;
};
struct wlr_wl_shell *wlr_wl_shell_create(struct wl_display *display);
void wlr_wl_shell_destroy(struct wlr_wl_shell *wlr_wl_shell);
void wlr_wl_shell_surface_ping(struct wlr_wl_shell_surface *surface);
void wlr_wl_shell_surface_configure(struct wlr_wl_shell_surface *surface,
uint32_t edges, int32_t width, int32_t height);
void wlr_wl_shell_surface_popup_done(struct wlr_wl_shell_surface *surface);
#endif

View file

@ -20,23 +20,37 @@ struct wlr_xwayland {
struct wl_event_source *sigusr1_source;
struct wl_listener destroy_listener;
struct wlr_xwm *xwm;
struct wl_list displayable_windows;
struct wl_list displayable_surfaces;
struct {
struct wl_signal new_surface;
} events;
void *data;
};
struct wlr_x11_window {
struct wlr_xwayland_surface {
xcb_window_t window_id;
uint32_t surface_id;
struct wl_list link;
struct wl_resource *surface;
struct wlr_surface *surface;
struct wl_listener surface_destroy_listener;
int16_t x, y;
uint16_t width, height;
bool override_redirect;
struct {
struct wl_signal destroy;
} events;
void *data;
};
void wlr_xwayland_destroy(struct wlr_xwayland *wlr_xwayland);
struct wlr_xwayland *wlr_xwayland_create(struct wl_display *wl_display,
struct wlr_compositor *compositor);
struct wlr_compositor *compositor);
void wlr_xwayland_surface_activate(struct wlr_xwayland *wlr_xwayland,
struct wlr_xwayland_surface *surface);
#endif

View file

@ -113,6 +113,7 @@ wlroots = declare_dependency(
include_directories: wlr_inc,
)
subdir('rootston')
subdir('examples')
pkgconfig = import('pkgconfig')

View file

@ -227,13 +227,13 @@ static void gles2_texture_get_buffer_size(struct wlr_texture *texture, struct
return;
}
if (!wlr_egl_query_buffer(tex->egl, resource, EGL_WIDTH,
(EGLint*)&width)) {
(EGLint*)width)) {
wlr_log(L_ERROR, "could not get size of the buffer "
"(no buffer found)");
return;
};
wlr_egl_query_buffer(tex->egl, resource, EGL_HEIGHT,
(EGLint*)&height);
(EGLint*)height);
return;
}

269
rootston/config.c Normal file
View file

@ -0,0 +1,269 @@
#ifndef _POSIX_C_SOURCE
#define _POSIX_C_SOURCE 200809L
#endif
#include <stdlib.h>
#include <limits.h>
#include <getopt.h>
#include <string.h>
#include <unistd.h>
#include <sys/param.h>
#include <wlr/util/log.h>
#include <wlr/types/wlr_box.h>
#include "rootston/config.h"
#include "rootston/ini.h"
static void usage(const char *name, int ret) {
fprintf(stderr,
"usage: %s [-C <FILE>]\n"
"\n"
" -C <FILE> Path to the configuration file\n"
" (default: rootston.ini).\n"
" See `rootston.ini.example` for config\n"
" file documentation.\n", name);
exit(ret);
}
static struct wlr_box *parse_geometry(const char *str) {
// format: {width}x{height}+{x}+{y}
if (strlen(str) > 255) {
wlr_log(L_ERROR, "cannot parse geometry string, too long");
return NULL;
}
char *buf = strdup(str);
struct wlr_box *box = calloc(1, sizeof(struct wlr_box));
bool has_width = false;
bool has_height = false;
bool has_x = false;
bool has_y = false;
char *pch = strtok(buf, "x+");
while (pch != NULL) {
errno = 0;
char *endptr;
long val = strtol(pch, &endptr, 0);
if ((errno == ERANGE && (val == LONG_MAX || val == LONG_MIN)) ||
(errno != 0 && val == 0)) {
goto invalid_input;
}
if (endptr == pch) {
goto invalid_input;
}
if (!has_width) {
box->width = val;
has_width = true;
} else if (!has_height) {
box->height = val;
has_height = true;
} else if (!has_x) {
box->x = val;
has_x = true;
} else if (!has_y) {
box->y = val;
has_y = true;
} else {
break;
}
pch = strtok(NULL, "x+");
}
if (!has_width || !has_height) {
goto invalid_input;
}
free(buf);
return box;
invalid_input:
wlr_log(L_ERROR, "could not parse geometry string: %s", str);
free(buf);
free(box);
return NULL;
}
static const char *output_prefix = "output:";
static const char *device_prefix = "device:";
static int config_ini_handler(void *user, const char *section, const char *name,
const char *value) {
struct roots_config *config = user;
if (strncmp(output_prefix, section, strlen(output_prefix)) == 0) {
const char *output_name = section + strlen(output_prefix);
struct output_config *oc;
bool found = false;
wl_list_for_each(oc, &config->outputs, link) {
if (strcmp(oc->name, output_name) == 0) {
found = true;
break;
}
}
if (!found) {
oc = calloc(1, sizeof(struct output_config));
oc->name = strdup(output_name);
oc->transform = WL_OUTPUT_TRANSFORM_NORMAL;
wl_list_insert(&config->outputs, &oc->link);
}
if (strcmp(name, "x") == 0) {
oc->x = strtol(value, NULL, 10);
} else if (strcmp(name, "y") == 0) {
oc->y = strtol(value, NULL, 10);
} else if (strcmp(name, "rotate") == 0) {
if (strcmp(value, "90") == 0) {
oc->transform = WL_OUTPUT_TRANSFORM_90;
} else if (strcmp(value, "180") == 0) {
oc->transform = WL_OUTPUT_TRANSFORM_180;
} else if (strcmp(value, "270") == 0) {
oc->transform = WL_OUTPUT_TRANSFORM_270;
} else if (strcmp(value, "flipped") == 0) {
oc->transform = WL_OUTPUT_TRANSFORM_FLIPPED;
} else if (strcmp(value, "flipped-90") == 0) {
oc->transform = WL_OUTPUT_TRANSFORM_FLIPPED_90;
} else if (strcmp(value, "flipped-180") == 0) {
oc->transform = WL_OUTPUT_TRANSFORM_FLIPPED_180;
} else if (strcmp(value, "flipped-270") == 0) {
oc->transform = WL_OUTPUT_TRANSFORM_FLIPPED_270;
} else {
wlr_log(L_ERROR, "got unknown transform value: %s", value);
}
}
} else if (strcmp(section, "cursor") == 0) {
if (strcmp(name, "map-to-output") == 0) {
free(config->cursor.mapped_output);
config->cursor.mapped_output = strdup(value);
} else if (strcmp(name, "geometry") == 0) {
free(config->cursor.mapped_box);
config->cursor.mapped_box = parse_geometry(value);
} else {
wlr_log(L_ERROR, "got unknown cursor config: %s", name);
}
} else if (strncmp(device_prefix, section, strlen(device_prefix)) == 0) {
const char *device_name = section + strlen(device_prefix);
struct device_config *dc;
bool found = false;
wl_list_for_each(dc, &config->devices, link) {
if (strcmp(dc->name, device_name) == 0) {
found = true;
break;
}
}
if (!found) {
dc = calloc(1, sizeof(struct device_config));
dc->name = strdup(device_name);
wl_list_insert(&config->devices, &dc->link);
}
if (strcmp(name, "map-to-output") == 0) {
free(dc->mapped_output);
dc->mapped_output = strdup(value);
} else if (strcmp(name, "geometry") == 0) {
free(dc->mapped_box);
dc->mapped_box = parse_geometry(value);
} else {
wlr_log(L_ERROR, "got unknown device config: %s", name);
}
} else {
wlr_log(L_ERROR, "got unknown config section: %s", section);
}
return 1;
}
struct roots_config *parse_args(int argc, char *argv[]) {
struct roots_config *config = calloc(1, sizeof(struct roots_config));
wl_list_init(&config->outputs);
wl_list_init(&config->devices);
int c;
while ((c = getopt(argc, argv, "C:h")) != -1) {
switch (c) {
case 'C':
config->config_path = strdup(optarg);
break;
case 'h':
case '?':
usage(argv[0], c != 'h');
}
}
if (!config->config_path) {
// get the config path from the current directory
char cwd[MAXPATHLEN];
if (getcwd(cwd, sizeof(cwd)) != NULL) {
char buf[MAXPATHLEN];
snprintf(buf, MAXPATHLEN, "%s/%s", cwd, "rootston.ini");
config->config_path = strdup(buf);
} else {
wlr_log(L_ERROR, "could not get cwd");
exit(1);
}
}
int result = ini_parse(config->config_path, config_ini_handler, config);
if (result == -1) {
wlr_log(L_DEBUG, "No config file found. Using empty config.");
} else if (result == -2) {
wlr_log(L_ERROR, "Could not allocate memory to parse config file");
exit(1);
} else if (result != 0) {
wlr_log(L_ERROR, "Could not parse config file");
exit(1);
}
return config;
}
void roots_config_destroy(struct roots_config *config) {
struct output_config *oc, *otmp = NULL;
wl_list_for_each_safe(oc, otmp, &config->outputs, link) {
free(oc->name);
free(oc);
}
struct device_config *dc, *dtmp = NULL;
wl_list_for_each_safe(dc, dtmp, &config->devices, link) {
free(dc->name);
free(dc->mapped_output);
free(dc->mapped_box);
free(dc);
}
free(config->config_path);
free(config->cursor.mapped_output);
free(config->cursor.mapped_box);
free(config);
}
struct output_config *config_get_output(struct roots_config *config,
struct wlr_output *output) {
struct output_config *o_config;
wl_list_for_each(o_config, &config->outputs, link) {
if (strcmp(o_config->name, output->name) == 0) {
return o_config;
}
}
return NULL;
}
struct device_config *config_get_device(struct roots_config *config,
struct wlr_input_device *device) {
struct device_config *d_config;
wl_list_for_each(d_config, &config->devices, link) {
if (strcmp(d_config->name, device->name) == 0) {
return d_config;
}
}
return NULL;
}

225
rootston/cursor.c Normal file
View file

@ -0,0 +1,225 @@
#include <stdint.h>
#include <string.h>
// TODO: BSD et al
#include <linux/input-event-codes.h>
#include <wayland-server.h>
#include <wlr/types/wlr_cursor.h>
#include <wlr/util/log.h>
#include "rootston/config.h"
#include "rootston/input.h"
#include "rootston/desktop.h"
void cursor_update_position(struct roots_input *input, uint32_t time) {
struct roots_desktop *desktop = input->server->desktop;
struct roots_view *view;
switch (input->mode) {
case ROOTS_CURSOR_PASSTHROUGH:
view = view_at(desktop, input->cursor->x, input->cursor->y);
if (view) {
struct wlr_box box;
view_get_input_bounds(view, &box);
double sx = input->cursor->x - view->x;
double sy = input->cursor->y - view->y;
wlr_seat_pointer_enter(input->wl_seat, view->wlr_surface, sx, sy);
wlr_seat_pointer_send_motion(input->wl_seat, time, sx, sy);
} else {
wlr_seat_pointer_clear_focus(input->wl_seat);
}
break;
case ROOTS_CURSOR_MOVE:
if (input->active_view) {
input->active_view->x = input->cursor->x - input->offs_x;
input->active_view->y = input->cursor->y - input->offs_y;
}
break;
case ROOTS_CURSOR_RESIZE:
break;
case ROOTS_CURSOR_ROTATE:
break;
}
}
static void set_view_focus(struct roots_input *input,
struct roots_desktop *desktop, struct roots_view *view) {
if (input->active_view == view) {
return;
}
struct roots_view *_view;
wl_list_for_each(_view, &desktop->views, link) {
view_activate(_view, _view == view);
}
input->active_view = view;
input->mode = ROOTS_CURSOR_PASSTHROUGH;
}
static void handle_cursor_motion(struct wl_listener *listener, void *data) {
struct roots_input *input = wl_container_of(listener, input, cursor_motion);
struct wlr_event_pointer_motion *event = data;
wlr_cursor_move(input->cursor, event->device,
event->delta_x, event->delta_y);
cursor_update_position(input, (uint32_t)event->time_usec);
}
static void handle_cursor_motion_absolute(struct wl_listener *listener,
void *data) {
struct roots_input *input = wl_container_of(listener,
input, cursor_motion_absolute);
struct wlr_event_pointer_motion_absolute *event = data;
wlr_cursor_warp_absolute(input->cursor, event->device,
event->x_mm / event->width_mm, event->y_mm / event->height_mm);
cursor_update_position(input, (uint32_t)event->time_usec);
}
static void handle_cursor_axis(struct wl_listener *listener, void *data) {
struct roots_input *input =
wl_container_of(listener, input, cursor_axis);
struct wlr_event_pointer_axis *event = data;
wlr_seat_pointer_send_axis(input->wl_seat, event->time_sec,
event->orientation, event->delta);
}
static void do_cursor_button_press(struct roots_input *input,
struct wlr_cursor *cursor, struct wlr_input_device *device,
uint32_t time, uint32_t button, uint32_t state) {
struct roots_desktop *desktop = input->server->desktop;
struct roots_view *view = view_at(desktop,
input->cursor->x, input->cursor->y);
uint32_t serial = wlr_seat_pointer_send_button(
input->wl_seat, time, button, state);
int i;
switch (state) {
case WLR_BUTTON_RELEASED:
input->active_view = NULL;
input->mode = ROOTS_CURSOR_PASSTHROUGH;
break;
case WLR_BUTTON_PRESSED:
i = input->input_events_idx;
input->input_events[i].serial = serial;
input->input_events[i].cursor = cursor;
input->input_events[i].device = device;
input->input_events_idx = (i + 1)
% (sizeof(input->input_events) / sizeof(input->input_events[0]));
set_view_focus(input, desktop, view);
if (view) {
wlr_seat_keyboard_enter(input->wl_seat, view->wlr_surface);
}
break;
}
}
static void handle_cursor_button(struct wl_listener *listener, void *data) {
struct roots_input *input = wl_container_of(listener, input, cursor_button);
struct wlr_event_pointer_button *event = data;
do_cursor_button_press(input, input->cursor, event->device,
(uint32_t)event->time_usec, event->button, event->state);
}
static void handle_tool_axis(struct wl_listener *listener, void *data) {
struct roots_input *input = wl_container_of(listener, input, cursor_tool_axis);
struct wlr_event_tablet_tool_axis *event = data;
if ((event->updated_axes & WLR_TABLET_TOOL_AXIS_X) &&
(event->updated_axes & WLR_TABLET_TOOL_AXIS_Y)) {
wlr_cursor_warp_absolute(input->cursor, event->device,
event->x_mm / event->width_mm, event->y_mm / event->height_mm);
cursor_update_position(input, (uint32_t)event->time_usec);
}
}
static void handle_tool_tip(struct wl_listener *listener, void *data) {
struct roots_input *input = wl_container_of(listener, input, cursor_tool_tip);
struct wlr_event_tablet_tool_tip *event = data;
do_cursor_button_press(input, input->cursor, event->device,
(uint32_t)event->time_usec, BTN_LEFT, event->state);
}
void cursor_initialize(struct roots_input *input) {
struct wlr_cursor *cursor = input->cursor;
wl_list_init(&input->cursor_motion.link);
wl_signal_add(&cursor->events.motion, &input->cursor_motion);
input->cursor_motion.notify = handle_cursor_motion;
wl_list_init(&input->cursor_motion_absolute.link);
wl_signal_add(&cursor->events.motion_absolute,
&input->cursor_motion_absolute);
input->cursor_motion_absolute.notify = handle_cursor_motion_absolute;
wl_list_init(&input->cursor_button.link);
wl_signal_add(&cursor->events.button, &input->cursor_button);
input->cursor_button.notify = handle_cursor_button;
wl_list_init(&input->cursor_axis.link);
wl_signal_add(&cursor->events.axis, &input->cursor_axis);
input->cursor_axis.notify = handle_cursor_axis;
wl_list_init(&input->cursor_tool_axis.link);
wl_signal_add(&cursor->events.tablet_tool_axis, &input->cursor_tool_axis);
input->cursor_tool_axis.notify = handle_tool_axis;
wl_list_init(&input->cursor_tool_tip.link);
wl_signal_add(&cursor->events.tablet_tool_tip, &input->cursor_tool_tip);
input->cursor_tool_tip.notify = handle_tool_tip;
}
static void reset_device_mappings(struct roots_config *config,
struct wlr_cursor *cursor, struct wlr_input_device *device) {
wlr_cursor_map_input_to_output(cursor, device, NULL);
struct device_config *dconfig;
if ((dconfig = config_get_device(config, device))) {
wlr_cursor_map_input_to_region(cursor, device, dconfig->mapped_box);
}
}
static void set_device_output_mappings(struct roots_config *config,
struct wlr_cursor *cursor, struct wlr_output *output,
struct wlr_input_device *device) {
struct device_config *dconfig;
dconfig = config_get_device(config, device);
if (dconfig && dconfig->mapped_output &&
strcmp(dconfig->mapped_output, output->name) == 0) {
wlr_cursor_map_input_to_output(cursor, device, output);
}
}
void cursor_load_config(struct roots_config *config,
struct wlr_cursor *cursor,
struct roots_input *input,
struct roots_desktop *desktop) {
struct roots_pointer *pointer;
struct roots_touch *touch;
struct roots_tablet_tool *tablet_tool;
struct roots_output *output;
// reset mappings
wlr_cursor_map_to_output(cursor, NULL);
wl_list_for_each(pointer, &input->pointers, link) {
reset_device_mappings(config, cursor, pointer->device);
}
wl_list_for_each(touch, &input->touch, link) {
reset_device_mappings(config, cursor, touch->device);
}
wl_list_for_each(tablet_tool, &input->tablet_tools, link) {
reset_device_mappings(config, cursor, tablet_tool->device);
}
// configure device to output mappings
const char *mapped_output = config->cursor.mapped_output;
wl_list_for_each(output, &desktop->outputs, link) {
if (mapped_output && strcmp(mapped_output, output->wlr_output->name) == 0) {
wlr_cursor_map_to_output(cursor, output->wlr_output);
}
wl_list_for_each(pointer, &input->pointers, link) {
set_device_output_mappings(config, cursor, output->wlr_output,
pointer->device);
}
wl_list_for_each(tablet_tool, &input->tablet_tools, link) {
set_device_output_mappings(config, cursor, output->wlr_output,
tablet_tool->device);
}
wl_list_for_each(touch, &input->touch, link) {
set_device_output_mappings(config, cursor, output->wlr_output,
touch->device);
}
}
}

102
rootston/desktop.c Normal file
View file

@ -0,0 +1,102 @@
#define _POSIX_C_SOURCE 199309L
#include <time.h>
#include <stdlib.h>
#include <wlr/types/wlr_box.h>
#include <wlr/types/wlr_compositor.h>
#include <wlr/types/wlr_cursor.h>
#include <wlr/types/wlr_gamma_control.h>
#include <wlr/types/wlr_output_layout.h>
#include <wlr/types/wlr_wl_shell.h>
#include <wlr/types/wlr_xdg_shell_v6.h>
#include <wlr/util/log.h>
#include "rootston/desktop.h"
#include "rootston/server.h"
void view_destroy(struct roots_view *view) {
wl_list_remove(&view->link);
free(view);
}
void view_get_input_bounds(struct roots_view *view, struct wlr_box *box) {
if (view->get_input_bounds) {
view->get_input_bounds(view, box);
return;
}
box->x = box->y = 0;
box->width = view->wlr_surface->current.width;
box->height = view->wlr_surface->current.height;
}
void view_activate(struct roots_view *view, bool activate) {
if (view->activate) {
view->activate(view, activate);
}
}
struct roots_view *view_at(struct roots_desktop *desktop, int x, int y) {
struct roots_view *view;
wl_list_for_each(view, &desktop->views, link) {
struct wlr_box box;
view_get_input_bounds(view, &box);
box.x += view->x;
box.y += view->y;
if (wlr_box_contains_point(&box, x, y)) {
return view;
}
}
return NULL;
}
struct roots_desktop *desktop_create(struct roots_server *server,
struct roots_config *config) {
struct roots_desktop *desktop = calloc(1, sizeof(struct roots_desktop));
wlr_log(L_DEBUG, "Initializing roots desktop");
wl_list_init(&desktop->views);
wl_list_init(&desktop->outputs);
wl_list_init(&desktop->output_add.link);
desktop->output_add.notify = output_add_notify;
wl_list_init(&desktop->output_remove.link);
desktop->output_remove.notify = output_remove_notify;
wl_signal_add(&server->backend->events.output_add,
&desktop->output_add);
wl_signal_add(&server->backend->events.output_remove,
&desktop->output_remove);
desktop->server = server;
desktop->config = config;
desktop->layout = wlr_output_layout_create();
desktop->compositor = wlr_compositor_create(
server->wl_display, server->renderer);
wlr_cursor_attach_output_layout(server->input->cursor, desktop->layout);
wlr_cursor_map_to_region(server->input->cursor, config->cursor.mapped_box);
cursor_load_config(config, server->input->cursor,
server->input, desktop);
desktop->xdg_shell_v6 = wlr_xdg_shell_v6_create(server->wl_display);
wl_signal_add(&desktop->xdg_shell_v6->events.new_surface,
&desktop->xdg_shell_v6_surface);
desktop->xdg_shell_v6_surface.notify = handle_xdg_shell_v6_surface;
desktop->wl_shell = wlr_wl_shell_create(server->wl_display);
wl_signal_add(&desktop->wl_shell->events.new_surface,
&desktop->wl_shell_surface);
desktop->wl_shell_surface.notify = handle_wl_shell_surface;
desktop->xwayland = wlr_xwayland_create(server->wl_display,
desktop->compositor);
wl_signal_add(&desktop->xwayland->events.new_surface,
&desktop->xwayland_surface);
desktop->xwayland_surface.notify = handle_xwayland_surface;
desktop->gamma_control_manager = wlr_gamma_control_manager_create(
server->wl_display);
return desktop;
}
void desktop_destroy(struct roots_desktop *desktop) {
// TODO
}

195
rootston/ini.c Normal file
View file

@ -0,0 +1,195 @@
/* inih -- simple .INI file parser
inih is released under the New BSD license (see LICENSE.txt). Go to the project
home page for more info:
https://github.com/benhoyt/inih
*/
#if defined(_MSC_VER) && !defined(_CRT_SECURE_NO_WARNINGS)
#define _CRT_SECURE_NO_WARNINGS
#endif
#include <stdio.h>
#include <ctype.h>
#include <string.h>
#include "rootston/ini.h"
#if !INI_USE_STACK
#include <stdlib.h>
#endif
#define MAX_SECTION 50
#define MAX_NAME 50
/* Strip whitespace chars off end of given string, in place. Return s. */
static char* rstrip(char* s)
{
char* p = s + strlen(s);
while (p > s && isspace((unsigned char)(*--p)))
*p = '\0';
return s;
}
/* Return pointer to first non-whitespace char in given string. */
static char* lskip(const char* s)
{
while (*s && isspace((unsigned char)(*s)))
s++;
return (char*)s;
}
/* Return pointer to first char (of chars) or inline comment in given string,
or pointer to null at end of string if neither found. Inline comment must
be prefixed by a whitespace character to register as a comment. */
static char* find_chars_or_comment(const char* s, const char* chars)
{
#if INI_ALLOW_INLINE_COMMENTS
int was_space = 0;
while (*s && (!chars || !strchr(chars, *s)) &&
!(was_space && strchr(INI_INLINE_COMMENT_PREFIXES, *s))) {
was_space = isspace((unsigned char)(*s));
s++;
}
#else
while (*s && (!chars || !strchr(chars, *s))) {
s++;
}
#endif
return (char*)s;
}
/* Version of strncpy that ensures dest (size bytes) is null-terminated. */
static char* strncpy0(char* dest, const char* src, size_t size)
{
strncpy(dest, src, size);
dest[size - 1] = '\0';
return dest;
}
/* See documentation in header file. */
int ini_parse_stream(ini_reader reader, void* stream, ini_handler handler,
void* user)
{
/* Uses a fair bit of stack (use heap instead if you need to) */
#if INI_USE_STACK
char line[INI_MAX_LINE];
#else
char* line;
#endif
char section[MAX_SECTION] = "";
char prev_name[MAX_NAME] = "";
char* start;
char* end;
char* name;
char* value;
int lineno = 0;
int error = 0;
#if !INI_USE_STACK
line = (char*)malloc(INI_MAX_LINE);
if (!line) {
return -2;
}
#endif
/* Scan through stream line by line */
while (reader(line, INI_MAX_LINE, stream) != NULL) {
lineno++;
start = line;
#if INI_ALLOW_BOM
if (lineno == 1 && (unsigned char)start[0] == 0xEF &&
(unsigned char)start[1] == 0xBB &&
(unsigned char)start[2] == 0xBF) {
start += 3;
}
#endif
start = lskip(rstrip(start));
if (*start == ';' || *start == '#') {
/* Per Python configparser, allow both ; and # comments at the
start of a line */
}
#if INI_ALLOW_MULTILINE
else if (*prev_name && *start && start > line) {
/* Non-blank line with leading whitespace, treat as continuation
of previous name's value (as per Python configparser). */
if (!handler(user, section, prev_name, start) && !error)
error = lineno;
}
#endif
else if (*start == '[') {
/* A "[section]" line */
end = find_chars_or_comment(start + 1, "]");
if (*end == ']') {
*end = '\0';
strncpy0(section, start + 1, sizeof(section));
*prev_name = '\0';
}
else if (!error) {
/* No ']' found on section line */
error = lineno;
}
}
else if (*start) {
/* Not a comment, must be a name[=:]value pair */
end = find_chars_or_comment(start, "=:");
if (*end == '=' || *end == ':') {
*end = '\0';
name = rstrip(start);
value = lskip(end + 1);
#if INI_ALLOW_INLINE_COMMENTS
end = find_chars_or_comment(value, NULL);
if (*end)
*end = '\0';
#endif
rstrip(value);
/* Valid name[=:]value pair found, call handler */
strncpy0(prev_name, name, sizeof(prev_name));
if (!handler(user, section, name, value) && !error)
error = lineno;
memset(value, 0, strlen(value));
}
else if (!error) {
/* No '=' or ':' found on name[=:]value line */
error = lineno;
}
}
#if INI_STOP_ON_FIRST_ERROR
if (error)
break;
#endif
}
#if !INI_USE_STACK
free(line);
#endif
return error;
}
/* See documentation in header file. */
int ini_parse_file(FILE* file, ini_handler handler, void* user)
{
return ini_parse_stream((ini_reader)fgets, file, handler, user);
}
/* See documentation in header file. */
int ini_parse(const char* filename, ini_handler handler, void* user)
{
FILE* file;
int error;
file = fopen(filename, "r");
if (!file)
return -1;
error = ini_parse_file(file, handler, user);
fclose(file);
return error;
}

112
rootston/input.c Normal file
View file

@ -0,0 +1,112 @@
#include <assert.h>
#include <stdlib.h>
#include <wayland-server.h>
#include <wlr/types/wlr_cursor.h>
#include <wlr/util/log.h>
#include <wlr/xcursor.h>
#include "rootston/server.h"
#include "rootston/config.h"
#include "rootston/input.h"
static const char *device_type(enum wlr_input_device_type type) {
switch (type) {
case WLR_INPUT_DEVICE_KEYBOARD:
return "keyboard";
case WLR_INPUT_DEVICE_POINTER:
return "pointer";
case WLR_INPUT_DEVICE_TOUCH:
return "touch";
case WLR_INPUT_DEVICE_TABLET_TOOL:
return "tablet tool";
case WLR_INPUT_DEVICE_TABLET_PAD:
return "tablet pad";
}
return NULL;
}
static void input_add_notify(struct wl_listener *listener, void *data) {
struct wlr_input_device *device = data;
struct roots_input *input = wl_container_of(listener, input, input_add);
wlr_log(L_DEBUG, "New input device: %s (%d:%d) %s", device->name,
device->vendor, device->product, device_type(device->type));
switch (device->type) {
case WLR_INPUT_DEVICE_KEYBOARD:
keyboard_add(device, input);
break;
case WLR_INPUT_DEVICE_POINTER:
pointer_add(device, input);
break;
case WLR_INPUT_DEVICE_TOUCH:
//touch_add(device, input);
break;
case WLR_INPUT_DEVICE_TABLET_TOOL:
//tablet_tool_add(device, input);
break;
default:
break;
}
}
static void input_remove_notify(struct wl_listener *listener, void *data) {
struct wlr_input_device *device = data;
struct roots_input *input = wl_container_of(listener, input, input_remove);
switch (device->type) {
case WLR_INPUT_DEVICE_KEYBOARD:
keyboard_remove(device, input);
break;
case WLR_INPUT_DEVICE_POINTER:
pointer_remove(device, input);
break;
case WLR_INPUT_DEVICE_TOUCH:
//touch_remove(device, input);
break;
case WLR_INPUT_DEVICE_TABLET_TOOL:
//tablet_tool_remove(device, input);
break;
default:
break;
}
}
struct roots_input *input_create(struct roots_server *server,
struct roots_config *config) {
wlr_log(L_DEBUG, "Initializing roots input");
struct roots_input *input = calloc(1, sizeof(struct roots_input));
assert(input);
input->config = config;
input->server = server;
struct wlr_xcursor_theme *theme;
assert(theme = wlr_xcursor_theme_load("default", 16));
assert(input->xcursor = wlr_xcursor_theme_get_cursor(theme, "left_ptr"));
assert(input->wl_seat = wlr_seat_create(server->wl_display, "seat0"));
wlr_seat_set_capabilities(input->wl_seat, WL_SEAT_CAPABILITY_KEYBOARD
| WL_SEAT_CAPABILITY_POINTER | WL_SEAT_CAPABILITY_TOUCH);
wl_list_init(&input->keyboards);
wl_list_init(&input->pointers);
wl_list_init(&input->touch);
wl_list_init(&input->tablet_tools);
wl_list_init(&input->input_add.link);
input->input_add.notify = input_add_notify;
wl_list_init(&input->input_remove.link);
input->input_remove.notify = input_remove_notify;
wl_signal_add(&server->backend->events.input_add,
&input->input_add);
wl_signal_add(&server->backend->events.input_remove,
&input->input_remove);
input->cursor = wlr_cursor_create();
cursor_initialize(input);
wlr_cursor_set_xcursor(input->cursor, input->xcursor);
return input;
}
void input_destroy(struct roots_input *input) {
// TODO
}

75
rootston/keyboard.c Normal file
View file

@ -0,0 +1,75 @@
#include <assert.h>
#include <stdint.h>
#include <stdlib.h>
#include <wayland-server.h>
#include <wlr/types/wlr_input_device.h>
#include <wlr/types/wlr_pointer.h>
#include <wlr/backend/multi.h>
#include <wlr/backend/session.h>
#include <wlr/util/log.h>
#include <xkbcommon/xkbcommon.h>
#include "rootston/input.h"
static void keyboard_key_notify(struct wl_listener *listener, void *data) {
struct wlr_event_keyboard_key *event = data;
struct roots_keyboard *keyboard = wl_container_of(listener, keyboard, key);
struct roots_input *input = keyboard->input;
struct roots_server *server = input->server;
enum wlr_key_state key_state = event->state;
uint32_t keycode = event->keycode + 8;
const xkb_keysym_t *syms;
int nsyms = xkb_state_key_get_syms(keyboard->device->keyboard->xkb_state,
keycode, &syms);
for (int i = 0; i < nsyms; ++i) {
xkb_keysym_t sym = syms[i];
if (sym == XKB_KEY_Escape) {
// TEMPORARY, probably
wl_display_terminate(server->wl_display);
} else if (key_state == WLR_KEY_PRESSED &&
sym >= XKB_KEY_XF86Switch_VT_1 &&
sym <= XKB_KEY_XF86Switch_VT_12) {
if (wlr_backend_is_multi(server->backend)) {
struct wlr_session *session =
wlr_multi_get_session(server->backend);
if (session) {
wlr_session_change_vt(session, sym - XKB_KEY_XF86Switch_VT_1 + 1);
}
}
}
}
}
void keyboard_add(struct wlr_input_device *device, struct roots_input *input) {
struct roots_keyboard *keyboard = calloc(sizeof(struct roots_keyboard), 1);
device->data = keyboard;
keyboard->device = device;
keyboard->input = input;
wl_list_init(&keyboard->key.link);
keyboard->key.notify = keyboard_key_notify;
wl_signal_add(&device->keyboard->events.key, &keyboard->key);
wl_list_insert(&input->keyboards, &keyboard->link);
struct xkb_rule_names rules;
memset(&rules, 0, sizeof(rules));
rules.rules = getenv("XKB_DEFAULT_RULES");
rules.model = getenv("XKB_DEFAULT_MODEL");
rules.layout = getenv("XKB_DEFAULT_LAYOUT");
rules.variant = getenv("XKB_DEFAULT_VARIANT");
rules.options = getenv("XKB_DEFAULT_OPTIONS");
struct xkb_context *context;
assert(context = xkb_context_new(XKB_CONTEXT_NO_FLAGS));
wlr_keyboard_set_keymap(device->keyboard,
xkb_map_new_from_names(context, &rules,
XKB_KEYMAP_COMPILE_NO_FLAGS));
xkb_context_unref(context);
wlr_seat_attach_keyboard(input->wl_seat, device);
}
void keyboard_remove(struct wlr_input_device *device, struct roots_input *input) {
struct roots_keyboard *keyboard = device->data;
wlr_seat_detach_keyboard(input->wl_seat, device->keyboard);
wl_list_remove(&keyboard->key.link);
wl_list_remove(&keyboard->link);
free(keyboard);
}

46
rootston/main.c Normal file
View file

@ -0,0 +1,46 @@
#define _POSIX_C_SOURCE 200112L
#include <assert.h>
#include <stdlib.h>
#include <wayland-server.h>
#include <wlr/backend.h>
#include <wlr/render.h>
#include <wlr/render/gles2.h>
#include <wlr/util/log.h>
#include "rootston/config.h"
#include "rootston/server.h"
struct roots_server server = { 0 };
int main(int argc, char **argv) {
assert(server.config = parse_args(argc, argv));
assert(server.wl_display = wl_display_create());
assert(server.wl_event_loop = wl_display_get_event_loop(server.wl_display));
assert(server.backend = wlr_backend_autocreate(server.wl_display));
assert(server.renderer = wlr_gles2_renderer_create(server.backend));
wl_display_init_shm(server.wl_display);
server.input = input_create(&server, server.config);
server.desktop = desktop_create(&server, server.config);
server.data_device_manager = wlr_data_device_manager_create(
server.wl_display);
const char *socket = wl_display_add_socket_auto(server.wl_display);
if (!socket) {
wlr_log_errno(L_ERROR, "Unable to open wayland socket");
wlr_backend_destroy(server.backend);
return 1;
}
wlr_log(L_INFO, "Running compositor on wayland display '%s'", socket);
setenv("_WAYLAND_DISPLAY", socket, true);
if (!wlr_backend_start(server.backend)) {
wlr_log(L_ERROR, "Failed to start backend");
wlr_backend_destroy(server.backend);
return 1;
}
wl_display_run(server.wl_display);
return 0;
}

16
rootston/meson.build Normal file
View file

@ -0,0 +1,16 @@
executable(
'rootston', [
'config.c',
'cursor.c',
'desktop.c',
'ini.c',
'input.c',
'keyboard.c',
'main.c',
'output.c',
'pointer.c',
'xdg_shell_v6.c',
'xwayland.c',
'wl_shell.c',
], dependencies: wlroots
)

140
rootston/output.c Normal file
View file

@ -0,0 +1,140 @@
#define _POSIX_C_SOURCE 199309L
#include <time.h>
#include <stdlib.h>
#include <stdbool.h>
#include <wlr/types/wlr_output_layout.h>
#include <wlr/types/wlr_compositor.h>
#include <wlr/types/wlr_wl_shell.h>
#include <wlr/types/wlr_xdg_shell_v6.h>
#include <wlr/render/matrix.h>
#include <wlr/util/log.h>
#include "rootston/server.h"
#include "rootston/desktop.h"
#include "rootston/config.h"
static inline int64_t timespec_to_msec(const struct timespec *a) {
return (int64_t)a->tv_sec * 1000 + a->tv_nsec / 1000000;
}
static void render_view(struct roots_desktop *desktop,
struct wlr_output *wlr_output, struct timespec *when,
struct roots_view *view, double ox, double oy) {
struct wlr_surface *surface = view->wlr_surface;
float matrix[16];
float transform[16];
wlr_surface_flush_damage(surface);
if (surface->texture->valid) {
wlr_matrix_translate(&transform, ox, oy, 0);
wlr_surface_get_matrix(surface, &matrix,
&wlr_output->transform_matrix, &transform);
wlr_render_with_matrix(desktop->server->renderer,
surface->texture, &matrix);
struct wlr_frame_callback *cb, *cnext;
wl_list_for_each_safe(cb, cnext, &surface->frame_callback_list, link) {
wl_callback_send_done(cb->resource, timespec_to_msec(when));
wl_resource_destroy(cb->resource);
}
}
}
static void output_frame_notify(struct wl_listener *listener, void *data) {
struct wlr_output *wlr_output = data;
struct roots_output *output = wl_container_of(listener, output, frame);
struct roots_desktop *desktop = output->desktop;
struct roots_server *server = desktop->server;
struct timespec now;
clock_gettime(CLOCK_MONOTONIC, &now);
wlr_output_make_current(wlr_output);
wlr_renderer_begin(server->renderer, wlr_output);
struct roots_view *view;
wl_list_for_each(view, &desktop->views, link) {
int width = view->wlr_surface->current.buffer_width;
int height = view->wlr_surface->current.buffer_height;
if (wlr_output_layout_intersects(desktop->layout, wlr_output,
view->x, view->y, view->x + width, view->y + height)) {
double ox = view->x, oy = view->y;
wlr_output_layout_output_coords(
desktop->layout, wlr_output, &ox, &oy);
render_view(desktop, wlr_output, &now, view, ox, oy);
}
}
wlr_renderer_end(server->renderer);
wlr_output_swap_buffers(wlr_output);
output->last_frame = desktop->last_frame = now;
}
void output_add_notify(struct wl_listener *listener, void *data) {
struct wlr_output *wlr_output = data;
struct roots_desktop *desktop = wl_container_of(listener, desktop, output_add);
struct roots_input *input = desktop->server->input;
struct roots_config *config = desktop->config;
wlr_log(L_DEBUG, "Output '%s' added", wlr_output->name);
wlr_log(L_DEBUG, "%s %s %"PRId32"mm x %"PRId32"mm",
wlr_output->make, wlr_output->model,
wlr_output->phys_width, wlr_output->phys_height);
if (wlr_output->modes->length > 0) {
wlr_output_set_mode(wlr_output, wlr_output->modes->items[0]);
}
struct roots_output *output = calloc(1, sizeof(struct roots_output));
clock_gettime(CLOCK_MONOTONIC, &output->last_frame);
output->desktop = desktop;
output->wlr_output = wlr_output;
output->frame.notify = output_frame_notify;
wl_list_init(&output->frame.link);
wl_signal_add(&wlr_output->events.frame, &output->frame);
wl_list_insert(&desktop->outputs, &output->link);
struct output_config *output_config = config_get_output(config, wlr_output);
if (output_config) {
wlr_output_transform(wlr_output, output_config->transform);
wlr_output_layout_add(desktop->layout,
wlr_output, output_config->x, output_config->y);
} else {
wlr_output_layout_add_auto(desktop->layout, wlr_output);
}
cursor_load_config(config, input->cursor, input, desktop);
struct wlr_xcursor_image *image = input->xcursor->images[0];
// TODO the cursor must be set depending on which surface it is displayed
// over which should happen in the compositor.
if (!wlr_output_set_cursor(wlr_output, image->buffer,
image->width, image->width, image->height)) {
wlr_log(L_DEBUG, "Failed to set hardware cursor");
return;
}
wlr_cursor_warp(input->cursor, NULL, input->cursor->x, input->cursor->y);
}
void output_remove_notify(struct wl_listener *listener, void *data) {
struct wlr_output *wlr_output = data;
struct roots_desktop *desktop = wl_container_of(listener, desktop, output_remove);
struct roots_output *output = NULL, *_output;
wl_list_for_each(_output, &desktop->outputs, link) {
if (_output->wlr_output == wlr_output) {
output = _output;
break;
}
}
if (!output) {
return; // We are unfamiliar with this output
}
wlr_output_layout_remove(desktop->layout, output->wlr_output);
// TODO: cursor
//example_config_configure_cursor(sample->config, sample->cursor,
// sample->compositor);
wl_list_remove(&output->link);
wl_list_remove(&output->frame.link);
wl_list_remove(&output->resolution.link);
free(output);
}

23
rootston/pointer.c Normal file
View file

@ -0,0 +1,23 @@
#include <stdlib.h>
#include <wayland-server.h>
#include <wlr/types/wlr_input_device.h>
#include <wlr/types/wlr_pointer.h>
#include "rootston/input.h"
void pointer_add(struct wlr_input_device *device, struct roots_input *input) {
struct roots_pointer *pointer = calloc(sizeof(struct roots_pointer), 1);
device->data = pointer;
pointer->device = device;
pointer->input = input;
wl_list_insert(&input->pointers, &pointer->link);
wlr_cursor_attach_input_device(input->cursor, device);
cursor_load_config(input->server->config, input->cursor,
input, input->server->desktop);
}
void pointer_remove(struct wlr_input_device *device, struct roots_input *input) {
struct roots_pointer *pointer = device->data;
wlr_cursor_detach_input_device(input->cursor, device);
wl_list_remove(&pointer->link);
free(pointer);
}

84
rootston/wl_shell.c Normal file
View file

@ -0,0 +1,84 @@
#include <assert.h>
#include <stdlib.h>
#include <stdbool.h>
#include <wayland-server.h>
#include <wlr/types/wlr_box.h>
#include <wlr/types/wlr_surface.h>
#include <wlr/types/wlr_wl_shell.h>
#include <wlr/util/log.h>
#include "rootston/desktop.h"
#include "rootston/server.h"
#include "rootston/input.h"
static void handle_move(struct wl_listener *listener, void *data) {
struct roots_wl_shell_surface *roots_surface =
wl_container_of(listener, roots_surface, request_move);
struct roots_view *view = roots_surface->view;
struct roots_input *input = view->desktop->server->input;
struct wlr_wl_shell_surface_move_event *e = data;
// TODO: Some of this might want to live in cursor.c I guess
struct roots_input_event *event = NULL;
size_t len = sizeof(input->input_events) / sizeof(*input->input_events);
for (size_t i = 0; i < len; ++i) {
if (input->input_events[i].cursor
&& input->input_events[i].serial == e->serial) {
event = &input->input_events[i];
break;
}
}
if (!event || input->mode != ROOTS_CURSOR_PASSTHROUGH) {
return;
}
input->mode = ROOTS_CURSOR_MOVE;
input->offs_x = input->cursor->x - view->x;
input->offs_y = input->cursor->y - view->y;
wlr_seat_pointer_clear_focus(input->wl_seat);
}
static void handle_destroy(struct wl_listener *listener, void *data) {
struct roots_wl_shell_surface *roots_surface =
wl_container_of(listener, roots_surface, destroy);
wl_list_remove(&roots_surface->destroy.link);
wl_list_remove(&roots_surface->ping_timeout.link);
wl_list_remove(&roots_surface->request_move.link);
wl_list_remove(&roots_surface->request_resize.link);
wl_list_remove(&roots_surface->request_set_fullscreen.link);
wl_list_remove(&roots_surface->request_set_maximized.link);
view_destroy(roots_surface->view);
free(roots_surface);
}
void handle_wl_shell_surface(struct wl_listener *listener, void *data) {
struct roots_desktop *desktop =
wl_container_of(listener, desktop, wl_shell_surface);
struct wlr_wl_shell_surface *surface = data;
wlr_log(L_DEBUG, "new shell surface: title=%s, class=%s",
surface->title, surface->class);
wlr_wl_shell_surface_ping(surface);
struct roots_wl_shell_surface *roots_surface =
calloc(1, sizeof(struct roots_wl_shell_surface));
// TODO: all of the trimmings
wl_list_init(&roots_surface->destroy.link);
roots_surface->destroy.notify = handle_destroy;
wl_signal_add(&surface->events.destroy, &roots_surface->destroy);
wl_list_init(&roots_surface->ping_timeout.link);
wl_list_init(&roots_surface->request_move.link);
roots_surface->request_move.notify = handle_move;
wl_signal_add(&surface->events.request_move, &roots_surface->request_move);
wl_list_init(&roots_surface->request_resize.link);
wl_list_init(&roots_surface->request_set_fullscreen.link);
wl_list_init(&roots_surface->request_set_maximized.link);
struct roots_view *view = calloc(1, sizeof(struct roots_view));
view->type = ROOTS_WL_SHELL_VIEW;
view->x = view->y = 200;
view->wl_shell_surface = surface;
view->roots_wl_shell_surface = roots_surface;
view->wlr_surface = surface->surface;
view->desktop = desktop;
roots_surface->view = view;
wl_list_insert(&desktop->views, &view->link);
}

100
rootston/xdg_shell_v6.c Normal file
View file

@ -0,0 +1,100 @@
#include <assert.h>
#include <stdlib.h>
#include <stdbool.h>
#include <wayland-server.h>
#include <wlr/types/wlr_box.h>
#include <wlr/types/wlr_surface.h>
#include <wlr/types/wlr_xdg_shell_v6.h>
#include <wlr/util/log.h>
#include "rootston/desktop.h"
#include "rootston/server.h"
#include "rootston/input.h"
static void get_input_bounds(struct roots_view *view, struct wlr_box *box) {
assert(view->type == ROOTS_XDG_SHELL_V6_VIEW);
struct wlr_xdg_surface_v6 *surf = view->xdg_surface_v6;
memcpy(box, surf->geometry, sizeof(struct wlr_box));
}
static void activate(struct roots_view *view, bool active) {
assert(view->type == ROOTS_XDG_SHELL_V6_VIEW);
struct wlr_xdg_surface_v6 *surf = view->xdg_surface_v6;
if (surf->role == WLR_XDG_SURFACE_V6_ROLE_TOPLEVEL) {
wlr_xdg_toplevel_v6_set_activated(surf, active);
}
}
static void handle_request_move(struct wl_listener *listener, void *data) {
struct roots_xdg_surface_v6 *roots_xdg_surface =
wl_container_of(listener, roots_xdg_surface, request_move);
struct roots_view *view = roots_xdg_surface->view;
struct roots_input *input = view->desktop->server->input;
struct wlr_xdg_toplevel_v6_move_event *e = data;
// TODO: Some of this might want to live in cursor.c I guess
struct roots_input_event *event = NULL;
size_t len = sizeof(input->input_events) / sizeof(*input->input_events);
for (size_t i = 0; i < len; ++i) {
if (input->input_events[i].cursor
&& input->input_events[i].serial == e->serial) {
event = &input->input_events[i];
break;
}
}
if (!event || input->mode != ROOTS_CURSOR_PASSTHROUGH) {
return;
}
input->mode = ROOTS_CURSOR_MOVE;
input->offs_x = input->cursor->x - view->x;
input->offs_y = input->cursor->y - view->y;
wlr_seat_pointer_clear_focus(input->wl_seat);
}
static void handle_destroy(struct wl_listener *listener, void *data) {
struct roots_xdg_surface_v6 *roots_xdg_surface =
wl_container_of(listener, roots_xdg_surface, destroy);
wl_list_remove(&roots_xdg_surface->destroy.link);
wl_list_remove(&roots_xdg_surface->ping_timeout.link);
wl_list_remove(&roots_xdg_surface->request_move.link);
wl_list_remove(&roots_xdg_surface->request_resize.link);
wl_list_remove(&roots_xdg_surface->request_show_window_menu.link);
wl_list_remove(&roots_xdg_surface->request_minimize.link);
view_destroy(roots_xdg_surface->view);
free(roots_xdg_surface);
}
void handle_xdg_shell_v6_surface(struct wl_listener *listener, void *data) {
struct roots_desktop *desktop =
wl_container_of(listener, desktop, xdg_shell_v6_surface);
struct wlr_xdg_surface_v6 *surface = data;
wlr_log(L_DEBUG, "new xdg surface: title=%s, app_id=%s",
surface->title, surface->app_id);
wlr_xdg_surface_v6_ping(surface);
struct roots_xdg_surface_v6 *roots_surface =
calloc(1, sizeof(struct roots_xdg_surface_v6));
// TODO: all of the trimmings
wl_list_init(&roots_surface->destroy.link);
roots_surface->destroy.notify = handle_destroy;
wl_signal_add(&surface->events.destroy, &roots_surface->destroy);
wl_list_init(&roots_surface->ping_timeout.link);
wl_list_init(&roots_surface->request_minimize.link);
wl_list_init(&roots_surface->request_move.link);
roots_surface->request_move.notify = handle_request_move;
wl_signal_add(&surface->events.request_move, &roots_surface->request_move);
wl_list_init(&roots_surface->request_resize.link);
wl_list_init(&roots_surface->request_show_window_menu.link);
struct roots_view *view = calloc(1, sizeof(struct roots_view));
view->type = ROOTS_XDG_SHELL_V6_VIEW;
view->x = view->y = 200;
view->xdg_surface_v6 = surface;
view->roots_xdg_surface_v6 = roots_surface;
view->wlr_surface = surface->surface;
view->get_input_bounds = get_input_bounds;
view->activate = activate;
view->desktop = desktop;
roots_surface->view = view;
wl_list_insert(&desktop->views, &view->link);
}

49
rootston/xwayland.c Normal file
View file

@ -0,0 +1,49 @@
#include <assert.h>
#include <stdlib.h>
#include <stdbool.h>
#include <wayland-server.h>
#include <wlr/types/wlr_box.h>
#include <wlr/types/wlr_surface.h>
#include <wlr/xwayland.h>
#include <wlr/util/log.h>
#include "rootston/desktop.h"
#include "rootston/server.h"
static void handle_destroy(struct wl_listener *listener, void *data) {
struct roots_xwayland_surface *roots_surface =
wl_container_of(listener, roots_surface, destroy);
wl_list_remove(&roots_surface->destroy.link);
view_destroy(roots_surface->view);
free(roots_surface);
}
static void x11_activate(struct roots_view *view, bool active) {
wlr_xwayland_surface_activate(view->desktop->xwayland,
view->xwayland_surface);
}
void handle_xwayland_surface(struct wl_listener *listener, void *data) {
struct roots_desktop *desktop =
wl_container_of(listener, desktop, xwayland_surface);
struct wlr_xwayland_surface *surface = data;
// TODO: get and log title, class, etc
wlr_log(L_DEBUG, "new xwayland surface");
struct roots_xwayland_surface *roots_surface =
calloc(1, sizeof(struct roots_xwayland_surface));
wl_list_init(&roots_surface->destroy.link);
roots_surface->destroy.notify = handle_destroy;
wl_signal_add(&surface->events.destroy, &roots_surface->destroy);
struct roots_view *view = calloc(1, sizeof(struct roots_view));
view->type = ROOTS_XWAYLAND_VIEW;
view->x = view->y = 200;
view->xwayland_surface = surface;
view->roots_xwayland_surface = roots_surface;
view->wlr_surface = surface->surface;
view->desktop = desktop;
view->activate = x11_activate;
roots_surface->view = view;
wl_list_insert(&desktop->views, &view->link);
}

View file

@ -1,13 +1,39 @@
#include <assert.h>
#include <stdlib.h>
#include <string.h>
#include <sys/mman.h>
#include <unistd.h>
#include <wayland-server.h>
#include <wlr/types/wlr_keyboard.h>
#include <wlr/interfaces/wlr_keyboard.h>
#include <wlr/util/log.h>
int os_create_anonymous_file(off_t size);
static void keyboard_led_update(struct wlr_keyboard *keyboard) {
uint32_t leds = 0;
for (uint32_t i = 0; i < WLR_LED_LAST; ++i) {
if (xkb_state_led_index_is_active(keyboard->xkb_state, keyboard->leds[i])) {
leds |= (1 << i);
}
}
wlr_keyboard_led_update(keyboard, leds);
}
void wlr_keyboard_update_state(struct wlr_keyboard *keyboard,
struct wlr_event_keyboard_key *event) {
uint32_t keycode = event->keycode + 8;
xkb_state_update_key(keyboard->xkb_state, keycode,
event->state == WLR_KEY_PRESSED ? XKB_KEY_DOWN : XKB_KEY_UP);
keyboard_led_update(keyboard);
wl_signal_emit(&keyboard->events.key, event);
}
void wlr_keyboard_init(struct wlr_keyboard *kb,
struct wlr_keyboard_impl *impl) {
kb->impl = impl;
wl_signal_init(&kb->events.key);
wl_signal_init(&kb->events.keymap);
}
void wlr_keyboard_destroy(struct wlr_keyboard *kb) {
@ -17,6 +43,7 @@ void wlr_keyboard_destroy(struct wlr_keyboard *kb) {
wl_list_remove(&kb->events.key.listener_list);
free(kb);
}
close(kb->keymap_fd);
}
void wlr_keyboard_led_update(struct wlr_keyboard *kb, uint32_t leds) {
@ -24,3 +51,27 @@ void wlr_keyboard_led_update(struct wlr_keyboard *kb, uint32_t leds) {
kb->impl->led_update(kb, leds);
}
}
void wlr_keyboard_set_keymap(struct wlr_keyboard *kb,
struct xkb_keymap *keymap) {
wlr_log(L_DEBUG, "Keymap set");
kb->keymap = keymap;
assert(kb->xkb_state = xkb_state_new(kb->keymap));
const char *led_names[3] = {
XKB_LED_NAME_NUM,
XKB_LED_NAME_CAPS,
XKB_LED_NAME_SCROLL
};
for (uint32_t i = 0; i < 3; ++i) {
kb->leds[i] = xkb_map_led_get_index(kb->keymap, led_names[i]);
}
char *keymap_str = xkb_keymap_get_as_string(kb->keymap,
XKB_KEYMAP_FORMAT_TEXT_V1);
kb->keymap_size = strlen(keymap_str) + 1;
kb->keymap_fd = os_create_anonymous_file(kb->keymap_size);
void *ptr = mmap(NULL, kb->keymap_size,
PROT_READ | PROT_WRITE, MAP_SHARED, kb->keymap_fd, 0);
strcpy(ptr, keymap_str);
free(keymap_str);
wl_signal_emit(&kb->events.keymap, kb);
}

View file

@ -78,21 +78,6 @@ static void wl_seat_get_keyboard(struct wl_client *client,
wl_resource_get_version(_handle), id);
wl_resource_set_implementation(handle->keyboard, &wl_keyboard_impl,
handle, &wl_keyboard_destroy);
if (wl_resource_get_version(handle->keyboard) >=
WL_KEYBOARD_REPEAT_INFO_SINCE_VERSION) {
wl_keyboard_send_repeat_info(handle->keyboard, 25, 600);
}
if (handle->wlr_seat->keyboard_state.keymap_size) {
// TODO: handle no keymap
wl_keyboard_send_keymap(handle->keyboard,
WL_KEYBOARD_KEYMAP_FORMAT_XKB_V1,
handle->wlr_seat->keyboard_state.keymap_fd,
handle->wlr_seat->keyboard_state.keymap_size);
}
wl_signal_emit(&handle->wlr_seat->events.keyboard_bound, handle);
}
static const struct wl_touch_interface wl_touch_impl = {
@ -174,6 +159,7 @@ static void wl_seat_bind(struct wl_client *wl_client, void *_wlr_seat,
wl_resource_set_implementation(handle->wl_resource, &wl_seat_impl,
handle, wlr_seat_handle_resource_destroy);
wl_list_insert(&wlr_seat->handles, &handle->link);
wl_seat_send_name(handle->wl_resource, wlr_seat->name);
wl_seat_send_capabilities(handle->wl_resource, wlr_seat->capabilities);
wl_signal_emit(&wlr_seat->events.client_bound, handle);
}
@ -185,14 +171,13 @@ struct wlr_seat *wlr_seat_create(struct wl_display *display, const char *name) {
}
wlr_seat->pointer_state.wlr_seat = wlr_seat;
wl_list_init(&wlr_seat->pointer_state.focus_resource_destroy_listener.link);
wl_list_init(&wlr_seat->pointer_state.focus_surface_destroy_listener.link);
wl_list_init(&wlr_seat->pointer_state.surface_destroy.link);
wl_list_init(&wlr_seat->pointer_state.resource_destroy.link);
wlr_seat->keyboard_state.wlr_seat = wlr_seat;
wl_list_init(&wlr_seat->keyboard_state.resource_destroy.link);
wl_list_init(
&wlr_seat->keyboard_state.focus_resource_destroy_listener.link);
wl_list_init(
&wlr_seat->keyboard_state.focus_surface_destroy_listener.link);
&wlr_seat->keyboard_state.surface_destroy.link);
struct wl_global *wl_global = wl_global_create(display,
&wl_seat_interface, 6, wlr_seat, wl_seat_bind);
@ -204,10 +189,10 @@ struct wlr_seat *wlr_seat_create(struct wl_display *display, const char *name) {
wlr_seat->display = display;
wlr_seat->name = strdup(name);
wl_list_init(&wlr_seat->handles);
wl_list_init(&wlr_seat->keyboards);
wl_signal_init(&wlr_seat->events.client_bound);
wl_signal_init(&wlr_seat->events.client_unbound);
wl_signal_init(&wlr_seat->events.keyboard_bound);
return wlr_seat;
}
@ -264,20 +249,18 @@ bool wlr_seat_pointer_surface_has_focus(struct wlr_seat *wlr_seat,
return surface == wlr_seat->pointer_state.focused_surface;
}
static void handle_pointer_focus_surface_destroyed(
struct wl_listener *listener, void *data) {
struct wlr_seat_pointer_state *state =
wl_container_of(listener, state, focus_surface_destroy_listener);
static void pointer_surface_destroy_notify(struct wl_listener *listener,
void *data) {
struct wlr_seat_pointer_state *state = wl_container_of(
listener, state, surface_destroy);
state->focused_surface = NULL;
wlr_seat_pointer_clear_focus(state->wlr_seat);
}
static void handle_pointer_focus_resource_destroyed(
struct wl_listener *listener, void *data) {
struct wlr_seat_pointer_state *state =
wl_container_of(listener, state, focus_resource_destroy_listener);
static void pointer_resource_destroy_notify(struct wl_listener *listener,
void *data) {
struct wlr_seat_pointer_state *state = wl_container_of(
listener, state, resource_destroy);
state->focused_surface = NULL;
wlr_seat_pointer_clear_focus(state->wlr_seat);
}
@ -325,21 +308,19 @@ void wlr_seat_pointer_enter(struct wlr_seat *wlr_seat,
}
// reinitialize the focus destroy events
wl_list_remove(
&wlr_seat->pointer_state.focus_surface_destroy_listener.link);
wl_list_init(&wlr_seat->pointer_state.focus_surface_destroy_listener.link);
wl_list_remove(
&wlr_seat->pointer_state.focus_resource_destroy_listener.link);
wl_list_init(&wlr_seat->pointer_state.focus_resource_destroy_listener.link);
wl_list_remove(&wlr_seat->pointer_state.surface_destroy.link);
wl_list_init(&wlr_seat->pointer_state.surface_destroy.link);
wl_list_remove(&wlr_seat->pointer_state.resource_destroy.link);
wl_list_init(&wlr_seat->pointer_state.resource_destroy.link);
if (surface) {
wl_signal_add(&surface->signals.destroy,
&wlr_seat->pointer_state.focus_surface_destroy_listener);
&wlr_seat->pointer_state.surface_destroy);
wl_resource_add_destroy_listener(surface->resource,
&wlr_seat->pointer_state.focus_resource_destroy_listener);
wlr_seat->pointer_state.focus_resource_destroy_listener.notify =
handle_pointer_focus_resource_destroyed;
wlr_seat->pointer_state.focus_surface_destroy_listener.notify =
handle_pointer_focus_surface_destroyed;
&wlr_seat->pointer_state.resource_destroy);
wlr_seat->pointer_state.resource_destroy.notify =
pointer_resource_destroy_notify;
wlr_seat->pointer_state.surface_destroy.notify =
pointer_surface_destroy_notify;
}
wlr_seat->pointer_state.focused_handle = handle;
@ -395,26 +376,114 @@ void wlr_seat_pointer_send_axis(struct wlr_seat *wlr_seat, uint32_t time,
wl_pointer_send_frame(pointer);
}
static void handle_keyboard_focus_surface_destroyed(
struct wl_listener *listener, void *data) {
struct wlr_seat_keyboard_state *state =
wl_container_of(listener, state, focus_surface_destroy_listener);
static void keyboard_key_notify(struct wl_listener *listener, void *data) {
struct wlr_seat_keyboard *seat_kb = wl_container_of(
listener, seat_kb, key);
struct wlr_seat *seat = seat_kb->seat;
struct wlr_seat_handle *handle = seat->keyboard_state.focused_handle;
if (!handle || !handle->keyboard) {
return;
}
struct wlr_keyboard *keyboard = seat_kb->keyboard;
struct wlr_event_keyboard_key *event = data;
enum wlr_key_state key_state = event->state;
if (handle->seat_keyboard != seat_kb) {
// TODO: We should probably lift all of the keys set by the other
// keyboard
wl_keyboard_send_keymap(handle->keyboard,
WL_KEYBOARD_KEYMAP_FORMAT_XKB_V1,
seat_kb->keyboard->keymap_fd,
seat_kb->keyboard->keymap_size);
if (wl_resource_get_version(handle->keyboard) >= 2) {
// TODO: Make this better
wl_keyboard_send_repeat_info(handle->keyboard, 25, 600);
}
handle->seat_keyboard = seat_kb;
}
uint32_t depressed = xkb_state_serialize_mods(keyboard->xkb_state,
XKB_STATE_MODS_DEPRESSED);
uint32_t latched = xkb_state_serialize_mods(keyboard->xkb_state,
XKB_STATE_MODS_LATCHED);
uint32_t locked = xkb_state_serialize_mods(keyboard->xkb_state,
XKB_STATE_MODS_LOCKED);
uint32_t group = xkb_state_serialize_layout(keyboard->xkb_state,
XKB_STATE_LAYOUT_EFFECTIVE);
uint32_t modifiers_serial = wl_display_next_serial(seat->display);
uint32_t key_serial = wl_display_next_serial(seat->display);
wl_keyboard_send_modifiers(handle->keyboard, modifiers_serial,
depressed, latched, locked, group);
wl_keyboard_send_key(handle->keyboard, key_serial,
(uint32_t)event->time_usec, event->keycode, key_state);
}
static void keyboard_keymap_notify(struct wl_listener *listener, void *data) {
struct wlr_seat_keyboard *seat_kb = wl_container_of(
listener, seat_kb, keymap);
wlr_log(L_DEBUG, "Keymap event for %p", seat_kb);
}
static void keyboard_destroy_notify(struct wl_listener *listener, void *data) {
struct wlr_seat_keyboard *seat_kb = wl_container_of(
listener, seat_kb, destroy);
wlr_seat_detach_keyboard(seat_kb->seat, seat_kb->keyboard);
}
void wlr_seat_attach_keyboard(struct wlr_seat *seat,
struct wlr_input_device *dev) {
assert(seat && dev && dev->type == WLR_INPUT_DEVICE_KEYBOARD);
struct wlr_keyboard *kb = dev->keyboard;
struct wlr_seat_keyboard *seat_kb =
calloc(1, sizeof(struct wlr_seat_keyboard));
seat_kb->keyboard = kb;
seat_kb->seat = seat;
wl_list_init(&seat_kb->key.link);
seat_kb->key.notify = keyboard_key_notify;
wl_signal_add(&kb->events.key, &seat_kb->key);
wl_list_init(&seat_kb->keymap.link);
seat_kb->keymap.notify = keyboard_keymap_notify;
wl_signal_add(&kb->events.keymap, &seat_kb->keymap);
// TODO: update keymap as necessary
wl_list_init(&seat_kb->destroy.link);
seat_kb->destroy.notify = keyboard_destroy_notify;
wl_signal_add(&dev->events.destroy, &seat_kb->destroy);
wl_list_insert(&seat->keyboards, &seat_kb->link);
}
void wlr_seat_detach_keyboard(struct wlr_seat *seat, struct wlr_keyboard *kb) {
struct wlr_seat_keyboard *seat_kb, *_tmp;
wl_list_for_each_safe(seat_kb, _tmp, &seat->keyboards, link) {
if (seat_kb->keyboard == kb) {
wl_list_remove(&seat_kb->link);
wl_list_remove(&seat_kb->key.link);
wl_list_remove(&seat_kb->keymap.link);
wl_list_remove(&seat_kb->destroy.link);
free(seat_kb);
break;
}
}
}
static void keyboard_surface_destroy_notify(struct wl_listener *listener,
void *data) {
struct wlr_seat_keyboard_state *state = wl_container_of(
listener, state, surface_destroy);
state->focused_surface = NULL;
wlr_seat_keyboard_clear_focus(state->wlr_seat);
}
static void handle_keyboard_focus_resource_destroyed(
struct wl_listener *listener, void *data) {
struct wlr_seat_keyboard_state *state =
wl_container_of(listener, state, focus_resource_destroy_listener);
static void keyboard_resource_destroy_notify(struct wl_listener *listener,
void *data) {
struct wlr_seat_keyboard_state *state = wl_container_of(
listener, state, resource_destroy);
state->focused_surface = NULL;
wlr_seat_keyboard_clear_focus(state->wlr_seat);
}
void wlr_seat_keyboard_enter(struct wlr_seat *wlr_seat,
struct wlr_surface *surface, struct wl_array keys) {
struct wlr_surface *surface) {
if (wlr_seat->keyboard_state.focused_surface == surface) {
// this surface already got an enter notify
return;
@ -441,30 +510,28 @@ void wlr_seat_keyboard_enter(struct wlr_seat *wlr_seat,
// enter the current surface
if (handle && handle->keyboard) {
// TODO: handle keys properly
struct wl_array keys;
wl_array_init(&keys);
uint32_t serial = wl_display_next_serial(wlr_seat->display);
wl_keyboard_send_enter(handle->keyboard, serial,
surface->resource, &keys);
// TODO: send modifiers
}
// reinitialize the focus destroy events
wl_list_remove(
&wlr_seat->keyboard_state.focus_surface_destroy_listener.link);
wl_list_init(&wlr_seat->keyboard_state.focus_surface_destroy_listener.link);
wl_list_remove(
&wlr_seat->keyboard_state.focus_resource_destroy_listener.link);
wl_list_init(
&wlr_seat->keyboard_state.focus_resource_destroy_listener.link);
wl_list_remove(&wlr_seat->keyboard_state.surface_destroy.link);
wl_list_init(&wlr_seat->keyboard_state.surface_destroy.link);
wl_list_remove(&wlr_seat->keyboard_state.resource_destroy.link);
wl_list_init(&wlr_seat->keyboard_state.resource_destroy.link);
if (surface) {
wl_signal_add(&surface->signals.destroy,
&wlr_seat->keyboard_state.focus_surface_destroy_listener);
&wlr_seat->keyboard_state.surface_destroy);
wl_resource_add_destroy_listener(surface->resource,
&wlr_seat->keyboard_state.focus_resource_destroy_listener);
wlr_seat->keyboard_state.focus_resource_destroy_listener.notify =
handle_keyboard_focus_resource_destroyed;
wlr_seat->keyboard_state.focus_surface_destroy_listener.notify =
handle_keyboard_focus_surface_destroyed;
&wlr_seat->keyboard_state.resource_destroy);
wlr_seat->keyboard_state.resource_destroy.notify =
keyboard_resource_destroy_notify;
wlr_seat->keyboard_state.surface_destroy.notify =
keyboard_surface_destroy_notify;
}
wlr_seat->keyboard_state.focused_handle = handle;
@ -474,65 +541,5 @@ void wlr_seat_keyboard_enter(struct wlr_seat *wlr_seat,
void wlr_seat_keyboard_clear_focus(struct wlr_seat *wlr_seat) {
struct wl_array keys;
wl_array_init(&keys);
wlr_seat_keyboard_enter(wlr_seat, NULL, keys);
}
static bool wlr_seat_keyboard_has_focus_resource(struct wlr_seat *wlr_seat) {
return wlr_seat->keyboard_state.focused_handle &&
wlr_seat->keyboard_state.focused_handle->keyboard;
}
uint32_t wlr_seat_keyboard_send_key(struct wlr_seat *wlr_seat, uint32_t time,
uint32_t key, uint32_t state) {
if (!wlr_seat_keyboard_has_focus_resource(wlr_seat)) {
return 0;
}
uint32_t serial = wl_display_next_serial(wlr_seat->display);
wl_keyboard_send_key(wlr_seat->keyboard_state.focused_handle->keyboard,
serial, time, key, state);
return serial;
}
void wlr_seat_keyboard_send_modifiers(struct wlr_seat *wlr_seat,
uint32_t mods_depressed, uint32_t mods_latched, uint32_t mods_locked,
uint32_t group) {
uint32_t serial = 0;
struct wl_resource *keyboard;
if (wlr_seat_keyboard_has_focus_resource(wlr_seat)) {
serial = wl_display_next_serial(wlr_seat->display);
keyboard = wlr_seat->keyboard_state.focused_handle->keyboard;
wl_keyboard_send_modifiers(keyboard, serial, mods_depressed,
mods_latched, mods_locked, group);
}
if (wlr_seat_pointer_has_focus_resource(wlr_seat) &&
wlr_seat->pointer_state.focused_handle->keyboard &&
wlr_seat->pointer_state.focused_handle !=
wlr_seat->keyboard_state.focused_handle) {
if (serial == 0) {
serial = wl_display_next_serial(wlr_seat->display);
}
keyboard = wlr_seat->pointer_state.focused_handle->keyboard;
wl_keyboard_send_modifiers(keyboard, serial, mods_depressed,
mods_latched, mods_locked, group);
}
}
void wlr_seat_keyboard_set_keymap(struct wlr_seat *wlr_seat, int keymap_fd,
size_t keymap_size) {
// TODO: we probably should wait to send the keymap if keys are pressed
struct wlr_seat_handle *handle;
wl_list_for_each(handle, &wlr_seat->handles, link) {
if (handle->keyboard) {
wl_keyboard_send_keymap(handle->keyboard,
WL_KEYBOARD_KEYMAP_FORMAT_XKB_V1, keymap_fd, keymap_size);
}
}
wlr_seat->keyboard_state.keymap_fd = keymap_fd;
wlr_seat->keyboard_state.keymap_size = keymap_size;
wlr_seat_keyboard_enter(wlr_seat, NULL);
}

View file

@ -1,61 +1,250 @@
#ifndef _POSIX_C_SOURCE
#define _POSIX_C_SOURCE 200809L
#endif
#include <assert.h>
#include <wayland-server.h>
#include <wlr/util/log.h>
#include <wlr/types/wlr_surface.h>
#include <wlr/types/wlr_wl_shell.h>
#include <stdlib.h>
#include <wayland-server-protocol.h>
static void shell_surface_pong(struct wl_client *client, struct wl_resource
*resource, uint32_t serial) {
wlr_log(L_DEBUG, "TODO: implement shell surface pong");
static const char *wlr_wl_shell_surface_role = "wl_shell_surface";
static void shell_surface_pong(struct wl_client *client,
struct wl_resource *resource, uint32_t serial) {
wlr_log(L_DEBUG, "got shell surface pong");
struct wlr_wl_shell_surface *surface = wl_resource_get_user_data(resource);
if (surface->ping_serial != serial) {
return;
}
wl_event_source_timer_update(surface->ping_timer, 0);
surface->ping_serial = 0;
}
static void shell_surface_move(struct wl_client *client, struct wl_resource
*resource, struct wl_resource *seat, uint32_t serial) {
wlr_log(L_DEBUG, "TODO: implement shell surface move");
static void shell_surface_move(struct wl_client *client,
struct wl_resource *resource, struct wl_resource *seat_resource,
uint32_t serial) {
wlr_log(L_DEBUG, "got shell surface move");
struct wlr_wl_shell_surface *surface = wl_resource_get_user_data(resource);
struct wlr_seat_handle *seat_handle =
wl_resource_get_user_data(seat_resource);
struct wlr_wl_shell_surface_move_event *event =
calloc(1, sizeof(struct wlr_wl_shell_surface_move_event));
if (event == NULL) {
wl_client_post_no_memory(client);
return;
}
event->client = client;
event->surface = surface;
event->seat_handle = seat_handle;
event->serial = serial;
wl_signal_emit(&surface->events.request_move, event);
free(event);
}
static void shell_surface_resize(struct wl_client *client,
struct wl_resource *resource, struct wl_resource *seat, uint32_t serial,
uint32_t edges) {
wlr_log(L_DEBUG, "TODO: implement shell surface resize");
struct wl_resource *resource, struct wl_resource *seat_resource,
uint32_t serial, enum wl_shell_surface_resize edges) {
wlr_log(L_DEBUG, "got shell surface resize");
struct wlr_wl_shell_surface *surface = wl_resource_get_user_data(resource);
struct wlr_seat_handle *seat_handle =
wl_resource_get_user_data(seat_resource);
struct wlr_wl_shell_surface_resize_event *event =
calloc(1, sizeof(struct wlr_wl_shell_surface_resize_event));
if (event == NULL) {
wl_client_post_no_memory(client);
return;
}
event->client = client;
event->surface = surface;
event->seat_handle = seat_handle;
event->serial = serial;
event->edges = edges;
wl_signal_emit(&surface->events.request_resize, event);
free(event);
}
static void shell_surface_set_state(struct wlr_wl_shell_surface *surface,
enum wlr_wl_shell_surface_state state,
struct wlr_wl_shell_surface_transient_state *transient_state,
struct wlr_wl_shell_surface_popup_state *popup_state) {
surface->state = state;
free(surface->transient_state);
surface->transient_state = transient_state;
free(surface->popup_state);
surface->popup_state = popup_state;
wl_signal_emit(&surface->events.set_state, surface);
}
static void shell_surface_set_toplevel(struct wl_client *client,
struct wl_resource *resource) {
wlr_log(L_DEBUG, "TODO: implement shell surface set_toplevel");
wlr_log(L_DEBUG, "got shell surface toplevel");
struct wlr_wl_shell_surface *surface = wl_resource_get_user_data(resource);
shell_surface_set_state(surface, WLR_WL_SHELL_SURFACE_STATE_TOPLEVEL, NULL,
NULL);
}
static void shell_surface_set_transient(struct wl_client *client,
struct wl_resource *resource, struct wl_resource *parent, int32_t x,
int32_t y, uint32_t flags) {
wlr_log(L_DEBUG, "TODO: implement shell surface set_transient");
struct wl_resource *resource, struct wl_resource *parent_resource,
int32_t x, int32_t y, enum wl_shell_surface_transient flags) {
wlr_log(L_DEBUG, "got shell surface transient");
struct wlr_wl_shell_surface *surface = wl_resource_get_user_data(resource);
struct wlr_wl_shell_surface *parent =
wl_resource_get_user_data(parent_resource);
// TODO: check if parent_resource == NULL?
struct wlr_wl_shell_surface_transient_state *transient_state =
calloc(1, sizeof(struct wlr_wl_shell_surface_transient_state));
if (transient_state == NULL) {
wl_client_post_no_memory(client);
return;
}
transient_state->parent = parent;
transient_state->x = x;
transient_state->y = y;
transient_state->flags = flags;
shell_surface_set_state(surface, WLR_WL_SHELL_SURFACE_STATE_TRANSIENT,
transient_state, NULL);
}
static void shell_surface_set_fullscreen(struct wl_client *client,
struct wl_resource *resource, uint32_t method, uint32_t framerate,
struct wl_resource *output) {
wlr_log(L_DEBUG, "TODO: implement shell surface set_fullscreen");
struct wl_resource *resource,
enum wl_shell_surface_fullscreen_method method, uint32_t framerate,
struct wl_resource *output_resource) {
wlr_log(L_DEBUG, "got shell surface fullscreen");
struct wlr_wl_shell_surface *surface = wl_resource_get_user_data(resource);
struct wlr_output *output = NULL;
if (output_resource != NULL) {
output = wl_resource_get_user_data(output_resource);
}
if (surface->state == WLR_WL_SHELL_SURFACE_STATE_TOPLEVEL) {
return;
}
struct wlr_wl_shell_surface_set_fullscreen_event *event =
calloc(1, sizeof(struct wlr_wl_shell_surface_set_fullscreen_event));
if (event == NULL) {
wl_client_post_no_memory(client);
return;
}
event->client = client;
event->surface = surface;
event->method = method;
event->framerate = framerate;
event->output = output;
wl_signal_emit(&surface->events.request_set_fullscreen, event);
free(event);
}
static void shell_surface_set_popup(struct wl_client *client,
struct wl_resource *resource, struct wl_resource *seat, uint32_t serial,
struct wl_resource *parent, int32_t x, int32_t y, uint32_t flags) {
wlr_log(L_DEBUG, "TODO: implement shell surface set_popup");
struct wl_resource *resource, struct wl_resource *seat_resource,
uint32_t serial, struct wl_resource *parent_resource, int32_t x,
int32_t y, enum wl_shell_surface_transient flags) {
// TODO: do a pointer grab
wlr_log(L_DEBUG, "got shell surface popup");
struct wlr_wl_shell_surface *surface = wl_resource_get_user_data(resource);
struct wlr_seat_handle *seat_handle =
wl_resource_get_user_data(seat_resource);
struct wlr_wl_shell_surface *parent =
wl_resource_get_user_data(parent_resource);
// TODO: check if parent_resource == NULL?
struct wlr_wl_shell_surface_transient_state *transient_state =
calloc(1, sizeof(struct wlr_wl_shell_surface_transient_state));
if (transient_state == NULL) {
wl_client_post_no_memory(client);
return;
}
transient_state->parent = parent;
transient_state->x = x;
transient_state->y = y;
transient_state->flags = flags;
struct wlr_wl_shell_surface_popup_state *popup_state =
calloc(1, sizeof(struct wlr_wl_shell_surface_transient_state));
if (popup_state == NULL) {
free(transient_state);
wl_client_post_no_memory(client);
return;
}
popup_state->seat_handle = seat_handle;
popup_state->serial = serial;
shell_surface_set_state(surface, WLR_WL_SHELL_SURFACE_STATE_POPUP,
transient_state, popup_state);
}
static void shell_surface_set_maximized(struct wl_client *client,
struct wl_resource *resource, struct wl_resource *output) {
wlr_log(L_DEBUG, "TODO: implement shell surface set_maximized");
struct wl_resource *resource, struct wl_resource *output_resource) {
wlr_log(L_DEBUG, "got shell surface maximized");
struct wlr_wl_shell_surface *surface = wl_resource_get_user_data(resource);
struct wlr_output *output = NULL;
if (output_resource != NULL) {
output = wl_resource_get_user_data(output_resource);
}
if (surface->state == WLR_WL_SHELL_SURFACE_STATE_TOPLEVEL) {
return;
}
struct wlr_wl_shell_surface_set_maximized_event *event =
calloc(1, sizeof(struct wlr_wl_shell_surface_set_maximized_event));
if (event == NULL) {
wl_client_post_no_memory(client);
return;
}
event->client = client;
event->surface = surface;
event->output = output;
wl_signal_emit(&surface->events.request_set_maximized, event);
free(event);
}
static void shell_surface_set_title(struct wl_client *client,
struct wl_resource *resource, const char *title) {
wlr_log(L_DEBUG, "TODO: implement shell surface set_title");
wlr_log(L_DEBUG, "new shell surface title: %s", title);
struct wlr_wl_shell_surface *surface = wl_resource_get_user_data(resource);
char *tmp = strdup(title);
if (tmp == NULL) {
return;
}
free(surface->title);
surface->title = tmp;
wl_signal_emit(&surface->events.set_title, surface);
}
static void shell_surface_set_class(struct wl_client *client,
struct wl_resource *resource, const char *class_) {
wlr_log(L_DEBUG, "TODO: implement shell surface set_class");
struct wl_resource *resource, const char *class) {
wlr_log(L_DEBUG, "new shell surface class: %s", class);
struct wlr_wl_shell_surface *surface = wl_resource_get_user_data(resource);
char *tmp = strdup(class);
if (tmp == NULL) {
return;
}
free(surface->class);
surface->class = tmp;
wl_signal_emit(&surface->events.set_class, surface);
}
struct wl_shell_surface_interface shell_surface_interface = {
@ -71,27 +260,94 @@ struct wl_shell_surface_interface shell_surface_interface = {
.set_class = shell_surface_set_class,
};
static void destroy_shell_surface(struct wl_resource *resource) {
struct wlr_wl_shell_surface *state = wl_resource_get_user_data(resource);
wl_list_remove(&state->link);
free(state);
static void wl_shell_surface_destroy(struct wlr_wl_shell_surface *surface) {
wl_signal_emit(&surface->events.destroy, surface);
wl_resource_set_user_data(surface->resource, NULL);
wl_list_remove(&surface->link);
wl_list_remove(&surface->surface_destroy_listener.link);
wl_event_source_remove(surface->ping_timer);
free(surface->transient_state);
free(surface->popup_state);
free(surface->title);
free(surface->class);
free(surface);
}
static void wl_shell_surface_resource_destroy(struct wl_resource *resource) {
struct wlr_wl_shell_surface *surface = wl_resource_get_user_data(resource);
if (surface != NULL) {
wl_shell_surface_destroy(surface);
}
}
static void handle_wlr_surface_destroyed(struct wl_listener *listener,
void *data) {
struct wlr_wl_shell_surface *surface =
wl_container_of(listener, surface, surface_destroy_listener);
wl_shell_surface_destroy(surface);
}
static int wlr_wl_shell_surface_ping_timeout(void *user_data) {
struct wlr_wl_shell_surface *surface = user_data;
wl_signal_emit(&surface->events.ping_timeout, surface);
surface->ping_serial = 0;
return 1;
}
static void wl_shell_get_shell_surface(struct wl_client *client,
struct wl_resource *resource, uint32_t id,
struct wl_resource *surface) {
struct wlr_texture *wlr_texture = wl_resource_get_user_data(surface);
struct wlr_wl_shell *wlr_wl_shell = wl_resource_get_user_data(resource);
struct wlr_wl_shell_surface *state =
struct wl_resource *surface_resource) {
struct wlr_surface *surface = wl_resource_get_user_data(surface_resource);
if (wlr_surface_set_role(surface, wlr_wl_shell_surface_role,
resource, WL_SHELL_ERROR_ROLE)) {
return;
}
struct wlr_wl_shell *wl_shell = wl_resource_get_user_data(resource);
struct wlr_wl_shell_surface *wl_surface =
calloc(1, sizeof(struct wlr_wl_shell_surface));
state->wlr_texture = wlr_texture;
state->surface = surface;
struct wl_resource *shell_surface_resource = wl_resource_create(client,
&wl_shell_surface_interface, wl_resource_get_version(resource), id);
wlr_log(L_DEBUG, "New wl_shell %p (res %p)", state, shell_surface_resource);
wl_resource_set_implementation(shell_surface_resource,
&shell_surface_interface, state, destroy_shell_surface);
wl_list_insert(&wlr_wl_shell->surfaces, &state->link);
if (wl_surface == NULL) {
wl_client_post_no_memory(client);
return;
}
wl_surface->shell = wl_shell;
wl_surface->client = client;
wl_surface->surface = surface;
wl_surface->resource = wl_resource_create(client,
&wl_shell_surface_interface, wl_resource_get_version(resource), id);
wlr_log(L_DEBUG, "new wl_shell %p (res %p)", wl_surface,
wl_surface->resource);
wl_resource_set_implementation(wl_surface->resource,
&shell_surface_interface, wl_surface,
wl_shell_surface_resource_destroy);
wl_signal_init(&wl_surface->events.destroy);
wl_signal_init(&wl_surface->events.ping_timeout);
wl_signal_init(&wl_surface->events.request_move);
wl_signal_init(&wl_surface->events.request_resize);
wl_signal_init(&wl_surface->events.request_set_fullscreen);
wl_signal_init(&wl_surface->events.request_set_maximized);
wl_signal_init(&wl_surface->events.set_state);
wl_signal_init(&wl_surface->events.set_title);
wl_signal_init(&wl_surface->events.set_class);
wl_signal_add(&wl_surface->surface->signals.destroy,
&wl_surface->surface_destroy_listener);
wl_surface->surface_destroy_listener.notify = handle_wlr_surface_destroyed;
struct wl_display *display = wl_client_get_display(client);
struct wl_event_loop *loop = wl_display_get_event_loop(display);
wl_surface->ping_timer = wl_event_loop_add_timer(loop,
wlr_wl_shell_surface_ping_timeout, wl_surface);
if (wl_surface->ping_timer == NULL) {
wl_client_post_no_memory(client);
}
wl_list_insert(&wl_shell->surfaces, &wl_surface->link);
wl_signal_emit(&wl_shell->events.new_surface, wl_surface);
}
static struct wl_shell_interface wl_shell_impl = {
@ -107,33 +363,35 @@ static void wl_shell_bind(struct wl_client *wl_client, void *_wl_shell,
struct wlr_wl_shell *wl_shell = _wl_shell;
assert(wl_client && wl_shell);
if (version > 1) {
wlr_log(L_ERROR, "Client requested unsupported wl_shell version, disconnecting");
wlr_log(L_ERROR,
"Client requested unsupported wl_shell version, disconnecting");
wl_client_destroy(wl_client);
return;
}
struct wl_resource *wl_resource = wl_resource_create(
wl_client, &wl_shell_interface, version, id);
wl_resource_set_implementation(wl_resource, &wl_shell_impl,
wl_shell, wl_shell_destroy);
struct wl_resource *wl_resource = wl_resource_create(wl_client,
&wl_shell_interface, version, id);
wl_resource_set_implementation(wl_resource, &wl_shell_impl, wl_shell,
wl_shell_destroy);
wl_list_insert(&wl_shell->wl_resources, wl_resource_get_link(wl_resource));
}
struct wlr_wl_shell *wlr_wl_shell_create(struct wl_display *display) {
struct wlr_wl_shell *wlr_wl_shell =
calloc(1, sizeof(struct wlr_wl_shell));
if (!wlr_wl_shell) {
struct wlr_wl_shell *wl_shell = calloc(1, sizeof(struct wlr_wl_shell));
if (!wl_shell) {
return NULL;
}
struct wl_global *wl_global = wl_global_create(display,
&wl_shell_interface, 1, wlr_wl_shell, wl_shell_bind);
wl_shell->ping_timeout = 10000;
struct wl_global *wl_global = wl_global_create(display, &wl_shell_interface,
1, wl_shell, wl_shell_bind);
if (!wl_global) {
free(wlr_wl_shell);
free(wl_shell);
return NULL;
}
wlr_wl_shell->wl_global = wl_global;
wl_list_init(&wlr_wl_shell->wl_resources);
wl_list_init(&wlr_wl_shell->surfaces);
return wlr_wl_shell;
wl_shell->wl_global = wl_global;
wl_list_init(&wl_shell->wl_resources);
wl_list_init(&wl_shell->surfaces);
wl_signal_init(&wl_shell->events.new_surface);
return wl_shell;
}
void wlr_wl_shell_destroy(struct wlr_wl_shell *wlr_wl_shell) {
@ -150,3 +408,25 @@ void wlr_wl_shell_destroy(struct wlr_wl_shell *wlr_wl_shell) {
// wl_global_destroy(wlr_wl_shell->wl_global);
free(wlr_wl_shell);
}
void wlr_wl_shell_surface_ping(struct wlr_wl_shell_surface *surface) {
if (surface->ping_serial != 0) {
// already pinged
return;
}
surface->ping_serial =
wl_display_next_serial(wl_client_get_display(surface->client));
wl_event_source_timer_update(surface->ping_timer,
surface->shell->ping_timeout);
wl_shell_surface_send_ping(surface->resource, surface->ping_serial);
}
void wlr_wl_shell_surface_configure(struct wlr_wl_shell_surface *surface,
uint32_t edges, int32_t width, int32_t height) {
wl_shell_surface_send_configure(surface->resource, edges, width, height);
}
void wlr_wl_shell_surface_popup_done(struct wlr_wl_shell_surface *surface) {
wl_shell_surface_send_popup_done(surface->resource);
}

View file

@ -105,7 +105,7 @@ static void exec_xwayland(struct wlr_xwayland *wlr_xwayland) {
}
static bool wlr_xwayland_init(struct wlr_xwayland *wlr_xwayland,
struct wl_display *wl_display, struct wlr_compositor *compositor);
struct wl_display *wl_display, struct wlr_compositor *compositor);
static void wlr_xwayland_finish(struct wlr_xwayland *wlr_xwayland);
static void xwayland_destroy_event(struct wl_listener *listener, void *data) {
@ -193,7 +193,8 @@ static bool wlr_xwayland_init(struct wlr_xwayland *wlr_xwayland,
wlr_xwayland->x_fd[0] = wlr_xwayland->x_fd[1] = -1;
wlr_xwayland->wl_fd[0] = wlr_xwayland->wl_fd[1] = -1;
wlr_xwayland->wm_fd[0] = wlr_xwayland->wm_fd[1] = -1;
wl_list_init(&wlr_xwayland->displayable_windows);
wl_list_init(&wlr_xwayland->displayable_surfaces);
wl_signal_init(&wlr_xwayland->events.new_surface);
wlr_xwayland->display = open_display_sockets(wlr_xwayland->x_fd);
if (wlr_xwayland->display < 0) {

View file

@ -5,50 +5,64 @@
#include "wlr/xwayland.h"
#include "xwm.h"
const char *atom_map[ATOM_LAST] = {
"WL_SURFACE_ID",
"WM_PROTOCOLS",
"WM_S0",
"_NET_SUPPORTED",
"_NET_WM_S0",
"_NET_WM_STATE",
"WM_TAKE_FOCUS",
};
/* General helpers */
// TODO: replace this with hash table?
static struct wlr_x11_window *lookup_window(struct wl_list *list, xcb_window_t window_id) {
struct wlr_x11_window *window;
wl_list_for_each(window, list, link) {
if (window->window_id == window_id) {
return window;
static struct wlr_xwayland_surface *lookup_surface(struct wl_list *list,
xcb_window_t window_id) {
struct wlr_xwayland_surface *surface;
wl_list_for_each(surface, list, link) {
if (surface->window_id == window_id) {
return surface;
}
}
return NULL;
}
static struct wlr_x11_window *lookup_window_any(struct wlr_xwm *xwm, xcb_window_t window_id) {
struct wlr_x11_window *window;
if ((window = lookup_window(&xwm->xwayland->displayable_windows, window_id)) ||
(window = lookup_window(&xwm->unpaired_windows, window_id)) ||
(window = lookup_window(&xwm->new_windows, window_id))) {
return window;
static struct wlr_xwayland_surface *lookup_surface_any(struct wlr_xwm *xwm,
xcb_window_t window_id) {
struct wlr_xwayland_surface *surface;
if ((surface = lookup_surface(&xwm->xwayland->displayable_surfaces, window_id)) ||
(surface = lookup_surface(&xwm->unpaired_surfaces, window_id)) ||
(surface = lookup_surface(&xwm->new_surfaces, window_id))) {
return surface;
}
return NULL;
}
static struct wlr_x11_window *wlr_x11_window_create(struct wlr_xwm *xwm,
xcb_window_t window_id, int16_t x, int16_t y,
static struct wlr_xwayland_surface *wlr_xwayland_surface_create(
struct wlr_xwm *xwm, xcb_window_t window_id, int16_t x, int16_t y,
uint16_t width, uint16_t height, bool override_redirect) {
struct wlr_x11_window *window;
window = calloc(1, sizeof(struct wlr_x11_window));
if (!window) {
wlr_log(L_ERROR, "Could not allocate wlr x11 window");
struct wlr_xwayland_surface *surface =
calloc(1, sizeof(struct wlr_xwayland_surface));
if (!surface) {
wlr_log(L_ERROR, "Could not allocate wlr xwayland surface");
return NULL;
}
window->window_id = window_id;
window->x = x;
window->y = y;
window->width = width;
window->height = height;
window->override_redirect = override_redirect;
wl_list_insert(&xwm->new_windows, &window->link);
return window;
surface->window_id = window_id;
surface->x = x;
surface->y = y;
surface->width = width;
surface->height = height;
surface->override_redirect = override_redirect;
wl_list_insert(&xwm->new_surfaces, &surface->link);
wl_signal_init(&surface->events.destroy);
return surface;
}
static void wlr_x11_window_destroy(struct wlr_x11_window *window) {
wl_list_remove(&window->link);
free(window);
static void wlr_xwayland_surface_destroy(struct wlr_xwayland_surface *surface) {
wl_signal_emit(&surface->events.destroy, surface);
wl_list_remove(&surface->link);
free(surface);
}
/* xcb helpers */
@ -66,105 +80,117 @@ static bool xcb_call(struct wlr_xwm *xwm, const char *func, uint32_t line,
return false;
}
static void map_shell_surface(struct wlr_xwm *xwm, struct wlr_x11_window *window,
static void map_shell_surface(struct wlr_xwm *xwm,
struct wlr_xwayland_surface *xwayland_surface,
struct wlr_surface *surface) {
// get xcb geometry for depth = alpha channel
window->surface = surface->resource;
xwayland_surface->surface = surface;
wl_list_remove(&window->link);
wl_list_insert(&xwm->xwayland->displayable_windows, &window->link);
wl_list_remove(&xwayland_surface->link);
wl_list_insert(&xwm->xwayland->displayable_surfaces,
&xwayland_surface->link);
wl_signal_emit(&xwm->xwayland->events.new_surface, xwayland_surface);
}
/* xcb event handlers */
static void handle_create_notify(struct wlr_xwm *xwm, xcb_create_notify_event_t *ev) {
static void handle_create_notify(struct wlr_xwm *xwm,
xcb_create_notify_event_t *ev) {
wlr_log(L_DEBUG, "XCB_CREATE_NOTIFY (%u)", ev->window);
wlr_x11_window_create(xwm, ev->window, ev->x, ev->y,
wlr_xwayland_surface_create(xwm, ev->window, ev->x, ev->y,
ev->width, ev->height, ev->override_redirect);
}
static void handle_destroy_notify(struct wlr_xwm *xwm, xcb_destroy_notify_event_t *ev) {
struct wlr_x11_window *window;
static void handle_destroy_notify(struct wlr_xwm *xwm,
xcb_destroy_notify_event_t *ev) {
wlr_log(L_DEBUG, "XCB_DESTROY_NOTIFY (%u)", ev->window);
if (!(window = lookup_window_any(xwm, ev->window))) {
struct wlr_xwayland_surface *surface = lookup_surface_any(xwm, ev->window);
if (surface == NULL) {
return;
}
wlr_x11_window_destroy(window);
wlr_xwayland_surface_destroy(surface);
}
static void handle_configure_request(struct wlr_xwm *xwm, xcb_configure_request_event_t *ev) {
struct wlr_x11_window *window;
static void handle_configure_request(struct wlr_xwm *xwm,
xcb_configure_request_event_t *ev) {
wlr_log(L_DEBUG, "XCB_CONFIGURE_REQUEST (%u) [%ux%u+%d,%d]", ev->window,
ev->width, ev->height, ev->x, ev->y);
if (!(window = lookup_window_any(xwm, ev->window))) {
struct wlr_xwayland_surface *surface = lookup_surface_any(xwm, ev->window);
if (surface == NULL) {
return;
}
window->x = ev->x;
window->y = ev->y;
window->width = ev->width;
window->height = ev->height;
surface->x = ev->x;
surface->y = ev->y;
surface->width = ev->width;
surface->height = ev->height;
// handle parent/sibling?
uint32_t values[] = { ev->x, ev->y, ev->width, ev->height, 0 };
uint32_t values[] = {ev->x, ev->y, ev->width, ev->height, 0};
uint32_t mask = XCB_CONFIG_WINDOW_X | XCB_CONFIG_WINDOW_Y |
XCB_CONFIG_WINDOW_WIDTH | XCB_CONFIG_WINDOW_HEIGHT |
XCB_CONFIG_WINDOW_BORDER_WIDTH;
xcb_configure_window(xwm->xcb_conn, ev->window, mask, values);
}
static void handle_map_request(struct wlr_xwm *xwm, xcb_map_request_event_t *ev) {
static void handle_map_request(struct wlr_xwm *xwm,
xcb_map_request_event_t *ev) {
wlr_log(L_DEBUG, "XCB_MAP_REQUEST (%u)", ev->window);
const uint32_t value_list = XCB_EVENT_MASK_FOCUS_CHANGE |
XCB_EVENT_MASK_PROPERTY_CHANGE;
XCB_CALL(xwm, xcb_change_window_attributes_checked(xwm->xcb_conn,
ev->window, XCB_CW_EVENT_MASK,
&(uint32_t){XCB_EVENT_MASK_FOCUS_CHANGE | XCB_EVENT_MASK_PROPERTY_CHANGE}));
ev->window, XCB_CW_EVENT_MASK, &value_list));
XCB_CALL(xwm, xcb_map_window_checked(xwm->xcb_conn, ev->window));
}
static void handle_map_notify(struct wlr_xwm *xwm, xcb_map_notify_event_t *ev) {
struct wlr_x11_window *window;
wlr_log(L_DEBUG, "XCB_MAP_NOTIFY (%u)", ev->window);
if ((window = lookup_window_any(xwm, ev->window))) {
window->override_redirect = ev->override_redirect;
struct wlr_xwayland_surface *surface = lookup_surface_any(xwm, ev->window);
if (surface != NULL) {
surface->override_redirect = ev->override_redirect;
} else {
wlr_x11_window_create(xwm, ev->window, 0, 0, 1, 1, ev->override_redirect);
wlr_xwayland_surface_create(xwm, ev->window, 0, 0, 1, 1,
ev->override_redirect);
}
}
static void handle_unmap_notify(struct wlr_xwm *xwm, xcb_unmap_notify_event_t *ev) {
struct wlr_x11_window *window;
static void handle_unmap_notify(struct wlr_xwm *xwm,
xcb_unmap_notify_event_t *ev) {
wlr_log(L_DEBUG, "XCB_UNMAP_NOTIFY (%u)", ev->window);
if (!(window = lookup_window_any(xwm, ev->window))) {
struct wlr_xwayland_surface *surface = lookup_surface_any(xwm, ev->window);
if (surface == NULL) {
return;
}
// remove pointer to surface only?
wlr_x11_window_destroy(window);
// TODO: remove pointer to surface only?
wlr_xwayland_surface_destroy(surface);
}
static void handle_property_notify(struct wlr_xwm *xwm, xcb_property_notify_event_t *ev) {
static void handle_property_notify(struct wlr_xwm *xwm,
xcb_property_notify_event_t *ev) {
wlr_log(L_DEBUG, "XCB_PROPERTY_NOTIFY (%u)", ev->window);
// TODO lookup window & get properties
}
static void handle_client_message(struct wlr_xwm *xwm, xcb_client_message_event_t *ev) {
static void handle_client_message(struct wlr_xwm *xwm,
xcb_client_message_event_t *ev) {
wlr_log(L_DEBUG, "XCB_CLIENT_MESSAGE (%u)", ev->window);
if (ev->type == xwm->atoms[WL_SURFACE_ID]) {
struct wlr_x11_window *window;
struct wl_resource *resource;
window = lookup_window(&xwm->new_windows, ev->window);
if (!window) {
struct wlr_xwayland_surface *surface = lookup_surface(
&xwm->new_surfaces, ev->window);
if (surface == NULL) {
wlr_log(L_DEBUG, "client message WL_SURFACE_ID but no new window %u ?",
ev->window);
return;
}
window->surface_id = ev->data.data32[0];
surface->surface_id = ev->data.data32[0];
/* Check if we got notified after wayland surface create event */
resource = wl_client_get_object(xwm->xwayland->client, window->surface_id);
struct wl_resource *resource = wl_client_get_object(
xwm->xwayland->client, surface->surface_id);
if (resource) {
map_shell_surface(xwm, window, wl_resource_get_user_data(resource));
map_shell_surface(xwm, surface, wl_resource_get_user_data(resource));
} else {
wl_list_remove(&window->link);
wl_list_insert(&xwm->unpaired_windows, &window->link);
wl_list_remove(&surface->link);
wl_list_insert(&xwm->unpaired_surfaces, &surface->link);
}
}
wlr_log(L_DEBUG, "unhandled client message %u", ev->type);
@ -220,20 +246,19 @@ static int x11_event_handler(int fd, uint32_t mask, void *data) {
static void create_surface_handler(struct wl_listener *listener, void *data) {
struct wlr_surface *surface = data;
struct wlr_xwm *xwm = wl_container_of(listener, xwm, surface_create_listener);
struct wlr_x11_window *window;
uint32_t surface_id;
struct wlr_xwm *xwm = wl_container_of(listener, xwm,
surface_create_listener);
if (wl_resource_get_client(surface->resource) != xwm->xwayland->client) {
return;
}
wlr_log(L_DEBUG, "New x11 surface: %p", surface);
surface_id = wl_resource_get_id(surface->resource);
wl_list_for_each(window, &xwm->unpaired_windows, link) {
if (window->surface_id == surface_id) {
map_shell_surface(xwm, window, surface);
uint32_t surface_id = wl_resource_get_id(surface->resource);
struct wlr_xwayland_surface *xwayland_surface;
wl_list_for_each(xwayland_surface, &xwm->unpaired_surfaces, link) {
if (xwayland_surface->surface_id == surface_id) {
map_shell_surface(xwm, xwayland_surface, surface);
xcb_flush(xwm->xcb_conn);
return;
}
@ -269,8 +294,8 @@ static void xcb_get_resources(struct wlr_xwm *xwm) {
}
static void xcb_init_wm(struct wlr_xwm *xwm) {
xcb_screen_iterator_t screen_iterator;
screen_iterator = xcb_setup_roots_iterator(xcb_get_setup(xwm->xcb_conn));
xcb_screen_iterator_t screen_iterator =
xcb_setup_roots_iterator(xcb_get_setup(xwm->xcb_conn));
xwm->screen = screen_iterator.data;
xwm->window = xcb_generate_id(xwm->xcb_conn);
@ -281,26 +306,45 @@ static void xcb_init_wm(struct wlr_xwm *xwm) {
XCB_EVENT_MASK_PROPERTY_CHANGE,
/* xwm->cursor, */
};
XCB_CALL(xwm, xcb_change_window_attributes_checked(xwm->xcb_conn, xwm->screen->root,
XCB_CW_EVENT_MASK /* | XCB_CW_CURSOR */, values));
XCB_CALL(xwm, xcb_change_window_attributes_checked(xwm->xcb_conn,
xwm->screen->root, XCB_CW_EVENT_MASK /* | XCB_CW_CURSOR */, values));
XCB_CALL(xwm, xcb_composite_redirect_subwindows_checked(xwm->xcb_conn,
xwm->screen->root, XCB_COMPOSITE_REDIRECT_MANUAL));
xwm->screen->root, XCB_COMPOSITE_REDIRECT_MANUAL));
XCB_CALL(xwm, xcb_create_window_checked(xwm->xcb_conn, XCB_COPY_FROM_PARENT,
xwm->window, xwm->screen->root, 0, 0, 1, 1, 0,
XCB_WINDOW_CLASS_INPUT_OUTPUT, xwm->screen->root_visual,
XCB_CW_EVENT_MASK, (uint32_t[]){XCB_EVENT_MASK_PROPERTY_CHANGE}));
xwm->window, xwm->screen->root, 0, 0, 1, 1, 0,
XCB_WINDOW_CLASS_INPUT_OUTPUT, xwm->screen->root_visual,
XCB_CW_EVENT_MASK, (uint32_t[]){XCB_EVENT_MASK_PROPERTY_CHANGE}));
xcb_atom_t supported[] = {
xwm->atoms[NET_WM_STATE],
};
XCB_CALL(xwm, xcb_change_property_checked(xwm->xcb_conn, XCB_PROP_MODE_REPLACE,
xwm->screen->root, xwm->atoms[NET_SUPPORTED], XCB_ATOM_ATOM,
32, sizeof(supported)/sizeof(*supported), supported));
XCB_CALL(xwm, xcb_change_property_checked(xwm->xcb_conn,
XCB_PROP_MODE_REPLACE, xwm->screen->root, xwm->atoms[NET_SUPPORTED],
XCB_ATOM_ATOM, 32, sizeof(supported)/sizeof(*supported), supported));
XCB_CALL(xwm, xcb_set_selection_owner_checked(xwm->xcb_conn, xwm->window,
xwm->atoms[WM_S0], XCB_CURRENT_TIME));
xwm->atoms[WM_S0], XCB_CURRENT_TIME));
XCB_CALL(xwm, xcb_set_selection_owner_checked(xwm->xcb_conn, xwm->window,
xwm->atoms[NET_WM_S0], XCB_CURRENT_TIME));
xwm->atoms[NET_WM_S0], XCB_CURRENT_TIME));
xcb_flush(xwm->xcb_conn);
}
void wlr_xwayland_surface_activate(struct wlr_xwayland *wlr_xwayland,
struct wlr_xwayland_surface *surface) {
struct wlr_xwm *xwm = wlr_xwayland->xwm;
xcb_client_message_event_t m = {0};
m.response_type = XCB_CLIENT_MESSAGE;
m.format = 32;
m.window = surface->window_id;
m.type = xwm->atoms[WM_PROTOCOLS];
m.data.data32[0] = xwm->atoms[WM_TAKE_FOCUS];
m.data.data32[1] = XCB_TIME_CURRENT_TIME;
xcb_send_event_checked(xwm->xcb_conn, 0, surface->window_id,
XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT, (char*)&m);
xcb_set_input_focus_checked(xwm->xcb_conn, XCB_INPUT_FOCUS_POINTER_ROOT,
surface->window_id, XCB_CURRENT_TIME);
xcb_configure_window_checked(xwm->xcb_conn, surface->window_id,
XCB_CONFIG_WINDOW_STACK_MODE, (uint32_t[]){XCB_STACK_MODE_ABOVE});
xcb_flush(xwm->xcb_conn);
}
@ -311,15 +355,16 @@ void xwm_destroy(struct wlr_xwm *xwm) {
if (xwm->event_source) {
wl_event_source_remove(xwm->event_source);
}
struct wlr_x11_window *window, *tmp;
wl_list_for_each_safe(window, tmp, &xwm->xwayland->displayable_windows, link) {
wlr_x11_window_destroy(window);
struct wlr_xwayland_surface *surface, *tmp;
wl_list_for_each_safe(surface, tmp, &xwm->xwayland->displayable_surfaces,
link) {
wlr_xwayland_surface_destroy(surface);
}
wl_list_for_each_safe(window, tmp, &xwm->new_windows, link) {
wlr_x11_window_destroy(window);
wl_list_for_each_safe(surface, tmp, &xwm->new_surfaces, link) {
wlr_xwayland_surface_destroy(surface);
}
wl_list_for_each_safe(window, tmp, &xwm->unpaired_windows, link) {
wlr_x11_window_destroy(window);
wl_list_for_each_safe(surface, tmp, &xwm->unpaired_surfaces, link) {
wlr_xwayland_surface_destroy(surface);
}
wl_list_remove(&xwm->surface_create_listener.link);
xcb_disconnect(xwm->xcb_conn);
@ -329,22 +374,27 @@ void xwm_destroy(struct wlr_xwm *xwm) {
struct wlr_xwm *xwm_create(struct wlr_xwayland *wlr_xwayland) {
struct wlr_xwm *xwm = calloc(1, sizeof(struct wlr_xwm));
int rc;
if (xwm == NULL) {
return NULL;
}
xwm->xwayland = wlr_xwayland;
wl_list_init(&xwm->new_windows);
wl_list_init(&xwm->unpaired_windows);
wl_list_init(&xwm->new_surfaces);
wl_list_init(&xwm->unpaired_surfaces);
xwm->xcb_conn = xcb_connect_to_fd(wlr_xwayland->wm_fd[0], NULL);
if ((rc = xcb_connection_has_error(xwm->xcb_conn))) {
int rc = xcb_connection_has_error(xwm->xcb_conn);
if (rc) {
wlr_log(L_ERROR, "xcb connect failed: %d", rc);
free(xwm);
return NULL;
}
struct wl_event_loop *event_loop = wl_display_get_event_loop(wlr_xwayland->wl_display);
struct wl_event_loop *event_loop = wl_display_get_event_loop(
wlr_xwayland->wl_display);
xwm->event_source = wl_event_loop_add_fd(event_loop, wlr_xwayland->wm_fd[0],
WL_EVENT_READABLE, x11_event_handler, xwm);
WL_EVENT_READABLE, x11_event_handler, xwm);
// probably not needed
// wl_event_source_check(xwm->event_source);
@ -355,7 +405,7 @@ struct wlr_xwm *xwm_create(struct wlr_xwayland *wlr_xwayland) {
xwm->surface_create_listener.notify = create_surface_handler;
wl_signal_add(&wlr_xwayland->compositor->events.create_surface,
&xwm->surface_create_listener);
&xwm->surface_create_listener);
return xwm;
}

View file

@ -52,17 +52,11 @@ enum atom_name {
NET_SUPPORTED,
NET_WM_S0,
NET_WM_STATE,
ATOM_LAST
WM_TAKE_FOCUS,
ATOM_LAST,
};
static const char * const atom_map[ATOM_LAST] = {
"WL_SURFACE_ID",
"WM_PROTOCOLS",
"WM_S0",
"_NET_SUPPORTED",
"_NET_WM_S0",
"_NET_WM_STATE",
};
extern const char *atom_map[ATOM_LAST];
struct wlr_xwm {
struct wlr_xwayland *xwayland;
@ -74,8 +68,8 @@ struct wlr_xwm {
xcb_screen_t *screen;
xcb_window_t window;
struct wl_list new_windows;
struct wl_list unpaired_windows;
struct wl_list new_surfaces;
struct wl_list unpaired_surfaces;
};
void xwm_destroy(struct wlr_xwm *xwm);