| // Copyright 2015 The Chromium Authors |
| // 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 <chrome-color-management-server-protocol.h> |
| #include <content-type-v1-server-protocol.h> |
| #include <cursor-shapes-unstable-v1-server-protocol.h> |
| #include <extended-drag-unstable-v1-server-protocol.h> |
| #include <gaming-input-unstable-v2-server-protocol.h> |
| #include <grp.h> |
| #include <idle-inhibit-unstable-v1-server-protocol.h> |
| #include <input-timestamps-unstable-v1-server-protocol.h> |
| #include <keyboard-extension-unstable-v1-server-protocol.h> |
| #include <keyboard-shortcuts-inhibit-unstable-v1-server-protocol.h> |
| #include <linux-dmabuf-unstable-v1-server-protocol.h> |
| #include <linux-explicit-synchronization-unstable-v1-server-protocol.h> |
| #include <notification-shell-unstable-v1-server-protocol.h> |
| #include <overlay-prioritizer-server-protocol.h> |
| #include <pointer-constraints-unstable-v1-server-protocol.h> |
| #include <pointer-gestures-unstable-v1-server-protocol.h> |
| #include <presentation-time-server-protocol.h> |
| #include <relative-pointer-unstable-v1-server-protocol.h> |
| #include <stylus-tools-unstable-v1-server-protocol.h> |
| #include <sys/socket.h> |
| #include <text-input-extension-unstable-v1-server-protocol.h> |
| #include <text-input-unstable-v1-server-protocol.h> |
| #include <touchpad-haptics-unstable-v1-server-protocol.h> |
| #include <viewporter-server-protocol.h> |
| #include <vsync-feedback-unstable-v1-server-protocol.h> |
| #include <xdg-decoration-unstable-v1-server-protocol.h> |
| #include <xdg-shell-server-protocol.h> |
| |
| #include <memory> |
| #include <string> |
| #include <utility> |
| |
| #include "ash/constants/ash_features.h" |
| #include "base/command_line.h" |
| #include "base/files/file_path.h" |
| #include "base/files/file_util.h" |
| #include "base/posix/eintr_wrapper.h" |
| #include "base/strings/stringprintf.h" |
| #include "base/system/sys_info.h" |
| #include "base/task/thread_pool.h" |
| #include "build/build_config.h" |
| #include "components/exo/display.h" |
| #include "components/exo/security_delegate.h" |
| #include "components/exo/wayland/client_tracker.h" |
| #include "components/exo/wayland/content_type.h" |
| #include "components/exo/wayland/overlay_prioritizer.h" |
| #include "components/exo/wayland/serial_tracker.h" |
| #include "components/exo/wayland/server_util.h" |
| #include "components/exo/wayland/surface_augmenter.h" |
| #include "components/exo/wayland/wayland_dmabuf_feedback_manager.h" |
| #include "components/exo/wayland/wayland_protocol_logger.h" |
| #include "components/exo/wayland/wayland_watcher.h" |
| #include "components/exo/wayland/wl_compositor.h" |
| #include "components/exo/wayland/wl_data_device_manager.h" |
| #include "components/exo/wayland/wl_seat.h" |
| #include "components/exo/wayland/wl_shell.h" |
| #include "components/exo/wayland/wl_shm.h" |
| #include "components/exo/wayland/wl_subcompositor.h" |
| #include "components/exo/wayland/wp_fractional_scale.h" |
| #include "components/exo/wayland/wp_presentation.h" |
| #include "components/exo/wayland/wp_single_pixel_buffer.h" |
| #include "components/exo/wayland/wp_viewporter.h" |
| #include "components/exo/wayland/xdg_shell.h" |
| #include "components/exo/wayland/zaura_shell.h" |
| #include "components/exo/wayland/zcr_alpha_compositing.h" |
| #include "components/exo/wayland/zcr_color_manager.h" |
| #include "components/exo/wayland/zcr_cursor_shapes.h" |
| #include "components/exo/wayland/zcr_extended_drag.h" |
| #include "components/exo/wayland/zcr_gaming_input.h" |
| #include "components/exo/wayland/zcr_keyboard_configuration.h" |
| #include "components/exo/wayland/zcr_keyboard_extension.h" |
| #include "components/exo/wayland/zcr_notification_shell.h" |
| #include "components/exo/wayland/zcr_remote_shell.h" |
| #include "components/exo/wayland/zcr_remote_shell_v2.h" |
| #include "components/exo/wayland/zcr_stylus.h" |
| #include "components/exo/wayland/zcr_stylus_tools.h" |
| #include "components/exo/wayland/zcr_test_controller.h" |
| #include "components/exo/wayland/zcr_touchpad_haptics.h" |
| #include "components/exo/wayland/zcr_ui_controls.h" |
| #include "components/exo/wayland/zcr_vsync_feedback.h" |
| #include "components/exo/wayland/zwp_idle_inhibit_manager.h" |
| #include "components/exo/wayland/zwp_input_timestamps_manager.h" |
| #include "components/exo/wayland/zwp_keyboard_shortcuts_inhibit_manager.h" |
| #include "components/exo/wayland/zwp_linux_dmabuf.h" |
| #include "components/exo/wayland/zwp_linux_explicit_synchronization.h" |
| #include "components/exo/wayland/zwp_pointer_constraints.h" |
| #include "components/exo/wayland/zwp_pointer_gestures.h" |
| #include "components/exo/wayland/zwp_relative_pointer_manager.h" |
| #include "components/exo/wayland/zwp_text_input_manager.h" |
| #include "components/exo/wayland/zxdg_decoration_manager.h" |
| #include "ui/ozone/public/ozone_platform.h" |
| |
| namespace exo { |
| namespace wayland { |
| namespace switches { |
| |
| // This flag can be used to override the default wayland socket name. It is |
| // useful when another wayland server is already running and using the |
| // default name. |
| constexpr char kWaylandServerSocket[] = "wayland-server-socket"; |
| |
| } // namespace switches |
| |
| namespace { |
| |
| // Default wayland socket name. |
| const base::FilePath::CharType kSocketName[] = FILE_PATH_LITERAL("wayland-0"); |
| |
| // Group used for wayland socket. |
| const char kWaylandSocketGroup[] = "wayland"; |
| |
| // Number of clients that can be waiting for accept() before we start refusing |
| // connections. This is *NOT* the maximum number of clients, just pending ones |
| // (see `man 2 listen`). |
| constexpr int kMaxPendingConnections = 128; |
| |
| // Callback used to find a Server instance for a given wl_display. |
| Server::ServerGetter g_server_getter; |
| |
| bool IsDrmAtomicAvailable() { |
| #if BUILDFLAG(IS_OZONE) |
| auto& host_properties = |
| ui::OzonePlatform::GetInstance()->GetPlatformRuntimeProperties(); |
| return host_properties.supports_overlays; |
| #else |
| LOG(WARNING) << "Ozone disabled, cannot determine whether DrmAtomic is " |
| "present. Assuming it is not"; |
| return false; |
| #endif |
| } |
| |
| void wayland_log(const char* fmt, va_list argp) { |
| LOG(WARNING) << "libwayland: " << base::StringPrintV(fmt, argp); |
| } |
| |
| int GetTextInputExtensionV1Version() { |
| return 14; |
| } |
| |
| } // namespace |
| |
| bool Server::Open() { |
| std::string socket_name = kSocketName; |
| base::CommandLine* command_line = base::CommandLine::ForCurrentProcess(); |
| if (command_line->HasSwitch(switches::kWaylandServerSocket)) { |
| socket_name = |
| command_line->GetSwitchValueASCII(switches::kWaylandServerSocket); |
| } |
| |
| char* runtime_dir_str = getenv("XDG_RUNTIME_DIR"); |
| if (!runtime_dir_str) { |
| LOG(ERROR) << "XDG_RUNTIME_DIR not set in the environment"; |
| return false; |
| } |
| base::FilePath socket_path = |
| base::FilePath(runtime_dir_str).Append(socket_name); |
| |
| if (!socket_path.IsAbsolute()) { |
| LOG(ERROR) << "Unable to create a wayland server. The provided path must " |
| "be absolute, got: " |
| << socket_path; |
| return false; |
| } |
| |
| // On debugging chromeos-chrome on linux platform, |
| // try to ensure the directory if missing. |
| if (!base::SysInfo::IsRunningOnChromeOS()) { |
| base::FilePath runtime_dir = socket_path.DirName(); |
| CHECK(base::DirectoryExists(runtime_dir) || |
| base::CreateDirectory(runtime_dir)) |
| << "Failed to create " << runtime_dir; |
| } |
| |
| if (!AddSocket(socket_path.MaybeAsASCII().c_str())) { |
| LOG(ERROR) << "Failed to add socket: " << socket_path; |
| return false; |
| } |
| |
| // 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 false; |
| } |
| if (wayland_group_res) { |
| if (HANDLE_EINTR(chown(socket_path.MaybeAsASCII().c_str(), -1, |
| wayland_group.gr_gid)) < 0) { |
| PLOG(ERROR) << "chown"; |
| return false; |
| } |
| } else { |
| LOG(WARNING) << "Group '" << kWaylandSocketGroup << "' not found"; |
| } |
| |
| if (!base::SetPosixFilePermissions(socket_path, 0660)) { |
| PLOG(ERROR) << "Could not set permissions: " << socket_path.value(); |
| return false; |
| } |
| return true; |
| } |
| |
| bool Server::OpenFd(base::ScopedFD fd) { |
| if (listen(fd.get(), kMaxPendingConnections) != 0) { |
| PLOG(ERROR) << "listen"; |
| return false; |
| } |
| |
| if (wl_display_add_socket_fd(wl_display_.get(), fd.get()) != 0) { |
| PLOG(ERROR) << "Failed to add socket " << fd.get() << " to wl_display"; |
| return false; |
| } |
| |
| // wl_display will only close() a socket that it successfully added, so is is |
| // only safe to release() at this point |
| std::ignore = fd.release(); |
| return true; |
| } |
| |
| //////////////////////////////////////////////////////////////////////////////// |
| // Server, public: |
| |
| Server::Server(Display* display, |
| std::unique_ptr<SecurityDelegate> security_delegate) |
| : display_(display), security_delegate_(std::move(security_delegate)) { |
| wl_log_set_handler_server(wayland_log); |
| |
| wl_display_.reset(wl_display_create()); |
| SetSecurityDelegate(wl_display_.get(), security_delegate_.get()); |
| |
| client_tracker_ = std::make_unique<ClientTracker>(wl_display_.get()); |
| wayland_protocol_logger_ = |
| std::make_unique<WaylandProtocolLogger>(wl_display_.get()); |
| } |
| |
| void Server::Initialize() { |
| serial_tracker_ = std::make_unique<SerialTracker>(wl_display_.get()); |
| rotation_serial_tracker_ = std::make_unique<SerialTracker>(wl_display_.get()); |
| wl_global_create(wl_display_.get(), &wl_compositor_interface, |
| kWlCompositorVersion, this, bind_compositor); |
| wl_global_create(wl_display_.get(), &wl_shm_interface, /*version=*/1, |
| display_, bind_shm); |
| wayland_feedback_manager_ = |
| std::make_unique<WaylandDmabufFeedbackManager>(display_); |
| if (wayland_feedback_manager_->GetVersionSupportedByPlatform() > 0) { |
| wl_global_create(wl_display_.get(), &zwp_linux_dmabuf_v1_interface, |
| wayland_feedback_manager_->GetVersionSupportedByPlatform(), |
| wayland_feedback_manager_.get(), bind_linux_dmabuf); |
| } |
| |
| wl_global_create(wl_display_.get(), &wl_subcompositor_interface, |
| /*version=*/1, display_, bind_subcompositor); |
| output_controller_ = std::make_unique<OutputController>(this); |
| wl_global_create(wl_display_.get(), &zcr_vsync_feedback_v1_interface, |
| /*version=*/1, display_, bind_vsync_feedback); |
| |
| data_device_manager_data_ = std::make_unique<WaylandDataDeviceManager>( |
| display_, serial_tracker_.get()); |
| wl_global_create(wl_display_.get(), &wl_data_device_manager_interface, |
| kWlDataDeviceManagerVersion, data_device_manager_data_.get(), |
| bind_data_device_manager); |
| |
| wl_global_create(wl_display_.get(), &surface_augmenter_interface, |
| kSurfaceAugmenterVersion, display_, bind_surface_augmenter); |
| wl_global_create( |
| wl_display_.get(), &wp_single_pixel_buffer_manager_v1_interface, |
| kSinglePixelBufferVersion, display_, bind_single_pixel_buffer); |
| wl_global_create(wl_display_.get(), &overlay_prioritizer_interface, |
| /*version=*/1, display_, bind_overlay_prioritizer); |
| wl_global_create(wl_display_.get(), &wp_fractional_scale_manager_v1_interface, |
| kFractionalScaleVersion, display_, |
| bind_fractional_scale_manager); |
| wl_global_create(wl_display_.get(), &wp_viewporter_interface, /*version=*/1, |
| display_, bind_viewporter); |
| wl_global_create(wl_display_.get(), &wp_presentation_interface, /*version=*/1, |
| display_, bind_presentation); |
| wl_global_create(wl_display_.get(), &zcr_alpha_compositing_v1_interface, |
| /*version=*/1, display_, bind_alpha_compositing); |
| wl_global_create(wl_display_.get(), &zcr_stylus_v2_interface, |
| kZcrStylusVersion, display_, bind_stylus_v2); |
| |
| seat_data_ = |
| std::make_unique<WaylandSeat>(display_->seat(), serial_tracker_.get()); |
| wl_global_create(wl_display_.get(), &wl_seat_interface, kWlSeatVersion, |
| seat_data_.get(), bind_seat); |
| |
| if (IsDrmAtomicAvailable()) { |
| // The release fence needed by linux-explicit-sync comes from DRM-atomic. |
| // If DRM atomic is not supported, linux-explicit-sync interface is |
| // disabled. |
| wl_global_create( |
| wl_display_.get(), &zwp_linux_explicit_synchronization_v1_interface, |
| /*version=*/2, display_, bind_linux_explicit_synchronization); |
| } |
| wl_global_create(wl_display_.get(), &zaura_shell_interface, |
| kZAuraShellVersion, display_, bind_aura_shell); |
| wl_global_create(wl_display_.get(), &wl_shell_interface, /*version=*/1, |
| display_, bind_shell); |
| wl_global_create(wl_display_.get(), &wp_content_type_manager_v1_interface, |
| /*version=*/1, display_, bind_content_type); |
| wl_global_create(wl_display_.get(), &zcr_cursor_shapes_v1_interface, |
| /*version=*/1, display_, bind_cursor_shapes); |
| wl_global_create(wl_display_.get(), &zcr_gaming_input_v2_interface, |
| /*version=*/3, display_, bind_gaming_input); |
| wl_global_create(wl_display_.get(), &zcr_keyboard_configuration_v1_interface, |
| kZcrKeyboardConfigurationVersion, display_, |
| bind_keyboard_configuration); |
| wl_global_create(wl_display_.get(), &zcr_notification_shell_v1_interface, |
| /*version=*/1, display_, bind_notification_shell); |
| |
| remote_shell_data_ = std::make_unique<WaylandRemoteShellData>( |
| display_, |
| WaylandRemoteShellData::OutputResourceProvider(base::BindRepeating( |
| &Server::GetOutputResource, base::Unretained(this)))); |
| wl_global_create(wl_display_.get(), &zcr_remote_shell_v1_interface, |
| kZcrRemoteShellVersion, remote_shell_data_.get(), |
| bind_remote_shell); |
| wl_global_create(wl_display_.get(), &zcr_remote_shell_v2_interface, |
| kZcrRemoteShellV2Version, remote_shell_data_.get(), |
| bind_remote_shell_v2); |
| |
| wl_global_create(wl_display_.get(), &zcr_stylus_tools_v1_interface, |
| /*version=*/1, display_, bind_stylus_tools); |
| wl_global_create(wl_display_.get(), |
| &zwp_input_timestamps_manager_v1_interface, /*version=*/1, |
| display_, bind_input_timestamps_manager); |
| wl_global_create(wl_display_.get(), &zwp_pointer_gestures_v1_interface, |
| /*version=*/1, display_, bind_pointer_gestures); |
| wl_global_create(wl_display_.get(), &zwp_pointer_constraints_v1_interface, |
| /*version=*/1, display_, bind_pointer_constraints); |
| wl_global_create(wl_display_.get(), |
| &zwp_relative_pointer_manager_v1_interface, /*version=*/1, |
| display_, bind_relative_pointer_manager); |
| wl_global_create(wl_display_.get(), &zcr_color_manager_v1_interface, |
| kZcrColorManagerVersion, this, bind_zcr_color_manager); |
| wl_global_create(wl_display_.get(), &zxdg_decoration_manager_v1_interface, |
| /*version=*/1, display_, bind_zxdg_decoration_manager); |
| wl_global_create(wl_display_.get(), &zcr_extended_drag_v1_interface, |
| /*version=*/1, display_, bind_extended_drag); |
| wl_global_create(wl_display_.get(), &zwp_idle_inhibit_manager_v1_interface, |
| /*version=*/1, display_, bind_zwp_idle_inhibit_manager); |
| |
| ui_controls_holder_ = std::make_unique<UiControls>(this); |
| test_controller_ = std::make_unique<TestController>(this); |
| |
| zcr_keyboard_extension_data_ = |
| std::make_unique<WaylandKeyboardExtension>(serial_tracker_.get()); |
| wl_global_create(wl_display_.get(), &zcr_keyboard_extension_v1_interface, |
| /*version=*/2, zcr_keyboard_extension_data_.get(), |
| bind_keyboard_extension); |
| |
| wl_global_create( |
| wl_display_.get(), &zwp_keyboard_shortcuts_inhibit_manager_v1_interface, |
| /*version=*/1, display_, bind_keyboard_shortcuts_inhibit_manager); |
| |
| zwp_text_manager_data_ = std::make_unique<WaylandTextInputManager>( |
| display_->seat()->xkb_tracker(), serial_tracker_.get()); |
| wl_global_create(wl_display_.get(), &zwp_text_input_manager_v1_interface, |
| /*version=*/1, zwp_text_manager_data_.get(), |
| bind_text_input_manager); |
| |
| zcr_text_input_extension_data_ = |
| std::make_unique<WaylandTextInputExtension>(); |
| wl_global_create(wl_display_.get(), &zcr_text_input_extension_v1_interface, |
| GetTextInputExtensionV1Version(), |
| zcr_text_input_extension_data_.get(), |
| bind_text_input_extension); |
| |
| xdg_shell_data_ = std::make_unique<WaylandXdgShell>( |
| display_, serial_tracker_.get(), rotation_serial_tracker_.get()); |
| wl_global_create(wl_display_.get(), &xdg_wm_base_interface, /*version=*/3, |
| xdg_shell_data_.get(), bind_xdg_shell); |
| |
| wl_global_create(wl_display_.get(), &zcr_touchpad_haptics_v1_interface, |
| /*version=*/1, display_, bind_touchpad_haptics); |
| } |
| |
| void Server::Finalize(StartCallback callback, bool success) { |
| // At this point, server creation was successful, so we should instantiate the |
| // watcher. |
| if (success) { |
| wayland_watcher_ = std::make_unique<wayland::WaylandWatcher>(this); |
| } |
| std::move(callback).Run(success); |
| } |
| |
| Server::~Server() { |
| RemoveSecurityDelegate(wl_display_.get()); |
| // TODO(crbug.com/40717074): Investigate if we can eliminate Shutdown |
| // methods. |
| serial_tracker_->Shutdown(); |
| } |
| |
| // static |
| std::unique_ptr<Server> Server::Create( |
| Display* display, |
| std::unique_ptr<SecurityDelegate> security_delegate) { |
| std::unique_ptr<Server> server( |
| new Server(display, std::move(security_delegate))); |
| server->Initialize(); |
| return server; |
| } |
| |
| // static. |
| Server* Server::GetServerForDisplay(wl_display* display) { |
| return g_server_getter ? g_server_getter.Run(display) : nullptr; |
| } |
| |
| // static. |
| void Server::SetServerGetter(Server::ServerGetter server_getter) { |
| CHECK(!server_getter || !g_server_getter); |
| g_server_getter = std::move(server_getter); |
| } |
| |
| void Server::StartWithDefaultPath(StartCallback callback) { |
| if (!Open()) { |
| std::move(callback).Run(/*success=*/false); |
| return; |
| } |
| Finalize(std::move(callback), /*success=*/true); |
| } |
| |
| void Server::StartWithFdAsync(base::ScopedFD fd, StartCallback callback) { |
| base::ThreadPool::PostTaskAndReplyWithResult( |
| FROM_HERE, base::MayBlock(), |
| base::BindOnce(&Server::OpenFd, base::Unretained(this), std::move(fd)), |
| base::BindOnce(&Server::Finalize, base::Unretained(this), |
| std::move(callback))); |
| } |
| |
| 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() { |
| // TODO(crbug.com/40948841): This should be updated to use |
| // wl_display_flush_clients() after an upstream libwayland fix has landed to |
| // address crashes during client-disconnect. |
| wl_client* client = nullptr; |
| wl_list* all_clients = wl_display_get_client_list(wl_display_.get()); |
| wl_client_for_each(client, all_clients) { |
| if (!IsClientDestroyed(client)) { |
| wl_client_flush(client); |
| } |
| } |
| } |
| |
| wl_display* Server::GetWaylandDisplay() { |
| return wl_display_.get(); |
| } |
| |
| wl_resource* Server::GetOutputResource(wl_client* client, int64_t display_id) { |
| return output_controller_->GetOutputResource(client, display_id); |
| } |
| |
| bool Server::IsClientDestroyed(wl_client* client) const { |
| return client_tracker_->IsClientDestroyed(client); |
| } |
| |
| } // namespace wayland |
| } // namespace exo |