aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--backend/drm/drm.c37
-rw-r--r--backend/drm/renderer.c2
-rw-r--r--include/rootston/view.h2
-rw-r--r--include/wlr/types/wlr_surface.h3
-rw-r--r--include/xwayland/xwm.h1
-rw-r--r--protocol/wlr-data-control-unstable-v1.xml9
-rw-r--r--rootston/desktop.c27
-rw-r--r--types/data_device/wlr_data_device.c7
-rw-r--r--types/data_device/wlr_drag.c2
-rw-r--r--types/tablet_v2/wlr_tablet_v2_tool.c2
-rw-r--r--types/wlr_data_control_v1.c4
-rw-r--r--types/wlr_output.c7
-rw-r--r--types/wlr_surface.c94
-rw-r--r--xwayland/xwm.c29
14 files changed, 198 insertions, 28 deletions
diff --git a/backend/drm/drm.c b/backend/drm/drm.c
index 619664d6..e0eed4c2 100644
--- a/backend/drm/drm.c
+++ b/backend/drm/drm.c
@@ -632,13 +632,25 @@ static bool drm_connector_set_cursor(struct wlr_output *output,
ret = drmGetCap(drm->fd, DRM_CAP_CURSOR_HEIGHT, &h);
h = ret ? 64 : h;
- struct wlr_drm_renderer *renderer =
- drm->parent ? &drm->parent->renderer : &drm->renderer;
- if (!init_drm_surface(&plane->surf, renderer, w, h,
- renderer->gbm_format, GBM_BO_USE_LINEAR | GBM_BO_USE_SCANOUT)) {
- wlr_log(WLR_ERROR, "Cannot allocate cursor resources");
- return false;
+ if (!drm->parent) {
+ if (!init_drm_surface(&plane->surf, &drm->renderer, w, h,
+ drm->renderer.gbm_format, GBM_BO_USE_LINEAR | GBM_BO_USE_SCANOUT)) {
+ wlr_log(WLR_ERROR, "Cannot allocate cursor resources");
+ return false;
+ }
+ } else {
+ if (!init_drm_surface(&plane->surf, &drm->parent->renderer, w, h,
+ drm->parent->renderer.gbm_format, GBM_BO_USE_LINEAR)) {
+ wlr_log(WLR_ERROR, "Cannot allocate cursor resources");
+ return false;
+ }
+
+ if (!init_drm_surface(&plane->mgpu_surf, &drm->renderer, w, h,
+ drm->renderer.gbm_format, GBM_BO_USE_LINEAR | GBM_BO_USE_SCANOUT)) {
+ wlr_log(WLR_ERROR, "Cannot allocate cursor resources");
+ return false;
+ }
}
}
@@ -708,6 +720,19 @@ static bool drm_connector_set_cursor(struct wlr_output *output,
}
struct gbm_bo *bo = plane->cursor_enabled ? plane->surf.back : NULL;
+ if (bo && drm->parent) {
+ bo = copy_drm_surface_mgpu(&plane->mgpu_surf, bo);
+ }
+
+ if (bo) {
+ // workaround for nouveau
+ // Buffers created with GBM_BO_USER_LINEAR are placed in NOUVEAU_GEM_DOMAIN_GART.
+ // When the bo is attached to the cursor plane it is moved to NOUVEAU_GEM_DOMAIN_VRAM.
+ // However, this does not wait for the render operations to complete, leaving an empty surface.
+ // see https://bugs.freedesktop.org/show_bug.cgi?id=109631
+ // The render operations can be waited for using:
+ glFinish();
+ }
bool ok = drm->iface->crtc_set_cursor(drm, crtc, bo);
if (ok) {
wlr_output_update_needs_swap(output);
diff --git a/backend/drm/renderer.c b/backend/drm/renderer.c
index b77a7ce0..72cfd430 100644
--- a/backend/drm/renderer.c
+++ b/backend/drm/renderer.c
@@ -230,7 +230,7 @@ struct gbm_bo *copy_drm_surface_mgpu(struct wlr_drm_surface *dest,
struct wlr_renderer *renderer = dest->renderer->wlr_rend;
wlr_renderer_begin(renderer, dest->width, dest->height);
- wlr_renderer_clear(renderer, (float[]){ 0.0, 0.0, 0.0, 1.0 });
+ wlr_renderer_clear(renderer, (float[]){ 0.0, 0.0, 0.0, 0.0 });
wlr_render_texture_with_matrix(renderer, tex, mat, 1.0f);
wlr_renderer_end(renderer);
diff --git a/include/rootston/view.h b/include/rootston/view.h
index b1feb5ce..0174e4f3 100644
--- a/include/rootston/view.h
+++ b/include/rootston/view.h
@@ -181,6 +181,8 @@ struct roots_subsurface {
struct roots_view_child view_child;
struct wlr_subsurface *wlr_subsurface;
struct wl_listener destroy;
+ struct wl_listener map;
+ struct wl_listener unmap;
};
struct roots_wl_shell_popup {
diff --git a/include/wlr/types/wlr_surface.h b/include/wlr/types/wlr_surface.h
index b8f8c02a..0c389987 100644
--- a/include/wlr/types/wlr_surface.h
+++ b/include/wlr/types/wlr_surface.h
@@ -130,6 +130,7 @@ struct wlr_subsurface {
bool synchronized;
bool reordered;
+ bool mapped;
struct wl_list parent_link;
struct wl_list parent_pending_link;
@@ -139,6 +140,8 @@ struct wlr_subsurface {
struct {
struct wl_signal destroy;
+ struct wl_signal map;
+ struct wl_signal unmap;
} events;
void *data;
diff --git a/include/xwayland/xwm.h b/include/xwayland/xwm.h
index a3bdc48c..00f2f3d5 100644
--- a/include/xwayland/xwm.h
+++ b/include/xwayland/xwm.h
@@ -80,6 +80,7 @@ enum atom_name {
DND_ACTION_COPY,
DND_ACTION_ASK,
DND_ACTION_PRIVATE,
+ _NET_CLIENT_LIST,
ATOM_LAST,
};
diff --git a/protocol/wlr-data-control-unstable-v1.xml b/protocol/wlr-data-control-unstable-v1.xml
index 3e39f2ac..75e8671b 100644
--- a/protocol/wlr-data-control-unstable-v1.xml
+++ b/protocol/wlr-data-control-unstable-v1.xml
@@ -69,15 +69,6 @@
appropriate destroy request has been called.
</description>
</request>
-
- <!-- Version 2 additions -->
-
- <event name="primary_selection" since="2">
- <description summary="advertise primary selection support">
- This event can be sent when binding to the wlr_data_control_manager
- global to advertise that it supports the primary selection.
- </description>
- </event>
</interface>
<interface name="zwlr_data_control_device_v1" version="2">
diff --git a/rootston/desktop.c b/rootston/desktop.c
index ff9544d5..6f76b97e 100644
--- a/rootston/desktop.c
+++ b/rootston/desktop.c
@@ -421,6 +421,8 @@ static void subsurface_destroy(struct roots_view_child *child) {
return;
}
wl_list_remove(&subsurface->destroy.link);
+ wl_list_remove(&subsurface->map.link);
+ wl_list_remove(&subsurface->unmap.link);
view_child_finish(&subsurface->view_child);
free(subsurface);
}
@@ -429,7 +431,25 @@ static void subsurface_handle_destroy(struct wl_listener *listener,
void *data) {
struct roots_subsurface *subsurface =
wl_container_of(listener, subsurface, destroy);
- subsurface_destroy((struct roots_view_child *)subsurface);
+ subsurface_destroy(&subsurface->view_child);
+}
+
+static void subsurface_handle_map(struct wl_listener *listener,
+ void *data) {
+ struct roots_subsurface *subsurface =
+ wl_container_of(listener, subsurface, map);
+ struct roots_view *view = subsurface->view_child.view;
+ view_damage_whole(view);
+ input_update_cursor_focus(view->desktop->server->input);
+}
+
+static void subsurface_handle_unmap(struct wl_listener *listener,
+ void *data) {
+ struct roots_subsurface *subsurface =
+ wl_container_of(listener, subsurface, unmap);
+ struct roots_view *view = subsurface->view_child.view;
+ view_damage_whole(view);
+ input_update_cursor_focus(view->desktop->server->input);
}
struct roots_subsurface *subsurface_create(struct roots_view *view,
@@ -444,7 +464,10 @@ struct roots_subsurface *subsurface_create(struct roots_view *view,
view_child_init(&subsurface->view_child, view, wlr_subsurface->surface);
subsurface->destroy.notify = subsurface_handle_destroy;
wl_signal_add(&wlr_subsurface->events.destroy, &subsurface->destroy);
- input_update_cursor_focus(view->desktop->server->input);
+ subsurface->map.notify = subsurface_handle_map;
+ wl_signal_add(&wlr_subsurface->events.map, &subsurface->map);
+ subsurface->unmap.notify = subsurface_handle_unmap;
+ wl_signal_add(&wlr_subsurface->events.unmap, &subsurface->unmap);
return subsurface;
}
diff --git a/types/data_device/wlr_data_device.c b/types/data_device/wlr_data_device.c
index feddfa2d..36a9d1b8 100644
--- a/types/data_device/wlr_data_device.c
+++ b/types/data_device/wlr_data_device.c
@@ -126,6 +126,13 @@ void seat_client_send_selection(struct wlr_seat_client *seat_client) {
source->accepted = false;
}
+ // Make all current offers inert
+ struct wlr_data_offer *offer, *tmp;
+ wl_list_for_each_safe(offer, tmp,
+ &seat_client->seat->selection_offers, link) {
+ data_offer_destroy(offer);
+ }
+
struct wl_resource *device_resource;
wl_resource_for_each(device_resource, &seat_client->data_devices) {
device_resource_send_selection(device_resource);
diff --git a/types/data_device/wlr_drag.c b/types/data_device/wlr_drag.c
index 558e9f22..fdfabdcf 100644
--- a/types/data_device/wlr_drag.c
+++ b/types/data_device/wlr_drag.c
@@ -96,8 +96,8 @@ static void drag_icon_set_mapped(struct wlr_drag_icon *icon, bool mapped) {
icon->mapped = true;
wlr_signal_emit_safe(&icon->events.map, icon);
} else if (!mapped && icon->mapped) {
- icon->mapped = false;
wlr_signal_emit_safe(&icon->events.unmap, icon);
+ icon->mapped = false;
}
}
diff --git a/types/tablet_v2/wlr_tablet_v2_tool.c b/types/tablet_v2/wlr_tablet_v2_tool.c
index 471673ae..5bb57028 100644
--- a/types/tablet_v2/wlr_tablet_v2_tool.c
+++ b/types/tablet_v2/wlr_tablet_v2_tool.c
@@ -23,7 +23,7 @@ static void handle_tablet_tool_v2_set_cursor(struct wl_client *client,
struct wl_resource *surface_resource,
int32_t hotspot_x, int32_t hotspot_y) {
struct wlr_tablet_tool_client_v2 *tool = tablet_tool_client_from_resource(resource);
- if (!tool) {
+ if (!tool || !tool->tool) {
return;
}
diff --git a/types/wlr_data_control_v1.c b/types/wlr_data_control_v1.c
index 99dd0dae..b23e37ee 100644
--- a/types/wlr_data_control_v1.c
+++ b/types/wlr_data_control_v1.c
@@ -667,10 +667,6 @@ static void manager_bind(struct wl_client *client, void *data, uint32_t version,
manager_handle_resource_destroy);
wl_list_insert(&manager->resources, wl_resource_get_link(resource));
-
- if (version >= ZWLR_DATA_CONTROL_MANAGER_V1_PRIMARY_SELECTION_SINCE_VERSION) {
- zwlr_data_control_manager_v1_send_primary_selection(resource);
- }
}
static void handle_display_destroy(struct wl_listener *listener, void *data) {
diff --git a/types/wlr_output.c b/types/wlr_output.c
index f49d48e3..a8c86771 100644
--- a/types/wlr_output.c
+++ b/types/wlr_output.c
@@ -159,6 +159,9 @@ bool wlr_output_set_mode(struct wlr_output *output,
if (!output->impl || !output->impl->set_mode) {
return false;
}
+ if (output->current_mode == mode) {
+ return true;
+ }
return output->impl->set_mode(output, mode);
}
@@ -167,6 +170,10 @@ bool wlr_output_set_custom_mode(struct wlr_output *output, int32_t width,
if (!output->impl || !output->impl->set_custom_mode) {
return false;
}
+ if (output->width == width && output->height == height &&
+ output->refresh == refresh) {
+ return true;
+ }
return output->impl->set_custom_mode(output, width, height, refresh);
}
diff --git a/types/wlr_surface.c b/types/wlr_surface.c
index 27176ef0..febfcd43 100644
--- a/types/wlr_surface.c
+++ b/types/wlr_surface.c
@@ -450,15 +450,25 @@ static void surface_commit(struct wl_client *client,
}
static void surface_set_buffer_transform(struct wl_client *client,
- struct wl_resource *resource, int transform) {
+ struct wl_resource *resource, int32_t transform) {
+ if (transform < WL_OUTPUT_TRANSFORM_NORMAL ||
+ transform > WL_OUTPUT_TRANSFORM_FLIPPED_270) {
+ wl_resource_post_error(resource, WL_SURFACE_ERROR_INVALID_TRANSFORM,
+ "Specified transform value (%d) is invalid", transform);
+ return;
+ }
struct wlr_surface *surface = wlr_surface_from_resource(resource);
surface->pending.committed |= WLR_SURFACE_STATE_TRANSFORM;
surface->pending.transform = transform;
}
static void surface_set_buffer_scale(struct wl_client *client,
- struct wl_resource *resource,
- int32_t scale) {
+ struct wl_resource *resource, int32_t scale) {
+ if (scale <= 0) {
+ wl_resource_post_error(resource, WL_SURFACE_ERROR_INVALID_SCALE,
+ "Specified scale value (%d) is not positive", scale);
+ return;
+ }
struct wlr_surface *surface = wlr_surface_from_resource(resource);
surface->pending.committed |= WLR_SURFACE_STATE_SCALE;
surface->pending.scale = scale;
@@ -524,11 +534,15 @@ static void surface_state_finish(struct wlr_surface_state *state) {
pixman_region32_fini(&state->input);
}
+static void subsurface_unmap(struct wlr_subsurface *subsurface);
+
static void subsurface_destroy(struct wlr_subsurface *subsurface) {
if (subsurface == NULL) {
return;
}
+ subsurface_unmap(subsurface);
+
wlr_signal_emit_safe(&subsurface->events.destroy, subsurface);
wl_list_remove(&subsurface->surface_destroy.link);
@@ -789,6 +803,60 @@ static const struct wl_subsurface_interface subsurface_implementation = {
.set_desync = subsurface_handle_set_desync,
};
+/**
+ * Checks if this subsurface needs to be marked as mapped. This can happen if:
+ * - The subsurface has a buffer
+ * - Its parent is mapped
+ */
+static void subsurface_consider_map(struct wlr_subsurface *subsurface,
+ bool check_parent) {
+ if (subsurface->mapped || !wlr_surface_has_buffer(subsurface->surface)) {
+ return;
+ }
+
+ if (check_parent) {
+ if (subsurface->parent == NULL) {
+ return;
+ }
+ if (wlr_surface_is_subsurface(subsurface->parent)) {
+ struct wlr_subsurface *parent =
+ wlr_subsurface_from_wlr_surface(subsurface->parent);
+ if (parent == NULL || !parent->mapped) {
+ return;
+ }
+ }
+ }
+
+ // Now we can map the subsurface
+ if (subsurface->mapped) {
+ return;
+ }
+
+ wlr_signal_emit_safe(&subsurface->events.map, subsurface);
+ subsurface->mapped = true;
+
+ // Try mapping all children too
+ struct wlr_subsurface *child;
+ wl_list_for_each(child, &subsurface->surface->subsurfaces, parent_link) {
+ subsurface_consider_map(child, false);
+ }
+}
+
+static void subsurface_unmap(struct wlr_subsurface *subsurface) {
+ if (!subsurface->mapped) {
+ return;
+ }
+
+ wlr_signal_emit_safe(&subsurface->events.unmap, subsurface);
+ subsurface->mapped = false;
+
+ // Unmap all children
+ struct wlr_subsurface *child;
+ wl_list_for_each(child, &subsurface->surface->subsurfaces, parent_link) {
+ subsurface_unmap(child);
+ }
+}
+
static void subsurface_role_commit(struct wlr_surface *surface) {
struct wlr_subsurface *subsurface =
wlr_subsurface_from_wlr_surface(surface);
@@ -819,17 +887,35 @@ static void subsurface_role_commit(struct wlr_surface *surface) {
&surface->buffer_damage, 0, 0,
surface->current.buffer_width, surface->current.buffer_height);
}
+
+ subsurface_consider_map(subsurface, true);
+}
+
+static void subsurface_role_precommit(struct wlr_surface *surface) {
+ struct wlr_subsurface *subsurface =
+ wlr_subsurface_from_wlr_surface(surface);
+ if (subsurface == NULL) {
+ return;
+ }
+
+ if (surface->pending.committed & WLR_SURFACE_STATE_BUFFER &&
+ surface->pending.buffer_resource == NULL) {
+ // This is a NULL commit
+ subsurface_unmap(subsurface);
+ }
}
const struct wlr_surface_role subsurface_role = {
.name = "wl_subsurface",
.commit = subsurface_role_commit,
+ .precommit = subsurface_role_precommit,
};
static void subsurface_handle_parent_destroy(struct wl_listener *listener,
void *data) {
struct wlr_subsurface *subsurface =
wl_container_of(listener, subsurface, parent_destroy);
+ subsurface_unmap(subsurface);
wl_list_remove(&subsurface->parent_link);
wl_list_remove(&subsurface->parent_pending_link);
wl_list_remove(&subsurface->parent_destroy.link);
@@ -872,6 +958,8 @@ struct wlr_subsurface *wlr_subsurface_create(struct wlr_surface *surface,
subsurface_resource_destroy);
wl_signal_init(&subsurface->events.destroy);
+ wl_signal_init(&subsurface->events.map);
+ wl_signal_init(&subsurface->events.unmap);
wl_signal_add(&surface->events.destroy, &subsurface->surface_destroy);
subsurface->surface_destroy.notify = subsurface_handle_surface_destroy;
diff --git a/xwayland/xwm.c b/xwayland/xwm.c
index fc294674..fc99490b 100644
--- a/xwayland/xwm.c
+++ b/xwayland/xwm.c
@@ -78,6 +78,7 @@ const char *atom_map[ATOM_LAST] = {
"XdndActionCopy",
"XdndActionAsk",
"XdndActionPrivate",
+ "_NET_CLIENT_LIST",
};
static const struct wlr_surface_role xwayland_surface_role;
@@ -212,6 +213,28 @@ static void xwm_send_wm_message(struct wlr_xwayland_surface *surface,
xcb_flush(xwm->xcb_conn);
}
+static void xwm_set_net_client_list(struct wlr_xwm *xwm) {
+ size_t mapped_surfaces = 0;
+ struct wlr_xwayland_surface *surface;
+ wl_list_for_each(surface, &xwm->surfaces, link) {
+ if (surface->mapped) {
+ mapped_surfaces++;
+ }
+ }
+
+ xcb_window_t windows[mapped_surfaces + 1];
+ size_t index = 0;
+ wl_list_for_each(surface, &xwm->surfaces, link) {
+ if (surface->mapped) {
+ windows[index++] = surface->window_id;
+ }
+ }
+
+ xcb_change_property(xwm->xcb_conn, XCB_PROP_MODE_REPLACE,
+ xwm->screen->root, xwm->atoms[_NET_CLIENT_LIST],
+ XCB_ATOM_WINDOW, 32, mapped_surfaces, windows);
+}
+
static void xwm_send_focus_window(struct wlr_xwm *xwm,
struct wlr_xwayland_surface *xsurface) {
if (!xsurface) {
@@ -702,6 +725,7 @@ static void xwayland_surface_role_commit(struct wlr_surface *wlr_surface) {
if (!surface->mapped && wlr_surface_has_buffer(surface->surface)) {
wlr_signal_emit_safe(&surface->events.map, surface);
surface->mapped = true;
+ xwm_set_net_client_list(surface->xwm);
}
}
@@ -718,6 +742,7 @@ static void xwayland_surface_role_precommit(struct wlr_surface *wlr_surface) {
if (surface->mapped) {
wlr_signal_emit_safe(&surface->events.unmap, surface);
surface->mapped = false;
+ xwm_set_net_client_list(surface->xwm);
}
}
}
@@ -768,8 +793,9 @@ static void xwm_map_shell_surface(struct wlr_xwm *xwm,
static void xsurface_unmap(struct wlr_xwayland_surface *surface) {
if (surface->mapped) {
- surface->mapped = false;
wlr_signal_emit_safe(&surface->events.unmap, surface);
+ surface->mapped = false;
+ xwm_set_net_client_list(surface->xwm);
}
if (surface->surface_id) {
@@ -1712,6 +1738,7 @@ struct wlr_xwm *xwm_create(struct wlr_xwayland *wlr_xwayland) {
xwm->atoms[_NET_WM_STATE_FULLSCREEN],
xwm->atoms[_NET_WM_STATE_MAXIMIZED_VERT],
xwm->atoms[_NET_WM_STATE_MAXIMIZED_HORZ],
+ xwm->atoms[_NET_CLIENT_LIST],
};
xcb_change_property(xwm->xcb_conn,
XCB_PROP_MODE_REPLACE,