diff options
Diffstat (limited to 'sway/desktop')
-rw-r--r-- | sway/desktop/desktop.c | 9 | ||||
-rw-r--r-- | sway/desktop/fx_renderer.c | 81 | ||||
-rw-r--r-- | sway/desktop/idle_inhibit_v1.c | 2 | ||||
-rw-r--r-- | sway/desktop/launcher.c | 211 | ||||
-rw-r--r-- | sway/desktop/layer_shell.c | 63 | ||||
-rw-r--r-- | sway/desktop/output.c | 155 | ||||
-rw-r--r-- | sway/desktop/render.c | 43 | ||||
-rw-r--r-- | sway/desktop/surface.c | 2 | ||||
-rw-r--r-- | sway/desktop/xdg_shell.c | 132 | ||||
-rw-r--r-- | sway/desktop/xwayland.c | 62 |
10 files changed, 594 insertions, 166 deletions
diff --git a/sway/desktop/desktop.c b/sway/desktop/desktop.c index ec45d80a..c8d4502c 100644 --- a/sway/desktop/desktop.c +++ b/sway/desktop/desktop.c @@ -6,10 +6,11 @@ void desktop_damage_surface(struct wlr_surface *surface, double lx, double ly, bool whole) { for (int i = 0; i < root->outputs->length; ++i) { struct sway_output *output = root->outputs->items[i]; - struct wlr_box *output_box = wlr_output_layout_get_box( - root->output_layout, output->wlr_output); - output_damage_surface(output, lx - output_box->x, - ly - output_box->y, surface, whole); + struct wlr_box output_box; + wlr_output_layout_get_box(root->output_layout, + output->wlr_output, &output_box); + output_damage_surface(output, lx - output_box.x, + ly - output_box.y, surface, whole); } } diff --git a/sway/desktop/fx_renderer.c b/sway/desktop/fx_renderer.c index 3b4279fe..782ceb88 100644 --- a/sway/desktop/fx_renderer.c +++ b/sway/desktop/fx_renderer.c @@ -35,6 +35,71 @@ static const GLfloat verts[] = { 0, 1, // bottom left }; +static const float transforms[][9] = { + [WL_OUTPUT_TRANSFORM_NORMAL] = { + 1.0f, 0.0f, 0.0f, + 0.0f, 1.0f, 0.0f, + 0.0f, 0.0f, 1.0f, + }, + [WL_OUTPUT_TRANSFORM_90] = { + 0.0f, 1.0f, 0.0f, + -1.0f, 0.0f, 0.0f, + 0.0f, 0.0f, 1.0f, + }, + [WL_OUTPUT_TRANSFORM_180] = { + -1.0f, 0.0f, 0.0f, + 0.0f, -1.0f, 0.0f, + 0.0f, 0.0f, 1.0f, + }, + [WL_OUTPUT_TRANSFORM_270] = { + 0.0f, -1.0f, 0.0f, + 1.0f, 0.0f, 0.0f, + 0.0f, 0.0f, 1.0f, + }, + [WL_OUTPUT_TRANSFORM_FLIPPED] = { + -1.0f, 0.0f, 0.0f, + 0.0f, 1.0f, 0.0f, + 0.0f, 0.0f, 1.0f, + }, + [WL_OUTPUT_TRANSFORM_FLIPPED_90] = { + 0.0f, 1.0f, 0.0f, + 1.0f, 0.0f, 0.0f, + 0.0f, 0.0f, 1.0f, + }, + [WL_OUTPUT_TRANSFORM_FLIPPED_180] = { + 1.0f, 0.0f, 0.0f, + 0.0f, -1.0f, 0.0f, + 0.0f, 0.0f, 1.0f, + }, + [WL_OUTPUT_TRANSFORM_FLIPPED_270] = { + 0.0f, -1.0f, 0.0f, + -1.0f, 0.0f, 0.0f, + 0.0f, 0.0f, 1.0f, + }, +}; + +static void matrix_projection(float mat[static 9], int width, int height, + enum wl_output_transform transform) { + memset(mat, 0, sizeof(*mat) * 9); + + const float *t = transforms[transform]; + float x = 2.0f / width; + float y = 2.0f / height; + + // Rotation + reflection + mat[0] = x * t[0]; + mat[1] = x * t[1]; + mat[3] = y * -t[3]; + mat[4] = y * -t[4]; + + // Translation + mat[2] = -copysign(1.0f, mat[0] + mat[1]); + mat[5] = -copysign(1.0f, mat[3] + mat[4]); + + // Identity + mat[8] = 1.0f; +} + static GLuint compile_shader(GLuint type, const GLchar *src) { GLuint shader = glCreateShader(type); glShaderSource(shader, 1, &src, NULL); @@ -156,7 +221,8 @@ struct fx_renderer *fx_renderer_create(struct wlr_egl *egl) { // TODO: wlr_egl_make_current or eglMakeCurrent? // TODO: assert instead of conditional statement? - if (!wlr_egl_make_current(egl)) { + if (!eglMakeCurrent(wlr_egl_get_display(egl), EGL_NO_SURFACE, EGL_NO_SURFACE, + wlr_egl_get_context(egl))) { sway_log(SWAY_ERROR, "GLES2 RENDERER: Could not make EGL current"); return NULL; } @@ -242,7 +308,11 @@ struct fx_renderer *fx_renderer_create(struct wlr_egl *egl) { goto error; } - wlr_egl_unset_current(renderer->egl); + if (!eglMakeCurrent(wlr_egl_get_display(renderer->egl), + EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT)) { + sway_log(SWAY_ERROR, "GLES2 RENDERER: Could not unset current EGL"); + goto error; + } sway_log(SWAY_INFO, "GLES2 RENDERER: Shaders Initialized Successfully"); return renderer; @@ -257,7 +327,10 @@ error: glDeleteProgram(renderer->shaders.tex_rgbx.program); glDeleteProgram(renderer->shaders.tex_ext.program); - wlr_egl_unset_current(renderer->egl); + if (!eglMakeCurrent(wlr_egl_get_display(renderer->egl), + EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT)) { + sway_log(SWAY_ERROR, "GLES2 RENDERER: Could not unset current EGL"); + } // TODO: more freeing? free(renderer); @@ -270,7 +343,7 @@ void fx_renderer_begin(struct fx_renderer *renderer, uint32_t width, uint32_t he glViewport(0, 0, width, height); // refresh projection matrix - wlr_matrix_projection(renderer->projection, width, height, + matrix_projection(renderer->projection, width, height, WL_OUTPUT_TRANSFORM_FLIPPED_180); glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA); diff --git a/sway/desktop/idle_inhibit_v1.c b/sway/desktop/idle_inhibit_v1.c index 82353038..3a4d0b87 100644 --- a/sway/desktop/idle_inhibit_v1.c +++ b/sway/desktop/idle_inhibit_v1.c @@ -1,5 +1,6 @@ #include <stdlib.h> #include <wlr/types/wlr_idle.h> +#include <wlr/types/wlr_idle_notify_v1.h> #include "log.h" #include "sway/desktop/idle_inhibit_v1.h" #include "sway/input/seat.h" @@ -140,6 +141,7 @@ void sway_idle_inhibit_v1_check_active( } } wlr_idle_set_enabled(manager->idle, NULL, !inhibited); + wlr_idle_notifier_v1_set_inhibited(server.idle_notifier_v1, inhibited); } struct sway_idle_inhibit_manager_v1 *sway_idle_inhibit_manager_v1_create( diff --git a/sway/desktop/launcher.c b/sway/desktop/launcher.c new file mode 100644 index 00000000..48e5d24c --- /dev/null +++ b/sway/desktop/launcher.c @@ -0,0 +1,211 @@ +#define _POSIX_C_SOURCE 200809L +#include <stdlib.h> +#include <string.h> +#include <wlr/types/wlr_xdg_activation_v1.h> +#include "sway/input/seat.h" +#include "sway/output.h" +#include "sway/desktop/launcher.h" +#include "sway/tree/node.h" +#include "sway/tree/container.h" +#include "sway/tree/workspace.h" +#include "sway/tree/root.h" +#include "log.h" + +/** + * Get the pid of a parent process given the pid of a child process. + * + * Returns the parent pid or NULL if the parent pid cannot be determined. + */ +static pid_t get_parent_pid(pid_t child) { + pid_t parent = -1; + char file_name[100]; + char *buffer = NULL; + const char *sep = " "; + FILE *stat = NULL; + size_t buf_size = 0; + + snprintf(file_name, sizeof(file_name), "/proc/%d/stat", child); + + if ((stat = fopen(file_name, "r"))) { + if (getline(&buffer, &buf_size, stat) != -1) { + strtok(buffer, sep); // pid + strtok(NULL, sep); // executable name + strtok(NULL, sep); // state + char *token = strtok(NULL, sep); // parent pid + parent = strtol(token, NULL, 10); + } + free(buffer); + fclose(stat); + } + + if (parent) { + return (parent == child) ? -1 : parent; + } + + return -1; +} + +void launcher_ctx_consume(struct launcher_ctx *ctx) { + // The view is now responsible for destroying this ctx + wl_list_remove(&ctx->token_destroy.link); + wl_list_init(&ctx->token_destroy.link); + + if (!ctx->activated) { + // An unactivated token hasn't been destroyed yet + wlr_xdg_activation_token_v1_destroy(ctx->token); + } + ctx->token = NULL; + + // Prevent additional matches + wl_list_remove(&ctx->link); + wl_list_init(&ctx->link); +} + +void launcher_ctx_destroy(struct launcher_ctx *ctx) { + if (ctx == NULL) { + return; + } + wl_list_remove(&ctx->node_destroy.link); + wl_list_remove(&ctx->token_destroy.link); + wl_list_remove(&ctx->link); + wlr_xdg_activation_token_v1_destroy(ctx->token); + free(ctx->name); + free(ctx); +} + +struct launcher_ctx *launcher_ctx_find_pid(pid_t pid) { + if (wl_list_empty(&server.pending_launcher_ctxs)) { + return NULL; + } + + struct launcher_ctx *ctx = NULL; + sway_log(SWAY_DEBUG, "Looking up workspace for pid %d", pid); + + do { + struct launcher_ctx *_ctx = NULL; + wl_list_for_each(_ctx, &server.pending_launcher_ctxs, link) { + if (pid == _ctx->pid) { + ctx = _ctx; + sway_log(SWAY_DEBUG, + "found %s match for pid %d: %s", + node_type_to_str(ctx->node->type), pid, node_get_name(ctx->node)); + break; + } + } + pid = get_parent_pid(pid); + } while (pid > 1); + + return ctx; +} + +struct sway_workspace *launcher_ctx_get_workspace( + struct launcher_ctx *ctx) { + struct sway_workspace *ws = NULL; + struct sway_output *output = NULL; + + switch (ctx->node->type) { + case N_CONTAINER: + // Unimplemented + // TODO: add container matching? + ws = ctx->node->sway_container->pending.workspace; + break; + case N_WORKSPACE: + ws = ctx->node->sway_workspace; + break; + case N_OUTPUT: + output = ctx->node->sway_output; + ws = workspace_by_name(ctx->name); + if (!ws) { + sway_log(SWAY_DEBUG, + "Creating workspace %s for pid %d because it disappeared", + ctx->name, ctx->pid); + if (!output->enabled) { + sway_log(SWAY_DEBUG, + "Workspace output %s is disabled, trying another one", + output->wlr_output->name); + output = NULL; + } + ws = workspace_create(output, ctx->name); + } + break; + case N_ROOT: + ws = workspace_create(NULL, ctx->name); + break; + } + + return ws; +} + +static void ctx_handle_node_destroy(struct wl_listener *listener, void *data) { + struct launcher_ctx *ctx = wl_container_of(listener, ctx, node_destroy); + switch (ctx->node->type) { + case N_CONTAINER: + // Unimplemented + break; + case N_WORKSPACE:; + struct sway_workspace *ws = ctx->node->sway_workspace; + wl_list_remove(&ctx->node_destroy.link); + wl_list_init(&ctx->node_destroy.link); + // We want to save this ws name to recreate later, hopefully on the + // same output + free(ctx->name); + ctx->name = strdup(ws->name); + if (!ws->output || ws->output->node.destroying) { + // If the output is being destroyed it would be pointless to track + // If the output is being disabled, we'll find out if it's still + // disabled when we try to match it. + ctx->node = &root->node; + break; + } + ctx->node = &ws->output->node; + wl_signal_add(&ctx->node->events.destroy, &ctx->node_destroy); + break; + case N_OUTPUT: + wl_list_remove(&ctx->node_destroy.link); + wl_list_init(&ctx->node_destroy.link); + // We'll make the ws ctx->name somewhere else + ctx->node = &root->node; + break; + case N_ROOT: + // Unreachable + break; + } +} + +static void token_handle_destroy(struct wl_listener *listener, void *data) { + struct launcher_ctx *ctx = wl_container_of(listener, ctx, token_destroy); + ctx->token = NULL; + launcher_ctx_destroy(ctx); +} + +struct launcher_ctx *launcher_ctx_create() { + struct sway_seat *seat = input_manager_current_seat(); + struct sway_workspace *ws = seat_get_focused_workspace(seat); + if (!ws) { + sway_log(SWAY_DEBUG, "Failed to create launch context. No workspace."); + return NULL; + } + + struct launcher_ctx *ctx = calloc(1, sizeof(struct launcher_ctx)); + struct wlr_xdg_activation_token_v1 *token = + wlr_xdg_activation_token_v1_create(server.xdg_activation_v1); + token->data = ctx; + ctx->name = strdup(ws->name); + ctx->token = token; + ctx->node = &ws->node; + + ctx->node_destroy.notify = ctx_handle_node_destroy; + wl_signal_add(&ctx->node->events.destroy, &ctx->node_destroy); + + ctx->token_destroy.notify = token_handle_destroy; + wl_signal_add(&token->events.destroy, &ctx->token_destroy); + + wl_list_init(&ctx->link); + wl_list_insert(&server.pending_launcher_ctxs, &ctx->link); + return ctx; +} + +const char *launcher_ctx_get_token_name(struct launcher_ctx *ctx) { + const char *token = wlr_xdg_activation_token_v1_get_name(ctx->token); + return token; +} diff --git a/sway/desktop/layer_shell.c b/sway/desktop/layer_shell.c index 27e457f1..6e3cc0e2 100644 --- a/sway/desktop/layer_shell.c +++ b/sway/desktop/layer_shell.c @@ -3,8 +3,8 @@ #include <string.h> #include <wayland-server-core.h> #include <wlr/types/wlr_layer_shell_v1.h> -#include <wlr/types/wlr_output_damage.h> #include <wlr/types/wlr_output.h> +#include <wlr/types/wlr_subcompositor.h> #include "log.h" #include "sway/desktop/transaction.h" #include "sway/input/cursor.h" @@ -270,10 +270,6 @@ static void handle_output_destroy(struct wl_listener *listener, void *data) { wl_resource_get_client(sway_layer->layer_surface->resource); bool set_focus = seat->exclusive_client == client; - wl_list_remove(&sway_layer->output_destroy.link); - wl_list_remove(&sway_layer->link); - wl_list_init(&sway_layer->link); - if (set_focus) { struct sway_layer_surface *layer = find_mapped_layer_by_client(client, sway_layer->layer_surface->output); @@ -282,7 +278,6 @@ static void handle_output_destroy(struct wl_listener *listener, void *data) { } } - sway_layer->layer_surface->output = NULL; wlr_layer_surface_v1_destroy(sway_layer->layer_surface); } @@ -291,10 +286,7 @@ static void handle_surface_commit(struct wl_listener *listener, void *data) { wl_container_of(listener, layer, surface_commit); struct wlr_layer_surface_v1 *layer_surface = layer->layer_surface; struct wlr_output *wlr_output = layer_surface->output; - if (wlr_output == NULL) { - return; - } - + sway_assert(wlr_output, "wlr_layer_surface_v1 has null output"); struct sway_output *output = wlr_output->data; struct wlr_box old_extent = layer->extent; @@ -341,13 +333,8 @@ static void unmap(struct sway_layer_surface *sway_layer) { cursor_rebase_all(); struct wlr_output *wlr_output = sway_layer->layer_surface->output; - if (wlr_output == NULL) { - return; - } + sway_assert(wlr_output, "wlr_layer_surface_v1 has null output"); struct sway_output *output = wlr_output->data; - if (output == NULL) { - return; - } output_damage_surface(output, sway_layer->geo.x, sway_layer->geo.y, sway_layer->layer_surface->surface, true); } @@ -375,22 +362,24 @@ static void handle_destroy(struct wl_listener *listener, void *data) { wl_list_remove(&sway_layer->surface_commit.link); wl_list_remove(&sway_layer->new_popup.link); wl_list_remove(&sway_layer->new_subsurface.link); - if (sway_layer->layer_surface->output != NULL) { - struct sway_output *output = sway_layer->layer_surface->output->data; - if (output != NULL) { - arrange_layers(output); - transaction_commit_dirty(); - } - wl_list_remove(&sway_layer->output_destroy.link); - sway_layer->layer_surface->output = NULL; - } + + struct wlr_output *wlr_output = sway_layer->layer_surface->output; + sway_assert(wlr_output, "wlr_layer_surface_v1 has null output"); + struct sway_output *output = wlr_output->data; + arrange_layers(output); + transaction_commit_dirty(); + wl_list_remove(&sway_layer->output_destroy.link); + sway_layer->layer_surface->output = NULL; + free(sway_layer); } static void handle_map(struct wl_listener *listener, void *data) { struct sway_layer_surface *sway_layer = wl_container_of(listener, sway_layer, map); - struct sway_output *output = sway_layer->layer_surface->output->data; + struct wlr_output *wlr_output = sway_layer->layer_surface->output; + sway_assert(wlr_output, "wlr_layer_surface_v1 has null output"); + struct sway_output *output = wlr_output->data; output_damage_surface(output, sway_layer->geo.x, sway_layer->geo.y, sway_layer->layer_surface->surface, true); wlr_surface_send_enter(sway_layer->layer_surface->surface, @@ -408,9 +397,7 @@ static void subsurface_damage(struct sway_layer_subsurface *subsurface, bool whole) { struct sway_layer_surface *layer = subsurface->layer_surface; struct wlr_output *wlr_output = layer->layer_surface->output; - if (!wlr_output) { - return; - } + sway_assert(wlr_output, "wlr_layer_surface_v1 has null output"); struct sway_output *output = wlr_output->data; int ox = subsurface->wlr_subsurface->current.x + layer->geo.x; int oy = subsurface->wlr_subsurface->current.y + layer->geo.y; @@ -496,15 +483,15 @@ static struct sway_layer_surface *popup_get_layer( static void popup_damage(struct sway_layer_popup *layer_popup, bool whole) { struct wlr_xdg_popup *popup = layer_popup->wlr_popup; struct wlr_surface *surface = popup->base->surface; - int popup_sx = popup->geometry.x - popup->base->current.geometry.x; - int popup_sy = popup->geometry.y - popup->base->current.geometry.y; + int popup_sx = popup->current.geometry.x - popup->base->current.geometry.x; + int popup_sy = popup->current.geometry.y - popup->base->current.geometry.y; int ox = popup_sx, oy = popup_sy; struct sway_layer_surface *layer; while (true) { if (layer_popup->parent_type == LAYER_PARENT_POPUP) { layer_popup = layer_popup->parent_popup; - ox += layer_popup->wlr_popup->geometry.x; - oy += layer_popup->wlr_popup->geometry.y; + ox += layer_popup->wlr_popup->current.geometry.x; + oy += layer_popup->wlr_popup->current.geometry.y; } else { layer = layer_popup->parent_layer; ox += layer->geo.x; @@ -513,6 +500,7 @@ static void popup_damage(struct sway_layer_popup *layer_popup, bool whole) { } } struct wlr_output *wlr_output = layer->layer_surface->output; + sway_assert(wlr_output, "wlr_layer_surface_v1 has null output"); struct sway_output *output = wlr_output->data; output_damage_surface(output, ox, oy, surface, whole); } @@ -521,6 +509,7 @@ static void popup_handle_map(struct wl_listener *listener, void *data) { struct sway_layer_popup *popup = wl_container_of(listener, popup, map); struct sway_layer_surface *layer = popup_get_layer(popup); struct wlr_output *wlr_output = layer->layer_surface->output; + sway_assert(wlr_output, "wlr_layer_surface_v1 has null output"); wlr_surface_send_enter(popup->wlr_popup->base->surface, wlr_output); popup_damage(popup, true); } @@ -550,7 +539,9 @@ static void popup_unconstrain(struct sway_layer_popup *popup) { struct sway_layer_surface *layer = popup_get_layer(popup); struct wlr_xdg_popup *wlr_popup = popup->wlr_popup; - struct sway_output *output = layer->layer_surface->output->data; + struct wlr_output *wlr_output = layer->layer_surface->output; + sway_assert(wlr_output, "wlr_layer_surface_v1 has null output"); + struct sway_output *output = wlr_output->data; // the output box expressed in the coordinate system of the toplevel parent // of the popup @@ -642,6 +633,10 @@ void handle_layer_shell_surface(struct wl_listener *listener, void *data) { sway_log(SWAY_ERROR, "no output to auto-assign layer surface '%s' to", layer_surface->namespace); + // Note that layer_surface->output can be NULL + // here, but none of our destroy callbacks are + // registered yet so we don't have to make them + // handle that case. wlr_layer_surface_v1_destroy(layer_surface); return; } diff --git a/sway/desktop/output.c b/sway/desktop/output.c index b6d19dd6..182ca428 100644 --- a/sway/desktop/output.c +++ b/sway/desktop/output.c @@ -10,11 +10,10 @@ #include <wlr/types/wlr_buffer.h> #include <wlr/types/wlr_drm_lease_v1.h> #include <wlr/types/wlr_matrix.h> -#include <wlr/types/wlr_output_damage.h> #include <wlr/types/wlr_output_layout.h> #include <wlr/types/wlr_output.h> #include <wlr/types/wlr_presentation_time.h> -#include <wlr/types/wlr_surface.h> +#include <wlr/types/wlr_compositor.h> #include <wlr/util/region.h> #include "config.h" #include "log.h" @@ -276,6 +275,25 @@ static void for_each_surface_container_iterator(struct sway_container *con, static void output_for_each_surface(struct sway_output *output, sway_surface_iterator_func_t iterator, void *user_data) { + if (server.session_lock.locked) { + if (server.session_lock.lock == NULL) { + return; + } + struct wlr_session_lock_surface_v1 *lock_surface; + wl_list_for_each(lock_surface, &server.session_lock.lock->surfaces, link) { + if (lock_surface->output != output->wlr_output) { + continue; + } + if (!lock_surface->mapped) { + continue; + } + + output_surface_for_each_surface(output, lock_surface->surface, + 0.0, 0.0, iterator, user_data); + } + return; + } + if (output_has_opaque_overlay_layer_surface(output)) { goto overlay; } @@ -435,6 +453,10 @@ static bool scan_out_fullscreen_view(struct sway_output *output, return false; } + if (server.session_lock.locked) { + return false; + } + if (!wl_list_empty(&view->saved_buffers)) { return false; } @@ -533,31 +555,43 @@ static int output_repaint_timer_handler(void *data) { } } - bool needs_frame; + int buffer_age; + if (!wlr_output_attach_render(output->wlr_output, &buffer_age)) { + return 0; + } + pixman_region32_t damage; pixman_region32_init(&damage); - if (!wlr_output_damage_attach_render(output->damage, - &needs_frame, &damage)) { + wlr_damage_ring_get_buffer_damage(&output->damage_ring, buffer_age, &damage); + if (!output->wlr_output->needs_frame && + !pixman_region32_not_empty(&output->damage_ring.current)) { + pixman_region32_fini(&damage); + wlr_output_rollback(output->wlr_output); return 0; } - if (needs_frame) { - struct timespec now; - clock_gettime(CLOCK_MONOTONIC, &now); + struct timespec now; + clock_gettime(CLOCK_MONOTONIC, &now); - output_render(output, &now, &damage); - } else { - wlr_output_rollback(output->wlr_output); - } + output_render(output, &now, &damage); pixman_region32_fini(&damage); return 0; } -static void damage_handle_frame(struct wl_listener *listener, void *user_data) { +static void handle_damage(struct wl_listener *listener, void *user_data) { + struct sway_output *output = + wl_container_of(listener, output, damage); + struct wlr_output_event_damage *event = user_data; + if (wlr_damage_ring_add(&output->damage_ring, event->damage)) { + wlr_output_schedule_frame(output->wlr_output); + } +} + +static void handle_frame(struct wl_listener *listener, void *user_data) { struct sway_output *output = - wl_container_of(listener, output, damage_frame); + wl_container_of(listener, output, frame); if (!output->enabled || !output->wlr_output->enabled) { return; } @@ -620,11 +654,18 @@ static void damage_handle_frame(struct wl_listener *listener, void *user_data) { send_frame_done(output, &data); } +static void handle_needs_frame(struct wl_listener *listener, void *user_data) { + struct sway_output *output = + wl_container_of(listener, output, needs_frame); + wlr_output_schedule_frame(output->wlr_output); +} + void output_damage_whole(struct sway_output *output) { // The output can exist with no wlr_output if it's just been disconnected // and the transaction to evacuate it has't completed yet. - if (output && output->wlr_output && output->damage) { - wlr_output_damage_add_whole(output->damage); + if (output != NULL && output->wlr_output != NULL) { + wlr_damage_ring_add_whole(&output->damage_ring); + wlr_output_schedule_frame(output->wlr_output); } } @@ -648,11 +689,15 @@ static void damage_surface_iterator(struct sway_output *output, ceil(output->wlr_output->scale) - surface->current.scale); } pixman_region32_translate(&damage, box.x, box.y); - wlr_output_damage_add(output->damage, &damage); + if (wlr_damage_ring_add(&output->damage_ring, &damage)) { + wlr_output_schedule_frame(output->wlr_output); + } pixman_region32_fini(&damage); if (whole) { - wlr_output_damage_add_box(output->damage, &box); + if (wlr_damage_ring_add_box(&output->damage_ring, &box)) { + wlr_output_schedule_frame(output->wlr_output); + } } if (!wl_list_empty(&surface->current.frame_callback_list)) { @@ -682,7 +727,9 @@ void output_damage_box(struct sway_output *output, struct wlr_box *_box) { box.x -= output->lx; box.y -= output->ly; scale_box(&box, output->wlr_output->scale); - wlr_output_damage_add_box(output->damage, &box); + if (wlr_damage_ring_add_box(&output->damage_ring, &box)) { + wlr_output_schedule_frame(output->wlr_output); + } } static void damage_child_views_iterator(struct sway_container *con, @@ -706,7 +753,9 @@ void output_damage_whole_container(struct sway_output *output, .height = con->current.height + 2, }; scale_box(&box, output->wlr_output->scale); - wlr_output_damage_add_box(output->damage, &box); + if (wlr_damage_ring_add_box(&output->damage_ring, &box)) { + wlr_output_schedule_frame(output->wlr_output); + } // Damage subsurfaces as well, which may extend outside the box if (con->view) { damage_child_views_iterator(con, output); @@ -715,20 +764,6 @@ void output_damage_whole_container(struct sway_output *output, } } -static void damage_handle_destroy(struct wl_listener *listener, void *data) { - struct sway_output *output = - wl_container_of(listener, output, damage_destroy); - if (!output->enabled) { - return; - } - output_disable(output); - - wl_list_remove(&output->damage_destroy.link); - wl_list_remove(&output->damage_frame.link); - - transaction_commit_dirty(); -} - static void update_output_manager_config(struct sway_server *server) { struct wlr_output_configuration_v1 *config = wlr_output_configuration_v1_create(); @@ -740,14 +775,15 @@ static void update_output_manager_config(struct sway_server *server) { } struct wlr_output_configuration_head_v1 *config_head = wlr_output_configuration_head_v1_create(config, output->wlr_output); - struct wlr_box *output_box = wlr_output_layout_get_box( - root->output_layout, output->wlr_output); - // We mark the output enabled even if it is switched off by DPMS + struct wlr_box output_box; + wlr_output_layout_get_box(root->output_layout, + output->wlr_output, &output_box); + // We mark the output enabled when it's switched off but not disabled config_head->state.enabled = output->current_mode != NULL && output->enabled; config_head->state.mode = output->current_mode; - if (output_box) { - config_head->state.x = output_box->x; - config_head->state.y = output_box->y; + if (!wlr_box_empty(&output_box)) { + config_head->state.x = output_box.x; + config_head->state.y = output_box.y; } } @@ -757,18 +793,24 @@ static void update_output_manager_config(struct sway_server *server) { static void handle_destroy(struct wl_listener *listener, void *data) { struct sway_output *output = wl_container_of(listener, output, destroy); struct sway_server *server = output->server; - output_begin_destroy(output); if (output->enabled) { output_disable(output); } + output_begin_destroy(output); + wl_list_remove(&output->link); wl_list_remove(&output->destroy.link); wl_list_remove(&output->commit.link); wl_list_remove(&output->mode.link); wl_list_remove(&output->present.link); + wl_list_remove(&output->damage.link); + wl_list_remove(&output->frame.link); + wl_list_remove(&output->needs_frame.link); + + wlr_damage_ring_finish(&output->damage_ring); output->wlr_output->data = NULL; output->wlr_output = NULL; @@ -796,10 +838,16 @@ static void handle_mode(struct wl_listener *listener, void *data) { if (!output->enabled) { return; } + arrange_layers(output); arrange_output(output); transaction_commit_dirty(); + int width, height; + wlr_output_transformed_resolution(output->wlr_output, &width, &height); + wlr_damage_ring_set_bounds(&output->damage_ring, width, height); + wlr_output_schedule_frame(output->wlr_output); + update_output_manager_config(output->server); } @@ -827,6 +875,13 @@ static void handle_commit(struct wl_listener *listener, void *data) { update_output_manager_config(output->server); } + + if (event->committed & (WLR_OUTPUT_STATE_MODE | WLR_OUTPUT_STATE_TRANSFORM)) { + int width, height; + wlr_output_transformed_resolution(output->wlr_output, &width, &height); + wlr_damage_ring_set_bounds(&output->damage_ring, width, height); + wlr_output_schedule_frame(output->wlr_output); + } } static void handle_present(struct wl_listener *listener, void *data) { @@ -862,10 +917,12 @@ void handle_new_output(struct wl_listener *listener, void *data) { if (wlr_output->non_desktop) { sway_log(SWAY_DEBUG, "Not configuring non-desktop output"); + struct sway_output_non_desktop *non_desktop = output_non_desktop_create(wlr_output); if (server->drm_lease_manager) { wlr_drm_lease_v1_manager_offer_output(server->drm_lease_manager, wlr_output); } + list_add(root->non_desktop_outputs, non_desktop); return; } @@ -880,7 +937,7 @@ void handle_new_output(struct wl_listener *listener, void *data) { return; } output->server = server; - output->damage = wlr_output_damage_create(wlr_output); + wlr_damage_ring_init(&output->damage_ring); wl_signal_add(&wlr_output->events.destroy, &output->destroy); output->destroy.notify = handle_destroy; @@ -890,10 +947,12 @@ void handle_new_output(struct wl_listener *listener, void *data) { output->mode.notify = handle_mode; wl_signal_add(&wlr_output->events.present, &output->present); output->present.notify = handle_present; - wl_signal_add(&output->damage->events.frame, &output->damage_frame); - output->damage_frame.notify = damage_handle_frame; - wl_signal_add(&output->damage->events.destroy, &output->damage_destroy); - output->damage_destroy.notify = damage_handle_destroy; + wl_signal_add(&wlr_output->events.damage, &output->damage); + output->damage.notify = handle_damage; + wl_signal_add(&wlr_output->events.frame, &output->frame); + output->frame.notify = handle_frame; + wl_signal_add(&wlr_output->events.needs_frame, &output->needs_frame); + output->needs_frame.notify = handle_needs_frame; output->repaint_timer = wl_event_loop_add_timer(server->wl_event_loop, output_repaint_timer_handler, output); @@ -1007,10 +1066,10 @@ void handle_output_power_manager_set_mode(struct wl_listener *listener, struct output_config *oc = new_output_config(output->wlr_output->name); switch (event->mode) { case ZWLR_OUTPUT_POWER_V1_MODE_OFF: - oc->dpms_state = DPMS_OFF; + oc->power = 0; break; case ZWLR_OUTPUT_POWER_V1_MODE_ON: - oc->dpms_state = DPMS_ON; + oc->power = 1; break; } oc = store_output_config(oc); diff --git a/sway/desktop/render.c b/sway/desktop/render.c index 6247d937..35e2150e 100644 --- a/sway/desktop/render.c +++ b/sway/desktop/render.c @@ -9,11 +9,11 @@ #include <wlr/render/gles2.h> #include <wlr/render/wlr_renderer.h> #include <wlr/types/wlr_buffer.h> +#include <wlr/types/wlr_damage_ring.h> #include <wlr/types/wlr_matrix.h> -#include <wlr/types/wlr_output_damage.h> #include <wlr/types/wlr_output_layout.h> #include <wlr/types/wlr_output.h> -#include <wlr/types/wlr_surface.h> +#include <wlr/types/wlr_compositor.h> #include <wlr/util/region.h> #include "log.h" #include "config.h" @@ -1301,6 +1301,41 @@ void output_render(struct sway_output *output, struct timespec *when, fx_renderer_clear((float[]){1, 1, 0, 1}); } + if (server.session_lock.locked) { + float clear_color[] = {0.0f, 0.0f, 0.0f, 1.0f}; + if (server.session_lock.lock == NULL) { + // abandoned lock -> red BG + clear_color[0] = 1.f; + } + int nrects; + pixman_box32_t *rects = pixman_region32_rectangles(damage, &nrects); + for (int i = 0; i < nrects; ++i) { + scissor_output(wlr_output, &rects[i]); + fx_renderer_clear(clear_color); + } + + if (server.session_lock.lock != NULL) { + struct render_data data = { + .damage = damage, + .deco_data = get_undecorated_decoration_data(), + }; + + struct wlr_session_lock_surface_v1 *lock_surface; + wl_list_for_each(lock_surface, &server.session_lock.lock->surfaces, link) { + if (lock_surface->output != wlr_output) { + continue; + } + if (!lock_surface->mapped) { + continue; + } + + output_surface_for_each_surface(output, lock_surface->surface, + 0.0, 0.0, render_surface_iterator, &data); + } + } + goto renderer_end; + } + if (output_has_opaque_overlay_layer_surface(output)) { goto render_overlay; } @@ -1409,7 +1444,7 @@ renderer_end: enum wl_output_transform transform = wlr_output_transform_invert(wlr_output->transform); - wlr_region_transform(&frame_damage, &output->damage->current, + wlr_region_transform(&frame_damage, &output->damage_ring.current, transform, width, height); if (debug.damage != DAMAGE_DEFAULT) { @@ -1423,5 +1458,7 @@ renderer_end: if (!wlr_output_commit(wlr_output)) { return; } + + wlr_damage_ring_rotate(&output->damage_ring); output->last_frame = *when; } diff --git a/sway/desktop/surface.c b/sway/desktop/surface.c index 767b2045..1d7b536d 100644 --- a/sway/desktop/surface.c +++ b/sway/desktop/surface.c @@ -1,7 +1,7 @@ #define _POSIX_C_SOURCE 200112L #include <stdlib.h> #include <time.h> -#include <wlr/types/wlr_surface.h> +#include <wlr/types/wlr_compositor.h> #include "sway/server.h" #include "sway/surface.h" diff --git a/sway/desktop/xdg_shell.c b/sway/desktop/xdg_shell.c index 5fae8296..8da922d5 100644 --- a/sway/desktop/xdg_shell.c +++ b/sway/desktop/xdg_shell.c @@ -24,11 +24,11 @@ static const struct sway_view_child_impl popup_impl; static void popup_get_view_coords(struct sway_view_child *child, int *sx, int *sy) { struct sway_xdg_popup *popup = (struct sway_xdg_popup *)child; - struct wlr_xdg_surface *surface = popup->wlr_xdg_surface; + struct wlr_xdg_popup *wlr_popup = popup->wlr_xdg_popup; - wlr_xdg_popup_get_toplevel_coords(surface->popup, - surface->popup->geometry.x - surface->current.geometry.x, - surface->popup->geometry.y - surface->current.geometry.y, + wlr_xdg_popup_get_toplevel_coords(wlr_popup, + wlr_popup->current.geometry.x - wlr_popup->base->current.geometry.x, + wlr_popup->current.geometry.y - wlr_popup->base->current.geometry.y, sx, sy); } @@ -65,7 +65,7 @@ static void popup_handle_destroy(struct wl_listener *listener, void *data) { static void popup_unconstrain(struct sway_xdg_popup *popup) { struct sway_view *view = popup->child.view; - struct wlr_xdg_popup *wlr_popup = popup->wlr_xdg_surface->popup; + struct wlr_xdg_popup *wlr_popup = popup->wlr_xdg_popup; struct sway_output *output = view->container->pending.workspace->output; @@ -91,7 +91,7 @@ static struct sway_xdg_popup *popup_create( return NULL; } view_child_init(&popup->child, &popup_impl, view, xdg_surface->surface); - popup->wlr_xdg_surface = xdg_surface; + popup->wlr_xdg_popup = xdg_surface->popup; wl_signal_add(&xdg_surface->events.new_popup, &popup->new_popup); popup->new_popup.notify = popup_handle_new_popup; @@ -119,7 +119,7 @@ static struct sway_xdg_shell_view *xdg_shell_view_from_view( static void get_constraints(struct sway_view *view, double *min_width, double *max_width, double *min_height, double *max_height) { struct wlr_xdg_toplevel_state *state = - &view->wlr_xdg_surface->toplevel->current; + &view->wlr_xdg_toplevel->current; *min_width = state->min_width > 0 ? state->min_width : DBL_MIN; *max_width = state->max_width > 0 ? state->max_width : DBL_MAX; *min_height = state->min_height > 0 ? state->min_height : DBL_MIN; @@ -133,9 +133,9 @@ static const char *get_string_prop(struct sway_view *view, } switch (prop) { case VIEW_PROP_TITLE: - return view->wlr_xdg_surface->toplevel->title; + return view->wlr_xdg_toplevel->title; case VIEW_PROP_APP_ID: - return view->wlr_xdg_surface->toplevel->app_id; + return view->wlr_xdg_toplevel->app_id; default: return NULL; } @@ -148,50 +148,45 @@ static uint32_t configure(struct sway_view *view, double lx, double ly, if (xdg_shell_view == NULL) { return 0; } - return wlr_xdg_toplevel_set_size(view->wlr_xdg_surface, width, height); + return wlr_xdg_toplevel_set_size(view->wlr_xdg_toplevel, + width, height); } static void set_activated(struct sway_view *view, bool activated) { if (xdg_shell_view_from_view(view) == NULL) { return; } - struct wlr_xdg_surface *surface = view->wlr_xdg_surface; - if (surface->role == WLR_XDG_SURFACE_ROLE_TOPLEVEL) { - wlr_xdg_toplevel_set_activated(surface, activated); - } + wlr_xdg_toplevel_set_activated(view->wlr_xdg_toplevel, activated); } static void set_tiled(struct sway_view *view, bool tiled) { if (xdg_shell_view_from_view(view) == NULL) { return; } - struct wlr_xdg_surface *surface = view->wlr_xdg_surface; enum wlr_edges edges = WLR_EDGE_NONE; if (tiled) { edges = WLR_EDGE_LEFT | WLR_EDGE_RIGHT | WLR_EDGE_TOP | WLR_EDGE_BOTTOM; } - wlr_xdg_toplevel_set_tiled(surface, edges); + wlr_xdg_toplevel_set_tiled(view->wlr_xdg_toplevel, edges); } static void set_fullscreen(struct sway_view *view, bool fullscreen) { if (xdg_shell_view_from_view(view) == NULL) { return; } - struct wlr_xdg_surface *surface = view->wlr_xdg_surface; - wlr_xdg_toplevel_set_fullscreen(surface, fullscreen); + wlr_xdg_toplevel_set_fullscreen(view->wlr_xdg_toplevel, fullscreen); } static void set_resizing(struct sway_view *view, bool resizing) { if (xdg_shell_view_from_view(view) == NULL) { return; } - struct wlr_xdg_surface *surface = view->wlr_xdg_surface; - wlr_xdg_toplevel_set_resizing(surface, resizing); + wlr_xdg_toplevel_set_resizing(view->wlr_xdg_toplevel, resizing); } static bool wants_floating(struct sway_view *view) { - struct wlr_xdg_toplevel *toplevel = view->wlr_xdg_surface->toplevel; + struct wlr_xdg_toplevel *toplevel = view->wlr_xdg_toplevel; struct wlr_xdg_toplevel_state *state = &toplevel->current; return (state->min_width != 0 && state->min_height != 0 && (state->min_width == state->max_width @@ -204,7 +199,7 @@ static void for_each_surface(struct sway_view *view, if (xdg_shell_view_from_view(view) == NULL) { return; } - wlr_xdg_surface_for_each_surface(view->wlr_xdg_surface, iterator, + wlr_xdg_surface_for_each_surface(view->wlr_xdg_toplevel->base, iterator, user_data); } @@ -213,8 +208,8 @@ static void for_each_popup_surface(struct sway_view *view, if (xdg_shell_view_from_view(view) == NULL) { return; } - wlr_xdg_surface_for_each_popup_surface(view->wlr_xdg_surface, iterator, - user_data); + wlr_xdg_surface_for_each_popup_surface(view->wlr_xdg_toplevel->base, + iterator, user_data); } static bool is_transient_for(struct sway_view *child, @@ -222,12 +217,12 @@ static bool is_transient_for(struct sway_view *child, if (xdg_shell_view_from_view(child) == NULL) { return false; } - struct wlr_xdg_surface *surface = child->wlr_xdg_surface; - while (surface && surface->role == WLR_XDG_SURFACE_ROLE_TOPLEVEL) { - if (surface->toplevel->parent == ancestor->wlr_xdg_surface) { + struct wlr_xdg_toplevel *toplevel = child->wlr_xdg_toplevel; + while (toplevel) { + if (toplevel->parent == ancestor->wlr_xdg_toplevel) { return true; } - surface = surface->toplevel->parent; + toplevel = toplevel->parent; } return false; } @@ -236,17 +231,13 @@ static void _close(struct sway_view *view) { if (xdg_shell_view_from_view(view) == NULL) { return; } - struct wlr_xdg_surface *surface = view->wlr_xdg_surface; - if (surface->role == WLR_XDG_SURFACE_ROLE_TOPLEVEL - && surface->toplevel) { - wlr_xdg_toplevel_send_close(surface); - } + wlr_xdg_toplevel_send_close(view->wlr_xdg_toplevel); } static void close_popups(struct sway_view *view) { struct wlr_xdg_popup *popup, *tmp; - wl_list_for_each_safe(popup, tmp, &view->wlr_xdg_surface->popups, link) { - wlr_xdg_popup_destroy(popup->base); + wl_list_for_each_safe(popup, tmp, &view->wlr_xdg_toplevel->base->popups, link) { + wlr_xdg_popup_destroy(popup); } } @@ -280,7 +271,7 @@ static void handle_commit(struct wl_listener *listener, void *data) { struct sway_xdg_shell_view *xdg_shell_view = wl_container_of(listener, xdg_shell_view, commit); struct sway_view *view = &xdg_shell_view->view; - struct wlr_xdg_surface *xdg_surface = view->wlr_xdg_surface; + struct wlr_xdg_surface *xdg_surface = view->wlr_xdg_toplevel->base; struct wlr_box new_geo; wlr_xdg_surface_get_geometry(xdg_surface, &new_geo); @@ -334,26 +325,27 @@ static void handle_new_popup(struct wl_listener *listener, void *data) { popup_create(wlr_popup, &xdg_shell_view->view); } +static void handle_request_maximize(struct wl_listener *listener, void *data) { + struct sway_xdg_shell_view *xdg_shell_view = + wl_container_of(listener, xdg_shell_view, request_maximize); + struct wlr_xdg_toplevel *toplevel = xdg_shell_view->view.wlr_xdg_toplevel; + wlr_xdg_surface_schedule_configure(toplevel->base); +} + static void handle_request_fullscreen(struct wl_listener *listener, void *data) { struct sway_xdg_shell_view *xdg_shell_view = wl_container_of(listener, xdg_shell_view, request_fullscreen); - struct wlr_xdg_toplevel_set_fullscreen_event *e = data; - struct wlr_xdg_surface *xdg_surface = - xdg_shell_view->view.wlr_xdg_surface; + struct wlr_xdg_toplevel *toplevel = xdg_shell_view->view.wlr_xdg_toplevel; struct sway_view *view = &xdg_shell_view->view; - if (!sway_assert(xdg_surface->role == WLR_XDG_SURFACE_ROLE_TOPLEVEL, - "xdg_shell requested fullscreen of surface with role %i", - xdg_surface->role)) { - return; - } - if (!xdg_surface->mapped) { + if (!toplevel->base->mapped) { return; } struct sway_container *container = view->container; - if (e->fullscreen && e->output && e->output->data) { - struct sway_output *output = e->output->data; + struct wlr_xdg_toplevel_requested *req = &toplevel->requested; + if (req->fullscreen && req->fullscreen_output && req->fullscreen_output->data) { + struct sway_output *output = req->fullscreen_output->data; struct sway_workspace *ws = output_get_active_workspace(output); if (ws && !container_is_scratchpad_hidden(container) && container->pending.workspace != ws) { @@ -365,7 +357,7 @@ static void handle_request_fullscreen(struct wl_listener *listener, void *data) } } - container_set_fullscreen(container, e->fullscreen); + container_set_fullscreen(container, req->fullscreen); arrange_root(); transaction_commit_dirty(); @@ -375,7 +367,8 @@ static void handle_request_move(struct wl_listener *listener, void *data) { struct sway_xdg_shell_view *xdg_shell_view = wl_container_of(listener, xdg_shell_view, request_move); struct sway_view *view = &xdg_shell_view->view; - if (!container_is_floating(view->container)) { + if (!container_is_floating(view->container) || + view->container->pending.fullscreen_mode) { return; } struct wlr_xdg_toplevel_move_event *e = data; @@ -412,6 +405,7 @@ static void handle_unmap(struct wl_listener *listener, void *data) { wl_list_remove(&xdg_shell_view->commit.link); wl_list_remove(&xdg_shell_view->new_popup.link); + wl_list_remove(&xdg_shell_view->request_maximize.link); wl_list_remove(&xdg_shell_view->request_fullscreen.link); wl_list_remove(&xdg_shell_view->request_move.link); wl_list_remove(&xdg_shell_view->request_resize.link); @@ -423,13 +417,13 @@ static void handle_map(struct wl_listener *listener, void *data) { struct sway_xdg_shell_view *xdg_shell_view = wl_container_of(listener, xdg_shell_view, map); struct sway_view *view = &xdg_shell_view->view; - struct wlr_xdg_surface *xdg_surface = view->wlr_xdg_surface; + struct wlr_xdg_toplevel *toplevel = view->wlr_xdg_toplevel; - view->natural_width = view->wlr_xdg_surface->current.geometry.width; - view->natural_height = view->wlr_xdg_surface->current.geometry.height; + view->natural_width = toplevel->base->current.geometry.width; + view->natural_height = toplevel->base->current.geometry.height; if (!view->natural_width && !view->natural_height) { - view->natural_width = view->wlr_xdg_surface->surface->current.width; - view->natural_height = view->wlr_xdg_surface->surface->current.height; + view->natural_width = toplevel->base->surface->current.width; + view->natural_height = toplevel->base->surface->current.height; } bool csd = false; @@ -440,44 +434,48 @@ static void handle_map(struct wl_listener *listener, void *data) { csd = mode == WLR_XDG_TOPLEVEL_DECORATION_V1_MODE_CLIENT_SIDE; } else { struct sway_server_decoration *deco = - decoration_from_surface(xdg_surface->surface); + decoration_from_surface(toplevel->base->surface); csd = !deco || deco->wlr_server_decoration->mode == WLR_SERVER_DECORATION_MANAGER_MODE_CLIENT; } - view_map(view, view->wlr_xdg_surface->surface, - xdg_surface->toplevel->requested.fullscreen, - xdg_surface->toplevel->requested.fullscreen_output, + view_map(view, toplevel->base->surface, + toplevel->requested.fullscreen, + toplevel->requested.fullscreen_output, csd); transaction_commit_dirty(); xdg_shell_view->commit.notify = handle_commit; - wl_signal_add(&xdg_surface->surface->events.commit, + wl_signal_add(&toplevel->base->surface->events.commit, &xdg_shell_view->commit); xdg_shell_view->new_popup.notify = handle_new_popup; - wl_signal_add(&xdg_surface->events.new_popup, + wl_signal_add(&toplevel->base->events.new_popup, &xdg_shell_view->new_popup); + xdg_shell_view->request_maximize.notify = handle_request_maximize; + wl_signal_add(&toplevel->events.request_maximize, + &xdg_shell_view->request_maximize); + xdg_shell_view->request_fullscreen.notify = handle_request_fullscreen; - wl_signal_add(&xdg_surface->toplevel->events.request_fullscreen, + wl_signal_add(&toplevel->events.request_fullscreen, &xdg_shell_view->request_fullscreen); xdg_shell_view->request_move.notify = handle_request_move; - wl_signal_add(&xdg_surface->toplevel->events.request_move, + wl_signal_add(&toplevel->events.request_move, &xdg_shell_view->request_move); xdg_shell_view->request_resize.notify = handle_request_resize; - wl_signal_add(&xdg_surface->toplevel->events.request_resize, + wl_signal_add(&toplevel->events.request_resize, &xdg_shell_view->request_resize); xdg_shell_view->set_title.notify = handle_set_title; - wl_signal_add(&xdg_surface->toplevel->events.set_title, + wl_signal_add(&toplevel->events.set_title, &xdg_shell_view->set_title); xdg_shell_view->set_app_id.notify = handle_set_app_id; - wl_signal_add(&xdg_surface->toplevel->events.set_app_id, + wl_signal_add(&toplevel->events.set_app_id, &xdg_shell_view->set_app_id); } @@ -491,7 +489,7 @@ static void handle_destroy(struct wl_listener *listener, void *data) { wl_list_remove(&xdg_shell_view->destroy.link); wl_list_remove(&xdg_shell_view->map.link); wl_list_remove(&xdg_shell_view->unmap.link); - view->wlr_xdg_surface = NULL; + view->wlr_xdg_toplevel = NULL; if (view->xdg_decoration) { view->xdg_decoration->view = NULL; } @@ -522,7 +520,7 @@ void handle_xdg_shell_surface(struct wl_listener *listener, void *data) { } view_init(&xdg_shell_view->view, SWAY_VIEW_XDG_SHELL, &view_impl); - xdg_shell_view->view.wlr_xdg_surface = xdg_surface; + xdg_shell_view->view.wlr_xdg_toplevel = xdg_surface->toplevel; xdg_shell_view->map.notify = handle_map; wl_signal_add(&xdg_surface->events.map, &xdg_shell_view->map); diff --git a/sway/desktop/xwayland.c b/sway/desktop/xwayland.c index 40288f97..e15a3341 100644 --- a/sway/desktop/xwayland.c +++ b/sway/desktop/xwayland.c @@ -5,7 +5,9 @@ #include <wayland-server-core.h> #include <wlr/types/wlr_output_layout.h> #include <wlr/types/wlr_output.h> +#include <wlr/types/wlr_xdg_activation_v1.h> #include <wlr/xwayland.h> +#include <xcb/xcb_icccm.h> #include "log.h" #include "sway/desktop.h" #include "sway/desktop/transaction.h" @@ -15,6 +17,7 @@ #include "sway/output.h" #include "sway/tree/arrange.h" #include "sway/tree/container.h" +#include "sway/server.h" #include "sway/tree/view.h" #include "sway/tree/workspace.h" @@ -121,6 +124,20 @@ static void unmanaged_handle_unmap(struct wl_listener *listener, void *data) { } } +static void unmanaged_handle_request_activate(struct wl_listener *listener, void *data) { + struct wlr_xwayland_surface *xsurface = data; + if (!xsurface->mapped) { + return; + } + struct sway_seat *seat = input_manager_current_seat(); + struct sway_container *focus = seat_get_focused_container(seat); + if (focus && focus->view && focus->view->pid != xsurface->pid) { + return; + } + + seat_set_focus_surface(seat, xsurface->surface, false); +} + static void unmanaged_handle_destroy(struct wl_listener *listener, void *data) { struct sway_xwayland_unmanaged *surface = wl_container_of(listener, surface, destroy); @@ -129,6 +146,7 @@ static void unmanaged_handle_destroy(struct wl_listener *listener, void *data) { wl_list_remove(&surface->unmap.link); wl_list_remove(&surface->destroy.link); wl_list_remove(&surface->override_redirect.link); + wl_list_remove(&surface->request_activate.link); free(surface); } @@ -176,6 +194,8 @@ static struct sway_xwayland_unmanaged *create_unmanaged( surface->destroy.notify = unmanaged_handle_destroy; wl_signal_add(&xsurface->events.set_override_redirect, &surface->override_redirect); surface->override_redirect.notify = unmanaged_handle_override_redirect; + wl_signal_add(&xsurface->events.request_activate, &surface->request_activate); + surface->request_activate.notify = unmanaged_handle_request_activate; return surface; } @@ -294,7 +314,7 @@ static bool wants_floating(struct sway_view *view) { } } - struct wlr_xwayland_surface_size_hints *size_hints = surface->size_hints; + xcb_size_hints_t *size_hints = surface->size_hints; if (size_hints != NULL && size_hints->min_width > 0 && size_hints->min_height > 0 && (size_hints->max_width == size_hints->min_width || @@ -348,7 +368,7 @@ static void destroy(struct sway_view *view) { static void get_constraints(struct sway_view *view, double *min_width, double *max_width, double *min_height, double *max_height) { struct wlr_xwayland_surface *surface = view->wlr_xwayland_surface; - struct wlr_xwayland_surface_size_hints *size_hints = surface->size_hints; + xcb_size_hints_t *size_hints = surface->size_hints; if (size_hints == NULL) { *min_width = DBL_MIN; @@ -448,6 +468,7 @@ static void handle_destroy(struct wl_listener *listener, void *data) { wl_list_remove(&xwayland_view->set_title.link); wl_list_remove(&xwayland_view->set_class.link); wl_list_remove(&xwayland_view->set_role.link); + wl_list_remove(&xwayland_view->set_startup_id.link); wl_list_remove(&xwayland_view->set_window_type.link); wl_list_remove(&xwayland_view->set_hints.link); wl_list_remove(&xwayland_view->set_decorations.link); @@ -577,7 +598,8 @@ static void handle_request_move(struct wl_listener *listener, void *data) { if (!xsurface->mapped) { return; } - if (!container_is_floating(view->container)) { + if (!container_is_floating(view->container) || + view->container->pending.fullscreen_mode) { return; } struct sway_seat *seat = input_manager_current_seat(); @@ -647,6 +669,31 @@ static void handle_set_role(struct wl_listener *listener, void *data) { view_execute_criteria(view); } +static void handle_set_startup_id(struct wl_listener *listener, void *data) { + struct sway_xwayland_view *xwayland_view = + wl_container_of(listener, xwayland_view, set_startup_id); + struct sway_view *view = &xwayland_view->view; + struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface; + if (xsurface->startup_id == NULL) { + return; + } + + struct wlr_xdg_activation_token_v1 *token = + wlr_xdg_activation_v1_find_token( + server.xdg_activation_v1, xsurface->startup_id); + if (token == NULL) { + // Tried to activate with an unknown or expired token + return; + } + + struct launcher_ctx *ctx = token->data; + if (token->data == NULL) { + // TODO: support external launchers in X + return; + } + view_assign_ctx(view, ctx); +} + static void handle_set_window_type(struct wl_listener *listener, void *data) { struct sway_xwayland_view *xwayland_view = wl_container_of(listener, xwayland_view, set_window_type); @@ -666,14 +713,15 @@ static void handle_set_hints(struct wl_listener *listener, void *data) { if (!xsurface->mapped) { return; } - if (!xsurface->hints_urgency && view->urgent_timer) { + const bool hints_urgency = xcb_icccm_wm_hints_get_urgency(xsurface->hints); + if (!hints_urgency && view->urgent_timer) { // The view is in the timeout period. We'll ignore the request to // unset urgency so that the view remains urgent until the timer clears // it. return; } if (view->allow_request_urgent) { - view_set_urgent(view, (bool)xsurface->hints_urgency); + view_set_urgent(view, hints_urgency); } } @@ -731,6 +779,10 @@ struct sway_xwayland_view *create_xwayland_view(struct wlr_xwayland_surface *xsu wl_signal_add(&xsurface->events.set_role, &xwayland_view->set_role); xwayland_view->set_role.notify = handle_set_role; + wl_signal_add(&xsurface->events.set_startup_id, + &xwayland_view->set_startup_id); + xwayland_view->set_startup_id.notify = handle_set_startup_id; + wl_signal_add(&xsurface->events.set_window_type, &xwayland_view->set_window_type); xwayland_view->set_window_type.notify = handle_set_window_type; |