| // Copyright 2018 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "ash/public/cpp/window_properties.h" |
| #include "ash/shell.h" |
| #include "ash/wm/desks/desks_util.h" |
| #include "ash/wm/work_area_insets.h" |
| #include "components/exo/wayland/server_util.h" |
| #include "components/exo/wayland/zcr_remote_shell_impl.h" |
| |
| namespace exo { |
| namespace wayland { |
| namespace { |
| |
| const struct zcr_remote_surface_v1_interface remote_surface_implementation = { |
| +[](wl_client* client, wl_resource* resource) { |
| wl_resource_destroy(resource); |
| }, |
| zcr_remote_shell::remote_surface_set_app_id, |
| zcr_remote_shell::remote_surface_set_window_geometry, |
| zcr_remote_shell::remote_surface_set_scale, |
| zcr_remote_shell::remote_surface_set_rectangular_shadow_DEPRECATED, |
| zcr_remote_shell:: |
| remote_surface_set_rectangular_shadow_background_opacity_DEPRECATED, |
| zcr_remote_shell::remote_surface_set_title, |
| zcr_remote_shell::remote_surface_set_top_inset, |
| zcr_remote_shell::remote_surface_activate, |
| zcr_remote_shell::remote_surface_maximize, |
| zcr_remote_shell::remote_surface_minimize, |
| zcr_remote_shell::remote_surface_restore, |
| zcr_remote_shell::remote_surface_fullscreen, |
| zcr_remote_shell::remote_surface_unfullscreen, |
| zcr_remote_shell::remote_surface_pin, |
| zcr_remote_shell::remote_surface_unpin, |
| zcr_remote_shell::remote_surface_set_system_modal, |
| zcr_remote_shell::remote_surface_unset_system_modal, |
| zcr_remote_shell::remote_surface_set_rectangular_surface_shadow, |
| zcr_remote_shell::remote_surface_set_systemui_visibility, |
| zcr_remote_shell::remote_surface_set_always_on_top, |
| zcr_remote_shell::remote_surface_unset_always_on_top, |
| zcr_remote_shell::remote_surface_ack_configure_DEPRECATED, |
| zcr_remote_shell::remote_surface_move_DEPRECATED, |
| zcr_remote_shell::remote_surface_set_orientation, |
| zcr_remote_shell::remote_surface_set_window_type, |
| zcr_remote_shell::remote_surface_resize_DEPRECATED, |
| zcr_remote_shell::remote_surface_set_resize_outset_DEPRECATED, |
| zcr_remote_shell::remote_surface_start_move, |
| zcr_remote_shell::remote_surface_set_can_maximize, |
| zcr_remote_shell::remote_surface_unset_can_maximize, |
| zcr_remote_shell::remote_surface_set_min_size, |
| zcr_remote_shell::remote_surface_set_max_size, |
| zcr_remote_shell::remote_surface_set_snapped_to_left, |
| zcr_remote_shell::remote_surface_set_snapped_to_right, |
| zcr_remote_shell::remote_surface_start_resize, |
| zcr_remote_shell::remote_surface_set_frame, |
| zcr_remote_shell::remote_surface_set_frame_buttons, |
| zcr_remote_shell::remote_surface_set_extra_title, |
| zcr_remote_shell::remote_surface_set_orientation_lock, |
| zcr_remote_shell::remote_surface_pip, |
| zcr_remote_shell::remote_surface_set_bounds, |
| zcr_remote_shell::remote_surface_set_aspect_ratio, |
| zcr_remote_shell::remote_surface_block_ime, |
| zcr_remote_shell::remote_surface_unblock_ime, |
| zcr_remote_shell::remote_surface_set_accessibility_id, |
| zcr_remote_shell::remote_surface_set_pip_original_window, |
| zcr_remote_shell::remote_surface_unset_pip_original_window, |
| zcr_remote_shell::remote_surface_set_system_gesture_exclusion, |
| zcr_remote_shell::remote_surface_set_resize_lock, |
| zcr_remote_shell::remote_surface_unset_resize_lock, |
| zcr_remote_shell::remote_surface_set_bounds_in_output, |
| }; |
| |
| const struct zcr_notification_surface_v1_interface |
| notification_surface_implementation = { |
| +[](wl_client* client, wl_resource* resource) { |
| wl_resource_destroy(resource); |
| }, |
| zcr_remote_shell::notification_surface_set_app_id, |
| }; |
| |
| const struct zcr_input_method_surface_v1_interface |
| input_method_surface_implementation = { |
| +[](wl_client* client, wl_resource* resource) { |
| wl_resource_destroy(resource); |
| }, |
| zcr_remote_shell::input_method_surface_set_bounds, |
| zcr_remote_shell::input_method_surface_set_bounds_in_output, |
| }; |
| |
| const struct zcr_toast_surface_v1_interface toast_surface_implementation = { |
| +[](wl_client* client, wl_resource* resource) { |
| wl_resource_destroy(resource); |
| }, |
| zcr_remote_shell::toast_surface_set_position, |
| zcr_remote_shell::toast_surface_set_size, |
| zcr_remote_shell::toast_surface_set_bounds_in_output, |
| }; |
| |
| const struct zcr_remote_output_v1_interface remote_output_implementation = { |
| +[](wl_client* client, wl_resource* resource) { |
| wl_resource_destroy(resource); |
| }, |
| }; |
| |
| const struct WaylandRemoteOutputEventMapping remote_output_event_mapping_v1 = { |
| zcr_remote_output_v1_send_identification_data, |
| zcr_remote_output_v1_send_display_id, |
| zcr_remote_output_v1_send_port, |
| zcr_remote_output_v1_send_insets, |
| zcr_remote_output_v1_send_stable_insets, |
| zcr_remote_output_v1_send_systemui_behavior, |
| ZCR_REMOTE_OUTPUT_V1_SYSTEMUI_BEHAVIOR_SINCE_VERSION, |
| ZCR_REMOTE_OUTPUT_V1_STABLE_INSETS_SINCE_VERSION, |
| }; |
| |
| const WaylandRemoteShellEventMapping wayland_remote_shell_event_mapping_v1 = { |
| zcr_remote_surface_v1_send_window_geometry_changed, |
| zcr_remote_surface_v1_send_change_zoom_level, |
| zcr_remote_surface_v1_send_state_type_changed, |
| zcr_remote_surface_v1_send_bounds_changed_in_output, |
| zcr_remote_surface_v1_send_bounds_changed, |
| zcr_remote_shell_v1_send_activated, |
| zcr_remote_shell_v1_send_desktop_focus_state_changed, |
| zcr_remote_shell_v1_send_workspace_info, |
| zcr_remote_surface_v1_send_drag_finished, |
| zcr_remote_surface_v1_send_drag_started, |
| zcr_remote_shell_v1_send_layout_mode, |
| zcr_remote_shell_v1_send_default_device_scale_factor, |
| zcr_remote_shell_v1_send_configure, |
| ZCR_REMOTE_SURFACE_V1_BOUNDS_CHANGED_IN_OUTPUT_SINCE_VERSION, |
| ZCR_REMOTE_SHELL_V1_DESKTOP_FOCUS_STATE_CHANGED_SINCE_VERSION, |
| ZCR_REMOTE_SHELL_V1_LAYOUT_MODE_SINCE_VERSION, |
| ZCR_REMOTE_SHELL_V1_DEFAULT_DEVICE_SCALE_FACTOR_SINCE_VERSION, |
| ZCR_REMOTE_SURFACE_V1_CHANGE_ZOOM_LEVEL_SINCE_VERSION, |
| ZCR_REMOTE_SHELL_V1_WORKSPACE_INFO_SINCE_VERSION, |
| ZCR_REMOTE_SHELL_V1_SET_USE_DEFAULT_DEVICE_SCALE_CANCELLATION_SINCE_VERSION, |
| }; |
| |
| int RemoteSurfaceContainer(uint32_t container) { |
| switch (container) { |
| case ZCR_REMOTE_SHELL_V1_CONTAINER_DEFAULT: |
| return ash::desks_util::GetActiveDeskContainerId(); |
| case ZCR_REMOTE_SHELL_V1_CONTAINER_OVERLAY: |
| return ash::kShellWindowId_SystemModalContainer; |
| default: |
| DLOG(WARNING) << "Unsupported container: " << container; |
| return ash::desks_util::GetActiveDeskContainerId(); |
| } |
| } |
| |
| void HandleRemoteSurfaceCloseCallback(wl_resource* resource) { |
| zcr_remote_surface_v1_send_close(resource); |
| wl_client_flush(wl_resource_get_client(resource)); |
| } |
| |
| void remote_shell_get_remote_surface(wl_client* client, |
| wl_resource* resource, |
| uint32_t id, |
| wl_resource* surface, |
| uint32_t container) { |
| WaylandRemoteShell* shell = GetUserDataAs<WaylandRemoteShell>(resource); |
| double default_scale_factor = |
| wl_resource_get_version(resource) >= 8 |
| ? zcr_remote_shell::GetDefaultDeviceScaleFactor() |
| : 1.0; |
| |
| std::unique_ptr<ClientControlledShellSurface> shell_surface = |
| shell->CreateShellSurface(GetUserDataAs<Surface>(surface), |
| RemoteSurfaceContainer(container), |
| default_scale_factor); |
| if (!shell_surface) { |
| wl_resource_post_error(resource, ZCR_REMOTE_SHELL_V1_ERROR_ROLE, |
| "surface has already been assigned a role"); |
| return; |
| } |
| |
| wl_resource* remote_surface_resource = |
| wl_resource_create(client, &zcr_remote_surface_v1_interface, |
| wl_resource_get_version(resource), id); |
| |
| if (wl_resource_get_version(remote_surface_resource) < 18) |
| shell_surface->set_server_reparent_window(true); |
| |
| shell_surface->set_delegate( |
| shell->CreateShellSurfaceDelegate(remote_surface_resource)); |
| shell_surface->set_close_callback( |
| base::BindRepeating(&HandleRemoteSurfaceCloseCallback, |
| base::Unretained(remote_surface_resource))); |
| shell_surface->set_surface_destroyed_callback(base::BindOnce( |
| &wl_resource_destroy, base::Unretained(remote_surface_resource))); |
| |
| DCHECK(wl_resource_get_version(remote_surface_resource) >= 10); |
| |
| SetImplementation(remote_surface_resource, &remote_surface_implementation, |
| std::move(shell_surface)); |
| } |
| |
| void remote_shell_get_notification_surface(wl_client* client, |
| wl_resource* resource, |
| uint32_t id, |
| wl_resource* surface, |
| const char* notification_key) { |
| if (GetUserDataAs<Surface>(surface)->HasSurfaceDelegate()) { |
| wl_resource_post_error(resource, ZCR_REMOTE_SHELL_V1_ERROR_ROLE, |
| "surface has already been assigned a role"); |
| return; |
| } |
| |
| std::unique_ptr<NotificationSurface> notification_surface = |
| GetUserDataAs<WaylandRemoteShell>(resource)->CreateNotificationSurface( |
| GetUserDataAs<Surface>(surface), std::string(notification_key)); |
| if (!notification_surface) { |
| wl_resource_post_error(resource, |
| ZCR_REMOTE_SHELL_V1_ERROR_INVALID_NOTIFICATION_KEY, |
| "invalid notification key"); |
| return; |
| } |
| |
| wl_resource* notification_surface_resource = |
| wl_resource_create(client, &zcr_notification_surface_v1_interface, |
| wl_resource_get_version(resource), id); |
| SetImplementation(notification_surface_resource, |
| ¬ification_surface_implementation, |
| std::move(notification_surface)); |
| } |
| |
| void remote_shell_get_input_method_surface(wl_client* client, |
| wl_resource* resource, |
| uint32_t id, |
| wl_resource* surface) { |
| if (GetUserDataAs<Surface>(surface)->HasSurfaceDelegate()) { |
| wl_resource_post_error(resource, ZCR_REMOTE_SHELL_V1_ERROR_ROLE, |
| "surface has already been assigned a role"); |
| return; |
| } |
| |
| std::unique_ptr<ClientControlledShellSurface> input_method_surface = |
| GetUserDataAs<WaylandRemoteShell>(resource)->CreateInputMethodSurface( |
| GetUserDataAs<Surface>(surface), |
| zcr_remote_shell::GetDefaultDeviceScaleFactor()); |
| if (!input_method_surface) { |
| wl_resource_post_error(resource, ZCR_REMOTE_SHELL_V1_ERROR_ROLE, |
| "Cannot create an IME surface"); |
| return; |
| } |
| |
| wl_resource* input_method_surface_resource = |
| wl_resource_create(client, &zcr_input_method_surface_v1_interface, |
| wl_resource_get_version(resource), id); |
| SetImplementation(input_method_surface_resource, |
| &input_method_surface_implementation, |
| std::move(input_method_surface)); |
| } |
| |
| void remote_shell_get_toast_surface(wl_client* client, |
| wl_resource* resource, |
| uint32_t id, |
| wl_resource* surface) { |
| if (GetUserDataAs<Surface>(surface)->HasSurfaceDelegate()) { |
| wl_resource_post_error(resource, ZCR_REMOTE_SHELL_V1_ERROR_ROLE, |
| "surface has already been assigned a role"); |
| return; |
| } |
| |
| std::unique_ptr<ClientControlledShellSurface> toast_surface = |
| GetUserDataAs<WaylandRemoteShell>(resource)->CreateToastSurface( |
| GetUserDataAs<Surface>(surface), |
| zcr_remote_shell::GetDefaultDeviceScaleFactor()); |
| if (!toast_surface) { |
| wl_resource_post_error(resource, ZCR_REMOTE_SHELL_V1_ERROR_ROLE, |
| "Cannot create an toast surface"); |
| return; |
| } |
| |
| wl_resource* toast_surface_resource = |
| wl_resource_create(client, &zcr_toast_surface_v1_interface, |
| wl_resource_get_version(resource), id); |
| SetImplementation(toast_surface_resource, &toast_surface_implementation, |
| std::move(toast_surface)); |
| } |
| |
| void remote_shell_get_remote_output(wl_client* client, |
| wl_resource* resource, |
| uint32_t id, |
| wl_resource* output_resource) { |
| WaylandDisplayHandler* display_handler = |
| GetUserDataAs<WaylandDisplayHandler>(output_resource); |
| |
| wl_resource* remote_output_resource = |
| wl_resource_create(client, &zcr_remote_output_v1_interface, |
| wl_resource_get_version(resource), id); |
| |
| auto remote_output = std::make_unique<WaylandRemoteOutput>( |
| remote_output_resource, remote_output_event_mapping_v1); |
| display_handler->AddObserver(remote_output.get()); |
| |
| SetImplementation(remote_output_resource, &remote_output_implementation, |
| std::move(remote_output)); |
| } |
| |
| const struct zcr_remote_shell_v1_interface remote_shell_implementation = { |
| +[](wl_client* client, wl_resource* resource) { |
| // Nothing to do here. |
| }, |
| remote_shell_get_remote_surface, |
| remote_shell_get_notification_surface, |
| remote_shell_get_input_method_surface, |
| remote_shell_get_toast_surface, |
| remote_shell_get_remote_output, |
| zcr_remote_shell::remote_shell_set_use_default_scale_cancellation}; |
| |
| } // namespace |
| |
| WaylandRemoteShellData::WaylandRemoteShellData( |
| Display* display, |
| OutputResourceProvider output_provider) |
| : display(display), output_provider(output_provider) {} |
| WaylandRemoteShellData::~WaylandRemoteShellData() {} |
| |
| void bind_remote_shell(wl_client* client, |
| void* data, |
| uint32_t version, |
| uint32_t id) { |
| wl_resource* resource = wl_resource_create( |
| client, &zcr_remote_shell_v1_interface, |
| std::min<uint32_t>(version, zcr_remote_shell_v1_interface.version), id); |
| |
| auto* remote_shell_data = static_cast<WaylandRemoteShellData*>(data); |
| SetImplementation( |
| resource, &remote_shell_implementation, |
| std::make_unique<WaylandRemoteShell>( |
| remote_shell_data->display, resource, |
| base::BindRepeating(remote_shell_data->output_provider, client), |
| wayland_remote_shell_event_mapping_v1, |
| /*use_default_scale_cancellation_default=*/true)); |
| } |
| |
| gfx::Insets GetWorkAreaInsetsInPixel(const display::Display& display, |
| float device_scale_factor, |
| const gfx::Size& size_in_pixel, |
| const gfx::Rect& work_area_in_dp) { |
| gfx::Rect local_work_area_in_dp = work_area_in_dp; |
| local_work_area_in_dp.Offset(-display.bounds().x(), -display.bounds().y()); |
| gfx::Rect work_area_in_pixel = |
| zcr_remote_shell::ScaleBoundsToPixelSnappedToParent( |
| size_in_pixel, display.bounds().size(), device_scale_factor, |
| local_work_area_in_dp); |
| gfx::Insets insets_in_pixel = |
| gfx::Rect(size_in_pixel).InsetsFrom(work_area_in_pixel); |
| |
| // TODO(oshima): I think this is more conservative than necessary. The correct |
| // way is to use enclosed rect when converting the work area from dp to |
| // client pixel, but that led to weird buffer size in overlay detection. |
| // (crbug.com/920650). Investigate if we can fix it and use enclosed rect. |
| return gfx::Insets::TLBR( |
| base::ClampRound( |
| base::ClampCeil(insets_in_pixel.top() / device_scale_factor) * |
| device_scale_factor), |
| base::ClampRound( |
| base::ClampCeil(insets_in_pixel.left() / device_scale_factor) * |
| device_scale_factor), |
| base::ClampRound( |
| base::ClampCeil(insets_in_pixel.bottom() / device_scale_factor) * |
| device_scale_factor), |
| base::ClampRound( |
| base::ClampCeil(insets_in_pixel.right() / device_scale_factor) * |
| device_scale_factor)); |
| } |
| |
| gfx::Rect GetStableWorkArea(const display::Display& display) { |
| auto* root = ash::Shell::GetRootWindowForDisplayId(display.id()); |
| return ash::WorkAreaInsets::ForWindow(root)->ComputeStableWorkArea(); |
| } |
| |
| } // namespace wayland |
| } // namespace exo |