| // 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 <gaming-input-unstable-v1-server-protocol.h> |
| #include <grp.h> |
| #include <keyboard-configuration-unstable-v1-server-protocol.h> |
| #include <linux/input.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-unstable-v1-server-protocol.h> |
| #include <stylus-unstable-v2-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-v5-server-protocol.h> |
| #include <xdg-shell-unstable-v6-server-protocol.h> |
| |
| #include <algorithm> |
| #include <cstdlib> |
| #include <iterator> |
| #include <string> |
| #include <utility> |
| |
| #include "ash/common/shell_observer.h" |
| #include "ash/public/cpp/shell_window_ids.h" |
| #include "ash/shell.h" |
| #include "base/bind.h" |
| #include "base/cancelable_callback.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/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/display.h" |
| #include "components/exo/gamepad.h" |
| #include "components/exo/gamepad_delegate.h" |
| #include "components/exo/keyboard.h" |
| #include "components/exo/keyboard_delegate.h" |
| #include "components/exo/keyboard_device_configuration_delegate.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/shared_memory.h" |
| #include "components/exo/shell_surface.h" |
| #include "components/exo/sub_surface.h" |
| #include "components/exo/surface.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 "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_observer.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/views/widget/widget.h" |
| #include "ui/views/widget/widget_observer.h" |
| #include "ui/wm/core/coordinate_conversion.h" |
| |
| #if defined(USE_OZONE) |
| #include <drm_fourcc.h> |
| #include <linux-dmabuf-unstable-v1-server-protocol.h> |
| #include <wayland-drm-server-protocol.h> |
| #endif |
| |
| #if BUILDFLAG(USE_XKBCOMMON) |
| #include <xkbcommon/xkbcommon.h> |
| #include "ui/events/keycodes/scoped_xkb.h" // nogncheck |
| #endif |
| |
| DECLARE_UI_CLASS_PROPERTY_TYPE(wl_resource*); |
| |
| namespace exo { |
| namespace wayland { |
| 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>); |
| } |
| |
| // 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()); |
| } |
| |
| // A property key containing the surface resource that is associated with |
| // window. If unset, no surface resource is associated with window. |
| DEFINE_UI_CLASS_PROPERTY_KEY(wl_resource*, kSurfaceResourceKey, nullptr); |
| |
| // A property key containing a boolean set to true if a viewport is associated |
| // with window. |
| DEFINE_UI_CLASS_PROPERTY_KEY(bool, kSurfaceHasViewportKey, false); |
| |
| // A property key containing a boolean set to true if a security object is |
| // associated with window. |
| DEFINE_UI_CLASS_PROPERTY_KEY(bool, kSurfaceHasSecurityKey, false); |
| |
| // A property key containing a boolean set to true if a blending object is |
| // associated with window. |
| DEFINE_UI_CLASS_PROPERTY_KEY(bool, kSurfaceHasBlendingKey, false); |
| |
| wl_resource* GetSurfaceResource(Surface* surface) { |
| return surface->GetProperty(kSurfaceResourceKey); |
| } |
| |
| //////////////////////////////////////////////////////////////////////////////// |
| // 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 = |
| base::MakeUnique<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) { |
| GetUserDataAs<Surface>(resource)->SetOpaqueRegion( |
| region_resource ? *GetUserDataAs<SkRegion>(region_resource) |
| : SkRegion(SkIRect::MakeEmpty())); |
| } |
| |
| void surface_set_input_region(wl_client* client, |
| wl_resource* resource, |
| wl_resource* region_resource) { |
| GetUserDataAs<Surface>(resource)->SetInputRegion( |
| region_resource ? *GetUserDataAs<SkRegion>(region_resource) |
| : SkRegion(SkIRect::MakeLargest())); |
| } |
| |
| void surface_commit(wl_client* client, wl_resource* resource) { |
| GetUserDataAs<Surface>(resource)->Commit(); |
| } |
| |
| void surface_set_buffer_transform(wl_client* client, |
| wl_resource* resource, |
| int transform) { |
| NOTIMPLEMENTED(); |
| } |
| |
| 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, ®ion_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::FileDescriptor(fd, true), 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) |
| |
| //////////////////////////////////////////////////////////////////////////////// |
| // wl_drm_interface: |
| |
| const struct drm_supported_format { |
| uint32_t drm_format; |
| gfx::BufferFormat buffer_format; |
| } drm_supported_formats[] = { |
| {WL_DRM_FORMAT_RGB565, gfx::BufferFormat::BGR_565}, |
| {WL_DRM_FORMAT_XBGR8888, gfx::BufferFormat::RGBX_8888}, |
| {WL_DRM_FORMAT_ABGR8888, gfx::BufferFormat::RGBA_8888}, |
| {WL_DRM_FORMAT_XRGB8888, gfx::BufferFormat::BGRX_8888}, |
| {WL_DRM_FORMAT_ARGB8888, gfx::BufferFormat::BGRA_8888}, |
| {WL_DRM_FORMAT_NV12, gfx::BufferFormat::YUV_420_BIPLANAR}, |
| {WL_DRM_FORMAT_YVU420, gfx::BufferFormat::YVU_420}}; |
| |
| void drm_authenticate(wl_client* client, wl_resource* resource, uint32_t id) { |
| wl_drm_send_authenticated(resource); |
| } |
| |
| void drm_create_buffer(wl_client* client, |
| wl_resource* resource, |
| uint32_t id, |
| uint32_t name, |
| int32_t width, |
| int32_t height, |
| uint32_t stride, |
| uint32_t format) { |
| wl_resource_post_error(resource, WL_DRM_ERROR_INVALID_NAME, |
| "GEM names are not supported"); |
| } |
| |
| void drm_create_planar_buffer(wl_client* client, |
| wl_resource* resource, |
| uint32_t id, |
| uint32_t name, |
| int32_t width, |
| int32_t height, |
| uint32_t format, |
| int32_t offset0, |
| int32_t stride0, |
| int32_t offset1, |
| int32_t stride1, |
| int32_t offset2, |
| int32_t stride3) { |
| wl_resource_post_error(resource, WL_DRM_ERROR_INVALID_NAME, |
| "GEM names are not supported"); |
| } |
| |
| void drm_create_prime_buffer(wl_client* client, |
| wl_resource* resource, |
| uint32_t id, |
| int32_t name, |
| int32_t width, |
| int32_t height, |
| uint32_t format, |
| int32_t offset0, |
| int32_t stride0, |
| int32_t offset1, |
| int32_t stride1, |
| int32_t offset2, |
| int32_t stride2) { |
| const auto* supported_format = |
| std::find_if(drm_supported_formats, |
| drm_supported_formats + arraysize(drm_supported_formats), |
| [format](const drm_supported_format& supported_format) { |
| return supported_format.drm_format == format; |
| }); |
| if (supported_format == |
| (drm_supported_formats + arraysize(drm_supported_formats))) { |
| wl_resource_post_error(resource, WL_DRM_ERROR_INVALID_FORMAT, |
| "invalid format 0x%x", format); |
| return; |
| } |
| |
| std::vector<gfx::NativePixmapPlane> planes; |
| planes.emplace_back(stride0, offset0, 0, 0); |
| planes.emplace_back(stride1, offset1, 0, 0); |
| planes.emplace_back(stride2, offset2, 0, 0); |
| std::vector<base::ScopedFD> fds; |
| |
| size_t num_planes = |
| gfx::NumberOfPlanesForBufferFormat(supported_format->buffer_format); |
| planes.resize(num_planes); |
| fds.push_back(base::ScopedFD(name)); |
| |
| std::unique_ptr<Buffer> buffer = |
| GetUserDataAs<Display>(resource)->CreateLinuxDMABufBuffer( |
| gfx::Size(width, height), supported_format->buffer_format, planes, |
| std::move(fds)); |
| 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)); |
| } |
| |
| const struct wl_drm_interface drm_implementation = { |
| drm_authenticate, drm_create_buffer, drm_create_planar_buffer, |
| drm_create_prime_buffer}; |
| |
| const uint32_t drm_version = 2; |
| |
| void bind_drm(wl_client* client, void* data, uint32_t version, uint32_t id) { |
| wl_resource* resource = wl_resource_create( |
| client, &wl_drm_interface, std::min(version, drm_version), id); |
| |
| wl_resource_set_implementation(resource, &drm_implementation, data, nullptr); |
| |
| if (version >= 2) |
| wl_drm_send_capabilities(resource, WL_DRM_CAPABILITY_PRIME); |
| |
| for (const auto& supported_format : drm_supported_formats) |
| wl_drm_send_format(resource, supported_format.drm_format); |
| } |
| |
| //////////////////////////////////////////////////////////////////////////////// |
| // 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"); |
| return; |
| } |
| } |
| |
| void linux_buffer_params_create(wl_client* client, |
| wl_resource* resource, |
| int32_t width, |
| int32_t height, |
| uint32_t 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; |
| } |
| |
| 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 (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; |
| } |
| |
| LinuxBufferParams* linux_buffer_params = |
| GetUserDataAs<LinuxBufferParams>(resource); |
| |
| size_t num_planes = |
| gfx::NumberOfPlanesForBufferFormat(supported_format->buffer_format); |
| |
| 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; |
| } |
| |
| 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); |
| if (plane_it == linux_buffer_params->planes.end()) { |
| wl_resource_post_error(resource, |
| ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_INCOMPLETE, |
| "missing a plane"); |
| return; |
| } |
| LinuxBufferParams::Plane& plane = plane_it->second; |
| planes.emplace_back(plane.stride, plane.offset, 0, 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); |
| } |
| |
| 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_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 = |
| base::MakeUnique<LinuxBufferParams>(GetUserDataAs<Display>(resource)); |
| |
| wl_resource* linux_buffer_params_resource = |
| wl_resource_create(client, &zwp_linux_buffer_params_v1_interface, 1, 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}; |
| |
| 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, 1, 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)->Move(); |
| } |
| |
| 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) { |
| ShellSurface* shell_surface = GetUserDataAs<ShellSurface>(resource); |
| if (shell_surface->enabled()) |
| return; |
| |
| shell_surface->SetFrame(true); |
| shell_surface->SetRectangularShadowEnabled(true); |
| shell_surface->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; |
| |
| // Parent widget can be found by locating the closest ancestor with a widget. |
| views::Widget* parent_widget = nullptr; |
| aura::Window* parent_window = |
| GetUserDataAs<Surface>(parent_resource)->window(); |
| while (parent_window) { |
| parent_widget = views::Widget::GetWidgetForNativeWindow(parent_window); |
| if (parent_widget) |
| break; |
| parent_window = parent_window->parent(); |
| } |
| |
| DLOG_IF(WARNING, parent_resource && !!parent_widget) |
| << "Parent surface is not a visible shell surface"; |
| |
| gfx::Point origin(x, y); |
| ShellSurface* parent_shell_surface = nullptr; |
| |
| // Set parent if found and it is associated with a shell surface. |
| if (parent_widget && |
| ShellSurface::GetMainSurface(parent_widget->GetNativeWindow())) { |
| wm::ConvertPointToScreen( |
| ShellSurface::GetMainSurface(parent_widget->GetNativeWindow()) |
| ->window(), |
| &origin); |
| // Shell surface widget delegate implementation of GetContentsView() |
| // returns a pointer to the shell surface instance. |
| parent_shell_surface = static_cast<ShellSurface*>( |
| parent_widget->widget_delegate()->GetContentsView()); |
| } |
| |
| if (flags & WL_SHELL_SURFACE_TRANSIENT_INACTIVE) { |
| shell_surface->SetOrigin(origin); |
| shell_surface->SetContainer(ash::kShellWindowId_SystemModalContainer); |
| shell_surface->SetActivatable(false); |
| } else { |
| shell_surface->SetFrame(true); |
| shell_surface->SetParent(parent_shell_surface); |
| } |
| shell_surface->SetRectangularShadowEnabled(true); |
| 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: |
| |
| void HandleShellSurfaceCloseCallback(wl_resource* resource) { |
| // Shell surface interface doesn't have a close event. Just send a ping event |
| // for now. |
| uint32_t serial = wl_display_next_serial( |
| wl_client_get_display(wl_resource_get_client(resource))); |
| wl_shell_surface_send_ping(resource, serial); |
| wl_client_flush(wl_resource_get_client(resource)); |
| } |
| |
| uint32_t HandleShellSurfaceConfigureCallback( |
| wl_resource* resource, |
| const gfx::Size& size, |
| ash::wm::WindowStateType state_type, |
| bool resizing, |
| bool activated) { |
| 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_close_callback( |
| base::Bind(&HandleShellSurfaceCloseCallback, |
| base::Unretained(shell_surface_resource))); |
| |
| 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: |
| |
| wl_output_transform OutputTransform(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; |
| } |
| |
| class WaylandPrimaryDisplayObserver : public display::DisplayObserver { |
| public: |
| WaylandPrimaryDisplayObserver(wl_resource* output_resource) |
| : output_resource_(output_resource) { |
| display::Screen::GetScreen()->AddObserver(this); |
| SendDisplayMetrics(); |
| } |
| ~WaylandPrimaryDisplayObserver() override { |
| display::Screen::GetScreen()->RemoveObserver(this); |
| } |
| |
| // Overridden from display::DisplayObserver: |
| void OnDisplayAdded(const display::Display& new_display) override {} |
| void OnDisplayRemoved(const display::Display& new_display) override {} |
| void OnDisplayMetricsChanged(const display::Display& display, |
| uint32_t changed_metrics) override { |
| if (display::Screen::GetScreen()->GetPrimaryDisplay().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 = |
| display::Screen::GetScreen()->GetPrimaryDisplay(); |
| |
| const display::ManagedDisplayInfo& info = |
| WMHelper::GetInstance()->GetDisplayInfo(display.id()); |
| |
| const float kInchInMm = 25.4f; |
| const char* kUnknownMake = "unknown"; |
| const char* kUnknownModel = "unknown"; |
| |
| 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, kUnknownMake, kUnknownModel, |
| 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 (wl_resource_get_version(output_resource_) >= |
| WL_OUTPUT_DONE_SINCE_VERSION) { |
| wl_output_send_done(output_resource_); |
| } |
| } |
| |
| // The output resource associated with the display. |
| wl_resource* const output_resource_; |
| |
| DISALLOW_COPY_AND_ASSIGN(WaylandPrimaryDisplayObserver); |
| }; |
| |
| const uint32_t output_version = 2; |
| |
| void bind_output(wl_client* client, void* data, uint32_t version, uint32_t id) { |
| wl_resource* resource = wl_resource_create( |
| client, &wl_output_interface, std::min(version, output_version), id); |
| |
| SetImplementation(resource, nullptr, |
| base::MakeUnique<WaylandPrimaryDisplayObserver>(resource)); |
| } |
| |
| //////////////////////////////////////////////////////////////////////////////// |
| // xdg_positioner_interface: |
| |
| 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) { |
| NOTIMPLEMENTED(); |
| } |
| |
| 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) { |
| NOTIMPLEMENTED(); |
| } |
| |
| void xdg_positioner_v6_set_anchor(wl_client* client, |
| wl_resource* resource, |
| uint32_t anchor) { |
| NOTIMPLEMENTED(); |
| } |
| |
| void xdg_positioner_v6_set_gravity(wl_client* client, |
| wl_resource* resource, |
| uint32_t gravity) { |
| NOTIMPLEMENTED(); |
| } |
| |
| void xdg_positioner_v6_set_constraint_adjustment( |
| wl_client* client, |
| wl_resource* resource, |
| uint32_t constraint_adjustment) { |
| NOTIMPLEMENTED(); |
| } |
| |
| void xdg_positioner_v6_set_offset(wl_client* client, |
| wl_resource* resource, |
| int32_t x, |
| int32_t y) { |
| NOTIMPLEMENTED(); |
| } |
| |
| 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; |
| } |
| } |
| |
| 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) { |
| if (!parent) { |
| GetUserDataAs<ShellSurface>(resource)->SetParent(nullptr); |
| return; |
| } |
| |
| // This is a noop if parent has not been mapped. |
| ShellSurface* shell_surface = GetUserDataAs<ShellSurface>(parent); |
| if (shell_surface->GetWidget()) |
| GetUserDataAs<ShellSurface>(resource)->SetParent(shell_surface); |
| } |
| |
| void xdg_toplevel_v6_set_title(wl_client* client, |
| wl_resource* resource, |
| const char* title) { |
| GetUserDataAs<ShellSurface>(resource)->SetTitle( |
| base::string16(base::UTF8ToUTF16(title))); |
| } |
| |
| void xdg_toplevel_v6_set_add_id(wl_client* client, |
| wl_resource* resource, |
| const char* app_id) { |
| GetUserDataAs<ShellSurface>(resource)->SetApplicationId(app_id); |
| } |
| |
| 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<ShellSurface>(resource)->Move(); |
| } |
| |
| void xdg_toplevel_v6_resize(wl_client* client, |
| wl_resource* resource, |
| wl_resource* seat, |
| uint32_t serial, |
| uint32_t edges) { |
| int component = XdgToplevelV6ResizeComponent(edges); |
| if (component != HTNOWHERE) |
| GetUserDataAs<ShellSurface>(resource)->Resize(component); |
| } |
| |
| void xdg_toplevel_v6_set_max_size(wl_client* client, |
| wl_resource* resource, |
| int32_t width, |
| int32_t height) { |
| NOTIMPLEMENTED(); |
| } |
| |
| void xdg_toplevel_v6_set_min_size(wl_client* client, |
| wl_resource* resource, |
| int32_t width, |
| int32_t height) { |
| NOTIMPLEMENTED(); |
| } |
| |
| void xdg_toplevel_v6_set_maximized(wl_client* client, wl_resource* resource) { |
| GetUserDataAs<ShellSurface>(resource)->Maximize(); |
| } |
| |
| void xdg_toplevel_v6_unset_maximized(wl_client* client, wl_resource* resource) { |
| GetUserDataAs<ShellSurface>(resource)->Restore(); |
| } |
| |
| void xdg_toplevel_v6_set_fullscreen(wl_client* client, |
| wl_resource* resource, |
| wl_resource* output) { |
| GetUserDataAs<ShellSurface>(resource)->SetFullscreen(true); |
| } |
| |
| void xdg_toplevel_v6_unset_fullscreen(wl_client* client, |
| wl_resource* resource) { |
| GetUserDataAs<ShellSurface>(resource)->SetFullscreen(false); |
| } |
| |
| void xdg_toplevel_v6_set_minimized(wl_client* client, wl_resource* resource) { |
| GetUserDataAs<ShellSurface>(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_add_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: |
| |
| void xdg_popup_v5_destroy(wl_client* client, wl_resource* resource) { |
| wl_resource_destroy(resource); |
| } |
| |
| const struct xdg_popup_interface xdg_popup_v5_implementation = { |
| xdg_popup_v5_destroy}; |
| |
| 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) { |
| NOTIMPLEMENTED(); |
| } |
| |
| const struct zxdg_popup_v6_interface xdg_popup_v6_implementation = { |
| xdg_popup_v6_destroy, xdg_popup_v6_grab}; |
| |
| //////////////////////////////////////////////////////////////////////////////// |
| // xdg_surface_interface: |
| |
| int XdgSurfaceV5ResizeComponent(uint32_t edges) { |
| switch (edges) { |
| case XDG_SURFACE_RESIZE_EDGE_TOP: |
| return HTTOP; |
| case XDG_SURFACE_RESIZE_EDGE_BOTTOM: |
| return HTBOTTOM; |
| case XDG_SURFACE_RESIZE_EDGE_LEFT: |
| return HTLEFT; |
| case XDG_SURFACE_RESIZE_EDGE_TOP_LEFT: |
| return HTTOPLEFT; |
| case XDG_SURFACE_RESIZE_EDGE_BOTTOM_LEFT: |
| return HTBOTTOMLEFT; |
| case XDG_SURFACE_RESIZE_EDGE_RIGHT: |
| return HTRIGHT; |
| case XDG_SURFACE_RESIZE_EDGE_TOP_RIGHT: |
| return HTTOPRIGHT; |
| case XDG_SURFACE_RESIZE_EDGE_BOTTOM_RIGHT: |
| return HTBOTTOMRIGHT; |
| default: |
| return HTBOTTOMRIGHT; |
| } |
| } |
| |
| void xdg_surface_v5_destroy(wl_client* client, wl_resource* resource) { |
| wl_resource_destroy(resource); |
| } |
| |
| void xdg_surface_v5_set_parent(wl_client* client, |
| wl_resource* resource, |
| wl_resource* parent) { |
| if (!parent) { |
| GetUserDataAs<ShellSurface>(resource)->SetParent(nullptr); |
| return; |
| } |
| |
| // This is a noop if parent has not been mapped. |
| ShellSurface* shell_surface = GetUserDataAs<ShellSurface>(parent); |
| if (shell_surface->GetWidget()) |
| GetUserDataAs<ShellSurface>(resource)->SetParent(shell_surface); |
| } |
| |
| void xdg_surface_v5_set_title(wl_client* client, |
| wl_resource* resource, |
| const char* title) { |
| GetUserDataAs<ShellSurface>(resource) |
| ->SetTitle(base::string16(base::UTF8ToUTF16(title))); |
| } |
| |
| void xdg_surface_v5_set_add_id(wl_client* client, |
| wl_resource* resource, |
| const char* app_id) { |
| GetUserDataAs<ShellSurface>(resource)->SetApplicationId(app_id); |
| } |
| |
| void xdg_surface_v5_show_window_menu(wl_client* client, |
| wl_resource* resource, |
| wl_resource* seat, |
| uint32_t serial, |
| int32_t x, |
| int32_t y) { |
| NOTIMPLEMENTED(); |
| } |
| |
| void xdg_surface_v5_move(wl_client* client, |
| wl_resource* resource, |
| wl_resource* seat, |
| uint32_t serial) { |
| GetUserDataAs<ShellSurface>(resource)->Move(); |
| } |
| |
| void xdg_surface_v5_resize(wl_client* client, |
| wl_resource* resource, |
| wl_resource* seat, |
| uint32_t serial, |
| uint32_t edges) { |
| int component = XdgSurfaceV5ResizeComponent(edges); |
| if (component != HTNOWHERE) |
| GetUserDataAs<ShellSurface>(resource)->Resize(component); |
| } |
| |
| void xdg_surface_v5_ack_configure(wl_client* client, |
| wl_resource* resource, |
| uint32_t serial) { |
| GetUserDataAs<ShellSurface>(resource)->AcknowledgeConfigure(serial); |
| } |
| |
| void xdg_surface_v5_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_v5_set_maximized(wl_client* client, wl_resource* resource) { |
| GetUserDataAs<ShellSurface>(resource)->Maximize(); |
| } |
| |
| void xdg_surface_v5_unset_maximized(wl_client* client, wl_resource* resource) { |
| GetUserDataAs<ShellSurface>(resource)->Restore(); |
| } |
| |
| void xdg_surface_v5_set_fullscreen(wl_client* client, |
| wl_resource* resource, |
| wl_resource* output) { |
| GetUserDataAs<ShellSurface>(resource)->SetFullscreen(true); |
| } |
| |
| void xdg_surface_v5_unset_fullscreen(wl_client* client, wl_resource* resource) { |
| GetUserDataAs<ShellSurface>(resource)->SetFullscreen(false); |
| } |
| |
| void xdg_surface_v5_set_minimized(wl_client* client, wl_resource* resource) { |
| GetUserDataAs<ShellSurface>(resource)->Minimize(); |
| } |
| |
| const struct xdg_surface_interface xdg_surface_v5_implementation = { |
| xdg_surface_v5_destroy, |
| xdg_surface_v5_set_parent, |
| xdg_surface_v5_set_title, |
| xdg_surface_v5_set_add_id, |
| xdg_surface_v5_show_window_menu, |
| xdg_surface_v5_move, |
| xdg_surface_v5_resize, |
| xdg_surface_v5_ack_configure, |
| xdg_surface_v5_set_window_geometry, |
| xdg_surface_v5_set_maximized, |
| xdg_surface_v5_unset_maximized, |
| xdg_surface_v5_set_fullscreen, |
| xdg_surface_v5_unset_fullscreen, |
| xdg_surface_v5_set_minimized}; |
| |
| void xdg_surface_v6_destroy(wl_client* client, wl_resource* resource) { |
| wl_resource_destroy(resource); |
| } |
| |
| void HandleXdgToplevelV6CloseCallback(wl_resource* resource) { |
| zxdg_toplevel_v6_send_close(resource); |
| wl_client_flush(wl_resource_get_client(resource)); |
| } |
| |
| void AddXdgToplevelV6State(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; |
| } |
| |
| uint32_t HandleXdgToplevelV6ConfigureCallback( |
| wl_resource* resource, |
| wl_resource* surface_resource, |
| const gfx::Size& size, |
| ash::wm::WindowStateType state_type, |
| bool resizing, |
| bool activated) { |
| wl_array states; |
| wl_array_init(&states); |
| if (state_type == ash::wm::WINDOW_STATE_TYPE_MAXIMIZED) |
| AddXdgToplevelV6State(&states, ZXDG_TOPLEVEL_V6_STATE_MAXIMIZED); |
| if (state_type == ash::wm::WINDOW_STATE_TYPE_FULLSCREEN) |
| AddXdgToplevelV6State(&states, ZXDG_TOPLEVEL_V6_STATE_FULLSCREEN); |
| if (resizing) |
| AddXdgToplevelV6State(&states, ZXDG_TOPLEVEL_V6_STATE_RESIZING); |
| if (activated) |
| AddXdgToplevelV6State(&states, ZXDG_TOPLEVEL_V6_STATE_ACTIVATED); |
| zxdg_toplevel_v6_send_configure(resource, size.width(), size.height(), |
| &states); |
| uint32_t serial = wl_display_next_serial( |
| wl_client_get_display(wl_resource_get_client(surface_resource))); |
| zxdg_surface_v6_send_configure(surface_resource, serial); |
| wl_client_flush(wl_resource_get_client(resource)); |
| wl_array_release(&states); |
| return serial; |
| } |
| |
| 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->SetEnabled(true); |
| |
| wl_resource* xdg_toplevel_resource = |
| wl_resource_create(client, &zxdg_toplevel_v6_interface, 1, id); |
| |
| shell_surface->set_close_callback( |
| base::Bind(&HandleXdgToplevelV6CloseCallback, |
| base::Unretained(xdg_toplevel_resource))); |
| |
| shell_surface->set_configure_callback(base::Bind( |
| &HandleXdgToplevelV6ConfigureCallback, |
| base::Unretained(xdg_toplevel_resource), base::Unretained(resource))); |
| |
| wl_resource_set_implementation(xdg_toplevel_resource, |
| &xdg_toplevel_v6_implementation, shell_surface, |
| nullptr); |
| } |
| |
| void HandleXdgPopupV6CloseCallback(wl_resource* resource) { |
| zxdg_popup_v6_send_popup_done(resource); |
| wl_client_flush(wl_resource_get_client(resource)); |
| } |
| |
| void xdg_surface_v6_get_popup(wl_client* client, |
| wl_resource* resource, |
| uint32_t id, |
| wl_resource* parent, |
| wl_resource* positioner) { |
| 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->SetEnabled(true); |
| |
| wl_resource* xdg_popup_resource = |
| wl_resource_create(client, &zxdg_popup_v6_interface, 1, id); |
| |
| shell_surface->set_close_callback(base::Bind( |
| &HandleXdgPopupV6CloseCallback, base::Unretained(xdg_popup_resource))); |
| |
| wl_resource_set_implementation( |
| xdg_popup_resource, &xdg_popup_v6_implementation, shell_surface, nullptr); |
| } |
| |
| 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_v5_destroy(wl_client* client, wl_resource* resource) { |
| // Nothing to do here. |
| } |
| |
| // Currently implemented version of the unstable xdg-shell interface. |
| #define XDG_SHELL_VERSION 5 |
| static_assert(XDG_SHELL_VERSION == XDG_SHELL_VERSION_CURRENT, |
| "Interface version doesn't match implementation version"); |
| |
| void xdg_shell_v5_use_unstable_version(wl_client* client, |
| wl_resource* resource, |
| int32_t version) { |
| if (version > XDG_SHELL_VERSION) { |
| wl_resource_post_error(resource, 1, |
| "xdg-shell version not implemented yet."); |
| } |
| } |
| |
| void HandleXdgSurfaceV5CloseCallback(wl_resource* resource) { |
| xdg_surface_send_close(resource); |
| wl_client_flush(wl_resource_get_client(resource)); |
| } |
| |
| void AddXdgSurfaceV5State(wl_array* states, xdg_surface_state state) { |
| xdg_surface_state* value = static_cast<xdg_surface_state*>( |
| wl_array_add(states, sizeof(xdg_surface_state))); |
| DCHECK(value); |
| *value = state; |
| } |
| |
| uint32_t HandleXdgSurfaceV5ConfigureCallback( |
| wl_resource* resource, |
| const gfx::Size& size, |
| ash::wm::WindowStateType state_type, |
| bool resizing, |
| bool activated) { |
| wl_array states; |
| wl_array_init(&states); |
| if (state_type == ash::wm::WINDOW_STATE_TYPE_MAXIMIZED) |
| AddXdgSurfaceV5State(&states, XDG_SURFACE_STATE_MAXIMIZED); |
| if (state_type == ash::wm::WINDOW_STATE_TYPE_FULLSCREEN) |
| AddXdgSurfaceV5State(&states, XDG_SURFACE_STATE_FULLSCREEN); |
| if (resizing) |
| AddXdgSurfaceV5State(&states, XDG_SURFACE_STATE_RESIZING); |
| if (activated) |
| AddXdgSurfaceV5State(&states, XDG_SURFACE_STATE_ACTIVATED); |
| uint32_t serial = wl_display_next_serial( |
| wl_client_get_display(wl_resource_get_client(resource))); |
| xdg_surface_send_configure(resource, size.width(), size.height(), &states, |
| serial); |
| wl_client_flush(wl_resource_get_client(resource)); |
| wl_array_release(&states); |
| return serial; |
| } |
| |
| void xdg_shell_v5_get_xdg_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, XDG_SHELL_ERROR_ROLE, |
| "surface has already been assigned a role"); |
| return; |
| } |
| |
| wl_resource* xdg_surface_resource = |
| wl_resource_create(client, &xdg_surface_interface, 1, id); |
| |
| shell_surface->set_close_callback( |
| base::Bind(&HandleXdgSurfaceV5CloseCallback, |
| base::Unretained(xdg_surface_resource))); |
| |
| shell_surface->set_configure_callback( |
| base::Bind(&HandleXdgSurfaceV5ConfigureCallback, |
| base::Unretained(xdg_surface_resource))); |
| |
| SetImplementation(xdg_surface_resource, &xdg_surface_v5_implementation, |
| std::move(shell_surface)); |
| } |
| |
| void HandleXdgPopupV5CloseCallback(wl_resource* resource) { |
| xdg_popup_send_popup_done(resource); |
| wl_client_flush(wl_resource_get_client(resource)); |
| } |
| |
| void xdg_shell_v5_get_xdg_popup(wl_client* client, |
| wl_resource* resource, |
| uint32_t id, |
| wl_resource* surface, |
| wl_resource* parent, |
| wl_resource* seat, |
| uint32_t serial, |
| int32_t x, |
| int32_t y) { |
| // Parent widget can be found by locating the closest ancestor with a widget. |
| views::Widget* parent_widget = nullptr; |
| aura::Window* parent_window = GetUserDataAs<Surface>(parent)->window(); |
| while (parent_window) { |
| parent_widget = views::Widget::GetWidgetForNativeWindow(parent_window); |
| if (parent_widget) |
| break; |
| parent_window = parent_window->parent(); |
| } |
| |
| // Early out if parent widget was not found or is not associated with a |
| // shell surface. |
| if (!parent_widget || |
| !ShellSurface::GetMainSurface(parent_widget->GetNativeWindow())) { |
| wl_resource_post_error(resource, XDG_SHELL_ERROR_INVALID_POPUP_PARENT, |
| "invalid popup parent surface"); |
| return; |
| } |
| |
| // TODO(reveman): Automatically close popup when clicking outside the |
| // popup window. |
| std::unique_ptr<ShellSurface> shell_surface = |
| GetUserDataAs<Display>(resource)->CreatePopupShellSurface( |
| GetUserDataAs<Surface>(surface), |
| // Shell surface widget delegate implementation of GetContentsView() |
| // returns a pointer to the shell surface instance. |
| static_cast<ShellSurface*>( |
| parent_widget->widget_delegate()->GetContentsView()), |
| gfx::Point(x, y)); |
| if (!shell_surface) { |
| wl_resource_post_error(resource, XDG_SHELL_ERROR_ROLE, |
| "surface has already been assigned a role"); |
| return; |
| } |
| |
| wl_resource* xdg_popup_resource = |
| wl_resource_create(client, &xdg_popup_interface, 1, id); |
| |
| shell_surface->set_close_callback(base::Bind( |
| &HandleXdgPopupV5CloseCallback, base::Unretained(xdg_popup_resource))); |
| |
| SetImplementation(xdg_popup_resource, &xdg_popup_v5_implementation, |
| std::move(shell_surface)); |
| } |
| |
| void xdg_shell_v5_pong(wl_client* client, |
| wl_resource* resource, |
| uint32_t serial) { |
| NOTIMPLEMENTED(); |
| } |
| |
| const struct xdg_shell_interface xdg_shell_v5_implementation = { |
| xdg_shell_v5_destroy, xdg_shell_v5_use_unstable_version, |
| xdg_shell_v5_get_xdg_surface, xdg_shell_v5_get_xdg_popup, |
| xdg_shell_v5_pong}; |
| |
| void bind_xdg_shell_v5(wl_client* client, |
| void* data, |
| uint32_t version, |
| uint32_t id) { |
| wl_resource* resource = |
| wl_resource_create(client, &xdg_shell_interface, 1, id); |
| |
| wl_resource_set_implementation(resource, &xdg_shell_v5_implementation, data, |
| nullptr); |
| } |
| |
| 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* xdg_positioner_resource = |
| wl_resource_create(client, &zxdg_positioner_v6_interface, 1, id); |
| |
| wl_resource_set_implementation(xdg_positioner_resource, |
| &xdg_positioner_v6_implementation, nullptr, |
| nullptr); |
| } |
| |
| 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)->CreateShellSurface( |
| 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: |
| |
| 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<ShellSurface>(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<ShellSurface>(resource)->SetGeometry( |
| gfx::Rect(x, y, width, height)); |
| } |
| |
| void remote_surface_set_scale(wl_client* client, |
| wl_resource* resource, |
| wl_fixed_t scale) { |
| GetUserDataAs<ShellSurface>(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) { |
| ShellSurface* shell_surface = GetUserDataAs<ShellSurface>(resource); |
| gfx::Rect content_bounds(x, y, width, height); |
| shell_surface->SetRectangularShadow_DEPRECATED(content_bounds); |
| } |
| |
| void remote_surface_set_rectangular_shadow_background_opacity( |
| wl_client* client, |
| wl_resource* resource, |
| wl_fixed_t opacity) { |
| GetUserDataAs<ShellSurface>(resource)->SetRectangularShadowBackgroundOpacity( |
| wl_fixed_to_double(opacity)); |
| } |
| |
| void remote_surface_set_title(wl_client* client, |
| wl_resource* resource, |
| const char* title) { |
| GetUserDataAs<ShellSurface>(resource)->SetTitle( |
| base::string16(base::UTF8ToUTF16(title))); |
| } |
| |
| void remote_surface_set_top_inset(wl_client* client, |
| wl_resource* resource, |
| int32_t height) { |
| GetUserDataAs<ShellSurface>(resource)->SetTopInset(height); |
| } |
| |
| void remote_surface_activate(wl_client* client, |
| wl_resource* resource, |
| uint32_t serial) { |
| GetUserDataAs<ShellSurface>(resource)->Activate(); |
| } |
| |
| void remote_surface_maximize(wl_client* client, wl_resource* resource) { |
| GetUserDataAs<ShellSurface>(resource)->Maximize(); |
| } |
| |
| void remote_surface_minimize(wl_client* client, wl_resource* resource) { |
| GetUserDataAs<ShellSurface>(resource)->Minimize(); |
| } |
| |
| void remote_surface_restore(wl_client* client, wl_resource* resource) { |
| GetUserDataAs<ShellSurface>(resource)->Restore(); |
| } |
| |
| void remote_surface_fullscreen(wl_client* client, wl_resource* resource) { |
| GetUserDataAs<ShellSurface>(resource)->SetFullscreen(true); |
| } |
| |
| void remote_surface_unfullscreen(wl_client* client, wl_resource* resource) { |
| GetUserDataAs<ShellSurface>(resource)->SetFullscreen(false); |
| } |
| |
| void remote_surface_pin(wl_client* client, |
| wl_resource* resource, |
| int32_t trusted) { |
| GetUserDataAs<ShellSurface>(resource)->SetPinned(true, trusted); |
| } |
| |
| void remote_surface_unpin(wl_client* client, wl_resource* resource) { |
| GetUserDataAs<ShellSurface>(resource)->SetPinned(false, /* trusted */ false); |
| } |
| |
| void remote_surface_set_system_modal(wl_client* client, wl_resource* resource) { |
| GetUserDataAs<ShellSurface>(resource)->SetSystemModal(true); |
| } |
| |
| void remote_surface_unset_system_modal(wl_client* client, |
| wl_resource* resource) { |
| GetUserDataAs<ShellSurface>(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) { |
| ShellSurface* shell_surface = GetUserDataAs<ShellSurface>(resource); |
| gfx::Rect content_bounds(x, y, width, height); |
| shell_surface->SetRectangularSurfaceShadow(content_bounds); |
| } |
| |
| void remote_surface_ack_configure(wl_client* client, |
| wl_resource* resource, |
| uint32_t serial) { |
| NOTIMPLEMENTED(); |
| } |
| |
| void remote_surface_set_moving(wl_client* client, wl_resource* resource) { |
| NOTIMPLEMENTED(); |
| } |
| |
| void remote_surface_unset_moving(wl_client* client, wl_resource* resource) { |
| NOTIMPLEMENTED(); |
| } |
| |
| 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, |
| 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_ack_configure, |
| remote_surface_set_moving, |
| remote_surface_unset_moving}; |
| |
| //////////////////////////////////////////////////////////////////////////////// |
| // notification_surface_interface: |
| |
| void notification_surface_destroy(wl_client* client, wl_resource* resource) { |
| wl_resource_destroy(resource); |
| } |
| |
| const struct zcr_notification_surface_v1_interface |
| notification_surface_implementation = {notification_surface_destroy}; |
| |
| //////////////////////////////////////////////////////////////////////////////// |
| // remote_shell_interface: |
| |
| // Implements remote shell interface and monitors workspace state needed |
| // for the remote shell interface. |
| class WaylandRemoteShell : public WMHelper::MaximizeModeObserver, |
| public WMHelper::ActivationObserver, |
| 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->AddMaximizeModeObserver(this); |
| helper->AddActivationObserver(this); |
| display::Screen::GetScreen()->AddObserver(this); |
| |
| layout_mode_ = helper->IsMaximizeModeWindowManagerEnabled() |
| ? ZCR_REMOTE_SHELL_V1_LAYOUT_MODE_TABLET |
| : ZCR_REMOTE_SHELL_V1_LAYOUT_MODE_WINDOWED; |
| |
| SendPrimaryDisplayMetrics(); |
| SendActivated(helper->GetActiveWindow(), nullptr); |
| } |
| ~WaylandRemoteShell() override { |
| auto* helper = WMHelper::GetInstance(); |
| helper->RemoveMaximizeModeObserver(this); |
| helper->RemoveActivationObserver(this); |
| display::Screen::GetScreen()->RemoveObserver(this); |
| } |
| |
| std::unique_ptr<ShellSurface> CreateShellSurface(Surface* surface, |
| int container) { |
| return display_->CreateRemoteShellSurface(surface, container); |
| } |
| |
| std::unique_ptr<NotificationSurface> CreateNotificationSurface( |
| Surface* surface, |
| const std::string& notification_id) { |
| return display_->CreateNotificationSurface(surface, notification_id); |
| } |
| |
| // Overridden from display::DisplayObserver: |
| void OnDisplayAdded(const display::Display& new_display) override {} |
| void OnDisplayRemoved(const display::Display& new_display) override {} |
| void OnDisplayMetricsChanged(const display::Display& display, |
| uint32_t changed_metrics) override { |
| if (display::Screen::GetScreen()->GetPrimaryDisplay().id() != display.id()) |
| return; |
| |
| // No need to update when a primary dislpay has changed without bounds |
| // change. See WaylandPrimaryDisplayObserver::OnDisplayMetricsChanged |
| // for more details. |
| if (changed_metrics & |
| (DISPLAY_METRIC_BOUNDS | DISPLAY_METRIC_DEVICE_SCALE_FACTOR | |
| DISPLAY_METRIC_ROTATION | DISPLAY_METRIC_WORK_AREA)) { |
| SendDisplayMetrics(display); |
| } |
| } |
| |
| // Overridden from WMHelper::MaximizeModeObserver: |
| void OnMaximizeModeStarted() override { |
| layout_mode_ = ZCR_REMOTE_SHELL_V1_LAYOUT_MODE_TABLET; |
| |
| send_configure_after_layout_change_ = true; |
| base::ThreadTaskRunnerHandle::Get()->PostDelayedTask( |
| FROM_HERE, base::Bind(&WaylandRemoteShell::MaybeSendConfigure, |
| weak_ptr_factory_.GetWeakPtr()), |
| base::TimeDelta::FromMilliseconds(kConfigureDelayAfterLayoutSwitchMs)); |
| } |
| void OnMaximizeModeEnded() override { |
| layout_mode_ = ZCR_REMOTE_SHELL_V1_LAYOUT_MODE_WINDOWED; |
| send_configure_after_layout_change_ = true; |
| base::ThreadTaskRunnerHandle::Get()->PostDelayedTask( |
| FROM_HERE, base::Bind(&WaylandRemoteShell::MaybeSendConfigure, |
| weak_ptr_factory_.GetWeakPtr()), |
| base::TimeDelta::FromMilliseconds(kConfigureDelayAfterLayoutSwitchMs)); |
| } |
| |
| // Overridden from WMHelper::ActivationObserver: |
| void OnWindowActivated(aura::Window* gained_active, |
| aura::Window* lost_active) override { |
| SendActivated(gained_active, lost_active); |
| } |
| |
| private: |
| void SendPrimaryDisplayMetrics() { |
| const display::Display primary = |
| display::Screen::GetScreen()->GetPrimaryDisplay(); |
| |
| SendDisplayMetrics(primary); |
| } |
| |
| void MaybeSendConfigure() { |
| if (send_configure_after_layout_change_) |
| SendPrimaryDisplayMetrics(); |
| } |
| |
| void SendDisplayMetrics(const display::Display& display) { |
| send_configure_after_layout_change_ = false; |
| |
| const gfx::Insets& work_area_insets = display.GetWorkAreaInsets(); |
| |
| zcr_remote_shell_v1_send_configuration_changed( |
| remote_shell_resource_, display.size().width(), display.size().height(), |
| OutputTransform(display.rotation()), |
| wl_fixed_from_double(display.device_scale_factor()), |
| work_area_insets.left(), work_area_insets.top(), |
| work_area_insets.right(), work_area_insets.bottom(), 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 send_configure_after_layout_change_ = false; |
| |
| 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) { |
| case ZCR_REMOTE_SHELL_V1_CONTAINER_DEFAULT: |
| return ash::kShellWindowId_DefaultContainer; |
| case ZCR_REMOTE_SHELL_V1_CONTAINER_OVERLAY: |
| return ash::kShellWindowId_SystemModalContainer; |
| default: |
| DLOG(WARNING) << "Unsupported container: " << container; |
| return ash::kShellWindowId_DefaultContainer; |
| } |
| } |
| |
| void HandleRemoteSurfaceCloseCallback(wl_resource* resource) { |
| zcr_remote_surface_v1_send_close(resource); |
| wl_client_flush(wl_resource_get_client(resource)); |
| } |
| |
| void HandleRemoteSurfaceStateChangedCallback( |
| wl_resource* resource, |
| ash::wm::WindowStateType old_state_type, |
| ash::wm::WindowStateType new_state_type) { |
| DCHECK_NE(old_state_type, new_state_type); |
| |
| uint32_t state_type = ZCR_REMOTE_SHELL_V1_STATE_TYPE_NORMAL; |
| switch (new_state_type) { |
| case ash::wm::WINDOW_STATE_TYPE_MINIMIZED: |
| state_type = ZCR_REMOTE_SHELL_V1_STATE_TYPE_MINIMIZED; |
| break; |
| case ash::wm::WINDOW_STATE_TYPE_MAXIMIZED: |
| state_type = ZCR_REMOTE_SHELL_V1_STATE_TYPE_MAXIMIZED; |
| break; |
| case ash::wm::WINDOW_STATE_TYPE_FULLSCREEN: |
| state_type = ZCR_REMOTE_SHELL_V1_STATE_TYPE_FULLSCREEN; |
| break; |
| case ash::wm::WINDOW_STATE_TYPE_PINNED: |
| state_type = ZCR_REMOTE_SHELL_V1_STATE_TYPE_PINNED; |
| break; |
| case ash::wm::WINDOW_STATE_TYPE_TRUSTED_PINNED: |
| state_type = ZCR_REMOTE_SHELL_V1_STATE_TYPE_TRUSTED_PINNED; |
| break; |
| default: |
| break; |
| } |
| |
| zcr_remote_surface_v1_send_state_type_changed(resource, state_type); |
| 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) { |
| std::unique_ptr<ShellSurface> shell_surface = |
| GetUserDataAs<WaylandRemoteShell>(resource)->CreateShellSurface( |
| GetUserDataAs<Surface>(surface), RemoteSurfaceContainer(container)); |
| 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); |
| |
| shell_surface->set_close_callback( |
| base::Bind(&HandleRemoteSurfaceCloseCallback, |
| base::Unretained(remote_surface_resource))); |
| shell_surface->set_state_changed_callback( |
| base::Bind(&HandleRemoteSurfaceStateChangedCallback, |
| base::Unretained(remote_surface_resource))); |
| |
| 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_id) { |
| 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_id)); |
| if (!notification_surface) { |
| wl_resource_post_error(resource, |
| ZCR_REMOTE_SHELL_V1_ERROR_INVALID_NOTIFICATION_ID, |
| "invalid notification id"); |
| 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)); |
| } |
| |
| const struct zcr_remote_shell_v1_interface remote_shell_implementation = { |
| remote_shell_destroy, remote_shell_get_remote_surface, |
| remote_shell_get_notification_surface}; |
| |
| const uint32_t remote_shell_version = 2; |
| |
| 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(version, remote_shell_version), id); |
| |
| SetImplementation(resource, &remote_shell_implementation, |
| base::MakeUnique<WaylandRemoteShell>( |
| static_cast<Display*>(data), resource)); |
| } |
| |
| //////////////////////////////////////////////////////////////////////////////// |
| // vsync_timing_interface: |
| |
| // Implements VSync timing interface by monitoring a compositor for updates |
| // to VSync parameters. |
| class VSyncTiming : public ui::CompositorVSyncManager::Observer { |
| public: |
| ~VSyncTiming() { vsync_manager_->RemoveObserver(this); } |
| |
| static std::unique_ptr<VSyncTiming> Create(ui::Compositor* compositor, |
| wl_resource* timing_resource) { |
| std::unique_ptr<VSyncTiming> vsync_timing( |
| new VSyncTiming(compositor, timing_resource)); |
| // Note: AddObserver() will call OnUpdateVSyncParameters. |
| vsync_timing->vsync_manager_->AddObserver(vsync_timing.get()); |
| return vsync_timing; |
| } |
| |
| // Overridden from ui::CompositorVSyncManager::Observer: |
| void OnUpdateVSyncParameters(base::TimeTicks timebase, |
| base::TimeDelta interval) override { |
| uint64_t timebase_us = timebase.ToInternalValue(); |
| uint64_t interval_us = interval.ToInternalValue(); |
| |
| // Ignore updates with interval 0. |
| if (!interval_us) |
| return; |
| |
| uint64_t offset_us = timebase_us % interval_us; |
| |
| // Avoid sending update events if interval did not change. |
| if (interval_us == last_interval_us_) { |
| int64_t offset_delta_us = |
| static_cast<int64_t>(last_offset_us_ - offset_us); |
| |
| // Reduce the amount of events by only sending an update if the offset |
| // changed compared to the last offset sent to the client by this amount. |
| const int64_t kOffsetDeltaThresholdInMicroseconds = 25; |
| |
| if (std::abs(offset_delta_us) < kOffsetDeltaThresholdInMicroseconds) |
| return; |
| } |
| |
| zcr_vsync_timing_v1_send_update(timing_resource_, timebase_us & 0xffffffff, |
| timebase_us >> 32, interval_us & 0xffffffff, |
| interval_us >> 32); |
| wl_client_flush(wl_resource_get_client(timing_resource_)); |
| |
| last_interval_us_ = interval_us; |
| last_offset_us_ = offset_us; |
| } |
| |
| private: |
| VSyncTiming(ui::Compositor* compositor, wl_resource* timing_resource) |
| : vsync_manager_(compositor->vsync_manager()), |
| timing_resource_(timing_resource) {} |
| |
| // The VSync manager being observed. |
| scoped_refptr<ui::CompositorVSyncManager> vsync_manager_; |
| |
| // The VSync timing resource. |
| wl_resource* const timing_resource_; |
| |
| uint64_t last_interval_us_{0}; |
| uint64_t last_offset_us_{0}; |
| |
| DISALLOW_COPY_AND_ASSIGN(VSyncTiming); |
| }; |
| |
| void vsync_timing_destroy(wl_client* client, wl_resource* resource) { |
| wl_resource_destroy(resource); |
| } |
| |
| const struct zcr_vsync_timing_v1_interface vsync_timing_implementation = { |
| vsync_timing_destroy}; |
| |
| //////////////////////////////////////////////////////////////////////////////// |
| // vsync_feedback_interface: |
| |
| void vsync_feedback_destroy(wl_client* client, wl_resource* resource) { |
| wl_resource_destroy(resource); |
| } |
| |
| void vsync_feedback_get_vsync_timing(wl_client* client, |
| wl_resource* resource, |
| uint32_t id, |
| wl_resource* output) { |
| wl_resource* timing_resource = |
| wl_resource_create(client, &zcr_vsync_timing_v1_interface, 1, id); |
| |
| // TODO(reveman): Multi-display support. |
| ui::Compositor* compositor = |
| ash::Shell::GetPrimaryRootWindow()->layer()->GetCompositor(); |
| |
| SetImplementation(timing_resource, &vsync_timing_implementation, |
| VSyncTiming::Create(compositor, timing_resource)); |
| } |
| |
| const struct zcr_vsync_feedback_v1_interface vsync_feedback_implementation = { |
| vsync_feedback_destroy, vsync_feedback_get_vsync_timing}; |
| |
| void bind_vsync_feedback(wl_client* client, |
| void* data, |
| uint32_t version, |
| uint32_t id) { |
| wl_resource* resource = |
| wl_resource_create(client, &zcr_vsync_feedback_v1_interface, 1, id); |
| |
| wl_resource_set_implementation(resource, &vsync_feedback_implementation, |
| nullptr, nullptr); |
| } |
| |
| //////////////////////////////////////////////////////////////////////////////// |
| // wl_data_device_interface: |
| |
| void data_device_start_drag(wl_client* client, |
| wl_resource* resource, |
| wl_resource* source_resource, |
| wl_resource* origin_resource, |
| wl_resource* icon_resource, |
| uint32_t serial) { |
| NOTIMPLEMENTED(); |
| } |
| |
| void data_device_set_selection(wl_client* client, |
| wl_resource* resource, |
| wl_resource* data_source, |
| uint32_t serial) { |
| NOTIMPLEMENTED(); |
| } |
| |
| const struct wl_data_device_interface data_device_implementation = { |
| data_device_start_drag, data_device_set_selection}; |
| |
| //////////////////////////////////////////////////////////////////////////////// |
| // wl_data_device_manager_interface: |
| |
| void data_device_manager_create_data_source(wl_client* client, |
| wl_resource* resource, |
| uint32_t id) { |
| NOTIMPLEMENTED(); |
| } |
| |
| void data_device_manager_get_data_device(wl_client* client, |
| wl_resource* resource, |
| uint32_t id, |
| wl_resource* seat_resource) { |
| wl_resource* data_device_resource = |
| wl_resource_create(client, &wl_data_device_interface, 1, id); |
| |
| wl_resource_set_implementation(data_device_resource, |
| &data_device_implementation, nullptr, nullptr); |
| } |
| |
| const struct wl_data_device_manager_interface |
| data_device_manager_implementation = { |
| data_device_manager_create_data_source, |
| data_device_manager_get_data_device}; |
| |
| void bind_data_device_manager(wl_client* client, |
| void* data, |
| uint32_t version, |
| uint32_t id) { |
| wl_resource* resource = |
| wl_resource_create(client, &wl_data_device_manager_interface, 1, id); |
| |
| wl_resource_set_implementation(resource, &data_device_manager_implementation, |
| data, nullptr); |
| } |
| |
| //////////////////////////////////////////////////////////////////////////////// |
| // wl_pointer_interface: |
| |
| // Pointer delegate class that accepts events for surfaces owned by the same |
| // client as a pointer resource. |
| class WaylandPointerDelegate : public PointerDelegate { |
| public: |
| explicit WaylandPointerDelegate(wl_resource* pointer_resource) |
| : pointer_resource_(pointer_resource) {} |
| |
| // Overridden from PointerDelegate: |
| void OnPointerDestroying(Pointer* pointer) override { delete this; } |
| bool CanAcceptPointerEventsForSurface(Surface* surface) const override { |
| wl_resource* surface_resource = GetSurfaceResource(surface); |
| // We can accept events for this surface if the client is the same as the |
| // pointer. |
| return surface_resource && |
| wl_resource_get_client(surface_resource) == client(); |
| } |
| void OnPointerEnter(Surface* surface, |
| const gfx::PointF& location, |
| int button_flags) override { |
| wl_resource* surface_resource = GetSurfaceResource(surface); |
| DCHECK(surface_resource); |
| // Should we be sending button events to the client before the enter event |
| // if client's pressed button state is different from |button_flags|? |
| wl_pointer_send_enter(pointer_resource_, next_serial(), surface_resource, |
| wl_fixed_from_double(location.x()), |
| wl_fixed_from_double(location.y())); |
| } |
| void OnPointerLeave(Surface* surface) override { |
| wl_resource* surface_resource = GetSurfaceResource(surface); |
| DCHECK(surface_resource); |
| wl_pointer_send_leave(pointer_resource_, next_serial(), surface_resource); |
| } |
| void OnPointerMotion(base::TimeTicks time_stamp, |
| const gfx::PointF& location) override { |
| wl_pointer_send_motion( |
| pointer_resource_, TimeTicksToMilliseconds(time_stamp), |
| wl_fixed_from_double(location.x()), wl_fixed_from_double(location.y())); |
| } |
| void OnPointerButton(base::TimeTicks time_stamp, |
| int button_flags, |
| bool pressed) override { |
| struct { |
| ui::EventFlags flag; |
| uint32_t value; |
| } buttons[] = { |
| {ui::EF_LEFT_MOUSE_BUTTON, BTN_LEFT}, |
| {ui::EF_RIGHT_MOUSE_BUTTON, BTN_RIGHT}, |
| {ui::EF_MIDDLE_MOUSE_BUTTON, BTN_MIDDLE}, |
| {ui::EF_FORWARD_MOUSE_BUTTON, BTN_FORWARD}, |
| {ui::EF_BACK_MOUSE_BUTTON, BTN_BACK}, |
| }; |
| uint32_t serial = next_serial(); |
| for (auto button : buttons) { |
| if (button_flags & button.flag) { |
| wl_pointer_send_button( |
| pointer_resource_, serial, TimeTicksToMilliseconds(time_stamp), |
| button.value, pressed ? WL_POINTER_BUTTON_STATE_PRESSED |
| : WL_POINTER_BUTTON_STATE_RELEASED); |
| } |
| } |
| } |
| void OnPointerScroll(base::TimeTicks time_stamp, |
| const gfx::Vector2dF& offset, |
| bool discrete) override { |
| // Same as Weston, the reference compositor. |
| const double kAxisStepDistance = 10.0 / ui::MouseWheelEvent::kWheelDelta; |
| |
| if (wl_resource_get_version(pointer_resource_) >= |
| WL_POINTER_AXIS_SOURCE_SINCE_VERSION) { |
| int32_t axis_source = discrete ? WL_POINTER_AXIS_SOURCE_WHEEL |
| : WL_POINTER_AXIS_SOURCE_FINGER; |
| wl_pointer_send_axis_source(pointer_resource_, axis_source); |
| } |
| |
| double x_value = offset.x() * kAxisStepDistance; |
| wl_pointer_send_axis(pointer_resource_, TimeTicksToMilliseconds(time_stamp), |
| WL_POINTER_AXIS_HORIZONTAL_SCROLL, |
| wl_fixed_from_double(-x_value)); |
| |
| double y_value = offset.y() * kAxisStepDistance; |
| wl_pointer_send_axis(pointer_resource_, TimeTicksToMilliseconds(time_stamp), |
| WL_POINTER_AXIS_VERTICAL_SCROLL, |
| wl_fixed_from_double(-y_value)); |
| } |
| void OnPointerScrollCancel(base::TimeTicks time_stamp) override { |
| // Wayland doesn't know the concept of a canceling kinetic scrolling. |
| // But we can send a 0 distance scroll to emulate this behavior. |
| OnPointerScroll(time_stamp, gfx::Vector2dF(0, 0), false); |
| OnPointerScrollStop(time_stamp); |
| } |
| void OnPointerScrollStop(base::TimeTicks time_stamp) override { |
| if (wl_resource_get_version(pointer_resource_) >= |
| WL_POINTER_AXIS_STOP_SINCE_VERSION) { |
| wl_pointer_send_axis_stop(pointer_resource_, |
| TimeTicksToMilliseconds(time_stamp), |
| WL_POINTER_AXIS_HORIZONTAL_SCROLL); |
| wl_pointer_send_axis_stop(pointer_resource_, |
| TimeTicksToMilliseconds(time_stamp), |
| WL_POINTER_AXIS_VERTICAL_SCROLL); |
| } |
| } |
| void OnPointerFrame() override { |
| if (wl_resource_get_version(pointer_resource_) >= |
| WL_POINTER_FRAME_SINCE_VERSION) { |
| wl_pointer_send_frame(pointer_resource_); |
| } |
| wl_client_flush(client()); |
| } |
| |
| private: |
| // The client who own this pointer instance. |
| wl_client* client() const { |
| return wl_resource_get_client(pointer_resource_); |
| } |
| |
| // Returns the next serial to use for pointer events. |
| uint32_t next_serial() const { |
| return wl_display_next_serial(wl_client_get_display(client())); |
| } |
| |
| // The pointer resource associated with the pointer. |
| wl_resource* const pointer_resource_; |
| |
| DISALLOW_COPY_AND_ASSIGN(WaylandPointerDelegate); |
| }; |
| |
| void pointer_set_cursor(wl_client* client, |
| wl_resource* resource, |
| uint32_t serial, |
| wl_resource* surface_resource, |
| int32_t hotspot_x, |
| int32_t hotspot_y) { |
| GetUserDataAs<Pointer>(resource)->SetCursor( |
| surface_resource ? GetUserDataAs<Surface>(surface_resource) : nullptr, |
| gfx::Point(hotspot_x, hotspot_y)); |
| } |
| |
| void pointer_release(wl_client* client, wl_resource* resource) { |
| wl_resource_destroy(resource); |
| } |
| |
| const struct wl_pointer_interface pointer_implementation = {pointer_set_cursor, |
| pointer_release}; |
| |
| #if BUILDFLAG(USE_XKBCOMMON) |
| |
| //////////////////////////////////////////////////////////////////////////////// |
| // wl_keyboard_interface: |
| |
| // Keyboard delegate class that accepts events for surfaces owned by the same |
| // client as a keyboard resource. |
| class WaylandKeyboardDelegate : public KeyboardDelegate { |
| public: |
| explicit WaylandKeyboardDelegate(wl_resource* keyboard_resource) |
| : keyboard_resource_(keyboard_resource), |
| xkb_context_(xkb_context_new(XKB_CONTEXT_NO_FLAGS)), |
| // TODO(reveman): Keep keymap synchronized with the keymap used by |
| // chromium and the host OS. |
| xkb_keymap_(xkb_keymap_new_from_names(xkb_context_.get(), |
| nullptr, |
| XKB_KEYMAP_COMPILE_NO_FLAGS)), |
| xkb_state_(xkb_state_new(xkb_keymap_.get())) { |
| std::unique_ptr<char, base::FreeDeleter> keymap_string( |
| xkb_keymap_get_as_string(xkb_keymap_.get(), XKB_KEYMAP_FORMAT_TEXT_V1)); |
| DCHECK(keymap_string.get()); |
| size_t keymap_size = strlen(keymap_string.get()) + 1; |
| base::SharedMemory shared_keymap; |
| bool rv = shared_keymap.CreateAndMapAnonymous(keymap_size); |
| DCHECK(rv); |
| memcpy(shared_keymap.memory(), keymap_string.get(), keymap_size); |
| wl_keyboard_send_keymap(keyboard_resource_, |
| WL_KEYBOARD_KEYMAP_FORMAT_XKB_V1, |
| shared_keymap.handle().fd, keymap_size); |
| } |
| |
| // Overridden from KeyboardDelegate: |
| void OnKeyboardDestroying(Keyboard* keyboard) override { delete this; } |
| bool CanAcceptKeyboardEventsForSurface(Surface* surface) const override { |
| wl_resource* surface_resource = GetSurfaceResource(surface); |
| // We can accept events for this surface if the client is the same as the |
| // keyboard. |
| return surface_resource && |
| wl_resource_get_client(surface_resource) == client(); |
| } |
| void OnKeyboardEnter(Surface* surface, |
| const std::vector<ui::DomCode>& pressed_keys) override { |
| wl_resource* surface_resource = GetSurfaceResource(surface); |
| DCHECK(surface_resource); |
| wl_array keys; |
| wl_array_init(&keys); |
| for (auto key : pressed_keys) { |
| uint32_t* value = |
| static_cast<uint32_t*>(wl_array_add(&keys, sizeof(uint32_t))); |
| DCHECK(value); |
| *value = DomCodeToKey(key); |
| } |
| wl_keyboard_send_enter(keyboard_resource_, next_serial(), surface_resource, |
| &keys); |
| wl_array_release(&keys); |
| wl_client_flush(client()); |
| } |
| void OnKeyboardLeave(Surface* surface) override { |
| wl_resource* surface_resource = GetSurfaceResource(surface); |
| DCHECK(surface_resource); |
| wl_keyboard_send_leave(keyboard_resource_, next_serial(), surface_resource); |
| wl_client_flush(client()); |
| } |
| void OnKeyboardKey(base::TimeTicks time_stamp, |
| ui::DomCode key, |
| bool pressed) override { |
| wl_keyboard_send_key(keyboard_resource_, next_serial(), |
| TimeTicksToMilliseconds(time_stamp), DomCodeToKey(key), |
| pressed ? WL_KEYBOARD_KEY_STATE_PRESSED |
| : WL_KEYBOARD_KEY_STATE_RELEASED); |
| wl_client_flush(client()); |
| } |
| void OnKeyboardModifiers(int modifier_flags) override { |
| xkb_state_update_mask(xkb_state_.get(), |
| ModifierFlagsToXkbModifiers(modifier_flags), 0, 0, 0, |
| 0, 0); |
| wl_keyboard_send_modifiers( |
| keyboard_resource_, next_serial(), |
| xkb_state_serialize_mods(xkb_state_.get(), XKB_STATE_MODS_DEPRESSED), |
| xkb_state_serialize_mods(xkb_state_.get(), XKB_STATE_MODS_LOCKED), |
| xkb_state_serialize_mods(xkb_state_.get(), XKB_STATE_MODS_LATCHED), |
| xkb_state_serialize_layout(xkb_state_.get(), |
| XKB_STATE_LAYOUT_EFFECTIVE)); |
| wl_client_flush(client()); |
| } |
| |
| private: |
| // Returns the corresponding key given a dom code. |
| uint32_t DomCodeToKey(ui::DomCode code) const { |
| // This assumes KeycodeConverter has been built with evdev/xkb codes. |
| xkb_keycode_t xkb_keycode = static_cast<xkb_keycode_t>( |
| ui::KeycodeConverter::DomCodeToNativeKeycode(code)); |
| |
| // Keycodes are offset by 8 in Xkb. |
| DCHECK_GE(xkb_keycode, 8u); |
| return xkb_keycode - 8; |
| } |
| |
| // Returns a set of Xkb modififers given a set of modifier flags. |
| uint32_t ModifierFlagsToXkbModifiers(int modifier_flags) { |
| struct { |
| ui::EventFlags flag; |
| const char* xkb_name; |
| } modifiers[] = { |
| {ui::EF_SHIFT_DOWN, XKB_MOD_NAME_SHIFT}, |
| {ui::EF_CONTROL_DOWN, XKB_MOD_NAME_CTRL}, |
| {ui::EF_ALT_DOWN, XKB_MOD_NAME_ALT}, |
| {ui::EF_COMMAND_DOWN, XKB_MOD_NAME_LOGO}, |
| {ui::EF_ALTGR_DOWN, "Mod5"}, |
| {ui::EF_MOD3_DOWN, "Mod3"}, |
| {ui::EF_NUM_LOCK_ON, XKB_MOD_NAME_NUM}, |
| {ui::EF_CAPS_LOCK_ON, XKB_MOD_NAME_CAPS}, |
| }; |
| uint32_t xkb_modifiers = 0; |
| for (auto modifier : modifiers) { |
| if (modifier_flags & modifier.flag) { |
| xkb_modifiers |= |
| 1 << xkb_keymap_mod_get_index(xkb_keymap_.get(), modifier.xkb_name); |
| } |
| } |
| return xkb_modifiers; |
| } |
| |
| // The client who own this keyboard instance. |
| wl_client* client() const { |
| return wl_resource_get_client(keyboard_resource_); |
| } |
| |
| // Returns the next serial to use for keyboard events. |
| uint32_t next_serial() const { |
| return wl_display_next_serial(wl_client_get_display(client())); |
| } |
| |
| // The keyboard resource associated with the keyboard. |
| wl_resource* const keyboard_resource_; |
| |
| // The Xkb state used for the keyboard. |
| std::unique_ptr<xkb_context, ui::XkbContextDeleter> xkb_context_; |
| std::unique_ptr<xkb_keymap, ui::XkbKeymapDeleter> xkb_keymap_; |
| std::unique_ptr<xkb_state, ui::XkbStateDeleter> xkb_state_; |
| |
| DISALLOW_COPY_AND_ASSIGN(WaylandKeyboardDelegate); |
| }; |
| |
| void keyboard_release(wl_client* client, wl_resource* resource) { |
| wl_resource_destroy(resource); |
| } |
| |
| const struct wl_keyboard_interface keyboard_implementation = {keyboard_release}; |
| |
| #endif |
| |
| //////////////////////////////////////////////////////////////////////////////// |
| // wl_touch_interface: |
| |
| // Touch delegate class that accepts events for surfaces owned by the same |
| // client as a touch resource. |
| class WaylandTouchDelegate : public TouchDelegate { |
| public: |
| explicit WaylandTouchDelegate(wl_resource* touch_resource) |
| : touch_resource_(touch_resource) {} |
| |
| // Overridden from TouchDelegate: |
| void OnTouchDestroying(Touch* touch) override { delete this; } |
| bool CanAcceptTouchEventsForSurface(Surface* surface) const override { |
| wl_resource* surface_resource = GetSurfaceResource(surface); |
| // We can accept events for this surface if the client is the same as the |
| // touch resource. |
| return surface_resource && |
| wl_resource_get_client(surface_resource) == client(); |
| } |
| void OnTouchDown(Surface* surface, |
| base::TimeTicks time_stamp, |
| int id, |
| const gfx::PointF& location) override { |
| wl_resource* surface_resource = GetSurfaceResource(surface); |
| DCHECK(surface_resource); |
| wl_touch_send_down(touch_resource_, next_serial(), |
| TimeTicksToMilliseconds(time_stamp), surface_resource, |
| id, wl_fixed_from_double(location.x()), |
| wl_fixed_from_double(location.y())); |
| } |
| void OnTouchUp(base::TimeTicks time_stamp, int id) override { |
| wl_touch_send_up(touch_resource_, next_serial(), |
| TimeTicksToMilliseconds(time_stamp), id); |
| } |
| void OnTouchMotion(base::TimeTicks time_stamp, |
| int id, |
| const gfx::PointF& location) override { |
| wl_touch_send_motion(touch_resource_, TimeTicksToMilliseconds(time_stamp), |
| id, wl_fixed_from_double(location.x()), |
| wl_fixed_from_double(location.y())); |
| } |
| void OnTouchShape(int id, float major, float minor) override { |
| if (wl_resource_get_version(touch_resource_) >= |
| WL_TOUCH_SHAPE_SINCE_VERSION) { |
| wl_touch_send_shape(touch_resource_, id, wl_fixed_from_double(major), |
| wl_fixed_from_double(minor)); |
| } |
| } |
| void OnTouchFrame() override { |
| if (wl_resource_get_version(touch_resource_) >= |
| WL_TOUCH_FRAME_SINCE_VERSION) { |
| wl_touch_send_frame(touch_resource_); |
| } |
| wl_client_flush(client()); |
| } |
| void OnTouchCancel() override { |
| wl_touch_send_cancel(touch_resource_); |
| } |
| |
| private: |
| // The client who own this touch instance. |
| wl_client* client() const { return wl_resource_get_client(touch_resource_); } |
| |
| // Returns the next serial to use for keyboard events. |
| uint32_t next_serial() const { |
| return wl_display_next_serial(wl_client_get_display(client())); |
| } |
| |
| // The touch resource associated with the touch. |
| wl_resource* const touch_resource_; |
| |
| DISALLOW_COPY_AND_ASSIGN(WaylandTouchDelegate); |
| }; |
| |
| void touch_release(wl_client* client, wl_resource* resource) { |
| wl_resource_destroy(resource); |
| } |
| |
| const struct wl_touch_interface touch_implementation = {touch_release}; |
| |
| //////////////////////////////////////////////////////////////////////////////// |
| // wl_seat_interface: |
| |
| void seat_get_pointer(wl_client* client, wl_resource* resource, uint32_t id) { |
| wl_resource* pointer_resource = wl_resource_create( |
| client, &wl_pointer_interface, wl_resource_get_version(resource), id); |
| |
| SetImplementation( |
| pointer_resource, &pointer_implementation, |
| base::MakeUnique<Pointer>(new WaylandPointerDelegate(pointer_resource))); |
| } |
| |
| void seat_get_keyboard(wl_client* client, wl_resource* resource, uint32_t id) { |
| #if BUILDFLAG(USE_XKBCOMMON) |
| uint32_t version = wl_resource_get_version(resource); |
| wl_resource* keyboard_resource = |
| wl_resource_create(client, &wl_keyboard_interface, version, id); |
| |
| SetImplementation(keyboard_resource, &keyboard_implementation, |
| base::MakeUnique<Keyboard>( |
| new WaylandKeyboardDelegate(keyboard_resource))); |
| |
| // TODO(reveman): Keep repeat info synchronized with chromium and the host OS. |
| if (version >= WL_KEYBOARD_REPEAT_INFO_SINCE_VERSION) |
| wl_keyboard_send_repeat_info(keyboard_resource, 40, 500); |
| #else |
| NOTIMPLEMENTED(); |
| #endif |
| } |
| |
| void seat_get_touch(wl_client* client, wl_resource* resource, uint32_t id) { |
| wl_resource* touch_resource = wl_resource_create( |
| client, &wl_touch_interface, wl_resource_get_version(resource), id); |
| |
| SetImplementation( |
| touch_resource, &touch_implementation, |
| base::MakeUnique<Touch>(new WaylandTouchDelegate(touch_resource))); |
| } |
| |
| void seat_release(wl_client* client, wl_resource* resource) { |
| wl_resource_destroy(resource); |
| } |
| |
| const struct wl_seat_interface seat_implementation = { |
| seat_get_pointer, seat_get_keyboard, seat_get_touch, seat_release}; |
| |
| const uint32_t seat_version = 5; |
| |
| void bind_seat(wl_client* client, void* data, uint32_t version, uint32_t id) { |
| wl_resource* resource = wl_resource_create( |
| client, &wl_seat_interface, std::min(version, seat_version), id); |
| |
| wl_resource_set_implementation(resource, &seat_implementation, data, nullptr); |
| |
| if (version >= WL_SEAT_NAME_SINCE_VERSION) |
| wl_seat_send_name(resource, "default"); |
| |
| uint32_t capabilities = WL_SEAT_CAPABILITY_POINTER | WL_SEAT_CAPABILITY_TOUCH; |
| #if BUILDFLAG(USE_XKBCOMMON) |
| capabilities |= WL_SEAT_CAPABILITY_KEYBOARD; |
| #endif |
| wl_seat_send_capabilities(resource, capabilities); |
| } |
| |
| //////////////////////////////////////////////////////////////////////////////// |
| // wp_viewport_interface: |
| |
| // Implements the viewport interface to a Surface. The "viewport"-state is set |
| // to null upon destruction. A window property will be set during the lifetime |
| // of this class to prevent multiple instances from being created for the same |
| // Surface. |
| class Viewport : public SurfaceObserver { |
| public: |
| explicit Viewport(Surface* surface) : surface_(surface) { |
| surface_->AddSurfaceObserver(this); |
| surface_->SetProperty(kSurfaceHasViewportKey, true); |
| } |
| ~Viewport() override { |
| if (surface_) { |
| surface_->RemoveSurfaceObserver(this); |
| surface_->SetCrop(gfx::RectF()); |
| surface_->SetViewport(gfx::Size()); |
| surface_->SetProperty(kSurfaceHasViewportKey, false); |
| } |
| } |
| |
| void SetSource(const gfx::RectF& rect) { |
| if (surface_) |
| surface_->SetCrop(rect); |
| } |
| |
| void SetDestination(const gfx::Size& size) { |
| if (surface_) |
| surface_->SetViewport(size); |
| } |
| |
| // Overridden from SurfaceObserver: |
| void OnSurfaceDestroying(Surface* surface) override { |
| surface->RemoveSurfaceObserver(this); |
| surface_ = nullptr; |
| } |
| |
| private: |
| Surface* surface_; |
| |
| DISALLOW_COPY_AND_ASSIGN(Viewport); |
| }; |
| |
| void viewport_destroy(wl_client* client, wl_resource* resource) { |
| wl_resource_destroy(resource); |
| } |
| |
| void viewport_set_source(wl_client* client, |
| wl_resource* resource, |
| wl_fixed_t x, |
| wl_fixed_t y, |
| wl_fixed_t width, |
| wl_fixed_t height) { |
| if (x == wl_fixed_from_int(-1) && y == wl_fixed_from_int(-1) && |
| width == wl_fixed_from_int(-1) && height == wl_fixed_from_int(-1)) { |
| GetUserDataAs<Viewport>(resource)->SetSource(gfx::RectF()); |
| return; |
| } |
| |
| if (x < 0 || y < 0 || width <= 0 || height <= 0) { |
| wl_resource_post_error(resource, WP_VIEWPORT_ERROR_BAD_VALUE, |
| "source rectangle must be non-empty (%dx%d) and" |
| "have positive origin (%d,%d)", |
| width, height, x, y); |
| return; |
| } |
| |
| GetUserDataAs<Viewport>(resource)->SetSource( |
| gfx::RectF(wl_fixed_to_double(x), wl_fixed_to_double(y), |
| wl_fixed_to_double(width), wl_fixed_to_double(height))); |
| } |
| |
| void viewport_set_destination(wl_client* client, |
| wl_resource* resource, |
| int32_t width, |
| int32_t height) { |
| if (width == -1 && height == -1) { |
| GetUserDataAs<Viewport>(resource)->SetDestination(gfx::Size()); |
| return; |
| } |
| |
| if (width <= 0 || height <= 0) { |
| wl_resource_post_error(resource, WP_VIEWPORT_ERROR_BAD_VALUE, |
| "destination size must be positive (%dx%d)", width, |
| height); |
| return; |
| } |
| |
| GetUserDataAs<Viewport>(resource)->SetDestination(gfx::Size(width, height)); |
| } |
| |
| const struct wp_viewport_interface viewport_implementation = { |
| viewport_destroy, viewport_set_source, viewport_set_destination}; |
| |
| //////////////////////////////////////////////////////////////////////////////// |
| // wp_viewporter_interface: |
| |
| void viewporter_destroy(wl_client* client, wl_resource* resource) { |
| wl_resource_destroy(resource); |
| } |
| |
| void viewporter_get_viewport(wl_client* client, |
| wl_resource* resource, |
| uint32_t id, |
| wl_resource* surface_resource) { |
| Surface* surface = GetUserDataAs<Surface>(surface_resource); |
| if (surface->GetProperty(kSurfaceHasViewportKey)) { |
| wl_resource_post_error(resource, WP_VIEWPORTER_ERROR_VIEWPORT_EXISTS, |
| "a viewport for that surface already exists"); |
| return; |
| } |
| |
| wl_resource* viewport_resource = wl_resource_create( |
| client, &wp_viewport_interface, wl_resource_get_version(resource), id); |
| |
| SetImplementation(viewport_resource, &viewport_implementation, |
| base::MakeUnique<Viewport>(surface)); |
| } |
| |
| const struct wp_viewporter_interface viewporter_implementation = { |
| viewporter_destroy, viewporter_get_viewport}; |
| |
| void bind_viewporter(wl_client* client, |
| void* data, |
| uint32_t version, |
| uint32_t id) { |
| wl_resource* resource = |
| wl_resource_create(client, &wp_viewporter_interface, 1, id); |
| |
| wl_resource_set_implementation(resource, &viewporter_implementation, data, |
| nullptr); |
| } |
| |
| //////////////////////////////////////////////////////////////////////////////// |
| // presentation_interface: |
| |
| void HandleSurfacePresentationCallback(wl_resource* resource, |
| base::TimeTicks presentation_time, |
| base::TimeDelta refresh) { |
| if (presentation_time.is_null()) { |
| wp_presentation_feedback_send_discarded(resource); |
| } else { |
| int64_t presentation_time_us = presentation_time.ToInternalValue(); |
| int64_t seconds = presentation_time_us / base::Time::kMicrosecondsPerSecond; |
| int64_t microseconds = |
| presentation_time_us % base::Time::kMicrosecondsPerSecond; |
| wp_presentation_feedback_send_presented( |
| resource, seconds >> 32, seconds & 0xffffffff, |
| microseconds * base::Time::kNanosecondsPerMicrosecond, |
| refresh.InMicroseconds() * base::Time::kNanosecondsPerMicrosecond, 0, 0, |
| WP_PRESENTATION_FEEDBACK_KIND_VSYNC | |
| WP_PRESENTATION_FEEDBACK_KIND_HW_CLOCK | |
| WP_PRESENTATION_FEEDBACK_KIND_HW_COMPLETION); |
| } |
| wl_client_flush(wl_resource_get_client(resource)); |
| } |
| |
| void presentation_destroy(wl_client* client, wl_resource* resource) { |
| wl_resource_destroy(resource); |
| } |
| |
| void presentation_feedback(wl_client* client, |
| wl_resource* resource, |
| wl_resource* surface_resource, |
| uint32_t id) { |
| wl_resource* presentation_feedback_resource = |
| wl_resource_create(client, &wp_presentation_feedback_interface, |
| wl_resource_get_version(resource), id); |
| |
| // base::Unretained is safe as the resource owns the callback. |
| auto cancelable_callback = base::MakeUnique< |
| base::CancelableCallback<void(base::TimeTicks, base::TimeDelta)>>( |
| base::Bind(&HandleSurfacePresentationCallback, |
| base::Unretained(presentation_feedback_resource))); |
| |
| GetUserDataAs<Surface>(surface_resource) |
| ->RequestPresentationCallback(cancelable_callback->callback()); |
| |
| SetImplementation(presentation_feedback_resource, nullptr, |
| std::move(cancelable_callback)); |
| } |
| |
| const struct wp_presentation_interface presentation_implementation = { |
| presentation_destroy, presentation_feedback}; |
| |
| void bind_presentation(wl_client* client, |
| void* data, |
| uint32_t version, |
| uint32_t id) { |
| wl_resource* resource = |
| wl_resource_create(client, &wp_presentation_interface, 1, id); |
| |
| wl_resource_set_implementation(resource, &presentation_implementation, data, |
| nullptr); |
| |
| wp_presentation_send_clock_id(resource, CLOCK_MONOTONIC); |
| } |
| |
| //////////////////////////////////////////////////////////////////////////////// |
| // security_interface: |
| |
| // Implements the security interface to a Surface. The "only visible on secure |
| // output"-state is set to false upon destruction. A window property will be set |
| // during the lifetime of this class to prevent multiple instances from being |
| // created for the same Surface. |
| class Security : public SurfaceObserver { |
| public: |
| explicit Security(Surface* surface) : surface_(surface) { |
| surface_->AddSurfaceObserver(this); |
| surface_->SetProperty(kSurfaceHasSecurityKey, true); |
| } |
| ~Security() override { |
| if (surface_) { |
| surface_->RemoveSurfaceObserver(this); |
| surface_->SetOnlyVisibleOnSecureOutput(false); |
| surface_->SetProperty(kSurfaceHasSecurityKey, false); |
| } |
| } |
| |
| void OnlyVisibleOnSecureOutput() { |
| if (surface_) |
| surface_->SetOnlyVisibleOnSecureOutput(true); |
| } |
| |
| // Overridden from SurfaceObserver: |
| void OnSurfaceDestroying(Surface* surface) override { |
| surface->RemoveSurfaceObserver(this); |
| surface_ = nullptr; |
| } |
| |
| private: |
| Surface* surface_; |
| |
| DISALLOW_COPY_AND_ASSIGN(Security); |
| }; |
| |
| void security_destroy(wl_client* client, wl_resource* resource) { |
| wl_resource_destroy(resource); |
| } |
| |
| void security_only_visible_on_secure_output(wl_client* client, |
| wl_resource* resource) { |
| GetUserDataAs<Security>(resource)->OnlyVisibleOnSecureOutput(); |
| } |
| |
| const struct zcr_security_v1_interface security_implementation = { |
| security_destroy, security_only_visible_on_secure_output}; |
| |
| //////////////////////////////////////////////////////////////////////////////// |
| // secure_output_interface: |
| |
| void secure_output_destroy(wl_client* client, wl_resource* resource) { |
| wl_resource_destroy(resource); |
| } |
| |
| void secure_output_get_security(wl_client* client, |
| wl_resource* resource, |
| uint32_t id, |
| wl_resource* surface_resource) { |
| Surface* surface = GetUserDataAs<Surface>(surface_resource); |
| if (surface->GetProperty(kSurfaceHasSecurityKey)) { |
| wl_resource_post_error(resource, ZCR_SECURE_OUTPUT_V1_ERROR_SECURITY_EXISTS, |
| "a security object for that surface already exists"); |
| return; |
| } |
| |
| wl_resource* security_resource = |
| wl_resource_create(client, &zcr_security_v1_interface, 1, id); |
| |
| SetImplementation(security_resource, &security_implementation, |
| base::MakeUnique<Security>(surface)); |
| } |
| |
| const struct zcr_secure_output_v1_interface secure_output_implementation = { |
| secure_output_destroy, secure_output_get_security}; |
| |
| void bind_secure_output(wl_client* client, |
| void* data, |
| uint32_t version, |
| uint32_t id) { |
| wl_resource* resource = |
| wl_resource_create(client, &zcr_secure_output_v1_interface, 1, id); |
| |
| wl_resource_set_implementation(resource, &secure_output_implementation, data, |
| nullptr); |
| } |
| |
| //////////////////////////////////////////////////////////////////////////////// |
| // blending_interface: |
| |
| // Implements the blending interface to a Surface. The "blend mode" and |
| // "alpha"-state is set to SrcOver and 1 upon destruction. A window property |
| // will be set during the lifetime of this class to prevent multiple instances |
| // from being created for the same Surface. |
| class Blending : public SurfaceObserver { |
| public: |
| explicit Blending(Surface* surface) : surface_(surface) { |
| surface_->AddSurfaceObserver(this); |
| surface_->SetProperty(kSurfaceHasBlendingKey, true); |
| } |
| ~Blending() override { |
| if (surface_) { |
| surface_->RemoveSurfaceObserver(this); |
| surface_->SetBlendMode(SkBlendMode::kSrcOver); |
| surface_->SetAlpha(1.0f); |
| surface_->SetProperty(kSurfaceHasBlendingKey, false); |
| } |
| } |
| |
| void SetBlendMode(SkBlendMode blend_mode) { |
| if (surface_) |
| surface_->SetBlendMode(blend_mode); |
| } |
| |
| void SetAlpha(float value) { |
| if (surface_) |
| surface_->SetAlpha(value); |
| } |
| |
| // Overridden from SurfaceObserver: |
| void OnSurfaceDestroying(Surface* surface) override { |
| surface->RemoveSurfaceObserver(this); |
| surface_ = nullptr; |
| } |
| |
| private: |
| Surface* surface_; |
| |
| DISALLOW_COPY_AND_ASSIGN(Blending); |
| }; |
| |
| void blending_destroy(wl_client* client, wl_resource* resource) { |
| wl_resource_destroy(resource); |
| } |
| |
| void blending_set_blending(wl_client* client, |
| wl_resource* resource, |
| uint32_t equation) { |
| switch (equation) { |
| case ZCR_BLENDING_V1_BLENDING_EQUATION_NONE: |
| GetUserDataAs<Blending>(resource)->SetBlendMode(SkBlendMode::kSrc); |
| break; |
| case ZCR_BLENDING_V1_BLENDING_EQUATION_PREMULT: |
| GetUserDataAs<Blending>(resource)->SetBlendMode(SkBlendMode::kSrcOver); |
| break; |
| case ZCR_BLENDING_V1_BLENDING_EQUATION_COVERAGE: |
| NOTIMPLEMENTED(); |
| break; |
| default: |
| DLOG(WARNING) << "Unsupported blending equation: " << equation; |
| break; |
| } |
| } |
| |
| void blending_set_alpha(wl_client* client, |
| wl_resource* resource, |
| wl_fixed_t alpha) { |
| GetUserDataAs<Blending>(resource)->SetAlpha(wl_fixed_to_double(alpha)); |
| } |
| |
| const struct zcr_blending_v1_interface blending_implementation = { |
| blending_destroy, blending_set_blending, blending_set_alpha}; |
| |
| //////////////////////////////////////////////////////////////////////////////// |
| // alpha_compositing_interface: |
| |
| void alpha_compositing_destroy(wl_client* client, wl_resource* resource) { |
| wl_resource_destroy(resource); |
| } |
| |
| void alpha_compositing_get_blending(wl_client* client, |
| wl_resource* resource, |
| uint32_t id, |
| wl_resource* surface_resource) { |
| Surface* surface = GetUserDataAs<Surface>(surface_resource); |
| if (surface->GetProperty(kSurfaceHasBlendingKey)) { |
| wl_resource_post_error(resource, |
| ZCR_ALPHA_COMPOSITING_V1_ERROR_BLENDING_EXISTS, |
| "a blending object for that surface already exists"); |
| return; |
| } |
| |
| wl_resource* blending_resource = |
| wl_resource_create(client, &zcr_blending_v1_interface, 1, id); |
| |
| SetImplementation(blending_resource, &blending_implementation, |
| base::MakeUnique<Blending>(surface)); |
| } |
| |
| const struct zcr_alpha_compositing_v1_interface |
| alpha_compositing_implementation = {alpha_compositing_destroy, |
| alpha_compositing_get_blending}; |
| |
| void bind_alpha_compositing(wl_client* client, |
| void* data, |
| uint32_t version, |
| uint32_t id) { |
| wl_resource* resource = |
| wl_resource_create(client, &zcr_alpha_compositing_v1_interface, 1, id); |
| |
| wl_resource_set_implementation(resource, &alpha_compositing_implementation, |
| data, nullptr); |
| } |
| |
| //////////////////////////////////////////////////////////////////////////////// |
| // gaming_input_interface: |
| |
| // Gamepad delegate class that forwards gamepad events to the client resource. |
| class WaylandGamepadDelegate : public GamepadDelegate { |
| public: |
| explicit WaylandGamepadDelegate(wl_resource* gamepad_resource) |
| : gamepad_resource_(gamepad_resource) {} |
| |
| // Overridden from GamepadDelegate: |
| void OnGamepadDestroying(Gamepad* gamepad) override { delete this; } |
| bool CanAcceptGamepadEventsForSurface(Surface* surface) const override { |
| wl_resource* surface_resource = GetSurfaceResource(surface); |
| return surface_resource && |
| wl_resource_get_client(surface_resource) == client(); |
| } |
| void OnStateChange(bool connected) override { |
| uint32_t status = connected ? ZCR_GAMEPAD_V1_GAMEPAD_STATE_ON |
| : ZCR_GAMEPAD_V1_GAMEPAD_STATE_OFF; |
| zcr_gamepad_v1_send_state_change(gamepad_resource_, status); |
| wl_client_flush(client()); |
| } |
| void OnAxis(int axis, double value) override { |
| zcr_gamepad_v1_send_axis(gamepad_resource_, NowInMilliseconds(), axis, |
| wl_fixed_from_double(value)); |
| } |
| void OnButton(int button, bool pressed, double value) override { |
| uint32_t state = pressed ? ZCR_GAMEPAD_V1_BUTTON_STATE_PRESSED |
| : ZCR_GAMEPAD_V1_BUTTON_STATE_RELEASED; |
| zcr_gamepad_v1_send_button(gamepad_resource_, NowInMilliseconds(), button, |
| state, wl_fixed_from_double(value)); |
| } |
| void OnFrame() override { |
| zcr_gamepad_v1_send_frame(gamepad_resource_, NowInMilliseconds()); |
| wl_client_flush(client()); |
| } |
| |
| private: |
| // The client who own this gamepad instance. |
| wl_client* client() const { |
| return wl_resource_get_client(gamepad_resource_); |
| } |
| |
| // The gamepad resource associated with the gamepad. |
| wl_resource* const gamepad_resource_; |
| |
| DISALLOW_COPY_AND_ASSIGN(WaylandGamepadDelegate); |
| }; |
| |
| void gamepad_destroy(wl_client* client, wl_resource* resource) { |
| wl_resource_destroy(resource); |
| } |
| |
| const struct zcr_gamepad_v1_interface gamepad_implementation = { |
| gamepad_destroy}; |
| |
| void gaming_input_get_gamepad(wl_client* client, |
| wl_resource* resource, |
| uint32_t id, |
| wl_resource* seat) { |
| wl_resource* gamepad_resource = wl_resource_create( |
| client, &zcr_gamepad_v1_interface, wl_resource_get_version(resource), id); |
| |
| base::Thread* gaming_input_thread = GetUserDataAs<base::Thread>(resource); |
| |
| SetImplementation( |
| gamepad_resource, &gamepad_implementation, |
| base::MakeUnique<Gamepad>(new WaylandGamepadDelegate(gamepad_resource), |
| gaming_input_thread->task_runner().get())); |
| } |
| |
| const struct zcr_gaming_input_v1_interface gaming_input_implementation = { |
| gaming_input_get_gamepad}; |
| |
| void bind_gaming_input(wl_client* client, |
| void* data, |
| uint32_t version, |
| uint32_t id) { |
| wl_resource* resource = |
| wl_resource_create(client, &zcr_gaming_input_v1_interface, version, id); |
| |
| std::unique_ptr<base::Thread> gaming_input_thread( |
| new base::Thread("Exo gaming input polling thread.")); |
| gaming_input_thread->StartWithOptions( |
| base::Thread::Options(base::MessageLoop::TYPE_IO, 0)); |
| |
| SetImplementation(resource, &gaming_input_implementation, |
| std::move(gaming_input_thread)); |
| } |
| |
| //////////////////////////////////////////////////////////////////////////////// |
| // touch_stylus interface: |
| |
| class WaylandTouchStylusDelegate : public TouchStylusDelegate { |
| public: |
| WaylandTouchStylusDelegate(wl_resource* resource, Touch* touch) |
| : resource_(resource), touch_(touch) { |
| touch_->SetStylusDelegate(this); |
| } |
| ~WaylandTouchStylusDelegate() override { |
| if (touch_ != nullptr) |
| touch_->SetStylusDelegate(nullptr); |
| } |
| void OnTouchDestroying(Touch* touch) override { touch_ = nullptr; } |
| void OnTouchTool(int touch_id, ui::EventPointerType type) override { |
| uint wayland_type = ZCR_TOUCH_STYLUS_V2_TOOL_TYPE_TOUCH; |
| if (type == ui::EventPointerType::POINTER_TYPE_PEN) |
| wayland_type = ZCR_TOUCH_STYLUS_V2_TOOL_TYPE_PEN; |
| else if (type == ui::EventPointerType::POINTER_TYPE_ERASER) |
| wayland_type = ZCR_TOUCH_STYLUS_V2_TOOL_TYPE_ERASER; |
| zcr_touch_stylus_v2_send_tool(resource_, touch_id, wayland_type); |
| } |
| void OnTouchForce(base::TimeTicks time_stamp, |
| int touch_id, |
| float force) override { |
| zcr_touch_stylus_v2_send_force(resource_, |
| TimeTicksToMilliseconds(time_stamp), |
| touch_id, wl_fixed_from_double(force)); |
| } |
| void OnTouchTilt(base::TimeTicks time_stamp, |
| int touch_id, |
| const gfx::Vector2dF& tilt) override { |
| zcr_touch_stylus_v2_send_tilt( |
| resource_, TimeTicksToMilliseconds(time_stamp), touch_id, |
| wl_fixed_from_double(tilt.x()), wl_fixed_from_double(tilt.y())); |
| } |
| |
| private: |
| wl_resource* resource_; |
| Touch* touch_; |
| |
| DISALLOW_COPY_AND_ASSIGN(WaylandTouchStylusDelegate); |
| }; |
| |
| void touch_stylus_destroy(wl_client* client, wl_resource* resource) { |
| wl_resource_destroy(resource); |
| } |
| |
| const struct zcr_touch_stylus_v2_interface touch_stylus_implementation = { |
| touch_stylus_destroy}; |
| |
| //////////////////////////////////////////////////////////////////////////////// |
| // stylus_v2 interface: |
| |
| void stylus_get_touch_stylus(wl_client* client, |
| wl_resource* resource, |
| uint32_t id, |
| wl_resource* touch_resource) { |
| Touch* touch = GetUserDataAs<Touch>(touch_resource); |
| if (touch->HasStylusDelegate()) { |
| wl_resource_post_error( |
| resource, ZCR_STYLUS_V2_ERROR_TOUCH_STYLUS_EXISTS, |
| "touch has already been associated with a stylus object"); |
| return; |
| } |
| |
| wl_resource* stylus_resource = |
| wl_resource_create(client, &zcr_touch_stylus_v2_interface, 1, id); |
| |
| SetImplementation( |
| stylus_resource, &touch_stylus_implementation, |
| base::MakeUnique<WaylandTouchStylusDelegate>(stylus_resource, touch)); |
| } |
| |
| const struct zcr_stylus_v2_interface stylus_v2_implementation = { |
| stylus_get_touch_stylus}; |
| |
| void bind_stylus_v2(wl_client* client, |
| void* data, |
| uint32_t version, |
| uint32_t id) { |
| wl_resource* resource = |
| wl_resource_create(client, &zcr_stylus_v2_interface, version, id); |
| wl_resource_set_implementation(resource, &stylus_v2_implementation, data, |
| nullptr); |
| } |
| |
| //////////////////////////////////////////////////////////////////////////////// |
| // pointer_stylus interface (deprecated) |
| // TODO(denniskempin): Remove once client no longer depends on this interface. |
| |
| void pointer_stylus_destroy_DEPRECATED(wl_client* client, |
| wl_resource* resource) { |
| wl_resource_destroy(resource); |
| } |
| |
| const struct zcr_pointer_stylus_v1_interface |
| pointer_stylus_implementation_DEPRECATED = { |
| pointer_stylus_destroy_DEPRECATED}; |
| |
| //////////////////////////////////////////////////////////////////////////////// |
| // stylus_v1 interface (deprecated): |
| // TODO(denniskempin): Remove once client no longer depends on this interface. |
| |
| void stylus_get_pointer_stylus_DEPRECATED(wl_client* client, |
| wl_resource* resource, |
| uint32_t id, |
| wl_resource* pointer_resource) { |
| wl_resource* stylus_resource = |
| wl_resource_create(client, &zcr_pointer_stylus_v1_interface, 1, id); |
| wl_resource_set_implementation(stylus_resource, |
| &pointer_stylus_implementation_DEPRECATED, |
| nullptr, nullptr); |
| } |
| |
| const struct zcr_stylus_v1_interface stylus_v1_implementation_DEPRECATED = { |
| stylus_get_pointer_stylus_DEPRECATED}; |
| |
| void bind_stylus_v1_DEPRECATED(wl_client* client, |
| void* data, |
| uint32_t version, |
| uint32_t id) { |
| wl_resource* resource = |
| wl_resource_create(client, &zcr_stylus_v1_interface, version, id); |
| wl_resource_set_implementation(resource, &stylus_v1_implementation_DEPRECATED, |
| data, nullptr); |
| } |
| |
| //////////////////////////////////////////////////////////////////////////////// |
| // keyboard_device_configuration interface: |
| |
| class WaylandKeyboardDeviceConfigurationDelegate |
| : public KeyboardDeviceConfigurationDelegate { |
| public: |
| WaylandKeyboardDeviceConfigurationDelegate(wl_resource* resource, |
| Keyboard* keyboard) |
| : resource_(resource), keyboard_(keyboard) { |
| keyboard_->SetDeviceConfigurationDelegate(this); |
| } |
| ~WaylandKeyboardDeviceConfigurationDelegate() override { |
| if (keyboard_) |
| keyboard_->SetDeviceConfigurationDelegate(nullptr); |
| } |
| |
| void OnKeyboardDestroying(Keyboard* keyboard) override { |
| keyboard_ = nullptr; |
| } |
| void OnKeyboardTypeChanged(bool is_physical) override { |
| zcr_keyboard_device_configuration_v1_send_type_change( |
| resource_, |
| is_physical |
| ? ZCR_KEYBOARD_DEVICE_CONFIGURATION_V1_KEYBOARD_TYPE_PHYSICAL |
| : ZCR_KEYBOARD_DEVICE_CONFIGURATION_V1_KEYBOARD_TYPE_VIRTUAL); |
| } |
| |
| private: |
| wl_resource* resource_; |
| Keyboard* keyboard_; |
| |
| DISALLOW_COPY_AND_ASSIGN(WaylandKeyboardDeviceConfigurationDelegate); |
| }; |
| |
| void keyboard_device_configuration_destroy(wl_client* client, |
| wl_resource* resource) { |
| wl_resource_destroy(resource); |
| } |
| |
| const struct zcr_keyboard_device_configuration_v1_interface |
| keyboard_device_configuration_implementation = { |
| keyboard_device_configuration_destroy}; |
| |
| //////////////////////////////////////////////////////////////////////////////// |
| // keyboard_configuration interface: |
| |
| void keyboard_configuration_get_keyboard_device_configuration( |
| wl_client* client, |
| wl_resource* resource, |
| uint32_t id, |
| wl_resource* keyboard_resource) { |
| Keyboard* keyboard = GetUserDataAs<Keyboard>(keyboard_resource); |
| if (keyboard->HasDeviceConfigurationDelegate()) { |
| wl_resource_post_error( |
| resource, |
| ZCR_KEYBOARD_CONFIGURATION_V1_ERROR_DEVICE_CONFIGURATION_EXISTS, |
| "keyboard has already been associated with a device configuration " |
| "object"); |
| return; |
| } |
| |
| wl_resource* keyboard_device_configuration_resource = wl_resource_create( |
| client, &zcr_keyboard_device_configuration_v1_interface, 1, id); |
| |
| SetImplementation( |
| keyboard_device_configuration_resource, |
| &keyboard_device_configuration_implementation, |
| base::MakeUnique<WaylandKeyboardDeviceConfigurationDelegate>( |
| keyboard_device_configuration_resource, keyboard)); |
| } |
| |
| const struct zcr_keyboard_configuration_v1_interface |
| keyboard_configuration_implementation = { |
| keyboard_configuration_get_keyboard_device_configuration}; |
| |
| void bind_keyboard_configuration(wl_client* client, |
| void* data, |
| uint32_t version, |
| uint32_t id) { |
| wl_resource* resource = wl_resource_create( |
| client, &zcr_keyboard_configuration_v1_interface, version, id); |
| wl_resource_set_implementation( |
| resource, &keyboard_configuration_implementation, data, nullptr); |
| } |
| |
| } // namespace |
| |
| //////////////////////////////////////////////////////////////////////////////// |
| // Server, public: |
| |
| Server::Server(Display* display) |
| : display_(display), wl_display_(wl_display_create()) { |
| wl_global_create(wl_display_.get(), &wl_compositor_interface, |
| compositor_version, display_, bind_compositor); |
| wl_global_create(wl_display_.get(), &wl_shm_interface, 1, display_, bind_shm); |
| #if defined(USE_OZONE) |
| wl_global_create(wl_display_.get(), &wl_drm_interface, drm_version, display_, |
| bind_drm); |
| wl_global_create(wl_display_.get(), &zwp_linux_dmabuf_v1_interface, 1, |
| display_, bind_linux_dmabuf); |
| #endif |
| wl_global_create(wl_display_.get(), &wl_subcompositor_interface, 1, display_, |
| bind_subcompositor); |
| wl_global_create(wl_display_.get(), &wl_shell_interface, 1, display_, |
| bind_shell); |
| wl_global_create(wl_display_.get(), &wl_output_interface, output_version, |
| display_, bind_output); |
| wl_global_create(wl_display_.get(), &xdg_shell_interface, 1, display_, |
| bind_xdg_shell_v5); |
| wl_global_create(wl_display_.get(), &zxdg_shell_v6_interface, 1, display_, |
| bind_xdg_shell_v6); |
| wl_global_create(wl_display_.get(), &zcr_vsync_feedback_v1_interface, 1, |
| display_, bind_vsync_feedback); |
| wl_global_create(wl_display_.get(), &wl_data_device_manager_interface, 1, |
| display_, bind_data_device_manager); |
| wl_global_create(wl_display_.get(), &wl_seat_interface, seat_version, |
| display_, bind_seat); |
| wl_global_create(wl_display_.get(), &wp_viewporter_interface, 1, display_, |
| bind_viewporter); |
| wl_global_create(wl_display_.get(), &wp_presentation_interface, 1, display_, |
| bind_presentation); |
| wl_global_create(wl_display_.get(), &zcr_secure_output_v1_interface, 1, |
| display_, bind_secure_output); |
| wl_global_create(wl_display_.get(), &zcr_alpha_compositing_v1_interface, 1, |
| display_, bind_alpha_compositing); |
| wl_global_create(wl_display_.get(), &zcr_remote_shell_v1_interface, |
| remote_shell_version, display_, bind_remote_shell); |
| wl_global_create(wl_display_.get(), &zcr_gaming_input_v1_interface, 1, |
| display_, bind_gaming_input); |
| wl_global_create(wl_display_.get(), &zcr_stylus_v1_interface, 2, display_, |
| bind_stylus_v1_DEPRECATED); |
| wl_global_create(wl_display_.get(), &zcr_stylus_v2_interface, 1, display_, |
| bind_stylus_v2); |
| wl_global_create(wl_display_.get(), &zcr_keyboard_configuration_v1_interface, |
| 2, display_, bind_keyboard_configuration); |
| } |
| |
| Server::~Server() {} |
| |
| // static |
| std::unique_ptr<Server> Server::Create(Display* display) { |
| std::unique_ptr<Server> server(new Server(display)); |
| |
| char* runtime_dir = getenv("XDG_RUNTIME_DIR"); |
| if (!runtime_dir) { |
| LOG(ERROR) << "XDG_RUNTIME_DIR not set in the environment"; |
| return nullptr; |
| } |
| |
| if (!server->AddSocket(kSocketName)) { |
| LOG(ERROR) << "Failed to add socket: " << kSocketName; |
| return nullptr; |
| } |
| |
| base::FilePath socket_path = base::FilePath(runtime_dir).Append(kSocketName); |
| |
| // Change permissions on the socket. |
| struct group wayland_group; |
| struct group* wayland_group_res = nullptr; |
| char buf[10000]; |
| if (HANDLE_EINTR(getgrnam_r(kWaylandSocketGroup, &wayland_group, buf, |
| sizeof(buf), &wayland_group_res)) < 0) { |
| PLOG(ERROR) << "getgrnam_r"; |
| return nullptr; |
| } |
| if (wayland_group_res) { |
| if (HANDLE_EINTR(chown(socket_path.MaybeAsASCII().c_str(), -1, |
| wayland_group.gr_gid)) < 0) { |
| PLOG(ERROR) << "chown"; |
| return nullptr; |
| } |
| } else { |
| LOG(WARNING) << "Group '" << kWaylandSocketGroup << "' not found"; |
| } |
| |
| if (!base::SetPosixFilePermissions(socket_path, 0660)) { |
| PLOG(ERROR) << "Could not set permissions: " << socket_path.value(); |
| return nullptr; |
| } |
| |
| return server; |
| } |
| |
| bool Server::AddSocket(const std::string name) { |
| DCHECK(!name.empty()); |
| return !wl_display_add_socket(wl_display_.get(), name.c_str()); |
| } |
| |
| int Server::GetFileDescriptor() const { |
| wl_event_loop* event_loop = wl_display_get_event_loop(wl_display_.get()); |
| DCHECK(event_loop); |
| return wl_event_loop_get_fd(event_loop); |
| } |
| |
| void Server::Dispatch(base::TimeDelta timeout) { |
| wl_event_loop* event_loop = wl_display_get_event_loop(wl_display_.get()); |
| DCHECK(event_loop); |
| wl_event_loop_dispatch(event_loop, timeout.InMilliseconds()); |
| } |
| |
| void Server::Flush() { |
| wl_display_flush_clients(wl_display_.get()); |
| } |
| |
| } // namespace wayland |
| } // namespace exo |