diff --git a/src/backends/native/meta-cursor-renderer-native.c b/src/backends/native/meta-cursor-renderer-native.c index 942bfe0c8..c3b7a546c 100644 --- a/src/backends/native/meta-cursor-renderer-native.c +++ b/src/backends/native/meta-cursor-renderer-native.c @@ -113,6 +113,8 @@ typedef struct _MetaCursorNativeGpuState guint active_bo; MetaCursorGbmBoState pending_bo_state; struct gbm_bo *bos[HW_CURSOR_BUFFER_COUNT]; + int widths[HW_CURSOR_BUFFER_COUNT]; + int heights[HW_CURSOR_BUFFER_COUNT]; } MetaCursorNativeGpuState; typedef struct _MetaCursorNativePrivate @@ -209,7 +211,9 @@ get_active_cursor_sprite_gbm_bo (MetaCursorNativeGpuState *cursor_gpu_state) static void set_pending_cursor_sprite_gbm_bo (MetaCursorSprite *cursor_sprite, MetaGpuKms *gpu_kms, - struct gbm_bo *bo) + struct gbm_bo *bo, + int width, + int height) { MetaCursorNativePrivate *cursor_priv; MetaCursorNativeGpuState *cursor_gpu_state; @@ -220,6 +224,8 @@ set_pending_cursor_sprite_gbm_bo (MetaCursorSprite *cursor_sprite, pending_bo = get_pending_cursor_sprite_gbm_bo_index (cursor_gpu_state); cursor_gpu_state->bos[pending_bo] = bo; + cursor_gpu_state->widths[pending_bo] = width; + cursor_gpu_state->heights[pending_bo] = height; cursor_gpu_state->pending_bo_state = META_CURSOR_GBM_BO_STATE_SET; } @@ -260,8 +266,6 @@ set_crtc_cursor (MetaCursorRendererNative *native, meta_cursor_renderer_native_get_instance_private (native); MetaCursorNativePrivate *cursor_priv = get_cursor_priv (cursor_sprite); MetaGpuKms *gpu_kms = META_GPU_KMS (meta_crtc_get_gpu (crtc)); - MetaCursorRendererNativeGpuData *cursor_renderer_gpu_data = - meta_cursor_renderer_native_gpu_data_from_gpu (gpu_kms); MetaCursorNativeGpuState *cursor_gpu_state = get_cursor_gpu_state (cursor_priv, gpu_kms); MetaKmsCrtc *kms_crtc; @@ -278,9 +282,20 @@ set_crtc_cursor (MetaCursorRendererNative *native, MetaKmsPlaneAssignment *plane_assignment; if (cursor_gpu_state->pending_bo_state == META_CURSOR_GBM_BO_STATE_SET) - bo = get_pending_cursor_sprite_gbm_bo (cursor_gpu_state); + { + guint pending_bo; + + pending_bo = get_pending_cursor_sprite_gbm_bo_index (cursor_gpu_state); + bo = get_pending_cursor_sprite_gbm_bo (cursor_gpu_state); + cursor_width = cursor_gpu_state->widths[pending_bo]; + cursor_height = cursor_gpu_state->heights[pending_bo]; + } else - bo = get_active_cursor_sprite_gbm_bo (cursor_gpu_state); + { + bo = get_active_cursor_sprite_gbm_bo (cursor_gpu_state); + cursor_width = cursor_gpu_state->widths[cursor_gpu_state->active_bo]; + cursor_height = cursor_gpu_state->heights[cursor_gpu_state->active_bo]; + } kms_crtc = meta_crtc_kms_get_kms_crtc (crtc); kms_device = meta_kms_crtc_get_device (kms_crtc); @@ -289,8 +304,6 @@ set_crtc_cursor (MetaCursorRendererNative *native, handle = gbm_bo_get_handle (bo); - cursor_width = cursor_renderer_gpu_data->cursor_width; - cursor_height = cursor_renderer_gpu_data->cursor_height; src_rect = (MetaFixed16Rectangle) { .x = meta_fixed_16_from_int (0), .y = meta_fixed_16_from_int (0), @@ -1109,6 +1122,8 @@ invalidate_cursor_gpu_state (MetaCursorSprite *cursor_sprite) pending_bo = get_pending_cursor_sprite_gbm_bo_index (cursor_gpu_state); g_clear_pointer (&cursor_gpu_state->bos[pending_bo], gbm_bo_destroy); + cursor_gpu_state->widths[pending_bo] = 0; + cursor_gpu_state->heights[pending_bo] = 0; cursor_gpu_state->pending_bo_state = META_CURSOR_GBM_BO_STATE_INVALIDATED; } } @@ -1194,10 +1209,10 @@ load_cursor_sprite_gbm_buffer_for_gpu (MetaCursorRendererNative *native, GBM_BO_USE_CURSOR | GBM_BO_USE_WRITE)) { struct gbm_bo *bo; - uint8_t buf[4 * cursor_width * cursor_height]; + uint8_t buf[4 * width * height]; uint i; - bo = gbm_bo_create (gbm_device, cursor_width, cursor_height, + bo = gbm_bo_create (gbm_device, width, height, gbm_format, GBM_BO_USE_CURSOR | GBM_BO_USE_WRITE); if (!bo) { @@ -1207,8 +1222,8 @@ load_cursor_sprite_gbm_buffer_for_gpu (MetaCursorRendererNative *native, memset (buf, 0, sizeof(buf)); for (i = 0; i < height; i++) - memcpy (buf + i * 4 * cursor_width, pixels + i * rowstride, width * 4); - if (gbm_bo_write (bo, buf, cursor_width * cursor_height * 4) != 0) + memcpy (buf + i * 4 * width, pixels + i * rowstride, width * 4); + if (gbm_bo_write (bo, buf, width * height * 4) != 0) { meta_warning ("Failed to write cursors buffer data: %s", g_strerror (errno)); @@ -1216,7 +1231,8 @@ load_cursor_sprite_gbm_buffer_for_gpu (MetaCursorRendererNative *native, return; } - set_pending_cursor_sprite_gbm_bo (cursor_sprite, gpu_kms, bo); + set_pending_cursor_sprite_gbm_bo (cursor_sprite, gpu_kms, bo, + width, height); } else { @@ -1374,7 +1390,7 @@ load_scaled_and_transformed_cursor_sprite (MetaCursorRendererNative *native, cursor_sprite, cairo_image_surface_get_data (surface), cairo_image_surface_get_width (surface), - cairo_image_surface_get_width (surface), + cairo_image_surface_get_height (surface), cairo_image_surface_get_stride (surface), gbm_format); @@ -1526,7 +1542,8 @@ realize_cursor_sprite_from_wl_buffer_for_gpu (MetaCursorRenderer *renderer, unset_can_preprocess (cursor_sprite); - set_pending_cursor_sprite_gbm_bo (cursor_sprite, gpu_kms, bo); + set_pending_cursor_sprite_gbm_bo (cursor_sprite, gpu_kms, bo, + width, height); } } #endif diff --git a/src/compositor/compositor.c b/src/compositor/compositor.c index a9a1ddfb0..e95eaf0ba 100644 --- a/src/compositor/compositor.c +++ b/src/compositor/compositor.c @@ -1035,7 +1035,12 @@ meta_compositor_size_change_window (MetaCompositor *compositor, MetaRectangle *old_frame_rect, MetaRectangle *old_buffer_rect) { - MetaWindowActor *window_actor = meta_window_actor_from_window (window); + MetaWindowActor *window_actor; + + if (window->compositor_skip_size_change) + return; + + window_actor = meta_window_actor_from_window (window); meta_window_actor_size_change (window_actor, which_change, old_frame_rect, old_buffer_rect); } diff --git a/src/core/window-private.h b/src/core/window-private.h index 4e2aeab13..63bb21377 100644 --- a/src/core/window-private.h +++ b/src/core/window-private.h @@ -326,6 +326,12 @@ struct _MetaWindow */ guint pending_compositor_effect : 4; /* MetaCompEffect */ + /* Suppress compositor size-change effects for a synthetic state + * transition (e.g. an immediate unmaximize/maximize toggle) where there + * is no visible resize to animate. + */ + guint compositor_skip_size_change : 1; + /* Iconic is the state in WM_STATE; happens for workspaces/shading * in addition to minimize */ diff --git a/src/wayland/meta-wayland-surface.c b/src/wayland/meta-wayland-surface.c index d93b30e10..1cb39fb00 100644 --- a/src/wayland/meta-wayland-surface.c +++ b/src/wayland/meta-wayland-surface.c @@ -363,10 +363,26 @@ surface_process_damage (MetaWaylandSurface *surface, } else { - src_rect = (graphene_rect_t) { - .size.width = surface_rect.width * surface->scale, - .size.height = surface_rect.height * surface->scale, - }; + if (surface->viewport.has_dst_size) + { + /* When wp_viewport sets a destination without a source rect, the + * viewport maps the full buffer to the dst size. Surface damage + * (from wl_surface.damage) is in dst/logical coordinates and + * scaled_region is in dst * surface->scale coordinates. Use the + * full buffer as the source rectangle so meta_region_crop_and_scale() + * maps damage through the viewport transform to buffer coordinates. */ + src_rect = (graphene_rect_t) { + .size.width = buffer_rect.width, + .size.height = buffer_rect.height, + }; + } + else + { + src_rect = (graphene_rect_t) { + .size.width = surface_rect.width * surface->scale, + .size.height = surface_rect.height * surface->scale, + }; + } } viewport_region = meta_region_crop_and_scale (scaled_region, &src_rect, diff --git a/src/wayland/meta-wayland-xdg-shell.c b/src/wayland/meta-wayland-xdg-shell.c index a9c148605..acc181577 100644 --- a/src/wayland/meta-wayland-xdg-shell.c +++ b/src/wayland/meta-wayland-xdg-shell.c @@ -438,6 +438,7 @@ xdg_toplevel_set_maximized (struct wl_client *client, { MetaWaylandSurface *surface = surface_from_xdg_toplevel_resource (resource); MetaWindow *window; + gboolean was_maximized; window = meta_wayland_surface_get_window (surface); if (!window) @@ -446,8 +447,23 @@ xdg_toplevel_set_maximized (struct wl_client *client, if (!window->has_maximize_func) return; + was_maximized = META_WINDOW_MAXIMIZED (window); + meta_window_force_placement (window, TRUE); + + /* If the window has not yet completed a configure-ack-commit cycle its + * renderer is not yet ready. Schedule a NOT_MAXIMIZED -> MAXIMIZED + * transition so clients that rely on an explicit transition update their + * maximize state. */ + if (!was_maximized) + meta_window_wayland_schedule_maximize_transition (window); + meta_window_maximize (window, META_MAXIMIZE_BOTH); + + /* meta_window_maximize() is a no-op when already maximized, but the client + * still expects a configure in reply. */ + if (was_maximized) + meta_window_wayland_maybe_send_configure (window); } static void @@ -1718,8 +1734,22 @@ meta_wayland_xdg_surface_post_apply_state (MetaWaylandSurfaceRole *surface_role meta_wayland_shell_surface_determine_geometry (shell_surface, &pending->new_geometry, &priv->geometry); + if (priv->geometry.width == 0 || priv->geometry.height == 0) { + MetaWaylandSurface *surface = + meta_wayland_surface_role_get_surface (surface_role); + + /* A client may commit window geometry before attaching its first + * buffer (e.g. answering an initial maximized configure). The + * geometry can't be resolved against an empty surface; defer + * silently until a buffer arrives rather than warning. */ + if (!surface->buffer_ref->buffer) + { + priv->has_set_geometry = TRUE; + return; + } + g_warning ("Invalid window geometry for xdg_surface@%d. Ignoring " "for now, but this will result in client termination " "in the future.", diff --git a/src/wayland/meta-window-wayland.c b/src/wayland/meta-window-wayland.c index 521566096..7e491d754 100644 --- a/src/wayland/meta-window-wayland.c +++ b/src/wayland/meta-window-wayland.c @@ -62,6 +62,15 @@ struct _MetaWindowWayland gboolean has_been_shown; + /* Set when a client requests maximization before it has completed a + * configure-ack-commit cycle. Cleared and consumed in finish_move_resize to + * force a NOT_MAXIMIZED -> MAXIMIZED transition for clients that only update + * their maximize state after seeing an explicit state transition. */ + gboolean needs_maximize_transition; + + /* Becomes TRUE the first time the client acks a tracked configure. */ + gboolean client_has_acked_configure; + cairo_surface_t *icon; cairo_surface_t *mini_icon; gboolean icon_dirty; @@ -995,6 +1004,7 @@ meta_window_wayland_finish_move_resize (MetaWindow *window, MetaMoveResizeFlags flags; MetaWaylandWindowConfiguration *acked_configuration; gboolean is_window_being_resized; + gboolean needs_reconfigure = FALSE; /* new_geom is in the logical pixel coordinate space, but MetaWindow wants its * rects to represent what in turn will end up on the stage, i.e. we need to @@ -1020,6 +1030,9 @@ meta_window_wayland_finish_move_resize (MetaWindow *window, acked_configuration = acquire_acked_configuration (wl_window, pending); + if (acked_configuration) + wl_window->client_has_acked_configure = TRUE; + /* x/y are ignored when we're doing interactive resizing */ is_window_being_resized = (meta_grab_op_is_resizing (display->grab_op) && display->grab_window == window); @@ -1067,6 +1080,42 @@ meta_window_wayland_finish_move_resize (MetaWindow *window, rect.x += dx; rect.y += dy; + /* For fixed-size states the compositor controls the size; the client must + * use what was configured. If it committed different dimensions (e.g. a + * stale saved size from a previous session), override with the expected + * size so we don't corrupt last_sent_rect and trigger a wrong configure. */ + if (META_WINDOW_MAXIMIZED (window) || + meta_window_is_fullscreen (window) || + window->tile_mode != META_TILE_NONE) + { + int expected_w, expected_h; + + if (acked_configuration && acked_configuration->has_size) + { + expected_w = acked_configuration->width; + expected_h = acked_configuration->height; + } + else if (wl_window->has_last_sent_configuration) + { + expected_w = wl_window->last_sent_rect.width; + expected_h = wl_window->last_sent_rect.height; + } + else + { + expected_w = new_geom.width; + expected_h = new_geom.height; + } + + if (new_geom.width != expected_w || new_geom.height != expected_h) + { + rect.x = wl_window->last_sent_rect.x; + rect.y = wl_window->last_sent_rect.y; + rect.width = expected_w; + rect.height = expected_h; + needs_reconfigure = TRUE; + } + } + if (rect.x != window->rect.x || rect.y != window->rect.y) flags |= META_MOVE_RESIZE_MOVE_ACTION; @@ -1085,6 +1134,33 @@ meta_window_wayland_finish_move_resize (MetaWindow *window, gravity = META_GRAVITY_STATIC; meta_window_move_resize_internal (window, flags, gravity, rect); + /* Force a NOT_MAXIMIZED -> MAXIMIZED configure transition for clients that + * need it to fire their 'maximize' event. Triggered when the client requested + * maximization before completing its first configure-ack-commit cycle. */ + { + gboolean do_maximize_transition = + META_WINDOW_MAXIMIZED (window) && wl_window->needs_maximize_transition; + + wl_window->needs_maximize_transition = FALSE; + + if (do_maximize_transition) + { + /* This toggle exists only to emit a state transition; the window is + * already maximized and stays maximized, so there is nothing to + * animate. Suppress the compositor size-change effects, whose paired + * synchronous firing would otherwise corrupt the actor's size-change + * accounting before either effect gets a chance to run. */ + window->compositor_skip_size_change = TRUE; + meta_window_unmaximize (window, META_MAXIMIZE_BOTH); + meta_window_maximize (window, META_MAXIMIZE_BOTH); + window->compositor_skip_size_change = FALSE; + } + else if (needs_reconfigure) + { + surface_state_changed (window); + } + } + g_clear_pointer (&acked_configuration, meta_wayland_window_configuration_free); } @@ -1279,3 +1355,23 @@ meta_window_wayland_get_max_size (MetaWindow *window, scale = 1.0 / (float) meta_window_wayland_get_geometry_scale (window); scale_size (width, height, scale); } + +void +meta_window_wayland_maybe_send_configure (MetaWindow *window) +{ + MetaWindowWayland *wl_window = META_WINDOW_WAYLAND (window); + + if (!wl_window->has_last_sent_configuration) + return; + + surface_state_changed (window); +} + +void +meta_window_wayland_schedule_maximize_transition (MetaWindow *window) +{ + MetaWindowWayland *wl_window = META_WINDOW_WAYLAND (window); + + if (!wl_window->client_has_acked_configure) + wl_window->needs_maximize_transition = TRUE; +} diff --git a/src/wayland/meta-window-wayland.h b/src/wayland/meta-window-wayland.h index 71377c187..d63b361df 100644 --- a/src/wayland/meta-window-wayland.h +++ b/src/wayland/meta-window-wayland.h @@ -81,4 +81,7 @@ void meta_window_wayland_set_custom_icon (MetaWindow *window, cairo_surface_t *icon, cairo_surface_t *mini_icon); +void meta_window_wayland_maybe_send_configure (MetaWindow *window); +void meta_window_wayland_schedule_maximize_transition (MetaWindow *window); + #endif