diff options
Diffstat (limited to 'sway/desktop/output.c')
-rw-r--r-- | sway/desktop/output.c | 335 |
1 files changed, 238 insertions, 97 deletions
diff --git a/sway/desktop/output.c b/sway/desktop/output.c index ca883144..2ccb28f2 100644 --- a/sway/desktop/output.c +++ b/sway/desktop/output.c @@ -1,15 +1,18 @@ #define _POSIX_C_SOURCE 200809L #include <assert.h> +#include <scenefx/render/pass.h> +#include <scenefx/render/fx_renderer/fx_effect_framebuffers.h> #include <stdlib.h> #include <strings.h> #include <time.h> #include <wayland-server-core.h> -#include <wlr/backend/drm.h> +#include <wlr/config.h> #include <wlr/backend/headless.h> +#include <wlr/render/swapchain.h> #include <wlr/render/gles2.h> #include <wlr/render/wlr_renderer.h> #include <wlr/types/wlr_buffer.h> -#include <wlr/types/wlr_drm_lease_v1.h> +#include <wlr/types/wlr_gamma_control_v1.h> #include <wlr/types/wlr_matrix.h> #include <wlr/types/wlr_output_layout.h> #include <wlr/types/wlr_output.h> @@ -18,10 +21,12 @@ #include <wlr/util/region.h> #include "config.h" #include "log.h" +#include "scenefx/render/pass.h" #include "sway/config.h" #include "sway/desktop/transaction.h" #include "sway/input/input-manager.h" #include "sway/input/seat.h" +#include "sway/ipc-server.h" #include "sway/layers.h" #include "sway/output.h" #include "sway/server.h" @@ -32,13 +37,27 @@ #include "sway/tree/view.h" #include "sway/tree/workspace.h" +#if WLR_HAS_DRM_BACKEND +#include <wlr/backend/drm.h> +#include <wlr/types/wlr_drm_lease_v1.h> +#endif + +bool output_match_name_or_id(struct sway_output *output, + const char *name_or_id) { + if (strcmp(name_or_id, "*") == 0) { + return true; + } + + char identifier[128]; + output_get_identifier(identifier, sizeof(identifier), output); + return strcasecmp(identifier, name_or_id) == 0 + || strcasecmp(output->wlr_output->name, name_or_id) == 0; +} + struct sway_output *output_by_name_or_id(const char *name_or_id) { for (int i = 0; i < root->outputs->length; ++i) { struct sway_output *output = root->outputs->items[i]; - char identifier[128]; - output_get_identifier(identifier, sizeof(identifier), output); - if (strcasecmp(identifier, name_or_id) == 0 - || strcasecmp(output->wlr_output->name, name_or_id) == 0) { + if (output_match_name_or_id(output, name_or_id)) { return output; } } @@ -48,10 +67,7 @@ struct sway_output *output_by_name_or_id(const char *name_or_id) { struct sway_output *all_output_by_name_or_id(const char *name_or_id) { struct sway_output *output; wl_list_for_each(output, &root->all_outputs, link) { - char identifier[128]; - output_get_identifier(identifier, sizeof(identifier), output); - if (strcasecmp(identifier, name_or_id) == 0 - || strcasecmp(output->wlr_output->name, name_or_id) == 0) { + if (output_match_name_or_id(output, name_or_id)) { return output; } } @@ -262,7 +278,7 @@ void output_drag_icons_for_each_surface(struct sway_output *output, double ox = drag_icon->x - output->lx; double oy = drag_icon->y - output->ly; - if (drag_icon->wlr_drag_icon->mapped) { + if (drag_icon->wlr_drag_icon->surface->mapped) { output_surface_for_each_surface(output, drag_icon->wlr_drag_icon->surface, ox, oy, iterator, user_data); @@ -292,7 +308,7 @@ static void output_for_each_surface(struct sway_output *output, if (lock_surface->output != output->wlr_output) { continue; } - if (!lock_surface->mapped) { + if (!lock_surface->surface->mapped) { continue; } @@ -369,14 +385,14 @@ overlay: } static int scale_length(int length, int offset, float scale) { - return round((offset + length) * scale) - round(offset * scale); + return roundf((offset + length) * scale) - roundf(offset * scale); } void scale_box(struct wlr_box *box, float scale) { box->width = scale_length(box->width, box->x, scale); box->height = scale_length(box->height, box->y, scale); - box->x = round(box->x * scale); - box->y = round(box->y * scale); + box->x = roundf(box->x * scale); + box->y = roundf(box->y * scale); } struct sway_workspace *output_get_active_workspace(struct sway_output *output) { @@ -454,7 +470,7 @@ static void count_surface_iterator(struct sway_output *output, } static bool scan_out_fullscreen_view(struct sway_output *output, - struct sway_view *view) { + struct wlr_output_state *pending, struct sway_view *view) { struct wlr_output *wlr_output = output->wlr_output; struct sway_workspace *workspace = output->current.active_workspace; if (!sway_assert(workspace, "Expected an active workspace")) { @@ -500,6 +516,12 @@ static bool scan_out_fullscreen_view(struct sway_output *output, if (n_surfaces != 1) { return false; } + size_t n_popups = 0; + output_view_for_each_popup_surface(output, view, + count_surface_iterator, &n_popups); + if (n_popups > 0) { + return false; + } if (surface->buffer == NULL) { return false; @@ -510,24 +532,56 @@ static bool scan_out_fullscreen_view(struct sway_output *output, return false; } - wlr_output_attach_buffer(wlr_output, &surface->buffer->base); - if (!wlr_output_test(wlr_output)) { + if (!wlr_output_is_direct_scanout_allowed(wlr_output)) { return false; } - wlr_presentation_surface_sampled_on_output(server.presentation, surface, + wlr_output_state_set_buffer(pending, &surface->buffer->base); + + if (!wlr_output_test_state(wlr_output, pending)) { + return false; + } + + wlr_presentation_surface_scanned_out_on_output(server.presentation, surface, wlr_output); - return wlr_output_commit(wlr_output); + return wlr_output_commit_state(wlr_output, pending); +} + +static void get_frame_damage(struct sway_output *output, + pixman_region32_t *frame_damage) { + struct wlr_output *wlr_output = output->wlr_output; + + int width, height; + wlr_output_transformed_resolution(wlr_output, &width, &height); + + pixman_region32_init(frame_damage); + + enum wl_output_transform transform = + wlr_output_transform_invert(wlr_output->transform); + wlr_region_transform(frame_damage, &output->damage_ring.current, + transform, width, height); + + if (debug.damage != DAMAGE_DEFAULT) { + pixman_region32_union_rect(frame_damage, frame_damage, + 0, 0, wlr_output->width, wlr_output->height); + } } static int output_repaint_timer_handler(void *data) { struct sway_output *output = data; - if (output->wlr_output == NULL) { + struct wlr_output *wlr_output = output->wlr_output; + if (wlr_output == NULL) { return 0; } - output->wlr_output->frame_pending = false; + wlr_output->frame_pending = false; + + if (!wlr_output->needs_frame && + !output->gamma_lut_changed && + !pixman_region32_not_empty(&output->damage_ring.current)) { + return 0; + } struct sway_workspace *workspace = output->current.active_workspace; if (workspace == NULL) { @@ -539,13 +593,33 @@ static int output_repaint_timer_handler(void *data) { fullscreen_con = workspace->current.fullscreen; } + struct wlr_output_state pending = {0}; + + if (output->gamma_lut_changed) { + output->gamma_lut_changed = false; + struct wlr_gamma_control_v1 *gamma_control = + wlr_gamma_control_manager_v1_get_control( + server.gamma_control_manager_v1, wlr_output); + if (!wlr_gamma_control_v1_apply(gamma_control, &pending)) { + goto out; + } + if (!wlr_output_test_state(wlr_output, &pending)) { + wlr_output_state_finish(&pending); + pending = (struct wlr_output_state){0}; + wlr_gamma_control_v1_send_failed_and_destroy(gamma_control); + } + } + + pending.committed |= WLR_OUTPUT_STATE_DAMAGE; + get_frame_damage(output, &pending.damage); + if (fullscreen_con && fullscreen_con->view && !debug.noscanout // Only output to monitor without compositing when saturation is changed && fullscreen_con->saturation == 1.0f) { // Try to scan-out the fullscreen view static bool last_scanned_out = false; bool scanned_out = - scan_out_fullscreen_view(output, fullscreen_con->view); + scan_out_fullscreen_view(output, &pending, fullscreen_con->view); if (scanned_out && !last_scanned_out) { sway_log(SWAY_DEBUG, "Scanning out fullscreen view on %s", @@ -559,32 +633,72 @@ static int output_repaint_timer_handler(void *data) { last_scanned_out = scanned_out; if (scanned_out) { - return 0; + goto out; } } + if (!wlr_output_configure_primary_swapchain(wlr_output, &pending, &wlr_output->swapchain)) { + goto out; + } + int buffer_age; - if (!wlr_output_attach_render(output->wlr_output, &buffer_age)) { - return 0; + struct wlr_buffer *buffer = wlr_swapchain_acquire(wlr_output->swapchain, &buffer_age); + if (buffer == NULL) { + goto out; + } + + struct fx_gles_render_pass *render_pass = fx_renderer_begin_buffer_pass( + wlr_output->renderer, buffer, wlr_output, + &(struct wlr_buffer_pass_options) { + .timer = NULL, + } + ); + if (render_pass == NULL) { + wlr_buffer_unlock(buffer); + goto out; } pixman_region32_t damage; pixman_region32_init(&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 (debug.damage == DAMAGE_RERENDER) { + int width, height; + wlr_output_transformed_resolution(wlr_output, &width, &height); + pixman_region32_union_rect(&damage, &damage, 0, 0, width, height); } + struct fx_render_context ctx = { + .output_damage = &damage, + .renderer = wlr_output->renderer, + .output = output, + .pass = render_pass, + }; + struct timespec now; clock_gettime(CLOCK_MONOTONIC, &now); - output_render(output, &now, &damage); + output_render(&ctx); pixman_region32_fini(&damage); + if (!wlr_render_pass_submit(&render_pass->base)) { + wlr_buffer_unlock(buffer); + goto out; + } + + wlr_output_state_set_buffer(&pending, buffer); + wlr_buffer_unlock(buffer); + + if (!wlr_output_commit_state(wlr_output, &pending)) { + goto out; + } + + wlr_damage_ring_rotate(&output->damage_ring); + output->last_frame = now; + +out: + wlr_output_state_finish(&pending); return 0; } @@ -610,9 +724,7 @@ static void handle_frame(struct wl_listener *listener, void *user_data) { if (output->max_render_time != 0) { struct timespec now; - clockid_t presentation_clock - = wlr_backend_get_presentation_clock(server.backend); - clock_gettime(presentation_clock, &now); + clock_gettime(CLOCK_MONOTONIC, &now); const long NSEC_IN_SECONDS = 1000000000; struct timespec predicted_refresh = output->last_presentation; @@ -690,14 +802,13 @@ static void damage_surface_iterator(struct sway_output *output, pixman_region32_init(&damage); wlr_surface_get_effective_damage(surface, &damage); wlr_region_scale(&damage, &damage, output->wlr_output->scale); - if (ceil(output->wlr_output->scale) > surface->current.scale) { + if (ceilf(output->wlr_output->scale) > surface->current.scale) { // When scaling up a surface, it'll become blurry so we need to // expand the damage region wlr_region_expand(&damage, &damage, - ceil(output->wlr_output->scale) - surface->current.scale); + ceilf(output->wlr_output->scale) - surface->current.scale); } pixman_region32_translate(&damage, box.x, box.y); - if (wlr_damage_ring_add(&output->damage_ring, &damage)) { wlr_output_schedule_frame(output->wlr_output); } @@ -754,19 +865,33 @@ static void damage_child_views_iterator(struct sway_container *con, void output_damage_whole_container(struct sway_output *output, struct sway_container *con) { - int shadow_sigma = con->shadow_enabled ? config->shadow_blur_sigma : 0; - // Pad the box by 1px, because the width is a double and might be a fraction struct wlr_box box = { - .x = con->current.x - output->lx - 1 - shadow_sigma + config->shadow_offset_x, - .y = con->current.y - output->ly - 1 - shadow_sigma + config->shadow_offset_y, - .width = con->current.width + 2 + shadow_sigma * 2, - .height = con->current.height + 2 + shadow_sigma * 2, + .x = con->current.x - output->lx - 1, + .y = con->current.y - output->ly - 1, + .width = con->current.width + 2, + .height = con->current.height + 2, }; scale_box(&box, output->wlr_output->scale); if (wlr_damage_ring_add_box(&output->damage_ring, &box)) { wlr_output_schedule_frame(output->wlr_output); } + + // Shadow damage + if (con->shadow_enabled && config_should_parameters_shadow()) { + const int shadow_sigma = config->shadow_blur_sigma; + struct wlr_box shadow_box = { + .x = con->current.x - output->lx - 1 - shadow_sigma + config->shadow_offset_x, + .y = con->current.y - output->ly - 1 - shadow_sigma + config->shadow_offset_y, + .width = con->current.width + 2 + (shadow_sigma * 2), + .height = con->current.height + 2 + (shadow_sigma * 2), + }; + scale_box(&shadow_box, output->wlr_output->scale); + if (wlr_damage_ring_add_box(&output->damage_ring, &shadow_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); @@ -790,38 +915,35 @@ static void update_output_manager_config(struct sway_server *server) { 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 (!wlr_box_empty(&output_box)) { - config_head->state.x = output_box.x; - config_head->state.y = output_box.y; - } + config_head->state.enabled = !wlr_box_empty(&output_box); + config_head->state.x = output_box.x; + config_head->state.y = output_box.y; } wlr_output_manager_v1_set_configuration(server->output_manager_v1, config); + + ipc_event_output(); } -static void handle_destroy(struct wl_listener *listener, void *data) { - struct sway_output *output = wl_container_of(listener, output, destroy); +static void begin_destroy(struct sway_output *output) { struct sway_server *server = output->server; if (output->enabled) { output_disable(output); } - fx_renderer_fini(output->renderer); - output_begin_destroy(output); wl_list_remove(&output->link); + wl_list_remove(&output->layout_destroy.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); + wl_list_remove(&output->request_state.link); wlr_damage_ring_finish(&output->damage_ring); @@ -833,35 +955,14 @@ static void handle_destroy(struct wl_listener *listener, void *data) { update_output_manager_config(server); } -static void handle_mode(struct wl_listener *listener, void *data) { - struct sway_output *output = wl_container_of(listener, output, mode); - if (!output->enabled && !output->enabling) { - struct output_config *oc = find_output_config(output); - if (output->wlr_output->current_mode != NULL && - (!oc || oc->enabled)) { - // We want to enable this output, but it didn't work last time, - // possibly because we hadn't enough CRTCs. Try again now that the - // output has a mode. - sway_log(SWAY_DEBUG, "Output %s has gained a CRTC, " - "trying to enable it", output->wlr_output->name); - apply_output_config(oc, output); - } - return; - } - 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); +static void handle_destroy(struct wl_listener *listener, void *data) { + struct sway_output *output = wl_container_of(listener, output, destroy); + begin_destroy(output); +} - update_output_manager_config(output->server); +static void handle_layout_destroy(struct wl_listener *listener, void *data) { + struct sway_output *output = wl_container_of(listener, output, layout_destroy); + begin_destroy(output); } static void update_textures(struct sway_container *con, void *data) { @@ -869,6 +970,12 @@ static void update_textures(struct sway_container *con, void *data) { container_update_marks_textures(con); } +static void update_output_scale_iterator(struct sway_output *output, + struct sway_view *view, struct wlr_surface *surface, + struct wlr_box *box, void *user_data) { + surface_update_outputs(surface); +} + static void handle_commit(struct wl_listener *listener, void *data) { struct sway_output *output = wl_container_of(listener, output, commit); struct wlr_output_event_commit *event = data; @@ -877,11 +984,20 @@ static void handle_commit(struct wl_listener *listener, void *data) { return; } - if (event->committed & WLR_OUTPUT_STATE_SCALE) { + if (event->state->committed & WLR_OUTPUT_STATE_SCALE) { output_for_each_container(output, update_textures, NULL); + output_for_each_surface(output, update_output_scale_iterator, NULL); } - if (event->committed & (WLR_OUTPUT_STATE_TRANSFORM | WLR_OUTPUT_STATE_SCALE)) { + if (event->state->committed & ( + WLR_OUTPUT_STATE_MODE | + WLR_OUTPUT_STATE_TRANSFORM | + WLR_OUTPUT_STATE_SCALE)) { + // Mark optimized blur as dirty + struct fx_effect_framebuffers *effect_fbos = + fx_effect_framebuffers_try_get(output->wlr_output); + effect_fbos->blur_buffer_dirty = true; + arrange_layers(output); arrange_output(output); transaction_commit_dirty(); @@ -889,12 +1005,19 @@ 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)) { + if (event->state->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); } + + // Next time the output is enabled, try to re-apply the gamma LUT + if ((event->state->committed & WLR_OUTPUT_STATE_ENABLED) && !output->wlr_output->enabled) { + output->gamma_lut_changed = true; + } } static void handle_present(struct wl_listener *listener, void *data) { @@ -909,6 +1032,13 @@ static void handle_present(struct wl_listener *listener, void *data) { output->refresh_nsec = output_event->refresh; } +static void handle_request_state(struct wl_listener *listener, void *data) { + struct sway_output *output = + wl_container_of(listener, output, request_state); + const struct wlr_output_event_request_state *event = data; + wlr_output_commit_state(output->wlr_output, event->state); +} + static unsigned int last_headless_num = 0; void handle_new_output(struct wl_listener *listener, void *data) { @@ -931,16 +1061,18 @@ 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 WLR_HAS_DRM_BACKEND if (server->drm_lease_manager) { wlr_drm_lease_v1_manager_offer_output(server->drm_lease_manager, wlr_output); } +#endif list_add(root->non_desktop_outputs, non_desktop); return; } if (!wlr_output_init_render(wlr_output, server->allocator, - server->wlr_renderer)) { + server->renderer)) { sway_log(SWAY_ERROR, "Failed to init output render"); return; } @@ -952,20 +1084,12 @@ void handle_new_output(struct wl_listener *listener, void *data) { output->server = server; wlr_damage_ring_init(&output->damage_ring); - // Init FX Renderer - struct wlr_egl *egl = wlr_gles2_renderer_get_egl(server->wlr_renderer); - output->renderer = fx_renderer_create(egl, wlr_output); - if (!output->renderer) { - sway_log(SWAY_ERROR, "Failed to create fx_renderer"); - abort(); - } - + wl_signal_add(&root->output_layout->events.destroy, &output->layout_destroy); + output->layout_destroy.notify = handle_layout_destroy; wl_signal_add(&wlr_output->events.destroy, &output->destroy); output->destroy.notify = handle_destroy; wl_signal_add(&wlr_output->events.commit, &output->commit); output->commit.notify = handle_commit; - wl_signal_add(&wlr_output->events.mode, &output->mode); - output->mode.notify = handle_mode; wl_signal_add(&wlr_output->events.present, &output->present); output->present.notify = handle_present; wl_signal_add(&wlr_output->events.damage, &output->damage); @@ -974,6 +1098,8 @@ void handle_new_output(struct wl_listener *listener, void *data) { output->frame.notify = handle_frame; wl_signal_add(&wlr_output->events.needs_frame, &output->needs_frame); output->needs_frame.notify = handle_needs_frame; + wl_signal_add(&wlr_output->events.request_state, &output->request_state); + output->request_state.notify = handle_request_state; output->repaint_timer = wl_event_loop_add_timer(server->wl_event_loop, output_repaint_timer_handler, output); @@ -997,6 +1123,21 @@ void handle_output_layout_change(struct wl_listener *listener, update_output_manager_config(server); } +void handle_gamma_control_set_gamma(struct wl_listener *listener, void *data) { + struct sway_server *server = + wl_container_of(listener, server, gamma_control_set_gamma); + const struct wlr_gamma_control_manager_v1_set_gamma_event *event = data; + + struct sway_output *output = event->output->data; + + if(!output) { + return; + } + + output->gamma_lut_changed = true; + wlr_output_schedule_frame(output->wlr_output); +} + static void output_manager_apply(struct sway_server *server, struct wlr_output_configuration_v1 *config, bool test_only) { // TODO: perform atomic tests on the whole backend atomically |