commit
f10da8291b
|
@ -19,6 +19,7 @@
|
||||||
#include <wlr/types/wlr_seat.h>
|
#include <wlr/types/wlr_seat.h>
|
||||||
#include <wlr/types/wlr_data_device_manager.h>
|
#include <wlr/types/wlr_data_device_manager.h>
|
||||||
#include "wlr/types/wlr_compositor.h"
|
#include "wlr/types/wlr_compositor.h"
|
||||||
|
#include <wlr/xwayland.h>
|
||||||
#include <xkbcommon/xkbcommon.h>
|
#include <xkbcommon/xkbcommon.h>
|
||||||
#include <wlr/util/log.h>
|
#include <wlr/util/log.h>
|
||||||
#include "shared.h"
|
#include "shared.h"
|
||||||
|
@ -35,6 +36,7 @@ struct sample_state {
|
||||||
struct wlr_data_device_manager *data_device_manager;
|
struct wlr_data_device_manager *data_device_manager;
|
||||||
struct wl_resource *focus;
|
struct wl_resource *focus;
|
||||||
struct wl_listener keyboard_bound;
|
struct wl_listener keyboard_bound;
|
||||||
|
struct wlr_xwayland *xwayland;
|
||||||
int keymap_fd;
|
int keymap_fd;
|
||||||
size_t keymap_size;
|
size_t keymap_size;
|
||||||
uint32_t serial;
|
uint32_t serial;
|
||||||
|
@ -83,6 +85,10 @@ static void handle_output_frame(struct output_state *output, struct timespec *ts
|
||||||
wl_list_for_each(xdg_surface, &sample->xdg_shell->surfaces, link) {
|
wl_list_for_each(xdg_surface, &sample->xdg_shell->surfaces, link) {
|
||||||
output_frame_handle_surface(sample, wlr_output, ts, xdg_surface->surface);
|
output_frame_handle_surface(sample, wlr_output, ts, xdg_surface->surface);
|
||||||
}
|
}
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
wlr_renderer_end(sample->renderer);
|
wlr_renderer_end(sample->renderer);
|
||||||
wlr_output_swap_buffers(wlr_output);
|
wlr_output_swap_buffers(wlr_output);
|
||||||
|
@ -176,11 +182,13 @@ int main() {
|
||||||
free(keymap);
|
free(keymap);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
state.xwayland = wlr_xwayland_create(compositor.display, state.wlr_compositor);
|
||||||
|
|
||||||
compositor.keyboard_key_cb = handle_keyboard_key;
|
compositor.keyboard_key_cb = handle_keyboard_key;
|
||||||
|
|
||||||
wl_display_run(compositor.display);
|
wl_display_run(compositor.display);
|
||||||
|
|
||||||
|
wlr_xwayland_destroy(state.xwayland);
|
||||||
close(state.keymap_fd);
|
close(state.keymap_fd);
|
||||||
wlr_seat_destroy(state.wl_seat);
|
wlr_seat_destroy(state.wl_seat);
|
||||||
wlr_data_device_manager_destroy(state.data_device_manager);
|
wlr_data_device_manager_destroy(state.data_device_manager);
|
||||||
|
|
|
@ -9,6 +9,10 @@ struct wlr_compositor {
|
||||||
struct wlr_renderer *renderer;
|
struct wlr_renderer *renderer;
|
||||||
struct wl_list surfaces;
|
struct wl_list surfaces;
|
||||||
struct wl_listener destroy_surface_listener;
|
struct wl_listener destroy_surface_listener;
|
||||||
|
|
||||||
|
struct {
|
||||||
|
struct wl_signal create_surface;
|
||||||
|
} events;
|
||||||
};
|
};
|
||||||
|
|
||||||
void wlr_compositor_destroy(struct wlr_compositor *wlr_compositor);
|
void wlr_compositor_destroy(struct wlr_compositor *wlr_compositor);
|
||||||
|
|
|
@ -0,0 +1,42 @@
|
||||||
|
#ifndef _WLR_XWAYLAND_H
|
||||||
|
#define _WLR_XWAYLAND_H
|
||||||
|
#include <time.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <wlr/types/wlr_compositor.h>
|
||||||
|
#include <xcb/xcb.h>
|
||||||
|
|
||||||
|
struct wlr_xwm;
|
||||||
|
|
||||||
|
struct wlr_xwayland {
|
||||||
|
pid_t pid;
|
||||||
|
int display;
|
||||||
|
int x_fd[2], wl_fd[2], wm_fd[2];
|
||||||
|
struct wl_client *client;
|
||||||
|
struct wl_display *wl_display;
|
||||||
|
struct wlr_compositor *compositor;
|
||||||
|
time_t server_start;
|
||||||
|
|
||||||
|
struct wl_event_source *sigusr1_source;
|
||||||
|
struct wl_listener destroy_listener;
|
||||||
|
struct wlr_xwm *xwm;
|
||||||
|
struct wl_list displayable_windows;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct wlr_x11_window {
|
||||||
|
xcb_window_t window_id;
|
||||||
|
uint32_t surface_id;
|
||||||
|
struct wl_list link;
|
||||||
|
|
||||||
|
struct wl_resource *surface;
|
||||||
|
struct wl_listener surface_destroy_listener;
|
||||||
|
int16_t x, y;
|
||||||
|
uint16_t width, height;
|
||||||
|
bool override_redirect;
|
||||||
|
};
|
||||||
|
|
||||||
|
void wlr_xwayland_destroy(struct wlr_xwayland *wlr_xwayland);
|
||||||
|
struct wlr_xwayland *wlr_xwayland_create(struct wl_display *wl_display,
|
||||||
|
struct wlr_compositor *compositor);
|
||||||
|
|
||||||
|
#endif
|
|
@ -32,6 +32,8 @@ libinput = dependency('libinput')
|
||||||
xkbcommon = dependency('xkbcommon')
|
xkbcommon = dependency('xkbcommon')
|
||||||
udev = dependency('libudev')
|
udev = dependency('libudev')
|
||||||
pixman = dependency('pixman-1')
|
pixman = dependency('pixman-1')
|
||||||
|
xcb = dependency('xcb')
|
||||||
|
xcb_composite = dependency('xcb-composite')
|
||||||
libcap = dependency('libcap', required: false)
|
libcap = dependency('libcap', required: false)
|
||||||
systemd = dependency('libsystemd', required: false)
|
systemd = dependency('libsystemd', required: false)
|
||||||
math = cc.find_library('m', required: false)
|
math = cc.find_library('m', required: false)
|
||||||
|
@ -50,6 +52,7 @@ subdir('render')
|
||||||
subdir('types')
|
subdir('types')
|
||||||
subdir('util')
|
subdir('util')
|
||||||
subdir('xcursor')
|
subdir('xcursor')
|
||||||
|
subdir('xwayland')
|
||||||
|
|
||||||
wlr_deps = [
|
wlr_deps = [
|
||||||
wayland_server,
|
wayland_server,
|
||||||
|
@ -64,6 +67,8 @@ wlr_deps = [
|
||||||
xkbcommon,
|
xkbcommon,
|
||||||
udev,
|
udev,
|
||||||
pixman,
|
pixman,
|
||||||
|
xcb,
|
||||||
|
xcb_composite,
|
||||||
libcap,
|
libcap,
|
||||||
systemd,
|
systemd,
|
||||||
math,
|
math,
|
||||||
|
@ -77,6 +82,7 @@ lib_wlr = library('wlroots', files('dummy.c'),
|
||||||
lib_wlr_types,
|
lib_wlr_types,
|
||||||
lib_wlr_util,
|
lib_wlr_util,
|
||||||
lib_wlr_xcursor,
|
lib_wlr_xcursor,
|
||||||
|
lib_wlr_xwayland,
|
||||||
],
|
],
|
||||||
dependencies: wlr_deps,
|
dependencies: wlr_deps,
|
||||||
include_directories: wlr_inc)
|
include_directories: wlr_inc)
|
||||||
|
|
|
@ -22,6 +22,7 @@ static void wl_compositor_create_surface(struct wl_client *client,
|
||||||
wl_resource_add_destroy_listener(surface_resource, &surface->compositor_listener);
|
wl_resource_add_destroy_listener(surface_resource, &surface->compositor_listener);
|
||||||
|
|
||||||
wl_list_insert(&compositor->surfaces, wl_resource_get_link(surface_resource));
|
wl_list_insert(&compositor->surfaces, wl_resource_get_link(surface_resource));
|
||||||
|
wl_signal_emit(&compositor->events.create_surface, surface);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void wl_compositor_create_region(struct wl_client *client,
|
static void wl_compositor_create_region(struct wl_client *client,
|
||||||
|
@ -80,5 +81,6 @@ struct wlr_compositor *wlr_compositor_create(struct wl_display *display,
|
||||||
compositor->renderer = renderer;
|
compositor->renderer = renderer;
|
||||||
wl_list_init(&compositor->wl_resources);
|
wl_list_init(&compositor->wl_resources);
|
||||||
wl_list_init(&compositor->surfaces);
|
wl_list_init(&compositor->surfaces);
|
||||||
|
wl_signal_init(&compositor->events.create_surface);
|
||||||
return compositor;
|
return compositor;
|
||||||
}
|
}
|
||||||
|
|
|
@ -386,6 +386,7 @@ struct wlr_surface *wlr_surface_create(struct wl_resource *res,
|
||||||
wl_resource_post_no_memory(res);
|
wl_resource_post_no_memory(res);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
wlr_log(L_DEBUG, "New wlr_surface %p (res %p)", surface, res);
|
||||||
surface->renderer = renderer;
|
surface->renderer = renderer;
|
||||||
surface->texture = wlr_render_texture_create(renderer);
|
surface->texture = wlr_render_texture_create(renderer);
|
||||||
surface->resource = res;
|
surface->resource = res;
|
||||||
|
|
|
@ -88,6 +88,7 @@ static void wl_shell_get_shell_surface(struct wl_client *client,
|
||||||
state->surface = surface;
|
state->surface = surface;
|
||||||
struct wl_resource *shell_surface_resource = wl_resource_create(client,
|
struct wl_resource *shell_surface_resource = wl_resource_create(client,
|
||||||
&wl_shell_surface_interface, wl_resource_get_version(resource), id);
|
&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,
|
wl_resource_set_implementation(shell_surface_resource,
|
||||||
&shell_surface_interface, state, destroy_shell_surface);
|
&shell_surface_interface, state, destroy_shell_surface);
|
||||||
wl_list_insert(&wlr_wl_shell->surfaces, &state->link);
|
wl_list_insert(&wlr_wl_shell->surfaces, &state->link);
|
||||||
|
|
|
@ -149,6 +149,7 @@ static void xdg_shell_get_xdg_surface(struct wl_client *client,
|
||||||
surface->surface = _surface;
|
surface->surface = _surface;
|
||||||
surface->resource = wl_resource_create(client,
|
surface->resource = wl_resource_create(client,
|
||||||
&zxdg_surface_v6_interface, wl_resource_get_version(_xdg_shell), id);
|
&zxdg_surface_v6_interface, wl_resource_get_version(_xdg_shell), id);
|
||||||
|
wlr_log(L_DEBUG, "new xdg_surface %p (res %p)", surface, surface->resource);
|
||||||
wl_resource_set_implementation(surface->resource,
|
wl_resource_set_implementation(surface->resource,
|
||||||
&zxdg_surface_v6_implementation, surface, xdg_surface_destroy);
|
&zxdg_surface_v6_implementation, surface, xdg_surface_destroy);
|
||||||
wl_list_insert(&xdg_shell->surfaces, &surface->link);
|
wl_list_insert(&xdg_shell->surfaces, &surface->link);
|
||||||
|
|
|
@ -0,0 +1,7 @@
|
||||||
|
lib_wlr_xwayland = static_library('wlr_xwayland', files(
|
||||||
|
'sockets.c',
|
||||||
|
'xwayland.c',
|
||||||
|
'xwm.c',
|
||||||
|
),
|
||||||
|
include_directories: wlr_inc,
|
||||||
|
dependencies: [wayland_server, xcb, xcb_composite, pixman])
|
|
@ -0,0 +1,153 @@
|
||||||
|
#define _XOPEN_SOURCE 700
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <stddef.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <signal.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <sys/socket.h>
|
||||||
|
#include <sys/un.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include "wlr/util/log.h"
|
||||||
|
#include "sockets.h"
|
||||||
|
|
||||||
|
static const char *lock_fmt = "/tmp/.X%d-lock";
|
||||||
|
static const char *socket_dir = "/tmp/.X11-unix";
|
||||||
|
static const char *socket_fmt = "/tmp/.X11-unix/X%d";
|
||||||
|
|
||||||
|
static int open_socket(struct sockaddr_un *addr, size_t path_size) {
|
||||||
|
int fd, rc;
|
||||||
|
socklen_t size = offsetof(struct sockaddr_un, sun_path) + path_size + 1;
|
||||||
|
|
||||||
|
fd = socket(PF_LOCAL, SOCK_STREAM | SOCK_CLOEXEC, 0);
|
||||||
|
if (fd < 0) {
|
||||||
|
wlr_log_errno(L_DEBUG, "Failed to create socket %c%s",
|
||||||
|
addr->sun_path[0] ? addr->sun_path[0] : '@',
|
||||||
|
addr->sun_path + 1);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (addr->sun_path[0]) {
|
||||||
|
unlink(addr->sun_path);
|
||||||
|
}
|
||||||
|
if (bind(fd, (struct sockaddr*)addr, size) < 0) {
|
||||||
|
rc = errno;
|
||||||
|
wlr_log_errno(L_DEBUG, "Failed to bind socket %c%s",
|
||||||
|
addr->sun_path[0] ? addr->sun_path[0] : '@',
|
||||||
|
addr->sun_path + 1);
|
||||||
|
goto cleanup;
|
||||||
|
}
|
||||||
|
if (listen(fd, 1) < 0) {
|
||||||
|
rc = errno;
|
||||||
|
wlr_log_errno(L_DEBUG, "Failed to listen to socket %c%s",
|
||||||
|
addr->sun_path[0] ? addr->sun_path[0] : '@',
|
||||||
|
addr->sun_path + 1);
|
||||||
|
goto cleanup;
|
||||||
|
}
|
||||||
|
|
||||||
|
return fd;
|
||||||
|
|
||||||
|
cleanup:
|
||||||
|
close(fd);
|
||||||
|
if (addr->sun_path[0]) {
|
||||||
|
unlink(addr->sun_path);
|
||||||
|
}
|
||||||
|
errno = rc;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool open_sockets(int socks[2], int display) {
|
||||||
|
struct sockaddr_un addr = { .sun_family = AF_LOCAL };
|
||||||
|
size_t path_size;
|
||||||
|
|
||||||
|
mkdir(socket_dir, 0777);
|
||||||
|
|
||||||
|
// TODO: non-linux apparently want another format
|
||||||
|
addr.sun_path[0] = 0;
|
||||||
|
path_size = snprintf(addr.sun_path + 1, sizeof(addr.sun_path) - 1, socket_fmt, display);
|
||||||
|
socks[0] = open_socket(&addr, path_size);
|
||||||
|
if (socks[0] < 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
path_size = snprintf(addr.sun_path, sizeof(addr.sun_path), socket_fmt, display);
|
||||||
|
socks[1] = open_socket(&addr, path_size);
|
||||||
|
if (socks[1] < 0) {
|
||||||
|
close(socks[0]);
|
||||||
|
socks[0] = -1;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void unlink_display_sockets(int display) {
|
||||||
|
char sun_path[64];
|
||||||
|
|
||||||
|
snprintf(sun_path, sizeof(sun_path), socket_fmt, display);
|
||||||
|
unlink(sun_path);
|
||||||
|
|
||||||
|
snprintf(sun_path, sizeof(sun_path), lock_fmt, display);
|
||||||
|
unlink(sun_path);
|
||||||
|
}
|
||||||
|
|
||||||
|
int open_display_sockets(int socks[2]) {
|
||||||
|
int lock_fd, display;
|
||||||
|
char lock_name[64];
|
||||||
|
|
||||||
|
for (display = 0; display <= 32; display++) {
|
||||||
|
snprintf(lock_name, sizeof(lock_name), lock_fmt, display);
|
||||||
|
if ((lock_fd = open(lock_name, O_WRONLY | O_CREAT | O_EXCL | O_CLOEXEC, 0444)) >= 0) {
|
||||||
|
if (!open_sockets(socks, display)) {
|
||||||
|
unlink(lock_name);
|
||||||
|
close(lock_fd);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
char pid[12];
|
||||||
|
snprintf(pid, sizeof(pid), "%10d", getpid());
|
||||||
|
if (write(lock_fd, pid, sizeof(pid) - 1) != sizeof(pid) - 1) {
|
||||||
|
unlink(lock_name);
|
||||||
|
close(lock_fd);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
close(lock_fd);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((lock_fd = open(lock_name, O_RDONLY | O_CLOEXEC)) < 0) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
char pid[12] = { 0 }, *end_pid;
|
||||||
|
ssize_t bytes = read(lock_fd, pid, sizeof(pid) - 1);
|
||||||
|
close(lock_fd);
|
||||||
|
|
||||||
|
if (bytes != sizeof(pid) - 1) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
long int read_pid;
|
||||||
|
read_pid = strtol(pid, &end_pid, 10);
|
||||||
|
if (read_pid < 0 || read_pid > INT32_MAX || end_pid != pid + sizeof(pid) - 2) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
errno = 0;
|
||||||
|
if (kill((pid_t)read_pid, 0) != 0 && errno == ESRCH) {
|
||||||
|
if (unlink(lock_name) != 0) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
// retry
|
||||||
|
display--;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (display > 32) {
|
||||||
|
wlr_log(L_ERROR, "No display available in the first 33");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return display;
|
||||||
|
}
|
|
@ -0,0 +1,7 @@
|
||||||
|
#ifndef XWAYLAND_SOCKETS_H
|
||||||
|
#define XWAYLAND_SOCKETS_H
|
||||||
|
|
||||||
|
void unlink_display_sockets(int display);
|
||||||
|
int open_display_sockets(int socks[2]);
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,250 @@
|
||||||
|
#define _XOPEN_SOURCE 700
|
||||||
|
#define _DEFAULT_SOURCE
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <signal.h>
|
||||||
|
#include <sys/socket.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <sys/wait.h>
|
||||||
|
#include <time.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <wayland-server.h>
|
||||||
|
#include "wlr/util/log.h"
|
||||||
|
#include "wlr/xwayland.h"
|
||||||
|
#include "sockets.h"
|
||||||
|
#include "xwm.h"
|
||||||
|
|
||||||
|
static void safe_close(int fd) {
|
||||||
|
if (fd >= 0) {
|
||||||
|
close(fd);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static int unset_cloexec(int fd) {
|
||||||
|
if (fcntl(fd, F_SETFD, 0) != 0) {
|
||||||
|
wlr_log_errno(L_ERROR, "fcntl() failed on fd %d", fd);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int fill_arg(char ***argv, const char *fmt, ...) {
|
||||||
|
int len;
|
||||||
|
char **cur_arg = *argv;
|
||||||
|
va_list args;
|
||||||
|
va_start(args, fmt);
|
||||||
|
len = vsnprintf(NULL, 0, fmt, args) + 1;
|
||||||
|
va_end(args);
|
||||||
|
while (*cur_arg) {
|
||||||
|
cur_arg++;
|
||||||
|
}
|
||||||
|
*cur_arg = malloc(len);
|
||||||
|
if (!*cur_arg) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
*argv = cur_arg;
|
||||||
|
va_start(args, fmt);
|
||||||
|
len = vsnprintf(*cur_arg, len, fmt, args);
|
||||||
|
va_end(args);
|
||||||
|
return len;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void exec_xwayland(struct wlr_xwayland *wlr_xwayland) {
|
||||||
|
if (unset_cloexec(wlr_xwayland->x_fd[0]) ||
|
||||||
|
unset_cloexec(wlr_xwayland->x_fd[1]) ||
|
||||||
|
unset_cloexec(wlr_xwayland->wm_fd[1]) ||
|
||||||
|
unset_cloexec(wlr_xwayland->wl_fd[1])) {
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Make Xwayland signal us when it's ready */
|
||||||
|
signal(SIGUSR1, SIG_IGN);
|
||||||
|
|
||||||
|
char *argv[] = {
|
||||||
|
"Xwayland", NULL /* display, e.g. :1 */,
|
||||||
|
"-rootless", "-terminate",
|
||||||
|
"-listen", NULL /* x_fd[0] */,
|
||||||
|
"-listen", NULL /* x_fd[1] */,
|
||||||
|
"-wm", NULL /* wm_fd[1] */,
|
||||||
|
NULL };
|
||||||
|
char **cur_arg = argv;
|
||||||
|
|
||||||
|
if (fill_arg(&cur_arg, ":%d", wlr_xwayland->display) < 0 ||
|
||||||
|
fill_arg(&cur_arg, "%d", wlr_xwayland->x_fd[0]) < 0 ||
|
||||||
|
fill_arg(&cur_arg, "%d", wlr_xwayland->x_fd[1]) < 0 ||
|
||||||
|
fill_arg(&cur_arg, "%d", wlr_xwayland->wm_fd[1]) < 0) {
|
||||||
|
wlr_log_errno(L_ERROR, "alloc/print failure");
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *xdg_runtime = getenv("XDG_RUNTIME_DIR");
|
||||||
|
if (!xdg_runtime) {
|
||||||
|
wlr_log(L_ERROR, "XDG_RUNTIME_DIR is not set");
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (clearenv()) {
|
||||||
|
wlr_log_errno(L_ERROR, "clearenv failed");
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
setenv("XDG_RUNTIME_DIR", xdg_runtime, true);
|
||||||
|
char wayland_socket_str[16];
|
||||||
|
snprintf(wayland_socket_str, sizeof(wayland_socket_str), "%d", wlr_xwayland->wl_fd[1]);
|
||||||
|
setenv("WAYLAND_SOCKET", wayland_socket_str, true);
|
||||||
|
|
||||||
|
wlr_log(L_INFO, "WAYLAND_SOCKET=%d Xwayland :%d -rootless -terminate -listen %d -listen %d -wm %d",
|
||||||
|
wlr_xwayland->wl_fd[1], wlr_xwayland->display, wlr_xwayland->x_fd[0],
|
||||||
|
wlr_xwayland->x_fd[1], wlr_xwayland->wm_fd[1]);
|
||||||
|
|
||||||
|
// TODO: close stdout/err depending on log level
|
||||||
|
|
||||||
|
execvp("Xwayland", argv);
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool wlr_xwayland_init(struct wlr_xwayland *wlr_xwayland,
|
||||||
|
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) {
|
||||||
|
struct wlr_xwayland *wlr_xwayland = wl_container_of(listener, wlr_xwayland, destroy_listener);
|
||||||
|
|
||||||
|
/* don't call client destroy */
|
||||||
|
wlr_xwayland->client = NULL;
|
||||||
|
wl_list_remove(&wlr_xwayland->destroy_listener.link);
|
||||||
|
wlr_xwayland_finish(wlr_xwayland);
|
||||||
|
|
||||||
|
if (time(NULL) - wlr_xwayland->server_start > 5) {
|
||||||
|
wlr_xwayland_init(wlr_xwayland, wlr_xwayland->wl_display,
|
||||||
|
wlr_xwayland->compositor);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void wlr_xwayland_finish(struct wlr_xwayland *wlr_xwayland) {
|
||||||
|
if (!wlr_xwayland || wlr_xwayland->display == -1) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (wlr_xwayland->client) {
|
||||||
|
wl_list_remove(&wlr_xwayland->destroy_listener.link);
|
||||||
|
wl_client_destroy(wlr_xwayland->client);
|
||||||
|
}
|
||||||
|
if (wlr_xwayland->sigusr1_source) {
|
||||||
|
wl_event_source_remove(wlr_xwayland->sigusr1_source);
|
||||||
|
}
|
||||||
|
|
||||||
|
xwm_destroy(wlr_xwayland->xwm);
|
||||||
|
|
||||||
|
safe_close(wlr_xwayland->x_fd[0]);
|
||||||
|
safe_close(wlr_xwayland->x_fd[1]);
|
||||||
|
safe_close(wlr_xwayland->wl_fd[0]);
|
||||||
|
safe_close(wlr_xwayland->wl_fd[1]);
|
||||||
|
safe_close(wlr_xwayland->wm_fd[0]);
|
||||||
|
safe_close(wlr_xwayland->wm_fd[1]);
|
||||||
|
|
||||||
|
unlink_display_sockets(wlr_xwayland->display);
|
||||||
|
wlr_xwayland->display = -1;
|
||||||
|
unsetenv("DISPLAY");
|
||||||
|
/* We do not kill the Xwayland process, it dies to broken pipe
|
||||||
|
* after we close our side of the wm/wl fds. This is more reliable
|
||||||
|
* than trying to kill something that might no longer be Xwayland.
|
||||||
|
*/
|
||||||
|
// TODO: figure how to wait for dying process though. Probably handle SIGCHILD
|
||||||
|
}
|
||||||
|
|
||||||
|
static int xserver_handle_ready(int signal_number, void *data) {
|
||||||
|
struct wlr_xwayland *wlr_xwayland = data;
|
||||||
|
|
||||||
|
wlr_log(L_DEBUG, "Xserver is ready");
|
||||||
|
|
||||||
|
wlr_xwayland->xwm = xwm_create(wlr_xwayland);
|
||||||
|
if (!wlr_xwayland->xwm) {
|
||||||
|
wlr_xwayland_finish(wlr_xwayland);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
wl_event_source_remove(wlr_xwayland->sigusr1_source);
|
||||||
|
wlr_xwayland->sigusr1_source = NULL;
|
||||||
|
|
||||||
|
char display_name[16];
|
||||||
|
snprintf(display_name, sizeof(display_name), ":%d", wlr_xwayland->display);
|
||||||
|
setenv("DISPLAY", display_name, true);
|
||||||
|
|
||||||
|
return 1; /* wayland event loop dispatcher's count */
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool wlr_xwayland_init(struct wlr_xwayland *wlr_xwayland,
|
||||||
|
struct wl_display *wl_display, struct wlr_compositor *compositor) {
|
||||||
|
memset(wlr_xwayland, 0, sizeof(struct wlr_xwayland));
|
||||||
|
wlr_xwayland->wl_display = wl_display;
|
||||||
|
wlr_xwayland->compositor = compositor;
|
||||||
|
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);
|
||||||
|
|
||||||
|
wlr_xwayland->display = open_display_sockets(wlr_xwayland->x_fd);
|
||||||
|
if (wlr_xwayland->display < 0) {
|
||||||
|
wlr_xwayland_finish(wlr_xwayland);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0, wlr_xwayland->wl_fd) != 0 ||
|
||||||
|
socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0, wlr_xwayland->wm_fd) != 0) {
|
||||||
|
wlr_log_errno(L_ERROR, "failed to create socketpair");
|
||||||
|
wlr_xwayland_finish(wlr_xwayland);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((wlr_xwayland->pid = fork()) == 0) {
|
||||||
|
exec_xwayland(wlr_xwayland);
|
||||||
|
wlr_log_errno(L_ERROR, "execvpe failed");
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (wlr_xwayland->pid < 0) {
|
||||||
|
wlr_log_errno(L_ERROR, "fork failed");
|
||||||
|
wlr_xwayland_finish(wlr_xwayland);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* close child fds */
|
||||||
|
close(wlr_xwayland->x_fd[0]);
|
||||||
|
close(wlr_xwayland->x_fd[1]);
|
||||||
|
close(wlr_xwayland->wl_fd[1]);
|
||||||
|
close(wlr_xwayland->wm_fd[1]);
|
||||||
|
wlr_xwayland->x_fd[0] = wlr_xwayland->x_fd[1] = -1;
|
||||||
|
wlr_xwayland->wl_fd[1] = wlr_xwayland->wm_fd[1] = -1;
|
||||||
|
|
||||||
|
wlr_xwayland->server_start = time(NULL);
|
||||||
|
|
||||||
|
if (!(wlr_xwayland->client = wl_client_create(wl_display, wlr_xwayland->wl_fd[0]))) {
|
||||||
|
wlr_log_errno(L_ERROR, "wl_client_create failed");
|
||||||
|
wlr_xwayland_finish(wlr_xwayland);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
wlr_xwayland->wl_fd[0] = -1; /* not ours anymore */
|
||||||
|
|
||||||
|
wlr_xwayland->destroy_listener.notify = xwayland_destroy_event;
|
||||||
|
wl_client_add_destroy_listener(wlr_xwayland->client, &wlr_xwayland->destroy_listener);
|
||||||
|
|
||||||
|
struct wl_event_loop *loop = wl_display_get_event_loop(wl_display);
|
||||||
|
wlr_xwayland->sigusr1_source = wl_event_loop_add_signal(loop, SIGUSR1, xserver_handle_ready, wlr_xwayland);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void wlr_xwayland_destroy(struct wlr_xwayland *wlr_xwayland) {
|
||||||
|
wlr_xwayland_finish(wlr_xwayland);
|
||||||
|
free(wlr_xwayland);
|
||||||
|
}
|
||||||
|
|
||||||
|
struct wlr_xwayland *wlr_xwayland_create(struct wl_display *wl_display,
|
||||||
|
struct wlr_compositor *compositor) {
|
||||||
|
struct wlr_xwayland *wlr_xwayland = calloc(1, sizeof(struct wlr_xwayland));
|
||||||
|
if (wlr_xwayland_init(wlr_xwayland, wl_display, compositor)) {
|
||||||
|
return wlr_xwayland;
|
||||||
|
}
|
||||||
|
free(wlr_xwayland);
|
||||||
|
return NULL;
|
||||||
|
}
|
|
@ -0,0 +1,360 @@
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <xcb/composite.h>
|
||||||
|
#include "wlr/util/log.h"
|
||||||
|
#include "wlr/types/wlr_surface.h"
|
||||||
|
#include "wlr/xwayland.h"
|
||||||
|
#include "xwm.h"
|
||||||
|
|
||||||
|
/* 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
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,
|
||||||
|
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");
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void wlr_x11_window_destroy(struct wlr_x11_window *window) {
|
||||||
|
wl_list_remove(&window->link);
|
||||||
|
free(window);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* xcb helpers */
|
||||||
|
#define XCB_CALL(xwm, x) xcb_call(xwm, __PRETTY_FUNCTION__, __LINE__, x)
|
||||||
|
static bool xcb_call(struct wlr_xwm *xwm, const char *func, uint32_t line,
|
||||||
|
xcb_void_cookie_t cookie) {
|
||||||
|
xcb_generic_error_t *error;
|
||||||
|
if (!(error = xcb_request_check(xwm->xcb_conn, cookie))) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
wlr_log(L_ERROR, "xcb call failed in %s:%u, x11 error code %d",
|
||||||
|
func, line, error->error_code);
|
||||||
|
free(error);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void map_shell_surface(struct wlr_xwm *xwm, struct wlr_x11_window *window,
|
||||||
|
struct wlr_surface *surface) {
|
||||||
|
// get xcb geometry for depth = alpha channel
|
||||||
|
window->surface = surface->resource;
|
||||||
|
|
||||||
|
wl_list_remove(&window->link);
|
||||||
|
wl_list_insert(&xwm->xwayland->displayable_windows, &window->link);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* xcb event handlers */
|
||||||
|
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,
|
||||||
|
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;
|
||||||
|
wlr_log(L_DEBUG, "XCB_DESTROY_NOTIFY (%u)", ev->window);
|
||||||
|
if (!(window = lookup_window_any(xwm, ev->window))) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
wlr_x11_window_destroy(window);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void handle_configure_request(struct wlr_xwm *xwm, xcb_configure_request_event_t *ev) {
|
||||||
|
struct wlr_x11_window *window;
|
||||||
|
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))) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
window->x = ev->x;
|
||||||
|
window->y = ev->y;
|
||||||
|
window->width = ev->width;
|
||||||
|
window->height = ev->height;
|
||||||
|
// handle parent/sibling?
|
||||||
|
|
||||||
|
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) {
|
||||||
|
wlr_log(L_DEBUG, "XCB_MAP_REQUEST (%u)", ev->window);
|
||||||
|
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}));
|
||||||
|
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;
|
||||||
|
} else {
|
||||||
|
wlr_x11_window_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;
|
||||||
|
wlr_log(L_DEBUG, "XCB_UNMAP_NOTIFY (%u)", ev->window);
|
||||||
|
if (!(window = lookup_window_any(xwm, ev->window))) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// remove pointer to surface only?
|
||||||
|
wlr_x11_window_destroy(window);
|
||||||
|
}
|
||||||
|
|
||||||
|
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) {
|
||||||
|
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) {
|
||||||
|
wlr_log(L_DEBUG, "client message WL_SURFACE_ID but no new window %u ?",
|
||||||
|
ev->window);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
window->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);
|
||||||
|
if (resource) {
|
||||||
|
map_shell_surface(xwm, window, wl_resource_get_user_data(resource));
|
||||||
|
} else {
|
||||||
|
wl_list_remove(&window->link);
|
||||||
|
wl_list_insert(&xwm->unpaired_windows, &window->link);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
wlr_log(L_DEBUG, "unhandled client message %u", ev->type);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* This is in xcb/xcb_event.h, but pulling xcb-util just for a constant
|
||||||
|
* others redefine anyway is meh
|
||||||
|
*/
|
||||||
|
#define XCB_EVENT_RESPONSE_TYPE_MASK (0x7f)
|
||||||
|
static int x11_event_handler(int fd, uint32_t mask, void *data) {
|
||||||
|
int count = 0;
|
||||||
|
xcb_generic_event_t *event;
|
||||||
|
struct wlr_xwm *xwm = data;
|
||||||
|
|
||||||
|
while ((event = xcb_poll_for_event(xwm->xcb_conn))) {
|
||||||
|
count++;
|
||||||
|
switch (event->response_type & XCB_EVENT_RESPONSE_TYPE_MASK) {
|
||||||
|
case XCB_CREATE_NOTIFY:
|
||||||
|
handle_create_notify(xwm, (xcb_create_notify_event_t *)event);
|
||||||
|
break;
|
||||||
|
case XCB_DESTROY_NOTIFY:
|
||||||
|
handle_destroy_notify(xwm, (xcb_destroy_notify_event_t *)event);
|
||||||
|
break;
|
||||||
|
case XCB_CONFIGURE_REQUEST:
|
||||||
|
handle_configure_request(xwm, (xcb_configure_request_event_t *)event);
|
||||||
|
break;
|
||||||
|
case XCB_MAP_REQUEST:
|
||||||
|
handle_map_request(xwm, (xcb_map_request_event_t *)event);
|
||||||
|
break;
|
||||||
|
case XCB_MAP_NOTIFY:
|
||||||
|
handle_map_notify(xwm, (xcb_map_notify_event_t *)event);
|
||||||
|
break;
|
||||||
|
case XCB_UNMAP_NOTIFY:
|
||||||
|
handle_unmap_notify(xwm, (xcb_unmap_notify_event_t *)event);
|
||||||
|
break;
|
||||||
|
case XCB_PROPERTY_NOTIFY:
|
||||||
|
handle_property_notify(xwm, (xcb_property_notify_event_t *)event);
|
||||||
|
break;
|
||||||
|
case XCB_CLIENT_MESSAGE:
|
||||||
|
handle_client_message(xwm, (xcb_client_message_event_t *)event);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
wlr_log(L_DEBUG, "X11 event: %d",
|
||||||
|
event->response_type & XCB_EVENT_RESPONSE_TYPE_MASK);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
xcb_flush(xwm->xcb_conn);
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
|
||||||
|
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);
|
||||||
|
xcb_flush(xwm->xcb_conn);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void xcb_get_resources(struct wlr_xwm *xwm) {
|
||||||
|
size_t i;
|
||||||
|
xcb_intern_atom_cookie_t cookies[ATOM_LAST];
|
||||||
|
|
||||||
|
for (i = 0; i < ATOM_LAST; i++) {
|
||||||
|
cookies[i] = xcb_intern_atom(xwm->xcb_conn, 0, strlen(atom_map[i]), atom_map[i]);
|
||||||
|
}
|
||||||
|
for (i = 0; i < ATOM_LAST; i++) {
|
||||||
|
xcb_intern_atom_reply_t *reply;
|
||||||
|
xcb_generic_error_t *error;
|
||||||
|
|
||||||
|
reply = xcb_intern_atom_reply(xwm->xcb_conn, cookies[i], &error);
|
||||||
|
|
||||||
|
if (reply && !error) {
|
||||||
|
xwm->atoms[i] = reply->atom;
|
||||||
|
}
|
||||||
|
if (reply) {
|
||||||
|
free(reply);
|
||||||
|
}
|
||||||
|
if (error) {
|
||||||
|
wlr_log(L_ERROR, "could not resolve atom %s, x11 error code %d",
|
||||||
|
atom_map[i], error->error_code);
|
||||||
|
free(error);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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));
|
||||||
|
xwm->screen = screen_iterator.data;
|
||||||
|
|
||||||
|
xwm->window = xcb_generate_id(xwm->xcb_conn);
|
||||||
|
|
||||||
|
uint32_t values[] = {
|
||||||
|
XCB_EVENT_MASK_SUBSTRUCTURE_NOTIFY |
|
||||||
|
XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT |
|
||||||
|
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_composite_redirect_subwindows_checked(xwm->xcb_conn,
|
||||||
|
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}));
|
||||||
|
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_set_selection_owner_checked(xwm->xcb_conn, xwm->window,
|
||||||
|
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));
|
||||||
|
xcb_flush(xwm->xcb_conn);
|
||||||
|
}
|
||||||
|
|
||||||
|
void xwm_destroy(struct wlr_xwm *xwm) {
|
||||||
|
if (!xwm) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
wl_list_for_each_safe(window, tmp, &xwm->new_windows, link) {
|
||||||
|
wlr_x11_window_destroy(window);
|
||||||
|
}
|
||||||
|
wl_list_for_each_safe(window, tmp, &xwm->unpaired_windows, link) {
|
||||||
|
wlr_x11_window_destroy(window);
|
||||||
|
}
|
||||||
|
wl_list_remove(&xwm->surface_create_listener.link);
|
||||||
|
xcb_disconnect(xwm->xcb_conn);
|
||||||
|
|
||||||
|
free(xwm);
|
||||||
|
}
|
||||||
|
|
||||||
|
struct wlr_xwm *xwm_create(struct wlr_xwayland *wlr_xwayland) {
|
||||||
|
struct wlr_xwm *xwm = calloc(1, sizeof(struct wlr_xwm));
|
||||||
|
int rc;
|
||||||
|
|
||||||
|
xwm->xwayland = wlr_xwayland;
|
||||||
|
wl_list_init(&xwm->new_windows);
|
||||||
|
wl_list_init(&xwm->unpaired_windows);
|
||||||
|
|
||||||
|
xwm->xcb_conn = xcb_connect_to_fd(wlr_xwayland->wm_fd[0], NULL);
|
||||||
|
if ((rc = xcb_connection_has_error(xwm->xcb_conn))) {
|
||||||
|
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);
|
||||||
|
xwm->event_source = wl_event_loop_add_fd(event_loop, wlr_xwayland->wm_fd[0],
|
||||||
|
WL_EVENT_READABLE, x11_event_handler, xwm);
|
||||||
|
// probably not needed
|
||||||
|
// wl_event_source_check(xwm->event_source);
|
||||||
|
|
||||||
|
// TODO more xcb init
|
||||||
|
// xcb_prefetch_extension_data(xwm->xcb_conn, &xcb_composite_id);
|
||||||
|
xcb_get_resources(xwm);
|
||||||
|
xcb_init_wm(xwm);
|
||||||
|
|
||||||
|
xwm->surface_create_listener.notify = create_surface_handler;
|
||||||
|
wl_signal_add(&wlr_xwayland->compositor->events.create_surface,
|
||||||
|
&xwm->surface_create_listener);
|
||||||
|
|
||||||
|
return xwm;
|
||||||
|
}
|
|
@ -0,0 +1,84 @@
|
||||||
|
#ifndef XWAYLAND_INTERNALS_H
|
||||||
|
#define XWAYLAND_INTERNALS_H
|
||||||
|
#include <wayland-server-core.h>
|
||||||
|
#include <wlr/xwayland.h>
|
||||||
|
|
||||||
|
/* wlc's atom list:
|
||||||
|
WL_SURFACE_ID,
|
||||||
|
WM_DELETE_WINDOW,
|
||||||
|
WM_TAKE_FOCUS,
|
||||||
|
WM_PROTOCOLS,
|
||||||
|
WM_NORMAL_HINTS,
|
||||||
|
MOTIF_WM_HINTS,
|
||||||
|
TEXT,
|
||||||
|
UTF8_STRING,
|
||||||
|
CLIPBOARD,
|
||||||
|
CLIPBOARD_MANAGER,
|
||||||
|
TARGETS,
|
||||||
|
PRIMARY,
|
||||||
|
WM_S0,
|
||||||
|
STRING,
|
||||||
|
WLC_SELECTION,
|
||||||
|
NET_WM_S0,
|
||||||
|
NET_WM_PID,
|
||||||
|
NET_WM_NAME,
|
||||||
|
NET_WM_STATE,
|
||||||
|
NET_WM_STATE_FULLSCREEN,
|
||||||
|
NET_WM_STATE_MODAL,
|
||||||
|
NET_WM_STATE_ABOVE,
|
||||||
|
NET_SUPPORTED,
|
||||||
|
NET_SUPPORTING_WM_CHECK,
|
||||||
|
NET_WM_WINDOW_TYPE,
|
||||||
|
NET_WM_WINDOW_TYPE_DESKTOP,
|
||||||
|
NET_WM_WINDOW_TYPE_DOCK,
|
||||||
|
NET_WM_WINDOW_TYPE_TOOLBAR,
|
||||||
|
NET_WM_WINDOW_TYPE_MENU,
|
||||||
|
NET_WM_WINDOW_TYPE_UTILITY,
|
||||||
|
NET_WM_WINDOW_TYPE_SPLASH,
|
||||||
|
NET_WM_WINDOW_TYPE_DIALOG,
|
||||||
|
NET_WM_WINDOW_TYPE_DROPDOWN_MENU,
|
||||||
|
NET_WM_WINDOW_TYPE_POPUP_MENU,
|
||||||
|
NET_WM_WINDOW_TYPE_TOOLTIP,
|
||||||
|
NET_WM_WINDOW_TYPE_NOTIFICATION,
|
||||||
|
NET_WM_WINDOW_TYPE_COMBO,
|
||||||
|
NET_WM_WINDOW_TYPE_DND,
|
||||||
|
NET_WM_WINDOW_TYPE_NORMAL,
|
||||||
|
*/
|
||||||
|
|
||||||
|
enum atom_name {
|
||||||
|
WL_SURFACE_ID,
|
||||||
|
WM_PROTOCOLS,
|
||||||
|
WM_S0,
|
||||||
|
NET_SUPPORTED,
|
||||||
|
NET_WM_S0,
|
||||||
|
NET_WM_STATE,
|
||||||
|
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",
|
||||||
|
};
|
||||||
|
|
||||||
|
struct wlr_xwm {
|
||||||
|
struct wlr_xwayland *xwayland;
|
||||||
|
struct wl_event_source *event_source;
|
||||||
|
struct wl_listener surface_create_listener;
|
||||||
|
|
||||||
|
xcb_atom_t atoms[ATOM_LAST];
|
||||||
|
xcb_connection_t *xcb_conn;
|
||||||
|
xcb_screen_t *screen;
|
||||||
|
xcb_window_t window;
|
||||||
|
|
||||||
|
struct wl_list new_windows;
|
||||||
|
struct wl_list unpaired_windows;
|
||||||
|
};
|
||||||
|
|
||||||
|
void xwm_destroy(struct wlr_xwm *xwm);
|
||||||
|
struct wlr_xwm *xwm_create(struct wlr_xwayland *wlr_xwayland);
|
||||||
|
|
||||||
|
#endif
|
Loading…
Reference in New Issue