aboutsummaryrefslogtreecommitdiff
path: root/xwayland
diff options
context:
space:
mode:
Diffstat (limited to 'xwayland')
-rw-r--r--xwayland/meson.build40
-rw-r--r--xwayland/xwayland.c26
-rw-r--r--xwayland/xwm.c129
3 files changed, 161 insertions, 34 deletions
diff --git a/xwayland/meson.build b/xwayland/meson.build
index ec486f58..0bd88924 100644
--- a/xwayland/meson.build
+++ b/xwayland/meson.build
@@ -1,3 +1,32 @@
+xwayland_libs = []
+xwayland_required = [
+ 'xcb',
+ 'xcb-composite',
+ 'xcb-render',
+ 'xcb-xfixes',
+]
+xwayland_optional = [
+ 'xcb-errors',
+ 'xcb-icccm',
+]
+
+foreach lib : xwayland_required
+ dep = dependency(lib, required: get_option('xwayland'))
+ if not dep.found()
+ subdir_done()
+ endif
+
+ xwayland_libs += dep
+endforeach
+
+foreach lib : xwayland_optional
+ dep = dependency(lib, required: get_option(lib))
+ if dep.found()
+ xwayland_libs += dep
+ conf_data.set('WLR_HAS_' + lib.underscorify().to_upper(), true)
+ endif
+endforeach
+
lib_wlr_xwayland = static_library(
'wlr_xwayland',
files(
@@ -12,14 +41,11 @@ lib_wlr_xwayland = static_library(
include_directories: wlr_inc,
dependencies: [
wayland_server,
- xcb,
- xcb_composite,
- xcb_xfixes,
- xcb_image,
- xcb_render,
- xcb_icccm,
- xcb_errors,
+ xwayland_libs,
xkbcommon,
pixman,
],
)
+
+wlr_parts += lib_wlr_xwayland
+conf_data.set('WLR_HAS_XWAYLAND', true)
diff --git a/xwayland/xwayland.c b/xwayland/xwayland.c
index 1068b942..fe09ea5e 100644
--- a/xwayland/xwayland.c
+++ b/xwayland/xwayland.c
@@ -74,6 +74,7 @@ static int fill_arg(char ***argv, const char *fmt, ...) {
return len;
}
+_Noreturn
static void exec_xwayland(struct wlr_xwayland *wlr_xwayland) {
if (unset_cloexec(wlr_xwayland->x_fd[0]) ||
unset_cloexec(wlr_xwayland->x_fd[1]) ||
@@ -123,9 +124,26 @@ static void exec_xwayland(struct wlr_xwayland *wlr_xwayland) {
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
+ // Closes stdout/stderr depending on log verbosity
+ enum wlr_log_importance verbosity = wlr_log_get_verbosity();
+ int devnull = open("/dev/null", O_WRONLY | O_CREAT, 0666);
+ if (devnull < 0) {
+ wlr_log_errno(WLR_ERROR, "XWayland: failed to open /dev/null");
+ _exit(EXIT_FAILURE);
+ }
+ if (verbosity < WLR_INFO) {
+ dup2(devnull, STDOUT_FILENO);
+ }
+ if (verbosity < WLR_ERROR) {
+ dup2(devnull, STDERR_FILENO);
+ }
+ // This returns if and only if the call fails
execvp("Xwayland", argv);
+
+ wlr_log_errno(WLR_ERROR, "failed to exec Xwayland");
+ close(devnull);
+ _exit(EXIT_FAILURE);
}
static void xwayland_finish_server(struct wlr_xwayland *wlr_xwayland) {
@@ -346,8 +364,6 @@ static bool xwayland_start_server(struct wlr_xwayland *wlr_xwayland) {
sigprocmask(SIG_BLOCK, &sigset, NULL);
if ((pid = fork()) == 0) {
exec_xwayland(wlr_xwayland);
- wlr_log_errno(WLR_ERROR, "failed to exec Xwayland");
- _exit(EXIT_FAILURE);
}
if (pid < 0) {
wlr_log_errno(WLR_ERROR, "second fork failed");
@@ -390,6 +406,10 @@ static bool xwayland_start_server_lazy(struct wlr_xwayland *wlr_xwayland) {
}
void wlr_xwayland_destroy(struct wlr_xwayland *wlr_xwayland) {
+ if (!wlr_xwayland) {
+ return;
+ }
+
wlr_xwayland_set_seat(wlr_xwayland, NULL);
xwayland_finish_server(wlr_xwayland);
xwayland_finish_display(wlr_xwayland);
diff --git a/xwayland/xwm.c b/xwayland/xwm.c
index bee3a005..9c803543 100644
--- a/xwayland/xwm.c
+++ b/xwayland/xwm.c
@@ -24,6 +24,7 @@ const char *atom_map[ATOM_LAST] = {
"WM_HINTS",
"WM_NORMAL_HINTS",
"WM_SIZE_HINTS",
+ "WM_WINDOW_ROLE",
"_MOTIF_WM_HINTS",
"UTF8_STRING",
"WM_S0",
@@ -39,6 +40,7 @@ const char *atom_map[ATOM_LAST] = {
"_NET_WM_MOVERESIZE",
"_NET_WM_NAME",
"_NET_SUPPORTING_WM_CHECK",
+ "_NET_WM_STATE_MODAL",
"_NET_WM_STATE_FULLSCREEN",
"_NET_WM_STATE_MAXIMIZED_VERT",
"_NET_WM_STATE_MAXIMIZED_HORZ",
@@ -147,13 +149,17 @@ static struct wlr_xwayland_surface *xwayland_surface_create(
wl_signal_init(&surface->events.request_resize);
wl_signal_init(&surface->events.request_maximize);
wl_signal_init(&surface->events.request_fullscreen);
+ wl_signal_init(&surface->events.request_activate);
wl_signal_init(&surface->events.map);
wl_signal_init(&surface->events.unmap);
wl_signal_init(&surface->events.set_class);
+ wl_signal_init(&surface->events.set_role);
wl_signal_init(&surface->events.set_title);
wl_signal_init(&surface->events.set_parent);
wl_signal_init(&surface->events.set_pid);
wl_signal_init(&surface->events.set_window_type);
+ wl_signal_init(&surface->events.set_hints);
+ wl_signal_init(&surface->events.set_override_redirect);
wl_signal_init(&surface->events.ping_timeout);
xcb_get_geometry_reply_t *geometry_reply =
@@ -240,8 +246,7 @@ static void xwm_send_focus_window(struct wlr_xwm *xwm,
XCB_CONFIG_WINDOW_STACK_MODE, values);
}
-
-void xwm_surface_activate(struct wlr_xwm *xwm,
+static void xwm_surface_activate(struct wlr_xwm *xwm,
struct wlr_xwayland_surface *xsurface) {
if (xwm->focus_surface == xsurface ||
(xsurface && xsurface->override_redirect)) {
@@ -267,6 +272,9 @@ static void xsurface_set_net_wm_state(struct wlr_xwayland_surface *xsurface) {
int i;
i = 0;
+ if (xsurface->modal) {
+ property[i++] = xwm->atoms[_NET_WM_STATE_MODAL];
+ }
if (xsurface->fullscreen) {
property[i++] = xwm->atoms[_NET_WM_STATE_FULLSCREEN];
}
@@ -321,6 +329,7 @@ static void xwayland_surface_destroy(
free(xsurface->title);
free(xsurface->class);
free(xsurface->instance);
+ free(xsurface->role);
free(xsurface->window_type);
free(xsurface->protocols);
free(xsurface->hints);
@@ -359,6 +368,28 @@ static void read_surface_class(struct wlr_xwm *xwm,
wlr_signal_emit_safe(&surface->events.set_class, surface);
}
+static void read_surface_role(struct wlr_xwm *xwm,
+ struct wlr_xwayland_surface *xsurface,
+ xcb_get_property_reply_t *reply) {
+ if (reply->type != XCB_ATOM_STRING &&
+ reply->type != xwm->atoms[UTF8_STRING]) {
+ return;
+ }
+
+ size_t len = xcb_get_property_value_length(reply);
+ char *role = xcb_get_property_value(reply);
+
+ free(xsurface->role);
+ if (len > 0) {
+ xsurface->role = strndup(role, len);
+ } else {
+ xsurface->role = NULL;
+ }
+
+ wlr_log(WLR_DEBUG, "XCB_ATOM_WM_WINDOW_ROLE: %s", xsurface->role);
+ wlr_signal_emit_safe(&xsurface->events.set_role, xsurface);
+}
+
static void read_surface_title(struct wlr_xwm *xwm,
struct wlr_xwayland_surface *xsurface,
xcb_get_property_reply_t *reply) {
@@ -492,6 +523,7 @@ static void read_surface_hints(struct wlr_xwm *xwm,
xsurface->hints_urgency = xcb_icccm_wm_hints_get_urgency(&hints);
wlr_log(WLR_DEBUG, "WM_HINTS (%d)", reply->value_len);
+ wlr_signal_emit_safe(&xsurface->events.set_hints, xsurface);
}
#else
static void read_surface_hints(struct wlr_xwm *xwm,
@@ -573,7 +605,9 @@ static void read_surface_net_wm_state(struct wlr_xwm *xwm,
xsurface->fullscreen = 0;
xcb_atom_t *atom = xcb_get_property_value(reply);
for (uint32_t i = 0; i < reply->value_len; i++) {
- if (atom[i] == xwm->atoms[_NET_WM_STATE_FULLSCREEN]) {
+ if (atom[i] == xwm->atoms[_NET_WM_STATE_MODAL]) {
+ xsurface->modal = true;
+ } else if (atom[i] == xwm->atoms[_NET_WM_STATE_FULLSCREEN]) {
xsurface->fullscreen = true;
} else if (atom[i] == xwm->atoms[_NET_WM_STATE_MAXIMIZED_VERT]) {
xsurface->maximized_vert = true;
@@ -629,6 +663,8 @@ static void read_surface_property(struct wlr_xwm *xwm,
read_surface_normal_hints(xwm, xsurface, reply);
} else if (property == xwm->atoms[MOTIF_WM_HINTS]) {
read_surface_motif_hints(xwm, xsurface, reply);
+ } else if (property == xwm->atoms[WM_WINDOW_ROLE]) {
+ read_surface_role(xwm, xsurface, reply);
} else {
char *prop_name = xwm_get_atom_name(xwm, property);
wlr_log(WLR_DEBUG, "unhandled X11 property %u (%s) for window %u",
@@ -652,9 +688,27 @@ static void xwayland_surface_role_commit(struct wlr_surface *wlr_surface) {
}
}
+static void xwayland_surface_role_precommit(struct wlr_surface *wlr_surface) {
+ assert(wlr_surface->role == &xwayland_surface_role);
+ struct wlr_xwayland_surface *surface = wlr_surface->role_data;
+ if (surface == NULL) {
+ return;
+ }
+
+ if (wlr_surface->pending.committed & WLR_SURFACE_STATE_BUFFER &&
+ wlr_surface->pending.buffer_resource == NULL) {
+ // This is a NULL commit
+ if (surface->mapped) {
+ wlr_signal_emit_safe(&surface->events.unmap, surface);
+ surface->mapped = false;
+ }
+ }
+}
+
static const struct wlr_surface_role xwayland_surface_role = {
.name = "wlr_xwayland_surface",
.commit = xwayland_surface_role_commit,
+ .precommit = xwayland_surface_role_precommit,
};
static void handle_surface_destroy(struct wl_listener *listener, void *data) {
@@ -764,9 +818,7 @@ static void xwm_handle_configure_request(struct wlr_xwm *xwm,
static void xwm_handle_configure_notify(struct wlr_xwm *xwm,
xcb_configure_notify_event_t *ev) {
- struct wlr_xwayland_surface *xsurface =
- lookup_surface(xwm, ev->window);
-
+ struct wlr_xwayland_surface *xsurface = lookup_surface(xwm, ev->window);
if (!xsurface) {
return;
}
@@ -775,6 +827,11 @@ static void xwm_handle_configure_notify(struct wlr_xwm *xwm,
xsurface->y = ev->y;
xsurface->width = ev->width;
xsurface->height = ev->height;
+
+ if (xsurface->override_redirect != ev->override_redirect) {
+ xsurface->override_redirect = ev->override_redirect;
+ wlr_signal_emit_safe(&xsurface->events.set_override_redirect, xsurface);
+ }
}
#define ICCCM_WITHDRAWN_STATE 0
@@ -814,6 +871,15 @@ static void xwm_handle_map_request(struct wlr_xwm *xwm,
static void xwm_handle_map_notify(struct wlr_xwm *xwm,
xcb_map_notify_event_t *ev) {
wlr_log(WLR_DEBUG, "XCB_MAP_NOTIFY (%u)", ev->window);
+ struct wlr_xwayland_surface *xsurface = lookup_surface(xwm, ev->window);
+ if (!xsurface) {
+ return;
+ }
+
+ if (xsurface->override_redirect != ev->override_redirect) {
+ xsurface->override_redirect = ev->override_redirect;
+ wlr_signal_emit_safe(&xsurface->events.set_override_redirect, xsurface);
+ }
}
static void xwm_handle_unmap_notify(struct wlr_xwm *xwm,
@@ -996,7 +1062,10 @@ static void xwm_handle_net_wm_state_message(struct wlr_xwm *xwm,
for (size_t i = 0; i < 2; ++i) {
uint32_t property = client_message->data.data32[1 + i];
- if (property == xwm->atoms[_NET_WM_STATE_FULLSCREEN] &&
+ if (property == xwm->atoms[_NET_WM_STATE_MODAL] &&
+ update_state(action, &xsurface->modal)) {
+ xsurface_set_net_wm_state(xsurface);
+ } else if (property == xwm->atoms[_NET_WM_STATE_FULLSCREEN] &&
update_state(action, &xsurface->fullscreen)) {
xsurface_set_net_wm_state(xsurface);
} else if (property == xwm->atoms[_NET_WM_STATE_MAXIMIZED_VERT] &&
@@ -1055,6 +1124,15 @@ static void xwm_handle_wm_protocols_message(struct wlr_xwm *xwm,
}
}
+static void xwm_handle_net_active_window_message(struct wlr_xwm *xwm,
+ xcb_client_message_event_t *ev) {
+ struct wlr_xwayland_surface *surface = lookup_surface(xwm, ev->window);
+ if (surface == NULL) {
+ return;
+ }
+ wlr_signal_emit_safe(&surface->events.request_activate, surface);
+}
+
static void xwm_handle_client_message(struct wlr_xwm *xwm,
xcb_client_message_event_t *ev) {
wlr_log(WLR_DEBUG, "XCB_CLIENT_MESSAGE (%u)", ev->window);
@@ -1067,6 +1145,8 @@ static void xwm_handle_client_message(struct wlr_xwm *xwm,
xwm_handle_net_wm_moveresize_message(xwm, ev);
} else if (ev->type == xwm->atoms[WM_PROTOCOLS]) {
xwm_handle_wm_protocols_message(xwm, ev);
+ } else if (ev->type == xwm->atoms[_NET_ACTIVE_WINDOW]) {
+ xwm_handle_net_active_window_message(xwm, ev);
} else if (!xwm_handle_selection_client_message(xwm, ev)) {
char *type_name = xwm_get_atom_name(xwm, ev->type);
wlr_log(WLR_DEBUG, "unhandled x11 client message %u (%s)", ev->type,
@@ -1598,6 +1678,7 @@ struct wlr_xwm *xwm_create(struct wlr_xwayland *wlr_xwayland) {
xwm->atoms[NET_WM_STATE],
xwm->atoms[_NET_ACTIVE_WINDOW],
xwm->atoms[_NET_WM_MOVERESIZE],
+ xwm->atoms[_NET_WM_STATE_MODAL],
xwm->atoms[_NET_WM_STATE_FULLSCREEN],
xwm->atoms[_NET_WM_STATE_MAXIMIZED_VERT],
xwm->atoms[_NET_WM_STATE_MAXIMIZED_HORZ],
@@ -1659,8 +1740,22 @@ bool xwm_atoms_contains(struct wlr_xwm *xwm, xcb_atom_t *atoms,
return false;
}
-bool wlr_xwayland_surface_is_unmanaged(
+void wlr_xwayland_surface_ping(struct wlr_xwayland_surface *surface) {
+ xcb_client_message_data_t data = { 0 };
+ data.data32[0] = surface->xwm->atoms[_NET_WM_PING];
+ data.data32[1] = XCB_CURRENT_TIME;
+ data.data32[2] = surface->window_id;
+
+ xwm_send_wm_message(surface, &data, XCB_EVENT_MASK_NO_EVENT);
+
+ wl_event_source_timer_update(surface->ping_timer,
+ surface->xwm->ping_timeout);
+ surface->pinging = true;
+}
+
+bool wlr_xwayland_or_surface_wants_focus(
const struct wlr_xwayland_surface *surface) {
+ bool ret = true;
static enum atom_name needles[] = {
NET_WM_WINDOW_TYPE_COMBO,
NET_WM_WINDOW_TYPE_DND,
@@ -1672,26 +1767,12 @@ bool wlr_xwayland_surface_is_unmanaged(
NET_WM_WINDOW_TYPE_TOOLTIP,
NET_WM_WINDOW_TYPE_UTILITY,
};
-
for (size_t i = 0; i < sizeof(needles) / sizeof(needles[0]); ++i) {
if (xwm_atoms_contains(surface->xwm, surface->window_type,
surface->window_type_len, needles[i])) {
- return true;
+ ret = false;
}
}
- return false;
-}
-
-void wlr_xwayland_surface_ping(struct wlr_xwayland_surface *surface) {
- xcb_client_message_data_t data = { 0 };
- data.data32[0] = surface->xwm->atoms[_NET_WM_PING];
- data.data32[1] = XCB_CURRENT_TIME;
- data.data32[2] = surface->window_id;
-
- xwm_send_wm_message(surface, &data, XCB_EVENT_MASK_NO_EVENT);
-
- wl_event_source_timer_update(surface->ping_timer,
- surface->xwm->ping_timeout);
- surface->pinging = true;
+ return ret;
}