blob: ef7e5a26a87aa7f930306f0ac2a509a499dcb86c [file] [log] [blame]
// Copyright 2015 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 "components/exo/wayland/server.h"
#include <alpha-compositing-unstable-v1-server-protocol.h>
#include <aura-shell-server-protocol.h>
#include <cursor-shapes-unstable-v1-server-protocol.h>
#include <gaming-input-unstable-v1-server-protocol.h>
#include <gaming-input-unstable-v2-server-protocol.h>
#include <grp.h>
#include <input-timestamps-unstable-v1-server-protocol.h>
#include <keyboard-configuration-unstable-v1-server-protocol.h>
#include <keyboard-extension-unstable-v1-server-protocol.h>
#include <linux/input.h>
#include <pointer-gestures-unstable-v1-server-protocol.h>
#include <presentation-time-server-protocol.h>
#include <remote-shell-unstable-v1-server-protocol.h>
#include <secure-output-unstable-v1-server-protocol.h>
#include <stddef.h>
#include <stdint.h>
#include <stylus-tools-unstable-v1-server-protocol.h>
#include <stylus-unstable-v2-server-protocol.h>
#include <text-input-unstable-v1-server-protocol.h>
#include <viewporter-server-protocol.h>
#include <vsync-feedback-unstable-v1-server-protocol.h>
#include <wayland-server-core.h>
#include <wayland-server-protocol-core.h>
#include <xdg-shell-unstable-v6-server-protocol.h>
#include <algorithm>
#include <cstdlib>
#include <iterator>
#include <map>
#include <memory>
#include <string>
#include <utility>
#include <vector>
#include "ash/display/screen_orientation_controller.h"
#include "ash/frame/caption_buttons/caption_button_types.h"
#include "ash/ime/ime_controller.h"
#include "ash/public/cpp/shell_window_ids.h"
#include "ash/public/cpp/window_properties.h"
#include "ash/public/interfaces/window_pin_type.mojom.h"
#include "ash/public/interfaces/window_state_type.mojom.h"
#include "ash/shell.h"
#include "ash/wm/window_resizer.h"
#include "base/bind.h"
#include "base/cancelable_callback.h"
#include "base/command_line.h"
#include "base/files/file_path.h"
#include "base/macros.h"
#include "base/memory/free_deleter.h"
#include "base/memory/ptr_util.h"
#include "base/memory/weak_ptr.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/stringprintf.h"
#include "base/strings/utf_string_conversions.h"
#include "base/threading/thread.h"
#include "base/threading/thread_task_runner_handle.h"
#include "components/exo/buffer.h"
#include "components/exo/client_controlled_shell_surface.h"
#include "components/exo/data_device.h"
#include "components/exo/data_device_delegate.h"
#include "components/exo/data_offer.h"
#include "components/exo/data_offer_delegate.h"
#include "components/exo/data_source.h"
#include "components/exo/data_source_delegate.h"
#include "components/exo/display.h"
#include "components/exo/gamepad_delegate.h"
#include "components/exo/gaming_seat.h"
#include "components/exo/gaming_seat_delegate.h"
#include "components/exo/input_method_surface.h"
#include "components/exo/keyboard.h"
#include "components/exo/keyboard_delegate.h"
#include "components/exo/keyboard_device_configuration_delegate.h"
#include "components/exo/keyboard_observer.h"
#include "components/exo/notification_surface.h"
#include "components/exo/notification_surface_manager.h"
#include "components/exo/pointer.h"
#include "components/exo/pointer_delegate.h"
#include "components/exo/pointer_gesture_pinch_delegate.h"
#include "components/exo/shared_memory.h"
#include "components/exo/shell_surface.h"
#include "components/exo/sub_surface.h"
#include "components/exo/surface.h"
#include "components/exo/text_input.h"
#include "components/exo/touch.h"
#include "components/exo/touch_delegate.h"
#include "components/exo/touch_stylus_delegate.h"
#include "components/exo/wm_helper.h"
#include "components/exo/xdg_shell_surface.h"
#include "services/viz/public/interfaces/compositing/compositor_frame_sink.mojom.h"
#include "third_party/skia/include/core/SkRegion.h"
#include "ui/base/class_property.h"
#include "ui/base/hit_test.h"
#include "ui/base/ui_features.h"
#include "ui/compositor/compositor_vsync_manager.h"
#include "ui/display/display_switches.h"
#include "ui/display/manager/display_util.h"
#include "ui/display/manager/managed_display_info.h"
#include "ui/display/screen.h"
#include "ui/events/keycodes/dom/keycode_converter.h"
#include "ui/gfx/buffer_format_util.h"
#include "ui/gfx/buffer_types.h"
#include "ui/gfx/presentation_feedback.h"
#include "ui/views/widget/widget.h"
#include "ui/views/widget/widget_observer.h"
#include "ui/wm/core/coordinate_conversion.h"
#include "ui/wm/core/window_animations.h"
#include "ui/wm/public/activation_change_observer.h"
#if defined(USE_OZONE)
#include <drm_fourcc.h>
#include <linux-dmabuf-unstable-v1-server-protocol.h>
#if defined(OS_CHROMEOS)
#include "ui/events/ozone/layout/xkb/xkb_keyboard_layout_engine.h"
#endif
#endif
#if BUILDFLAG(USE_XKBCOMMON)
#include <xkbcommon/xkbcommon.h>
#include "ui/events/keycodes/scoped_xkb.h" // nogncheck
#endif
DEFINE_UI_CLASS_PROPERTY_TYPE(wl_resource*);
namespace exo {
namespace wayland {
namespace switches {
// This flag can be used to emulate device scale factor for remote shell.
constexpr char kForceRemoteShellScale[] = "force-remote-shell-scale";
// This flag can be used to override the default wayland socket name. It is
// useful when another wayland server is already running and using the
// default name.
constexpr char kWaylandServerSocket[] = "wayland-server-socket";
}
namespace {
// We don't send configure immediately after tablet mode switch
// because layout can change due to orientation lock state or accelerometer.
const int kConfigureDelayAfterLayoutSwitchMs = 300;
// Default wayland socket name.
const base::FilePath::CharType kSocketName[] = FILE_PATH_LITERAL("wayland-0");
// Group used for wayland socket.
const char kWaylandSocketGroup[] = "wayland";
template <class T>
T* GetUserDataAs(wl_resource* resource) {
return static_cast<T*>(wl_resource_get_user_data(resource));
}
template <class T>
std::unique_ptr<T> TakeUserDataAs(wl_resource* resource) {
std::unique_ptr<T> user_data = base::WrapUnique(GetUserDataAs<T>(resource));
wl_resource_set_user_data(resource, nullptr);
return user_data;
}
template <class T>
void DestroyUserData(wl_resource* resource) {
TakeUserDataAs<T>(resource);
}
template <class T>
void SetImplementation(wl_resource* resource,
const void* implementation,
std::unique_ptr<T> user_data) {
wl_resource_set_implementation(resource, implementation, user_data.release(),
DestroyUserData<T>);
}
// Returns the scale factor to be used by remote shell clients.
double GetDefaultDeviceScaleFactor() {
// A flag used by VM to emulate a device scale for a particular board.
base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
if (command_line->HasSwitch(switches::kForceRemoteShellScale)) {
std::string value =
command_line->GetSwitchValueASCII(switches::kForceRemoteShellScale);
double scale = 1.0;
if (base::StringToDouble(value, &scale))
return std::max(1.0, scale);
}
return WMHelper::GetInstance()->GetDefaultDeviceScaleFactor();
}
// Convert a timestamp to a time value that can be used when interfacing
// with wayland. Note that we cast a int64_t value to uint32_t which can
// potentially overflow.
uint32_t TimeTicksToMilliseconds(base::TimeTicks ticks) {
return (ticks - base::TimeTicks()).InMilliseconds();
}
uint32_t NowInMilliseconds() {
return TimeTicksToMilliseconds(base::TimeTicks::Now());
}
class WaylandInputDelegate {
public:
class Observer {
public:
virtual void OnDelegateDestroying(WaylandInputDelegate* delegate) = 0;
virtual void OnSendTimestamp(base::TimeTicks time_stamp) = 0;
protected:
virtual ~Observer() = default;
};
void AddObserver(Observer* observer) { observers_.AddObserver(observer); }
void RemoveObserver(Observer* observer) {
observers_.RemoveObserver(observer);
}
void SendTimestamp(base::TimeTicks time_stamp) {
for (auto& observer : observers_)
observer.OnSendTimestamp(time_stamp);
}
protected:
WaylandInputDelegate() = default;
virtual ~WaylandInputDelegate() {
for (auto& observer : observers_)
observer.OnDelegateDestroying(this);
}
private:
base::ObserverList<Observer> observers_;
DISALLOW_COPY_AND_ASSIGN(WaylandInputDelegate);
};
uint32_t WaylandDataDeviceManagerDndAction(DndAction action) {
switch (action) {
case DndAction::kNone:
return WL_DATA_DEVICE_MANAGER_DND_ACTION_NONE;
case DndAction::kCopy:
return WL_DATA_DEVICE_MANAGER_DND_ACTION_COPY;
case DndAction::kMove:
return WL_DATA_DEVICE_MANAGER_DND_ACTION_MOVE;
case DndAction::kAsk:
return WL_DATA_DEVICE_MANAGER_DND_ACTION_ASK;
}
NOTREACHED();
}
uint32_t WaylandDataDeviceManagerDndActions(
const base::flat_set<DndAction>& dnd_actions) {
uint32_t actions = 0;
for (DndAction action : dnd_actions)
actions |= WaylandDataDeviceManagerDndAction(action);
return actions;
}
DndAction DataDeviceManagerDndAction(uint32_t value) {
switch (value) {
case WL_DATA_DEVICE_MANAGER_DND_ACTION_NONE:
return DndAction::kNone;
case WL_DATA_DEVICE_MANAGER_DND_ACTION_COPY:
return DndAction::kCopy;
case WL_DATA_DEVICE_MANAGER_DND_ACTION_MOVE:
return DndAction::kMove;
case WL_DATA_DEVICE_MANAGER_DND_ACTION_ASK:
return DndAction::kAsk;
default:
NOTREACHED();
return DndAction::kNone;
}
}
base::flat_set<DndAction> DataDeviceManagerDndActions(uint32_t value) {
base::flat_set<DndAction> actions;
if (value & WL_DATA_DEVICE_MANAGER_DND_ACTION_COPY)
actions.insert(DndAction::kCopy);
if (value & WL_DATA_DEVICE_MANAGER_DND_ACTION_MOVE)
actions.insert(DndAction::kMove);
if (value & WL_DATA_DEVICE_MANAGER_DND_ACTION_ASK)
actions.insert(DndAction::kAsk);
return actions;
}
uint32_t ResizeDirection(int component) {
switch (component) {
case HTCAPTION:
return ZCR_REMOTE_SURFACE_V1_RESIZE_DIRECTION_NONE;
case HTTOP:
return ZCR_REMOTE_SURFACE_V1_RESIZE_DIRECTION_TOP;
case HTTOPRIGHT:
return ZCR_REMOTE_SURFACE_V1_RESIZE_DIRECTION_TOPRIGHT;
case HTRIGHT:
return ZCR_REMOTE_SURFACE_V1_RESIZE_DIRECTION_RIGHT;
case HTBOTTOMRIGHT:
return ZCR_REMOTE_SURFACE_V1_RESIZE_DIRECTION_BOTTOMRIGHT;
case HTBOTTOM:
return ZCR_REMOTE_SURFACE_V1_RESIZE_DIRECTION_BOTTOM;
case HTBOTTOMLEFT:
return ZCR_REMOTE_SURFACE_V1_RESIZE_DIRECTION_BOTTOMLEFT;
case HTLEFT:
return ZCR_REMOTE_SURFACE_V1_RESIZE_DIRECTION_LEFT;
case HTTOPLEFT:
return ZCR_REMOTE_SURFACE_V1_RESIZE_DIRECTION_TOPLEFT;
default:
LOG(ERROR) << "Unknown component:" << component;
break;
}
NOTREACHED();
return ZCR_REMOTE_SURFACE_V1_RESIZE_DIRECTION_NONE;
}
int Component(uint32_t direction) {
switch (direction) {
case ZCR_REMOTE_SURFACE_V1_RESIZE_DIRECTION_NONE:
return HTNOWHERE;
case ZCR_REMOTE_SURFACE_V1_RESIZE_DIRECTION_TOP:
return HTTOP;
case ZCR_REMOTE_SURFACE_V1_RESIZE_DIRECTION_TOPRIGHT:
return HTTOPRIGHT;
case ZCR_REMOTE_SURFACE_V1_RESIZE_DIRECTION_RIGHT:
return HTRIGHT;
case ZCR_REMOTE_SURFACE_V1_RESIZE_DIRECTION_BOTTOMRIGHT:
return HTBOTTOMRIGHT;
case ZCR_REMOTE_SURFACE_V1_RESIZE_DIRECTION_BOTTOM:
return HTBOTTOM;
case ZCR_REMOTE_SURFACE_V1_RESIZE_DIRECTION_BOTTOMLEFT:
return HTBOTTOMLEFT;
case ZCR_REMOTE_SURFACE_V1_RESIZE_DIRECTION_LEFT:
return HTLEFT;
case ZCR_REMOTE_SURFACE_V1_RESIZE_DIRECTION_TOPLEFT:
return HTTOPLEFT;
default:
VLOG(2) << "Unknown direction:" << direction;
break;
}
return HTNOWHERE;
}
uint32_t CaptionButtonMask(uint32_t mask) {
uint32_t caption_button_icon_mask = 0;
if (mask & ZCR_REMOTE_SURFACE_V1_FRAME_BUTTON_TYPE_BACK)
caption_button_icon_mask |= 1 << ash::CAPTION_BUTTON_ICON_BACK;
if (mask & ZCR_REMOTE_SURFACE_V1_FRAME_BUTTON_TYPE_MENU)
caption_button_icon_mask |= 1 << ash::CAPTION_BUTTON_ICON_MENU;
if (mask & ZCR_REMOTE_SURFACE_V1_FRAME_BUTTON_TYPE_MINIMIZE)
caption_button_icon_mask |= 1 << ash::CAPTION_BUTTON_ICON_MINIMIZE;
if (mask & ZCR_REMOTE_SURFACE_V1_FRAME_BUTTON_TYPE_MAXIMIZE_RESTORE)
caption_button_icon_mask |= 1 << ash::CAPTION_BUTTON_ICON_MAXIMIZE_RESTORE;
if (mask & ZCR_REMOTE_SURFACE_V1_FRAME_BUTTON_TYPE_CLOSE)
caption_button_icon_mask |= 1 << ash::CAPTION_BUTTON_ICON_CLOSE;
if (mask & ZCR_REMOTE_SURFACE_V1_FRAME_BUTTON_TYPE_ZOOM)
caption_button_icon_mask |= 1 << ash::CAPTION_BUTTON_ICON_ZOOM;
return caption_button_icon_mask;
}
// A property key containing the surface resource that is associated with
// window. If unset, no surface resource is associated with surface object.
DEFINE_UI_CLASS_PROPERTY_KEY(wl_resource*, kSurfaceResourceKey, nullptr);
// A property key containing a boolean set to true if a viewport is associated
// with with surface object.
DEFINE_UI_CLASS_PROPERTY_KEY(bool, kSurfaceHasViewportKey, false);
// A property key containing a boolean set to true if a security object is
// associated with surface object.
DEFINE_UI_CLASS_PROPERTY_KEY(bool, kSurfaceHasSecurityKey, false);
// A property key containing a boolean set to true if a blending object is
// associated with surface object.
DEFINE_UI_CLASS_PROPERTY_KEY(bool, kSurfaceHasBlendingKey, false);
// A property key containing a boolean set to true if the stylus_tool
// object is associated with surface object.
DEFINE_UI_CLASS_PROPERTY_KEY(bool, kSurfaceHasStylusToolKey, false);
// A property key containing the data offer resource that is associated with
// data offer object.
DEFINE_UI_CLASS_PROPERTY_KEY(wl_resource*, kDataOfferResourceKey, nullptr);
// A property key containing a boolean set to true if na aura surface object is
// associated with surface object.
DEFINE_UI_CLASS_PROPERTY_KEY(bool, kSurfaceHasAuraSurfaceKey, false);
wl_resource* GetSurfaceResource(Surface* surface) {
return surface->GetProperty(kSurfaceResourceKey);
}
wl_resource* GetDataOfferResource(const DataOffer* data_offer) {
return data_offer->GetProperty(kDataOfferResourceKey);
}
////////////////////////////////////////////////////////////////////////////////
// wl_buffer_interface:
void buffer_destroy(wl_client* client, wl_resource* resource) {
wl_resource_destroy(resource);
}
const struct wl_buffer_interface buffer_implementation = {buffer_destroy};
void HandleBufferReleaseCallback(wl_resource* resource) {
wl_buffer_send_release(resource);
wl_client_flush(wl_resource_get_client(resource));
}
////////////////////////////////////////////////////////////////////////////////
// wl_surface_interface:
void surface_destroy(wl_client* client, wl_resource* resource) {
wl_resource_destroy(resource);
}
void surface_attach(wl_client* client,
wl_resource* resource,
wl_resource* buffer,
int32_t x,
int32_t y) {
// TODO(reveman): Implement buffer offset support.
DLOG_IF(WARNING, x || y) << "Unsupported buffer offset: "
<< gfx::Point(x, y).ToString();
GetUserDataAs<Surface>(resource)
->Attach(buffer ? GetUserDataAs<Buffer>(buffer) : nullptr);
}
void surface_damage(wl_client* client,
wl_resource* resource,
int32_t x,
int32_t y,
int32_t width,
int32_t height) {
GetUserDataAs<Surface>(resource)->Damage(gfx::Rect(x, y, width, height));
}
void HandleSurfaceFrameCallback(wl_resource* resource,
base::TimeTicks frame_time) {
if (!frame_time.is_null()) {
wl_callback_send_done(resource, TimeTicksToMilliseconds(frame_time));
// TODO(reveman): Remove this potentially blocking flush and instead watch
// the file descriptor to be ready for write without blocking.
wl_client_flush(wl_resource_get_client(resource));
}
wl_resource_destroy(resource);
}
void surface_frame(wl_client* client,
wl_resource* resource,
uint32_t callback) {
wl_resource* callback_resource =
wl_resource_create(client, &wl_callback_interface, 1, callback);
// base::Unretained is safe as the resource owns the callback.
auto cancelable_callback =
std::make_unique<base::CancelableCallback<void(base::TimeTicks)>>(
base::Bind(&HandleSurfaceFrameCallback,
base::Unretained(callback_resource)));
GetUserDataAs<Surface>(resource)
->RequestFrameCallback(cancelable_callback->callback());
SetImplementation(callback_resource, nullptr, std::move(cancelable_callback));
}
void surface_set_opaque_region(wl_client* client,
wl_resource* resource,
wl_resource* region_resource) {
SkRegion region = region_resource ? *GetUserDataAs<SkRegion>(region_resource)
: SkRegion(SkIRect::MakeEmpty());
GetUserDataAs<Surface>(resource)->SetOpaqueRegion(cc::Region(region));
}
void surface_set_input_region(wl_client* client,
wl_resource* resource,
wl_resource* region_resource) {
Surface* surface = GetUserDataAs<Surface>(resource);
if (region_resource) {
surface->SetInputRegion(
cc::Region(*GetUserDataAs<SkRegion>(region_resource)));
} else
surface->ResetInputRegion();
}
void surface_commit(wl_client* client, wl_resource* resource) {
GetUserDataAs<Surface>(resource)->Commit();
}
void surface_set_buffer_transform(wl_client* client,
wl_resource* resource,
int32_t transform) {
Transform buffer_transform;
switch (transform) {
case WL_OUTPUT_TRANSFORM_NORMAL:
buffer_transform = Transform::NORMAL;
break;
case WL_OUTPUT_TRANSFORM_90:
buffer_transform = Transform::ROTATE_90;
break;
case WL_OUTPUT_TRANSFORM_180:
buffer_transform = Transform::ROTATE_180;
break;
case WL_OUTPUT_TRANSFORM_270:
buffer_transform = Transform::ROTATE_270;
break;
case WL_OUTPUT_TRANSFORM_FLIPPED:
case WL_OUTPUT_TRANSFORM_FLIPPED_90:
case WL_OUTPUT_TRANSFORM_FLIPPED_180:
case WL_OUTPUT_TRANSFORM_FLIPPED_270:
NOTIMPLEMENTED();
return;
default:
wl_resource_post_error(resource, WL_SURFACE_ERROR_INVALID_TRANSFORM,
"buffer transform must be one of the values from "
"the wl_output.transform enum ('%d' specified)",
transform);
return;
}
GetUserDataAs<Surface>(resource)->SetBufferTransform(buffer_transform);
}
void surface_set_buffer_scale(wl_client* client,
wl_resource* resource,
int32_t scale) {
if (scale < 1) {
wl_resource_post_error(resource, WL_SURFACE_ERROR_INVALID_SCALE,
"buffer scale must be at least one "
"('%d' specified)",
scale);
return;
}
GetUserDataAs<Surface>(resource)->SetBufferScale(scale);
}
const struct wl_surface_interface surface_implementation = {
surface_destroy,
surface_attach,
surface_damage,
surface_frame,
surface_set_opaque_region,
surface_set_input_region,
surface_commit,
surface_set_buffer_transform,
surface_set_buffer_scale};
////////////////////////////////////////////////////////////////////////////////
// wl_region_interface:
void region_destroy(wl_client* client, wl_resource* resource) {
wl_resource_destroy(resource);
}
void region_add(wl_client* client,
wl_resource* resource,
int32_t x,
int32_t y,
int32_t width,
int32_t height) {
GetUserDataAs<SkRegion>(resource)
->op(SkIRect::MakeXYWH(x, y, width, height), SkRegion::kUnion_Op);
}
static void region_subtract(wl_client* client,
wl_resource* resource,
int32_t x,
int32_t y,
int32_t width,
int32_t height) {
GetUserDataAs<SkRegion>(resource)
->op(SkIRect::MakeXYWH(x, y, width, height), SkRegion::kDifference_Op);
}
const struct wl_region_interface region_implementation = {
region_destroy, region_add, region_subtract};
////////////////////////////////////////////////////////////////////////////////
// wl_compositor_interface:
void compositor_create_surface(wl_client* client,
wl_resource* resource,
uint32_t id) {
std::unique_ptr<Surface> surface =
GetUserDataAs<Display>(resource)->CreateSurface();
wl_resource* surface_resource = wl_resource_create(
client, &wl_surface_interface, wl_resource_get_version(resource), id);
// Set the surface resource property for type-checking downcast support.
surface->SetProperty(kSurfaceResourceKey, surface_resource);
SetImplementation(surface_resource, &surface_implementation,
std::move(surface));
}
void compositor_create_region(wl_client* client,
wl_resource* resource,
uint32_t id) {
wl_resource* region_resource =
wl_resource_create(client, &wl_region_interface, 1, id);
SetImplementation(region_resource, &region_implementation,
base::WrapUnique(new SkRegion));
}
const struct wl_compositor_interface compositor_implementation = {
compositor_create_surface, compositor_create_region};
const uint32_t compositor_version = 3;
void bind_compositor(wl_client* client,
void* data,
uint32_t version,
uint32_t id) {
wl_resource* resource =
wl_resource_create(client, &wl_compositor_interface,
std::min(version, compositor_version), id);
wl_resource_set_implementation(resource, &compositor_implementation, data,
nullptr);
}
////////////////////////////////////////////////////////////////////////////////
// wl_shm_pool_interface:
const struct shm_supported_format {
uint32_t shm_format;
gfx::BufferFormat buffer_format;
} shm_supported_formats[] = {
{WL_SHM_FORMAT_XBGR8888, gfx::BufferFormat::RGBX_8888},
{WL_SHM_FORMAT_ABGR8888, gfx::BufferFormat::RGBA_8888},
{WL_SHM_FORMAT_XRGB8888, gfx::BufferFormat::BGRX_8888},
{WL_SHM_FORMAT_ARGB8888, gfx::BufferFormat::BGRA_8888}};
void shm_pool_create_buffer(wl_client* client,
wl_resource* resource,
uint32_t id,
int32_t offset,
int32_t width,
int32_t height,
int32_t stride,
uint32_t format) {
const auto* supported_format =
std::find_if(shm_supported_formats,
shm_supported_formats + arraysize(shm_supported_formats),
[format](const shm_supported_format& supported_format) {
return supported_format.shm_format == format;
});
if (supported_format ==
(shm_supported_formats + arraysize(shm_supported_formats))) {
wl_resource_post_error(resource, WL_SHM_ERROR_INVALID_FORMAT,
"invalid format 0x%x", format);
return;
}
if (offset < 0) {
wl_resource_post_error(resource, WL_SHM_ERROR_INVALID_FORMAT,
"invalid offset %d", offset);
return;
}
std::unique_ptr<Buffer> buffer =
GetUserDataAs<SharedMemory>(resource)->CreateBuffer(
gfx::Size(width, height), supported_format->buffer_format, offset,
stride);
if (!buffer) {
wl_resource_post_no_memory(resource);
return;
}
wl_resource* buffer_resource =
wl_resource_create(client, &wl_buffer_interface, 1, id);
buffer->set_release_callback(base::Bind(&HandleBufferReleaseCallback,
base::Unretained(buffer_resource)));
SetImplementation(buffer_resource, &buffer_implementation, std::move(buffer));
}
void shm_pool_destroy(wl_client* client, wl_resource* resource) {
wl_resource_destroy(resource);
}
void shm_pool_resize(wl_client* client, wl_resource* resource, int32_t size) {
// Nothing to do here.
}
const struct wl_shm_pool_interface shm_pool_implementation = {
shm_pool_create_buffer, shm_pool_destroy, shm_pool_resize};
////////////////////////////////////////////////////////////////////////////////
// wl_shm_interface:
void shm_create_pool(wl_client* client,
wl_resource* resource,
uint32_t id,
int fd,
int32_t size) {
std::unique_ptr<SharedMemory> shared_memory =
GetUserDataAs<Display>(resource)->CreateSharedMemory(
base::SharedMemoryHandle::ImportHandle(fd, size), size);
if (!shared_memory) {
wl_resource_post_no_memory(resource);
return;
}
wl_resource* shm_pool_resource =
wl_resource_create(client, &wl_shm_pool_interface, 1, id);
SetImplementation(shm_pool_resource, &shm_pool_implementation,
std::move(shared_memory));
}
const struct wl_shm_interface shm_implementation = {shm_create_pool};
void bind_shm(wl_client* client, void* data, uint32_t version, uint32_t id) {
wl_resource* resource = wl_resource_create(client, &wl_shm_interface, 1, id);
wl_resource_set_implementation(resource, &shm_implementation, data, nullptr);
for (const auto& supported_format : shm_supported_formats)
wl_shm_send_format(resource, supported_format.shm_format);
}
#if defined(USE_OZONE)
////////////////////////////////////////////////////////////////////////////////
// linux_buffer_params_interface:
const struct dmabuf_supported_format {
uint32_t dmabuf_format;
gfx::BufferFormat buffer_format;
} dmabuf_supported_formats[] = {
{DRM_FORMAT_RGB565, gfx::BufferFormat::BGR_565},
{DRM_FORMAT_XBGR8888, gfx::BufferFormat::RGBX_8888},
{DRM_FORMAT_ABGR8888, gfx::BufferFormat::RGBA_8888},
{DRM_FORMAT_XRGB8888, gfx::BufferFormat::BGRX_8888},
{DRM_FORMAT_ARGB8888, gfx::BufferFormat::BGRA_8888},
{DRM_FORMAT_NV12, gfx::BufferFormat::YUV_420_BIPLANAR},
{DRM_FORMAT_YVU420, gfx::BufferFormat::YVU_420}};
struct LinuxBufferParams {
struct Plane {
base::ScopedFD fd;
uint32_t stride;
uint32_t offset;
};
explicit LinuxBufferParams(Display* display) : display(display) {}
Display* const display;
std::map<uint32_t, Plane> planes;
};
void linux_buffer_params_destroy(wl_client* client, wl_resource* resource) {
wl_resource_destroy(resource);
}
void linux_buffer_params_add(wl_client* client,
wl_resource* resource,
int32_t fd,
uint32_t plane_idx,
uint32_t offset,
uint32_t stride,
uint32_t modifier_hi,
uint32_t modifier_lo) {
LinuxBufferParams* linux_buffer_params =
GetUserDataAs<LinuxBufferParams>(resource);
LinuxBufferParams::Plane plane{base::ScopedFD(fd), stride, offset};
const auto& inserted = linux_buffer_params->planes.insert(
std::pair<uint32_t, LinuxBufferParams::Plane>(plane_idx,
std::move(plane)));
if (!inserted.second) { // The plane was already there.
wl_resource_post_error(resource, ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_PLANE_SET,
"plane already set");
}
}
bool ValidateLinuxBufferParams(wl_resource* resource,
int32_t width,
int32_t height,
gfx::BufferFormat format,
uint32_t flags) {
if (width <= 0 || height <= 0) {
wl_resource_post_error(resource,
ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_INVALID_DIMENSIONS,
"invalid width or height");
return false;
}
if (flags & (ZWP_LINUX_BUFFER_PARAMS_V1_FLAGS_Y_INVERT |
ZWP_LINUX_BUFFER_PARAMS_V1_FLAGS_INTERLACED)) {
wl_resource_post_error(resource,
ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_INCOMPLETE,
"flags not supported");
return false;
}
LinuxBufferParams* linux_buffer_params =
GetUserDataAs<LinuxBufferParams>(resource);
size_t num_planes = gfx::NumberOfPlanesForBufferFormat(format);
for (uint32_t i = 0; i < num_planes; ++i) {
auto plane_it = linux_buffer_params->planes.find(i);
if (plane_it == linux_buffer_params->planes.end()) {
wl_resource_post_error(resource,
ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_INCOMPLETE,
"missing a plane");
return false;
}
}
if (linux_buffer_params->planes.size() != num_planes) {
wl_resource_post_error(resource, ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_PLANE_IDX,
"plane idx out of bounds");
return false;
}
return true;
}
void linux_buffer_params_create(wl_client* client,
wl_resource* resource,
int32_t width,
int32_t height,
uint32_t format,
uint32_t flags) {
const auto* supported_format = std::find_if(
std::begin(dmabuf_supported_formats), std::end(dmabuf_supported_formats),
[format](const dmabuf_supported_format& supported_format) {
return supported_format.dmabuf_format == format;
});
if (supported_format == std::end(dmabuf_supported_formats)) {
wl_resource_post_error(resource,
ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_INVALID_FORMAT,
"format not supported");
return;
}
if (!ValidateLinuxBufferParams(resource, width, height,
supported_format->buffer_format, flags))
return;
LinuxBufferParams* linux_buffer_params =
GetUserDataAs<LinuxBufferParams>(resource);
size_t num_planes =
gfx::NumberOfPlanesForBufferFormat(supported_format->buffer_format);
std::vector<gfx::NativePixmapPlane> planes;
std::vector<base::ScopedFD> fds;
for (uint32_t i = 0; i < num_planes; ++i) {
auto plane_it = linux_buffer_params->planes.find(i);
LinuxBufferParams::Plane& plane = plane_it->second;
planes.emplace_back(plane.stride, plane.offset, 0);
fds.push_back(std::move(plane.fd));
}
std::unique_ptr<Buffer> buffer =
linux_buffer_params->display->CreateLinuxDMABufBuffer(
gfx::Size(width, height), supported_format->buffer_format, planes,
std::move(fds));
if (!buffer) {
zwp_linux_buffer_params_v1_send_failed(resource);
return;
}
wl_resource* buffer_resource =
wl_resource_create(client, &wl_buffer_interface, 1, 0);
buffer->set_release_callback(base::Bind(&HandleBufferReleaseCallback,
base::Unretained(buffer_resource)));
SetImplementation(buffer_resource, &buffer_implementation, std::move(buffer));
zwp_linux_buffer_params_v1_send_created(resource, buffer_resource);
}
void linux_buffer_params_create_immed(wl_client* client,
wl_resource* resource,
uint32_t buffer_id,
int32_t width,
int32_t height,
uint32_t format,
uint32_t flags) {
const auto* supported_format = std::find_if(
std::begin(dmabuf_supported_formats), std::end(dmabuf_supported_formats),
[format](const dmabuf_supported_format& supported_format) {
return supported_format.dmabuf_format == format;
});
if (supported_format == std::end(dmabuf_supported_formats)) {
wl_resource_post_error(resource,
ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_INVALID_FORMAT,
"format not supported");
return;
}
if (!ValidateLinuxBufferParams(resource, width, height,
supported_format->buffer_format, flags))
return;
LinuxBufferParams* linux_buffer_params =
GetUserDataAs<LinuxBufferParams>(resource);
size_t num_planes =
gfx::NumberOfPlanesForBufferFormat(supported_format->buffer_format);
std::vector<gfx::NativePixmapPlane> planes;
std::vector<base::ScopedFD> fds;
for (uint32_t i = 0; i < num_planes; ++i) {
auto plane_it = linux_buffer_params->planes.find(i);
LinuxBufferParams::Plane& plane = plane_it->second;
planes.emplace_back(plane.stride, plane.offset, 0);
fds.push_back(std::move(plane.fd));
}
std::unique_ptr<Buffer> buffer =
linux_buffer_params->display->CreateLinuxDMABufBuffer(
gfx::Size(width, height), supported_format->buffer_format, planes,
std::move(fds));
if (!buffer) {
// On import failure in case of a create_immed request, the protocol
// allows us to raise a fatal error from zwp_linux_dmabuf_v1 version 2+.
wl_resource_post_error(resource,
ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_INVALID_WL_BUFFER,
"dmabuf import failed");
return;
}
wl_resource* buffer_resource =
wl_resource_create(client, &wl_buffer_interface, 1, buffer_id);
buffer->set_release_callback(base::Bind(&HandleBufferReleaseCallback,
base::Unretained(buffer_resource)));
SetImplementation(buffer_resource, &buffer_implementation, std::move(buffer));
}
const struct zwp_linux_buffer_params_v1_interface
linux_buffer_params_implementation = {
linux_buffer_params_destroy, linux_buffer_params_add,
linux_buffer_params_create, linux_buffer_params_create_immed};
////////////////////////////////////////////////////////////////////////////////
// linux_dmabuf_interface:
void linux_dmabuf_destroy(wl_client* client, wl_resource* resource) {
wl_resource_destroy(resource);
}
void linux_dmabuf_create_params(wl_client* client,
wl_resource* resource,
uint32_t id) {
std::unique_ptr<LinuxBufferParams> linux_buffer_params =
std::make_unique<LinuxBufferParams>(GetUserDataAs<Display>(resource));
wl_resource* linux_buffer_params_resource =
wl_resource_create(client, &zwp_linux_buffer_params_v1_interface,
wl_resource_get_version(resource), id);
SetImplementation(linux_buffer_params_resource,
&linux_buffer_params_implementation,
std::move(linux_buffer_params));
}
const struct zwp_linux_dmabuf_v1_interface linux_dmabuf_implementation = {
linux_dmabuf_destroy, linux_dmabuf_create_params};
const uint32_t linux_dmabuf_version = 2;
void bind_linux_dmabuf(wl_client* client,
void* data,
uint32_t version,
uint32_t id) {
wl_resource* resource =
wl_resource_create(client, &zwp_linux_dmabuf_v1_interface,
std::min(version, linux_dmabuf_version), id);
wl_resource_set_implementation(resource, &linux_dmabuf_implementation, data,
nullptr);
for (const auto& supported_format : dmabuf_supported_formats)
zwp_linux_dmabuf_v1_send_format(resource, supported_format.dmabuf_format);
}
#endif
////////////////////////////////////////////////////////////////////////////////
// wl_subsurface_interface:
void subsurface_destroy(wl_client* client, wl_resource* resource) {
wl_resource_destroy(resource);
}
void subsurface_set_position(wl_client* client,
wl_resource* resource,
int32_t x,
int32_t y) {
GetUserDataAs<SubSurface>(resource)->SetPosition(gfx::Point(x, y));
}
void subsurface_place_above(wl_client* client,
wl_resource* resource,
wl_resource* reference_resource) {
GetUserDataAs<SubSurface>(resource)
->PlaceAbove(GetUserDataAs<Surface>(reference_resource));
}
void subsurface_place_below(wl_client* client,
wl_resource* resource,
wl_resource* sibling_resource) {
GetUserDataAs<SubSurface>(resource)
->PlaceBelow(GetUserDataAs<Surface>(sibling_resource));
}
void subsurface_set_sync(wl_client* client, wl_resource* resource) {
GetUserDataAs<SubSurface>(resource)->SetCommitBehavior(true);
}
void subsurface_set_desync(wl_client* client, wl_resource* resource) {
GetUserDataAs<SubSurface>(resource)->SetCommitBehavior(false);
}
const struct wl_subsurface_interface subsurface_implementation = {
subsurface_destroy, subsurface_set_position, subsurface_place_above,
subsurface_place_below, subsurface_set_sync, subsurface_set_desync};
////////////////////////////////////////////////////////////////////////////////
// wl_subcompositor_interface:
void subcompositor_destroy(wl_client* client, wl_resource* resource) {
wl_resource_destroy(resource);
}
void subcompositor_get_subsurface(wl_client* client,
wl_resource* resource,
uint32_t id,
wl_resource* surface,
wl_resource* parent) {
std::unique_ptr<SubSurface> subsurface =
GetUserDataAs<Display>(resource)->CreateSubSurface(
GetUserDataAs<Surface>(surface), GetUserDataAs<Surface>(parent));
if (!subsurface) {
wl_resource_post_error(resource, WL_SUBCOMPOSITOR_ERROR_BAD_SURFACE,
"invalid surface");
return;
}
wl_resource* subsurface_resource =
wl_resource_create(client, &wl_subsurface_interface, 1, id);
SetImplementation(subsurface_resource, &subsurface_implementation,
std::move(subsurface));
}
const struct wl_subcompositor_interface subcompositor_implementation = {
subcompositor_destroy, subcompositor_get_subsurface};
void bind_subcompositor(wl_client* client,
void* data,
uint32_t version,
uint32_t id) {
wl_resource* resource =
wl_resource_create(client, &wl_subcompositor_interface, 1, id);
wl_resource_set_implementation(resource, &subcompositor_implementation, data,
nullptr);
}
////////////////////////////////////////////////////////////////////////////////
// wl_shell_surface_interface:
void shell_surface_pong(wl_client* client,
wl_resource* resource,
uint32_t serial) {
NOTIMPLEMENTED();
}
void shell_surface_move(wl_client* client,
wl_resource* resource,
wl_resource* seat_resource,
uint32_t serial) {
GetUserDataAs<ShellSurface>(resource)->StartMove();
}
void shell_surface_resize(wl_client* client,
wl_resource* resource,
wl_resource* seat_resource,
uint32_t serial,
uint32_t edges) {
NOTIMPLEMENTED();
}
void shell_surface_set_toplevel(wl_client* client, wl_resource* resource) {
GetUserDataAs<ShellSurface>(resource)->SetEnabled(true);
}
void shell_surface_set_transient(wl_client* client,
wl_resource* resource,
wl_resource* parent_resource,
int x,
int y,
uint32_t flags) {
ShellSurface* shell_surface = GetUserDataAs<ShellSurface>(resource);
if (shell_surface->enabled())
return;
if (flags & WL_SHELL_SURFACE_TRANSIENT_INACTIVE) {
shell_surface->SetContainer(ash::kShellWindowId_SystemModalContainer);
shell_surface->SetActivatable(false);
}
shell_surface->SetEnabled(true);
}
void shell_surface_set_fullscreen(wl_client* client,
wl_resource* resource,
uint32_t method,
uint32_t framerate,
wl_resource* output_resource) {
ShellSurface* shell_surface = GetUserDataAs<ShellSurface>(resource);
if (shell_surface->enabled())
return;
shell_surface->SetEnabled(true);
shell_surface->SetFullscreen(true);
}
void shell_surface_set_popup(wl_client* client,
wl_resource* resource,
wl_resource* seat_resource,
uint32_t serial,
wl_resource* parent_resource,
int32_t x,
int32_t y,
uint32_t flags) {
NOTIMPLEMENTED();
}
void shell_surface_set_maximized(wl_client* client,
wl_resource* resource,
wl_resource* output_resource) {
ShellSurface* shell_surface = GetUserDataAs<ShellSurface>(resource);
if (shell_surface->enabled())
return;
shell_surface->SetEnabled(true);
shell_surface->Maximize();
}
void shell_surface_set_title(wl_client* client,
wl_resource* resource,
const char* title) {
GetUserDataAs<ShellSurface>(resource)
->SetTitle(base::string16(base::UTF8ToUTF16(title)));
}
void shell_surface_set_class(wl_client* client,
wl_resource* resource,
const char* clazz) {
GetUserDataAs<ShellSurface>(resource)->SetApplicationId(clazz);
}
const struct wl_shell_surface_interface shell_surface_implementation = {
shell_surface_pong, shell_surface_move,
shell_surface_resize, shell_surface_set_toplevel,
shell_surface_set_transient, shell_surface_set_fullscreen,
shell_surface_set_popup, shell_surface_set_maximized,
shell_surface_set_title, shell_surface_set_class};
////////////////////////////////////////////////////////////////////////////////
// wl_shell_interface:
uint32_t HandleShellSurfaceConfigureCallback(
wl_resource* resource,
const gfx::Size& size,
ash::mojom::WindowStateType state_type,
bool resizing,
bool activated,
const gfx::Vector2d& origin_offset) {
wl_shell_surface_send_configure(resource, WL_SHELL_SURFACE_RESIZE_NONE,
size.width(), size.height());
wl_client_flush(wl_resource_get_client(resource));
return 0;
}
void shell_get_shell_surface(wl_client* client,
wl_resource* resource,
uint32_t id,
wl_resource* surface) {
std::unique_ptr<ShellSurface> shell_surface =
GetUserDataAs<Display>(resource)->CreateShellSurface(
GetUserDataAs<Surface>(surface));
if (!shell_surface) {
wl_resource_post_error(resource, WL_SHELL_ERROR_ROLE,
"surface has already been assigned a role");
return;
}
wl_resource* shell_surface_resource =
wl_resource_create(client, &wl_shell_surface_interface, 1, id);
// Shell surfaces are initially disabled and needs to be explicitly mapped
// before they are enabled and can become visible.
shell_surface->SetEnabled(false);
shell_surface->set_configure_callback(
base::Bind(&HandleShellSurfaceConfigureCallback,
base::Unretained(shell_surface_resource)));
shell_surface->set_surface_destroyed_callback(base::Bind(
&wl_resource_destroy, base::Unretained(shell_surface_resource)));
SetImplementation(shell_surface_resource, &shell_surface_implementation,
std::move(shell_surface));
}
const struct wl_shell_interface shell_implementation = {
shell_get_shell_surface};
void bind_shell(wl_client* client, void* data, uint32_t version, uint32_t id) {
wl_resource* resource =
wl_resource_create(client, &wl_shell_interface, 1, id);
wl_resource_set_implementation(resource, &shell_implementation, data,
nullptr);
}
////////////////////////////////////////////////////////////////////////////////
// wl_output_interface:
// Returns the transform that a compositor will apply to a surface to
// compensate for the rotation of an output device.
wl_output_transform OutputTransform(display::Display::Rotation rotation) {
// Note: |rotation| describes the counter clockwise rotation that a
// display's output is currently adjusted for, which is the inverse
// of what we need to return.
switch (rotation) {
case display::Display::ROTATE_0:
return WL_OUTPUT_TRANSFORM_NORMAL;
case display::Display::ROTATE_90:
return WL_OUTPUT_TRANSFORM_270;
case display::Display::ROTATE_180:
return WL_OUTPUT_TRANSFORM_180;
case display::Display::ROTATE_270:
return WL_OUTPUT_TRANSFORM_90;
}
NOTREACHED();
return WL_OUTPUT_TRANSFORM_NORMAL;
}
class WaylandDisplayObserver : public display::DisplayObserver {
public:
class ScaleObserver : public base::SupportsWeakPtr<ScaleObserver> {
public:
ScaleObserver() {}
virtual void OnDisplayScalesChanged(const display::Display& display) = 0;
protected:
virtual ~ScaleObserver() {}
};
WaylandDisplayObserver(int64_t id, wl_resource* output_resource)
: id_(id), output_resource_(output_resource) {
display::Screen::GetScreen()->AddObserver(this);
SendDisplayMetrics();
}
~WaylandDisplayObserver() override {
display::Screen::GetScreen()->RemoveObserver(this);
}
void SetScaleObserver(base::WeakPtr<ScaleObserver> scale_observer) {
scale_observer_ = scale_observer;
SendDisplayMetrics();
}
bool HasScaleObserver() const { return !!scale_observer_; }
// Overridden from display::DisplayObserver:
void OnDisplayMetricsChanged(const display::Display& display,
uint32_t changed_metrics) override {
if (id_ != display.id())
return;
// There is no need to check DISPLAY_METRIC_PRIMARY because when primary
// changes, bounds always changes. (new primary should have had non
// 0,0 origin).
// Only exception is when switching to newly connected primary with
// the same bounds. This happens whenyou're in docked mode, suspend,
// unplug the dislpay, then resume to the internal display which has
// the same resolution. Since metrics does not change, there is no need
// to notify clients.
if (changed_metrics &
(DISPLAY_METRIC_BOUNDS | DISPLAY_METRIC_DEVICE_SCALE_FACTOR |
DISPLAY_METRIC_ROTATION)) {
SendDisplayMetrics();
}
}
private:
void SendDisplayMetrics() {
display::Display display;
bool rv =
display::Screen::GetScreen()->GetDisplayWithDisplayId(id_, &display);
DCHECK(rv);
const display::ManagedDisplayInfo& info =
WMHelper::GetInstance()->GetDisplayInfo(display.id());
const float kInchInMm = 25.4f;
const char* kUnknown = "unknown";
const std::string& make = info.manufacturer_id();
const std::string& model = info.product_id();
gfx::Rect bounds = info.bounds_in_native();
wl_output_send_geometry(
output_resource_, bounds.x(), bounds.y(),
static_cast<int>(kInchInMm * bounds.width() / info.device_dpi()),
static_cast<int>(kInchInMm * bounds.height() / info.device_dpi()),
WL_OUTPUT_SUBPIXEL_UNKNOWN, make.empty() ? kUnknown : make.c_str(),
model.empty() ? kUnknown : model.c_str(),
OutputTransform(display.rotation()));
if (wl_resource_get_version(output_resource_) >=
WL_OUTPUT_SCALE_SINCE_VERSION) {
wl_output_send_scale(output_resource_, display.device_scale_factor());
}
// TODO(reveman): Send real list of modes.
wl_output_send_mode(
output_resource_, WL_OUTPUT_MODE_CURRENT | WL_OUTPUT_MODE_PREFERRED,
bounds.width(), bounds.height(), static_cast<int>(60000));
if (HasScaleObserver())
scale_observer_->OnDisplayScalesChanged(display);
if (wl_resource_get_version(output_resource_) >=
WL_OUTPUT_DONE_SINCE_VERSION) {
wl_output_send_done(output_resource_);
}
wl_client_flush(wl_resource_get_client(output_resource_));
}
// The ID of the display being observed.
const int64_t id_;
// The output resource associated with the display.
wl_resource* const output_resource_;
base::WeakPtr<ScaleObserver> scale_observer_;
DISALLOW_COPY_AND_ASSIGN(WaylandDisplayObserver);
};
const uint32_t output_version = 2;
void bind_output(wl_client* client, void* data, uint32_t version, uint32_t id) {
Server::Output* output = static_cast<Server::Output*>(data);
wl_resource* resource = wl_resource_create(
client, &wl_output_interface, std::min(version, output_version), id);
SetImplementation(
resource, nullptr,
std::make_unique<WaylandDisplayObserver>(output->id(), resource));
}
////////////////////////////////////////////////////////////////////////////////
// xdg_positioner_interface:
uint32_t InvertBitfield(uint32_t bitfield, uint32_t mask) {
return (bitfield & ~mask) | ((bitfield & mask) ^ mask);
}
// TODO(oshima): propagate x/y flip state to children.
struct WaylandPositioner {
static constexpr uint32_t kHorizontalAnchors =
ZXDG_POSITIONER_V6_ANCHOR_LEFT | ZXDG_POSITIONER_V6_ANCHOR_RIGHT;
static constexpr uint32_t kVerticalAnchors =
ZXDG_POSITIONER_V6_ANCHOR_TOP | ZXDG_POSITIONER_V6_ANCHOR_BOTTOM;
static constexpr uint32_t kHorizontalGravities =
ZXDG_POSITIONER_V6_GRAVITY_LEFT | ZXDG_POSITIONER_V6_GRAVITY_RIGHT;
static constexpr uint32_t kVerticalGravities =
ZXDG_POSITIONER_V6_GRAVITY_TOP | ZXDG_POSITIONER_V6_GRAVITY_BOTTOM;
static int CalculateX(const gfx::Size& size,
const gfx::Rect& anchor_rect,
uint32_t anchor,
uint32_t gravity,
int offset,
bool flipped) {
if (flipped) {
anchor = InvertBitfield(anchor, kHorizontalAnchors);
gravity = InvertBitfield(gravity, kHorizontalGravities);
offset = -offset;
}
int x = offset;
if (anchor & ZXDG_POSITIONER_V6_ANCHOR_LEFT)
x += anchor_rect.x();
else if (anchor & ZXDG_POSITIONER_V6_ANCHOR_RIGHT)
x += anchor_rect.right();
else
x += anchor_rect.CenterPoint().x();
if (gravity & ZXDG_POSITIONER_V6_GRAVITY_LEFT)
return x - size.width();
if (gravity & ZXDG_POSITIONER_V6_GRAVITY_RIGHT)
return x;
return x - size.width() / 2;
}
static int CalculateY(const gfx::Size& size,
const gfx::Rect& anchor_rect,
uint32_t anchor,
uint32_t gravity,
int offset,
bool flipped) {
if (flipped) {
anchor = InvertBitfield(anchor, kVerticalAnchors);
gravity = InvertBitfield(gravity, kVerticalGravities);
offset = -offset;
}
int y = offset;
if (anchor & ZXDG_POSITIONER_V6_ANCHOR_TOP)
y += anchor_rect.y();
else if (anchor & ZXDG_POSITIONER_V6_ANCHOR_BOTTOM)
y += anchor_rect.bottom();
else
y += anchor_rect.CenterPoint().y();
if (gravity & ZXDG_POSITIONER_V6_GRAVITY_TOP)
return y - size.height();
if (gravity & ZXDG_POSITIONER_V6_GRAVITY_BOTTOM)
return y;
return y - size.height() / 2;
}
// Calculate and return position from current state.
gfx::Point CalculatePosition(const gfx::Rect& work_area) {
// TODO(oshima): The size must be smaller than work area.
gfx::Rect bounds(gfx::Point(CalculateX(size, anchor_rect, anchor, gravity,
offset.x(), x_flipped),
CalculateY(size, anchor_rect, anchor, gravity,
offset.y(), y_flipped)),
size);
// Adjust x position if the bounds are not fully contained by the work area.
if (work_area.x() > bounds.x() || work_area.right() < bounds.right()) {
// Allow sliding horizontally if the surface is attached below
// or above the parent surface.
bool can_slide_x = (anchor & ZXDG_POSITIONER_V6_ANCHOR_BOTTOM &&
gravity & ZXDG_POSITIONER_V6_GRAVITY_BOTTOM) ||
(anchor & ZXDG_POSITIONER_V6_ANCHOR_TOP &&
gravity & ZXDG_POSITIONER_V6_GRAVITY_TOP);
if (adjustment & ZXDG_POSITIONER_V6_CONSTRAINT_ADJUSTMENT_SLIDE_X &&
can_slide_x) {
if (bounds.x() < work_area.x())
bounds.set_x(work_area.x());
else if (bounds.right() > work_area.right())
bounds.set_x(work_area.right() - size.width());
} else if (adjustment & ZXDG_POSITIONER_V6_CONSTRAINT_ADJUSTMENT_FLIP_X) {
x_flipped = !x_flipped;
bounds.set_x(CalculateX(size, anchor_rect, anchor, gravity, offset.x(),
x_flipped));
}
}
// Adjust y position if the bounds are not fully contained by the work area.
if (work_area.y() > bounds.y() || work_area.bottom() < bounds.bottom()) {
// Allow sliding vertically if the surface is attached left or
// right of the parent surface.
bool can_slide_y = (anchor & ZXDG_POSITIONER_V6_ANCHOR_LEFT &&
gravity & ZXDG_POSITIONER_V6_GRAVITY_LEFT) ||
(anchor & ZXDG_POSITIONER_V6_ANCHOR_RIGHT &&
gravity & ZXDG_POSITIONER_V6_GRAVITY_RIGHT);
if (adjustment & ZXDG_POSITIONER_V6_CONSTRAINT_ADJUSTMENT_SLIDE_Y &&
can_slide_y) {
if (bounds.y() < work_area.y())
bounds.set_y(work_area.y());
else if (bounds.bottom() > work_area.bottom())
bounds.set_y(work_area.bottom() - size.height());
} else if (adjustment & ZXDG_POSITIONER_V6_CONSTRAINT_ADJUSTMENT_FLIP_Y) {
y_flipped = !y_flipped;
bounds.set_y(CalculateY(size, anchor_rect, anchor, gravity, offset.y(),
y_flipped));
}
}
return bounds.origin();
}
gfx::Size size;
gfx::Rect anchor_rect;
uint32_t anchor = ZXDG_POSITIONER_V6_ANCHOR_NONE;
uint32_t gravity = ZXDG_POSITIONER_V6_GRAVITY_NONE;
uint32_t adjustment = ZXDG_POSITIONER_V6_CONSTRAINT_ADJUSTMENT_NONE;
gfx::Vector2d offset;
bool y_flipped = false;
bool x_flipped = false;
};
void xdg_positioner_v6_destroy(wl_client* client, wl_resource* resource) {
wl_resource_destroy(resource);
}
void xdg_positioner_v6_set_size(wl_client* client,
wl_resource* resource,
int32_t width,
int32_t height) {
if (width < 1 || height < 1) {
wl_resource_post_error(resource, ZXDG_POSITIONER_V6_ERROR_INVALID_INPUT,
"width and height must be positive and non-zero");
return;
}
GetUserDataAs<WaylandPositioner>(resource)->size = gfx::Size(width, height);
}
void xdg_positioner_v6_set_anchor_rect(wl_client* client,
wl_resource* resource,
int32_t x,
int32_t y,
int32_t width,
int32_t height) {
if (width < 1 || height < 1) {
wl_resource_post_error(resource, ZXDG_POSITIONER_V6_ERROR_INVALID_INPUT,
"width and height must be positive and non-zero");
return;
}
GetUserDataAs<WaylandPositioner>(resource)->anchor_rect =
gfx::Rect(x, y, width, height);
}
void xdg_positioner_v6_set_anchor(wl_client* client,
wl_resource* resource,
uint32_t anchor) {
if (((anchor & ZXDG_POSITIONER_V6_ANCHOR_LEFT) &&
(anchor & ZXDG_POSITIONER_V6_ANCHOR_RIGHT)) ||
((anchor & ZXDG_POSITIONER_V6_ANCHOR_TOP) &&
(anchor & ZXDG_POSITIONER_V6_ANCHOR_BOTTOM))) {
wl_resource_post_error(resource, ZXDG_POSITIONER_V6_ERROR_INVALID_INPUT,
"same-axis values are not allowed");
return;
}
GetUserDataAs<WaylandPositioner>(resource)->anchor = anchor;
}
void xdg_positioner_v6_set_gravity(wl_client* client,
wl_resource* resource,
uint32_t gravity) {
if (((gravity & ZXDG_POSITIONER_V6_GRAVITY_LEFT) &&
(gravity & ZXDG_POSITIONER_V6_GRAVITY_RIGHT)) ||
((gravity & ZXDG_POSITIONER_V6_GRAVITY_TOP) &&
(gravity & ZXDG_POSITIONER_V6_GRAVITY_BOTTOM))) {
wl_resource_post_error(resource, ZXDG_POSITIONER_V6_ERROR_INVALID_INPUT,
"same-axis values are not allowed");
return;
}
GetUserDataAs<WaylandPositioner>(resource)->gravity = gravity;
}
void xdg_positioner_v6_set_constraint_adjustment(wl_client* client,
wl_resource* resource,
uint32_t adjustment) {
GetUserDataAs<WaylandPositioner>(resource)->adjustment = adjustment;
}
void xdg_positioner_v6_set_offset(wl_client* client,
wl_resource* resource,
int32_t x,
int32_t y) {
GetUserDataAs<WaylandPositioner>(resource)->offset = gfx::Vector2d(x, y);
}
const struct zxdg_positioner_v6_interface xdg_positioner_v6_implementation = {
xdg_positioner_v6_destroy,
xdg_positioner_v6_set_size,
xdg_positioner_v6_set_anchor_rect,
xdg_positioner_v6_set_anchor,
xdg_positioner_v6_set_gravity,
xdg_positioner_v6_set_constraint_adjustment,
xdg_positioner_v6_set_offset};
////////////////////////////////////////////////////////////////////////////////
// xdg_toplevel_interface:
int XdgToplevelV6ResizeComponent(uint32_t edges) {
switch (edges) {
case ZXDG_TOPLEVEL_V6_RESIZE_EDGE_TOP:
return HTTOP;
case ZXDG_TOPLEVEL_V6_RESIZE_EDGE_BOTTOM:
return HTBOTTOM;
case ZXDG_TOPLEVEL_V6_RESIZE_EDGE_LEFT:
return HTLEFT;
case ZXDG_TOPLEVEL_V6_RESIZE_EDGE_TOP_LEFT:
return HTTOPLEFT;
case ZXDG_TOPLEVEL_V6_RESIZE_EDGE_BOTTOM_LEFT:
return HTBOTTOMLEFT;
case ZXDG_TOPLEVEL_V6_RESIZE_EDGE_RIGHT:
return HTRIGHT;
case ZXDG_TOPLEVEL_V6_RESIZE_EDGE_TOP_RIGHT:
return HTTOPRIGHT;
case ZXDG_TOPLEVEL_V6_RESIZE_EDGE_BOTTOM_RIGHT:
return HTBOTTOMRIGHT;
default:
return HTBOTTOMRIGHT;
}
}
using XdgSurfaceConfigureCallback =
base::Callback<void(const gfx::Size& size,
ash::mojom::WindowStateType state_type,
bool resizing,
bool activated)>;
uint32_t HandleXdgSurfaceV6ConfigureCallback(
wl_resource* resource,
const XdgSurfaceConfigureCallback& callback,
const gfx::Size& size,
ash::mojom::WindowStateType state_type,
bool resizing,
bool activated,
const gfx::Vector2d& origin_offset) {
uint32_t serial = wl_display_next_serial(
wl_client_get_display(wl_resource_get_client(resource)));
callback.Run(size, state_type, resizing, activated);
zxdg_surface_v6_send_configure(resource, serial);
wl_client_flush(wl_resource_get_client(resource));
return serial;
}
// Wrapper around shell surface that allows us to handle the case where the
// xdg surface resource is destroyed before the toplevel resource.
class WaylandToplevel : public aura::WindowObserver {
public:
WaylandToplevel(wl_resource* resource, wl_resource* surface_resource)
: resource_(resource),
shell_surface_(GetUserDataAs<XdgShellSurface>(surface_resource)),
weak_ptr_factory_(this) {
shell_surface_->host_window()->AddObserver(this);
shell_surface_->set_close_callback(
base::Bind(&WaylandToplevel::OnClose, weak_ptr_factory_.GetWeakPtr()));
shell_surface_->set_configure_callback(
base::Bind(&HandleXdgSurfaceV6ConfigureCallback, surface_resource,
base::Bind(&WaylandToplevel::OnConfigure,
weak_ptr_factory_.GetWeakPtr())));
}
~WaylandToplevel() override {
if (shell_surface_)
shell_surface_->host_window()->RemoveObserver(this);
}
// Overridden from aura::WindowObserver:
void OnWindowDestroying(aura::Window* window) override {
shell_surface_ = nullptr;
}
void SetParent(WaylandToplevel* parent) {
if (!shell_surface_)
return;
if (!parent) {
shell_surface_->SetParent(nullptr);
return;
}
// This is a no-op if parent is not mapped.
if (parent->shell_surface_ && parent->shell_surface_->GetWidget())
shell_surface_->SetParent(parent->shell_surface_);
}
void SetTitle(const base::string16& title) {
if (shell_surface_)
shell_surface_->SetTitle(title);
}
void SetApplicationId(const char* application_id) {
if (shell_surface_)
shell_surface_->SetApplicationId(application_id);
}
void Move() {
if (shell_surface_)
shell_surface_->StartMove();
}
void Resize(int component) {
if (!shell_surface_)
return;
if (component != HTNOWHERE)
shell_surface_->StartResize(component);
}
void SetMaximumSize(const gfx::Size& size) {
if (shell_surface_)
shell_surface_->SetMaximumSize(size);
}
void SetMinimumSize(const gfx::Size& size) {
if (shell_surface_)
shell_surface_->SetMinimumSize(size);
}
void Maximize() {
if (shell_surface_)
shell_surface_->Maximize();
}
void Restore() {
if (shell_surface_)
shell_surface_->Restore();
}
void SetFullscreen(bool fullscreen) {
if (shell_surface_)
shell_surface_->SetFullscreen(fullscreen);
}
void Minimize() {
if (shell_surface_)
shell_surface_->Minimize();
}
private:
void OnClose() {
zxdg_toplevel_v6_send_close(resource_);
wl_client_flush(wl_resource_get_client(resource_));
}
static void AddState(wl_array* states, zxdg_toplevel_v6_state state) {
zxdg_toplevel_v6_state* value = static_cast<zxdg_toplevel_v6_state*>(
wl_array_add(states, sizeof(zxdg_toplevel_v6_state)));
DCHECK(value);
*value = state;
}
void OnConfigure(const gfx::Size& size,
ash::mojom::WindowStateType state_type,
bool resizing,
bool activated) {
wl_array states;
wl_array_init(&states);
if (state_type == ash::mojom::WindowStateType::MAXIMIZED)
AddState(&states, ZXDG_TOPLEVEL_V6_STATE_MAXIMIZED);
if (state_type == ash::mojom::WindowStateType::FULLSCREEN)
AddState(&states, ZXDG_TOPLEVEL_V6_STATE_FULLSCREEN);
if (resizing)
AddState(&states, ZXDG_TOPLEVEL_V6_STATE_RESIZING);
if (activated)
AddState(&states, ZXDG_TOPLEVEL_V6_STATE_ACTIVATED);
zxdg_toplevel_v6_send_configure(resource_, size.width(), size.height(),
&states);
wl_array_release(&states);
}
wl_resource* const resource_;
XdgShellSurface* shell_surface_;
base::WeakPtrFactory<WaylandToplevel> weak_ptr_factory_;
DISALLOW_COPY_AND_ASSIGN(WaylandToplevel);
};
void xdg_toplevel_v6_destroy(wl_client* client, wl_resource* resource) {
wl_resource_destroy(resource);
}
void xdg_toplevel_v6_set_parent(wl_client* client,
wl_resource* resource,
wl_resource* parent) {
WaylandToplevel* parent_surface = nullptr;
if (parent)
parent_surface = GetUserDataAs<WaylandToplevel>(parent);
GetUserDataAs<WaylandToplevel>(resource)->SetParent(parent_surface);
}
void xdg_toplevel_v6_set_title(wl_client* client,
wl_resource* resource,
const char* title) {
GetUserDataAs<WaylandToplevel>(resource)->SetTitle(
base::string16(base::UTF8ToUTF16(title)));
}
void xdg_toplevel_v6_set_app_id(wl_client* client,
wl_resource* resource,
const char* app_id) {
NOTIMPLEMENTED();
}
void xdg_toplevel_v6_show_window_menu(wl_client* client,
wl_resource* resource,
wl_resource* seat,
uint32_t serial,
int32_t x,
int32_t y) {
NOTIMPLEMENTED();
}
void xdg_toplevel_v6_move(wl_client* client,
wl_resource* resource,
wl_resource* seat,
uint32_t serial) {
GetUserDataAs<WaylandToplevel>(resource)->Move();
}
void xdg_toplevel_v6_resize(wl_client* client,
wl_resource* resource,
wl_resource* seat,
uint32_t serial,
uint32_t edges) {
GetUserDataAs<WaylandToplevel>(resource)->Resize(
XdgToplevelV6ResizeComponent(edges));
}
void xdg_toplevel_v6_set_max_size(wl_client* client,
wl_resource* resource,
int32_t width,
int32_t height) {
GetUserDataAs<WaylandToplevel>(resource)->SetMaximumSize(
gfx::Size(width, height));
}
void xdg_toplevel_v6_set_min_size(wl_client* client,
wl_resource* resource,
int32_t width,
int32_t height) {
GetUserDataAs<WaylandToplevel>(resource)->SetMinimumSize(
gfx::Size(width, height));
}
void xdg_toplevel_v6_set_maximized(wl_client* client, wl_resource* resource) {
GetUserDataAs<WaylandToplevel>(resource)->Maximize();
}
void xdg_toplevel_v6_unset_maximized(wl_client* client, wl_resource* resource) {
GetUserDataAs<WaylandToplevel>(resource)->Restore();
}
void xdg_toplevel_v6_set_fullscreen(wl_client* client,
wl_resource* resource,
wl_resource* output) {
GetUserDataAs<WaylandToplevel>(resource)->SetFullscreen(true);
}
void xdg_toplevel_v6_unset_fullscreen(wl_client* client,
wl_resource* resource) {
GetUserDataAs<WaylandToplevel>(resource)->SetFullscreen(false);
}
void xdg_toplevel_v6_set_minimized(wl_client* client, wl_resource* resource) {
GetUserDataAs<WaylandToplevel>(resource)->Minimize();
}
const struct zxdg_toplevel_v6_interface xdg_toplevel_v6_implementation = {
xdg_toplevel_v6_destroy, xdg_toplevel_v6_set_parent,
xdg_toplevel_v6_set_title, xdg_toplevel_v6_set_app_id,
xdg_toplevel_v6_show_window_menu, xdg_toplevel_v6_move,
xdg_toplevel_v6_resize, xdg_toplevel_v6_set_max_size,
xdg_toplevel_v6_set_min_size, xdg_toplevel_v6_set_maximized,
xdg_toplevel_v6_unset_maximized, xdg_toplevel_v6_set_fullscreen,
xdg_toplevel_v6_unset_fullscreen, xdg_toplevel_v6_set_minimized};
////////////////////////////////////////////////////////////////////////////////
// xdg_popup_interface:
// Wrapper around shell surface that allows us to handle the case where the
// xdg surface resource is destroyed before the popup resource.
class WaylandPopup : aura::WindowObserver {
public:
WaylandPopup(wl_resource* resource, wl_resource* surface_resource)
: resource_(resource),
shell_surface_(GetUserDataAs<ShellSurface>(surface_resource)),
weak_ptr_factory_(this) {
shell_surface_->host_window()->AddObserver(this);
shell_surface_->set_close_callback(
base::Bind(&WaylandPopup::OnClose, weak_ptr_factory_.GetWeakPtr()));
shell_surface_->set_configure_callback(
base::Bind(&HandleXdgSurfaceV6ConfigureCallback, surface_resource,
base::Bind(&WaylandPopup::OnConfigure,
weak_ptr_factory_.GetWeakPtr())));
}
~WaylandPopup() override {
if (shell_surface_)
shell_surface_->host_window()->RemoveObserver(this);
}
void Grab() {
if (!shell_surface_) {
wl_resource_post_error(resource_, ZXDG_POPUP_V6_ERROR_INVALID_GRAB,
"the surface has already been destroyed");
return;
}
if (shell_surface_->GetWidget()) {
wl_resource_post_error(resource_, ZXDG_POPUP_V6_ERROR_INVALID_GRAB,
"grab must be called before construction");
return;
}
shell_surface_->Grab();
}
// Overridden from aura::WindowObserver:
void OnWindowDestroying(aura::Window* window) override {
shell_surface_ = nullptr;
}
private:
void OnClose() {
zxdg_popup_v6_send_popup_done(resource_);
wl_client_flush(wl_resource_get_client(resource_));
}
void OnConfigure(const gfx::Size& size,
ash::mojom::WindowStateType state_type,
bool resizing,
bool activated) {
// Nothing to do here as popups don't have additional configure state.
}
wl_resource* const resource_;
ShellSurface* shell_surface_;
base::WeakPtrFactory<WaylandPopup> weak_ptr_factory_;
DISALLOW_COPY_AND_ASSIGN(WaylandPopup);
};
void xdg_popup_v6_destroy(wl_client* client, wl_resource* resource) {
wl_resource_destroy(resource);
}
void xdg_popup_v6_grab(wl_client* client,
wl_resource* resource,
wl_resource* seat,
uint32_t serial) {
GetUserDataAs<WaylandPopup>(resource)->Grab();
}
const struct zxdg_popup_v6_interface xdg_popup_v6_implementation = {
xdg_popup_v6_destroy, xdg_popup_v6_grab};
////////////////////////////////////////////////////////////////////////////////
// xdg_surface_interface:
void xdg_surface_v6_destroy(wl_client* client, wl_resource* resource) {
wl_resource_destroy(resource);
}
void xdg_surface_v6_get_toplevel(wl_client* client,
wl_resource* resource,
uint32_t id) {
ShellSurface* shell_surface = GetUserDataAs<ShellSurface>(resource);
if (shell_surface->enabled()) {
wl_resource_post_error(resource, ZXDG_SURFACE_V6_ERROR_ALREADY_CONSTRUCTED,
"surface has already been constructed");
return;
}
shell_surface->SetCanMinimize(true);
shell_surface->SetEnabled(true);
wl_resource* xdg_toplevel_resource =
wl_resource_create(client, &zxdg_toplevel_v6_interface, 1, id);
SetImplementation(
xdg_toplevel_resource, &xdg_toplevel_v6_implementation,
std::make_unique<WaylandToplevel>(xdg_toplevel_resource, resource));
}
void xdg_surface_v6_get_popup(wl_client* client,
wl_resource* resource,
uint32_t id,
wl_resource* parent_resource,
wl_resource* positioner_resource) {
XdgShellSurface* shell_surface = GetUserDataAs<XdgShellSurface>(resource);
if (shell_surface->enabled()) {
wl_resource_post_error(resource, ZXDG_SURFACE_V6_ERROR_ALREADY_CONSTRUCTED,
"surface has already been constructed");
return;
}
XdgShellSurface* parent = GetUserDataAs<XdgShellSurface>(parent_resource);
if (!parent->GetWidget()) {
wl_resource_post_error(resource, ZXDG_SURFACE_V6_ERROR_NOT_CONSTRUCTED,
"popup parent not constructed");
return;
}
if (shell_surface->GetWidget()) {
wl_resource_post_error(resource, ZXDG_SURFACE_V6_ERROR_ALREADY_CONSTRUCTED,
"get_popup is called after constructed");
return;
}
display::Display display =
display::Screen::GetScreen()->GetDisplayNearestWindow(
parent->GetWidget()->GetNativeWindow());
gfx::Rect work_area = display.work_area();
wm::ConvertRectFromScreen(parent->GetWidget()->GetNativeWindow(), &work_area);
WaylandPositioner* positioner =
GetUserDataAs<WaylandPositioner>(positioner_resource);
// Try layout using parent's flip state.
positioner->x_flipped = parent->x_flipped();
positioner->y_flipped = parent->y_flipped();
gfx::Point position = positioner->CalculatePosition(work_area);
// Remember the new flip state for its child popups.
shell_surface->set_x_flipped(positioner->x_flipped);
shell_surface->set_y_flipped(positioner->y_flipped);
// |position| is relative to the parent's contents view origin, and |origin|
// is in screen coordinates.
gfx::Point origin = position;
views::View::ConvertPointToScreen(
parent->GetWidget()->widget_delegate()->GetContentsView(), &origin);
shell_surface->SetOrigin(origin);
shell_surface->SetContainer(ash::kShellWindowId_MenuContainer);
shell_surface->DisableMovement();
shell_surface->SetActivatable(false);
shell_surface->SetCanMinimize(false);
shell_surface->SetParent(parent);
shell_surface->SetPopup();
shell_surface->SetEnabled(true);
wl_resource* xdg_popup_resource =
wl_resource_create(client, &zxdg_popup_v6_interface, 1, id);
SetImplementation(
xdg_popup_resource, &xdg_popup_v6_implementation,
std::make_unique<WaylandPopup>(xdg_popup_resource, resource));
}
void xdg_surface_v6_set_window_geometry(wl_client* client,
wl_resource* resource,
int32_t x,
int32_t y,
int32_t width,
int32_t height) {
GetUserDataAs<ShellSurface>(resource)->SetGeometry(
gfx::Rect(x, y, width, height));
}
void xdg_surface_v6_ack_configure(wl_client* client,
wl_resource* resource,
uint32_t serial) {
GetUserDataAs<ShellSurface>(resource)->AcknowledgeConfigure(serial);
}
const struct zxdg_surface_v6_interface xdg_surface_v6_implementation = {
xdg_surface_v6_destroy, xdg_surface_v6_get_toplevel,
xdg_surface_v6_get_popup, xdg_surface_v6_set_window_geometry,
xdg_surface_v6_ack_configure};
////////////////////////////////////////////////////////////////////////////////
// xdg_shell_interface:
void xdg_shell_v6_destroy(wl_client* client, wl_resource* resource) {
// Nothing to do here.
}
void xdg_shell_v6_create_positioner(wl_client* client,
wl_resource* resource,
uint32_t id) {
wl_resource* positioner_resource =
wl_resource_create(client, &zxdg_positioner_v6_interface, 1, id);
SetImplementation(positioner_resource, &xdg_positioner_v6_implementation,
std::make_unique<WaylandPositioner>());
}
void xdg_shell_v6_get_xdg_surface(wl_client* client,
wl_resource* resource,
uint32_t id,
wl_resource* surface) {
std::unique_ptr<ShellSurface> shell_surface =
GetUserDataAs<Display>(resource)->CreateXdgShellSurface(
GetUserDataAs<Surface>(surface));
if (!shell_surface) {
wl_resource_post_error(resource, ZXDG_SHELL_V6_ERROR_ROLE,
"surface has already been assigned a role");
return;
}
// Xdg shell v6 surfaces are initially disabled and needs to be explicitly
// mapped before they are enabled and can become visible.
shell_surface->SetEnabled(false);
wl_resource* xdg_surface_resource =
wl_resource_create(client, &zxdg_surface_v6_interface, 1, id);
SetImplementation(xdg_surface_resource, &xdg_surface_v6_implementation,
std::move(shell_surface));
}
void xdg_shell_v6_pong(wl_client* client,
wl_resource* resource,
uint32_t serial) {
NOTIMPLEMENTED();
}
const struct zxdg_shell_v6_interface xdg_shell_v6_implementation = {
xdg_shell_v6_destroy, xdg_shell_v6_create_positioner,
xdg_shell_v6_get_xdg_surface, xdg_shell_v6_pong};
void bind_xdg_shell_v6(wl_client* client,
void* data,
uint32_t version,
uint32_t id) {
wl_resource* resource =
wl_resource_create(client, &zxdg_shell_v6_interface, 1, id);
wl_resource_set_implementation(resource, &xdg_shell_v6_implementation, data,
nullptr);
}
////////////////////////////////////////////////////////////////////////////////
// remote_surface_interface:
SurfaceFrameType RemoteShellSurfaceFrameType(uint32_t frame_type) {
switch (frame_type) {
case ZCR_REMOTE_SURFACE_V1_FRAME_TYPE_NONE:
return SurfaceFrameType::NONE;
case ZCR_REMOTE_SURFACE_V1_FRAME_TYPE_NORMAL:
return SurfaceFrameType::NORMAL;
case ZCR_REMOTE_SURFACE_V1_FRAME_TYPE_SHADOW:
return SurfaceFrameType::SHADOW;
case ZCR_REMOTE_SURFACE_V1_FRAME_TYPE_AUTOHIDE:
return SurfaceFrameType::AUTOHIDE;
case ZCR_REMOTE_SURFACE_V1_FRAME_TYPE_OVERLAY:
return SurfaceFrameType::OVERLAY;
default:
VLOG(2) << "Unknown remote-shell frame type: " << frame_type;
return SurfaceFrameType::NONE;
}
}
void remote_surface_destroy(wl_client* client, wl_resource* resource) {
wl_resource_destroy(resource);
}
void remote_surface_set_app_id(wl_client* client,
wl_resource* resource,
const char* app_id) {
GetUserDataAs<ShellSurfaceBase>(resource)->SetApplicationId(app_id);
}
void remote_surface_set_window_geometry(wl_client* client,
wl_resource* resource,
int32_t x,
int32_t y,
int32_t width,
int32_t height) {
GetUserDataAs<ShellSurfaceBase>(resource)->SetGeometry(
gfx::Rect(x, y, width, height));
}
void remote_surface_set_orientation(wl_client* client,
wl_resource* resource,
int32_t orientation) {
GetUserDataAs<ClientControlledShellSurface>(resource)->SetOrientation(
orientation == ZCR_REMOTE_SURFACE_V1_ORIENTATION_PORTRAIT
? Orientation::PORTRAIT
: Orientation::LANDSCAPE);
}
void remote_surface_set_scale(wl_client* client,
wl_resource* resource,
wl_fixed_t scale) {
GetUserDataAs<ClientControlledShellSurface>(resource)->SetScale(
wl_fixed_to_double(scale));
}
void remote_surface_set_rectangular_shadow_DEPRECATED(wl_client* client,
wl_resource* resource,
int32_t x,
int32_t y,
int32_t width,
int32_t height) {
NOTIMPLEMENTED();
}
void remote_surface_set_rectangular_shadow_background_opacity_DEPRECATED(
wl_client* client,
wl_resource* resource,
wl_fixed_t opacity) {
NOTIMPLEMENTED();
}
void remote_surface_set_title(wl_client* client,
wl_resource* resource,
const char* title) {
GetUserDataAs<ShellSurfaceBase>(resource)->SetTitle(
base::string16(base::UTF8ToUTF16(title)));
}
void remote_surface_set_top_inset(wl_client* client,
wl_resource* resource,
int32_t height) {
GetUserDataAs<ClientControlledShellSurface>(resource)->SetTopInset(height);
}
void remote_surface_activate(wl_client* client,
wl_resource* resource,
uint32_t serial) {
ShellSurfaceBase* shell_surface = GetUserDataAs<ShellSurfaceBase>(resource);
shell_surface->Activate();
}
void remote_surface_maximize(wl_client* client, wl_resource* resource) {
GetUserDataAs<ClientControlledShellSurface>(resource)->SetMaximized();
}
void remote_surface_minimize(wl_client* client, wl_resource* resource) {
GetUserDataAs<ClientControlledShellSurface>(resource)->SetMinimized();
}
void remote_surface_restore(wl_client* client, wl_resource* resource) {
GetUserDataAs<ClientControlledShellSurface>(resource)->SetRestored();
}
void remote_surface_fullscreen(wl_client* client, wl_resource* resource) {
GetUserDataAs<ClientControlledShellSurface>(resource)->SetFullscreen(true);
}
void remote_surface_unfullscreen(wl_client* client, wl_resource* resource) {
GetUserDataAs<ClientControlledShellSurface>(resource)->SetFullscreen(false);
}
void remote_surface_pin(wl_client* client,
wl_resource* resource,
int32_t trusted) {
GetUserDataAs<ClientControlledShellSurface>(resource)->SetPinned(
trusted ? ash::mojom::WindowPinType::TRUSTED_PINNED
: ash::mojom::WindowPinType::PINNED);
}
void remote_surface_unpin(wl_client* client, wl_resource* resource) {
GetUserDataAs<ClientControlledShellSurface>(resource)->SetPinned(
ash::mojom::WindowPinType::NONE);
}
void remote_surface_set_system_modal(wl_client* client, wl_resource* resource) {
GetUserDataAs<ClientControlledShellSurface>(resource)->SetSystemModal(true);
}
void remote_surface_unset_system_modal(wl_client* client,
wl_resource* resource) {
GetUserDataAs<ClientControlledShellSurface>(resource)->SetSystemModal(false);
}
void remote_surface_set_rectangular_surface_shadow(wl_client* client,
wl_resource* resource,
int32_t x,
int32_t y,
int32_t width,
int32_t height) {
ClientControlledShellSurface* shell_surface =
GetUserDataAs<ClientControlledShellSurface>(resource);
shell_surface->SetShadowBounds(gfx::Rect(x, y, width, height));
}
void remote_surface_set_systemui_visibility(wl_client* client,
wl_resource* resource,
uint32_t visibility) {
GetUserDataAs<ClientControlledShellSurface>(resource)->SetSystemUiVisibility(
visibility != ZCR_REMOTE_SURFACE_V1_SYSTEMUI_VISIBILITY_STATE_VISIBLE);
}
void remote_surface_set_always_on_top(wl_client* client,
wl_resource* resource) {
GetUserDataAs<ClientControlledShellSurface>(resource)->SetAlwaysOnTop(true);
}
void remote_surface_unset_always_on_top(wl_client* client,
wl_resource* resource) {
GetUserDataAs<ClientControlledShellSurface>(resource)->SetAlwaysOnTop(false);
}
void remote_surface_ack_configure(wl_client* client,
wl_resource* resource,
uint32_t serial) {
GetUserDataAs<ShellSurfaceBase>(resource)->AcknowledgeConfigure(serial);
}
void remote_surface_move(wl_client* client, wl_resource* resource) {
// DEPRECATED
}
void remote_surface_set_window_type(wl_client* client,
wl_resource* resource,
uint32_t type) {
if (type == ZCR_REMOTE_SURFACE_V1_WINDOW_TYPE_SYSTEM_UI) {
auto* widget = GetUserDataAs<ShellSurfaceBase>(resource)->GetWidget();
if (widget) {
widget->GetNativeWindow()->SetProperty(ash::kHideInOverviewKey, true);
wm::SetWindowVisibilityAnimationType(
widget->GetNativeWindow(), wm::WINDOW_VISIBILITY_ANIMATION_TYPE_FADE);
}
}
}
void remote_surface_resize(wl_client* client, wl_resource* resource) {
// DEPRECATED
}
void remote_surface_set_resize_outset(wl_client* client,
wl_resource* resource,
int32_t outset) {
GetUserDataAs<ClientControlledShellSurface>(resource)->SetResizeOutset(
outset);
}
void remote_surface_start_move(wl_client* client,
wl_resource* resource,
int32_t x,
int32_t y) {
GetUserDataAs<ClientControlledShellSurface>(resource)->StartDrag(
HTCAPTION, gfx::Point(x, y));
}
void remote_surface_set_can_maximize(wl_client* client, wl_resource* resource) {
GetUserDataAs<ClientControlledShellSurface>(resource)->SetCanMaximize(true);
}
void remote_surface_unset_can_maximize(wl_client* client,
wl_resource* resource) {
GetUserDataAs<ClientControlledShellSurface>(resource)->SetCanMaximize(false);
}
void remote_surface_set_min_size(wl_client* client,
wl_resource* resource,
int32_t width,
int32_t height) {
GetUserDataAs<ClientControlledShellSurface>(resource)->SetMinimumSize(
gfx::Size(width, height));
}
void remote_surface_set_max_size(wl_client* client,
wl_resource* resource,
int32_t width,
int32_t height) {
GetUserDataAs<ClientControlledShellSurface>(resource)->SetMaximumSize(
gfx::Size(width, height));
}
void remote_surface_set_snapped_to_left(wl_client* client,
wl_resource* resource) {
GetUserDataAs<ClientControlledShellSurface>(resource)->SetSnappedToLeft();
}
void remote_surface_set_snapped_to_right(wl_client* client,
wl_resource* resource) {
GetUserDataAs<ClientControlledShellSurface>(resource)->SetSnappedToRight();
}
void remote_surface_start_resize(wl_client* client,
wl_resource* resource,
uint32_t direction,
int32_t x,
int32_t y) {
GetUserDataAs<ClientControlledShellSurface>(resource)->StartDrag(
Component(direction), gfx::Point(x, y));
}
void remote_surface_set_frame(wl_client* client,
wl_resource* resource,
uint32_t type) {
ClientControlledShellSurface* shell_surface =
GetUserDataAs<ClientControlledShellSurface>(resource);
shell_surface->root_surface()->SetFrame(RemoteShellSurfaceFrameType(type));
}
void remote_surface_set_frame_buttons(wl_client* client,
wl_resource* resource,
uint32_t visible_button_mask,
uint32_t enabled_button_mask) {
GetUserDataAs<ClientControlledShellSurface>(resource)->SetFrameButtons(
CaptionButtonMask(visible_button_mask),
CaptionButtonMask(enabled_button_mask));
}
void remote_surface_set_extra_title(wl_client* client,
wl_resource* resource,
const char* extra_title) {
GetUserDataAs<ClientControlledShellSurface>(resource)->SetExtraTitle(
base::string16(base::UTF8ToUTF16(extra_title)));
}
ash::OrientationLockType OrientationLock(uint32_t orientation_lock) {
switch (orientation_lock) {
case ZCR_REMOTE_SURFACE_V1_ORIENTATION_LOCK_NONE:
return ash::OrientationLockType::kAny;
case ZCR_REMOTE_SURFACE_V1_ORIENTATION_LOCK_CURRENT:
return ash::OrientationLockType::kCurrent;
case ZCR_REMOTE_SURFACE_V1_ORIENTATION_LOCK_PORTRAIT:
return ash::OrientationLockType::kPortrait;
case ZCR_REMOTE_SURFACE_V1_ORIENTATION_LOCK_LANDSCAPE:
return ash::OrientationLockType::kLandscape;
case ZCR_REMOTE_SURFACE_V1_ORIENTATION_LOCK_PORTRAIT_PRIMARY:
return ash::OrientationLockType::kPortraitPrimary;
case ZCR_REMOTE_SURFACE_V1_ORIENTATION_LOCK_PORTRAIT_SECONDARY:
return ash::OrientationLockType::kPortraitSecondary;
case ZCR_REMOTE_SURFACE_V1_ORIENTATION_LOCK_LANDSCAPE_PRIMARY:
return ash::OrientationLockType::kLandscapePrimary;
case ZCR_REMOTE_SURFACE_V1_ORIENTATION_LOCK_LANDSCAPE_SECONDARY:
return ash::OrientationLockType::kLandscapeSecondary;
}
VLOG(2) << "Unexpected value of orientation_lock: " << orientation_lock;
return ash::OrientationLockType::kAny;
}
void remote_surface_set_orientation_lock(wl_client* client,
wl_resource* resource,
uint32_t orientation_lock) {
GetUserDataAs<ClientControlledShellSurface>(resource)->SetOrientationLock(
OrientationLock(orientation_lock));
}
void remote_surface_pip(wl_client* client, wl_resource* resource) {
GetUserDataAs<ClientControlledShellSurface>(resource)->SetPip();
}
const struct zcr_remote_surface_v1_interface remote_surface_implementation = {
remote_surface_destroy,
remote_surface_set_app_id,
remote_surface_set_window_geometry,
remote_surface_set_scale,
remote_surface_set_rectangular_shadow_DEPRECATED,
remote_surface_set_rectangular_shadow_background_opacity_DEPRECATED,
remote_surface_set_title,
remote_surface_set_top_inset,
remote_surface_activate,
remote_surface_maximize,
remote_surface_minimize,
remote_surface_restore,
remote_surface_fullscreen,
remote_surface_unfullscreen,
remote_surface_pin,
remote_surface_unpin,
remote_surface_set_system_modal,
remote_surface_unset_system_modal,
remote_surface_set_rectangular_surface_shadow,
remote_surface_set_systemui_visibility,
remote_surface_set_always_on_top,
remote_surface_unset_always_on_top,
remote_surface_ack_configure,
remote_surface_move,
remote_surface_set_orientation,
remote_surface_set_window_type,
remote_surface_resize,
remote_surface_set_resize_outset,
remote_surface_start_move,
remote_surface_set_can_maximize,
remote_surface_unset_can_maximize,
remote_surface_set_min_size,
remote_surface_set_max_size,
remote_surface_set_snapped_to_left,
remote_surface_set_snapped_to_right,
remote_surface_start_resize,
remote_surface_set_frame,
remote_surface_set_frame_buttons,
remote_surface_set_extra_title,
remote_surface_set_orientation_lock,
remote_surface_pip};
////////////////////////////////////////////////////////////////////////////////
// notification_surface_interface:
void notification_surface_destroy(wl_client* client, wl_resource* resource) {
wl_resource_destroy(resource);
}
void notification_surface_set_app_id(wl_client* client,
wl_resource* resource,
const char* app_id) {
GetUserDataAs<NotificationSurface>(resource)->SetApplicationId(app_id);
}
const struct zcr_notification_surface_v1_interface
notification_surface_implementation = {notification_surface_destroy,
notification_surface_set_app_id};
////////////////////////////////////////////////////////////////////////////////
// input_method_surface_interface:
void input_method_surface_destroy(wl_client* client, wl_resource* resource) {
wl_resource_destroy(resource);
}
const struct zcr_input_method_surface_v1_interface
input_method_surface_implementation = {input_method_surface_destroy};
////////////////////////////////////////////////////////////////////////////////
// remote_shell_interface:
// Implements remote shell interface and monitors workspace state needed
// for the remote shell interface.
class WaylandRemoteShell : public ash::TabletModeObserver,
public wm::ActivationChangeObserver,
public display::DisplayObserver {
public:
WaylandRemoteShell(Display* display, wl_resource* remote_shell_resource)
: display_(display),
remote_shell_resource_(remote_shell_resource),
weak_ptr_factory_(this) {
auto* helper = WMHelper::GetInstance();
helper->AddTabletModeObserver(this);
helper->AddActivationObserver(this);
display::Screen::GetScreen()->AddObserver(this);
layout_mode_ = helper->IsTabletModeWindowManagerEnabled()
? ZCR_REMOTE_SHELL_V1_LAYOUT_MODE_TABLET
: ZCR_REMOTE_SHELL_V1_LAYOUT_MODE_WINDOWED;
if (wl_resource_get_version(remote_shell_resource_) >= 8) {
double scale_factor = GetDefaultDeviceScaleFactor();
// Send using 16.16 fixed point.
const int kDecimalBits = 24;
int32_t fixed_scale =
static_cast<int32_t>(scale_factor * (1 << kDecimalBits));
zcr_remote_shell_v1_send_default_device_scale_factor(
remote_shell_resource_, fixed_scale);
}
SendDisplayMetrics();
SendActivated(helper->GetActiveWindow(), nullptr);
}
~WaylandRemoteShell() override {
auto* helper = WMHelper::GetInstance();
helper->RemoveTabletModeObserver(this);
helper->RemoveActivationObserver(this);
display::Screen::GetScreen()->RemoveObserver(this);
}
bool HasRelativeSurfaceHierarchy() const {
return wl_resource_get_version(remote_shell_resource_) >= 9;
}
std::unique_ptr<ClientControlledShellSurface> CreateShellSurface(
Surface* surface,
int container,
double default_device_scale_factor) {
return display_->CreateClientControlledShellSurface(
surface, container, default_device_scale_factor);
}
std::unique_ptr<NotificationSurface> CreateNotificationSurface(
Surface* surface,
const std::string& notification_key) {
return display_->CreateNotificationSurface(surface, notification_key);
}
std::unique_ptr<InputMethodSurface> CreateInputMethodSurface(
Surface* surface,
double default_device_scale_factor) {
return display_->CreateInputMethodSurface(surface,
default_device_scale_factor);
}
// Overridden from display::DisplayObserver:
void OnDisplayAdded(const display::Display& new_display) override {
ScheduleSendDisplayMetrics(0);
}
void OnDisplayRemoved(const display::Display& old_display) override {
ScheduleSendDisplayMetrics(0);
}
void OnDisplayMetricsChanged(const display::Display& display,
uint32_t changed_metrics) override {
// No need to update when a primary display has changed without bounds
// change. See WaylandDisplayObserver::OnDisplayMetricsChanged
// for more details.
if (changed_metrics &
(DISPLAY_METRIC_BOUNDS | DISPLAY_METRIC_DEVICE_SCALE_FACTOR |
DISPLAY_METRIC_ROTATION | DISPLAY_METRIC_WORK_AREA)) {
ScheduleSendDisplayMetrics(0);
}
}
// Overridden from ash::TabletModeObserver:
void OnTabletModeStarted() override {
layout_mode_ = ZCR_REMOTE_SHELL_V1_LAYOUT_MODE_TABLET;
ScheduleSendDisplayMetrics(kConfigureDelayAfterLayoutSwitchMs);
}
void OnTabletModeEnding() override {
layout_mode_ = ZCR_REMOTE_SHELL_V1_LAYOUT_MODE_WINDOWED;
ScheduleSendDisplayMetrics(kConfigureDelayAfterLayoutSwitchMs);
}
void OnTabletModeEnded() override {}
// Overridden from wm::ActivationChangeObserver:
void OnWindowActivated(ActivationReason reason,
aura::Window* gained_active,
aura::Window* lost_active) override {
SendActivated(gained_active, lost_active);
}
private:
void ScheduleSendDisplayMetrics(int delay_ms) {
needs_send_display_metrics_ = true;
base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
FROM_HERE, base::Bind(&WaylandRemoteShell::SendDisplayMetrics,
weak_ptr_factory_.GetWeakPtr()),
base::TimeDelta::FromMilliseconds(delay_ms));
}
// Returns the transform that a display's output is currently adjusted for.
wl_output_transform DisplayTransform(display::Display::Rotation rotation) {
switch (rotation) {
case display::Display::ROTATE_0:
return WL_OUTPUT_TRANSFORM_NORMAL;
case display::Display::ROTATE_90:
return WL_OUTPUT_TRANSFORM_90;
case display::Display::ROTATE_180:
return WL_OUTPUT_TRANSFORM_180;
case display::Display::ROTATE_270:
return WL_OUTPUT_TRANSFORM_270;
}
NOTREACHED();
return WL_OUTPUT_TRANSFORM_NORMAL;
}
void SendDisplayMetrics() {
if (!needs_send_display_metrics_)
return;
needs_send_display_metrics_ = false;
const display::Screen* screen = display::Screen::GetScreen();
for (const auto& display : screen->GetAllDisplays()) {
const gfx::Rect& bounds = display.bounds();
const gfx::Insets& insets = display.GetWorkAreaInsets();
double device_scale_factor = WMHelper::GetInstance()
->GetDisplayInfo(display.id())
.device_scale_factor();
zcr_remote_shell_v1_send_workspace(
remote_shell_resource_, static_cast<uint32_t>(display.id() >> 32),
static_cast<uint32_t>(display.id()), bounds.x(), bounds.y(),
bounds.width(), bounds.height(), insets.left(), insets.top(),
insets.right(), insets.bottom(), DisplayTransform(display.rotation()),
wl_fixed_from_double(device_scale_factor), display.IsInternal());
}
zcr_remote_shell_v1_send_configure(remote_shell_resource_, layout_mode_);
wl_client_flush(wl_resource_get_client(remote_shell_resource_));
}
void SendActivated(aura::Window* gained_active, aura::Window* lost_active) {
Surface* gained_active_surface =
gained_active ? ShellSurface::GetMainSurface(gained_active) : nullptr;
Surface* lost_active_surface =
lost_active ? ShellSurface::GetMainSurface(lost_active) : nullptr;
wl_resource* gained_active_surface_resource =
gained_active_surface ? GetSurfaceResource(gained_active_surface)
: nullptr;
wl_resource* lost_active_surface_resource =
lost_active_surface ? GetSurfaceResource(lost_active_surface) : nullptr;
wl_client* client = wl_resource_get_client(remote_shell_resource_);
// If surface that gained active is not owned by remote shell client then
// set it to null.
if (gained_active_surface_resource &&
wl_resource_get_client(gained_active_surface_resource) != client) {
gained_active_surface_resource = nullptr;
}
// If surface that lost active is not owned by remote shell client then
// set it to null.
if (lost_active_surface_resource &&
wl_resource_get_client(lost_active_surface_resource) != client) {
lost_active_surface_resource = nullptr;
}
zcr_remote_shell_v1_send_activated(remote_shell_resource_,
gained_active_surface_resource,
lost_active_surface_resource);
wl_client_flush(client);
}
// The exo display instance. Not owned.
Display* const display_;
// The remote shell resource associated with observer.
wl_resource* const remote_shell_resource_;
bool needs_send_display_metrics_ = true;
int layout_mode_ = ZCR_REMOTE_SHELL_V1_LAYOUT_MODE_WINDOWED;
base::WeakPtrFactory<WaylandRemoteShell> weak_ptr_factory_;
DISALLOW_COPY_AND_ASSIGN(WaylandRemoteShell);
};
void remote_shell_destroy(wl_client* client, wl_resource* resource) {
// Nothing to do here.
}
int RemoteSurfaceContainer(uint32_t container) {
switch (container) {