chromium/components/exo/wayland/clients/test/client_version_test.cc

// Copyright 2021 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/clients/test/client_version_test.h"

#include <alpha-compositing-unstable-v1-client-protocol.h>
#include <aura-output-management-server-protocol.h>
#include <aura-shell-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 <idle-inhibit-unstable-v1-server-protocol.h>
#include <keyboard-configuration-unstable-v1-server-protocol.h>
#include <keyboard-extension-unstable-v1-server-protocol.h>
#include <keyboard-shortcuts-inhibit-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 <relative-pointer-unstable-v1-server-protocol.h>
#include <remote-shell-unstable-v1-server-protocol.h>
#include <remote-shell-unstable-v2-server-protocol.h>
#include <secure-output-unstable-v1-server-protocol.h>
#include <stylus-tools-unstable-v1-server-protocol.h>
#include <stylus-unstable-v2-server-protocol.h>
#include <surface-augmenter-server-protocol.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 <ui-controls-unstable-v1-server-protocol.h>
#include <viewporter-client-protocol.h>
#include <wayland-client-core.h>
#include <wayland-client-protocol.h>
#include <xdg-decoration-unstable-v1-server-protocol.h>
#include <xdg-shell-server-protocol.h>

#include <memory>
#include <string>
#include <unordered_map>
#include <utility>

#include "base/check_op.h"
#include "base/logging.h"

#include "components/exo/wayland/clients/client_helper.h"

namespace exo::wayland::clients {
namespace {

struct Globals {
  Globals() = default;
  ~Globals() = default;

  std::unique_ptr<wl_compositor> compositor;
  std::vector<std::string> protocols;
  std::string protocol_tested;
  ClientVersionTest::VersionValidityType validity_type =
      ClientVersionTest::VersionValidityType::VALID_ADVERTISED;
  std::unique_ptr<surface_augmenter> surface_augmenter;
  std::unique_ptr<overlay_prioritizer> overlay_prioritizer;
  std::unique_ptr<wl_shm> wl_shm;
  std::unique_ptr<wl_shell> wl_shell;
  std::unique_ptr<wl_seat> wl_seat;
  std::unique_ptr<wp_presentation> wp_presentation;
  std::unique_ptr<zaura_output_manager> zaura_output_manager;
  std::unique_ptr<zaura_output_manager_v2> zaura_output_manager_v2;
  std::unique_ptr<zaura_shell> zaura_shell;
  std::unique_ptr<zwp_linux_dmabuf_v1> zwp_linux_dmabuf_v1;
  std::unique_ptr<wl_subcompositor> wl_subcompositor;
  std::unique_ptr<zwp_input_timestamps_manager_v1>
      zwp_input_timestamps_manager_v1;
  std::unique_ptr<zwp_fullscreen_shell_v1> zwp_fullscreen_shell_v1;
  std::unique_ptr<wl_output> wl_output;
  std::unique_ptr<zwp_linux_explicit_synchronization_v1>
      zwp_linux_explicit_synchronization_v1;
  std::unique_ptr<zcr_vsync_feedback_v1> zcr_vsync_feedback_v1;
  std::unique_ptr<wl_data_device_manager> wl_data_device_manager;
  std::unique_ptr<wp_content_type_manager_v1> wp_content_type_manager_v1;
  std::unique_ptr<wp_fractional_scale_manager_v1>
      wp_fractional_scale_manager_v1;
  std::unique_ptr<wp_viewporter> wp_viewporter;
  std::unique_ptr<xdg_wm_base> xdg_wm_base;
  std::unique_ptr<zwp_text_input_manager_v1> zwp_text_input_manager_v1;
  std::unique_ptr<zcr_secure_output_v1> zcr_secure_output_v1;
  std::unique_ptr<zcr_alpha_compositing_v1> zcr_alpha_compositing_v1;
  std::unique_ptr<zcr_stylus_v2> zcr_stylus_v2;
  std::unique_ptr<zcr_color_manager_v1> zcr_color_manager_v1;
  std::unique_ptr<zcr_cursor_shapes_v1> zcr_cursor_shapes_v1;
  std::unique_ptr<zcr_gaming_input_v2> zcr_gaming_input_v2;
  std::unique_ptr<zcr_text_input_extension_v1> zcr_text_input_extension_v1;
  std::unique_ptr<zcr_keyboard_configuration_v1> zcr_keyboard_configuration_v1;
  std::unique_ptr<zcr_keyboard_extension_v1> zcr_keyboard_extension_v1;
  std::unique_ptr<zwp_keyboard_shortcuts_inhibit_manager_v1>
      zwp_keyboard_shortcuts_inhibit_manager_v1;
  std::unique_ptr<zcr_notification_shell_v1> zcr_notification_shell_v1;
  std::unique_ptr<zcr_remote_shell_v1> zcr_remote_shell_v1;
  std::unique_ptr<zcr_remote_shell_v2> zcr_remote_shell_v2;
  std::unique_ptr<zcr_stylus_tools_v1> zcr_stylus_tools_v1;
  std::unique_ptr<zcr_touchpad_haptics_v1> zcr_touchpad_haptics_v1;
  std::unique_ptr<zcr_ui_controls_v1> zcr_ui_controls_v1;
  std::unique_ptr<zwp_pointer_gestures_v1> zwp_pointer_gestures_v1;
  std::unique_ptr<zwp_pointer_constraints_v1> zwp_pointer_constraints_v1;
  std::unique_ptr<zwp_relative_pointer_manager_v1>
      zwp_relative_pointer_manager_v1;
  std::unique_ptr<zxdg_decoration_manager_v1> zxdg_decoration_manager_v1;
  std::unique_ptr<zcr_extended_drag_v1> zcr_extended_drag_v1;
  std::unique_ptr<zxdg_output_manager_v1> zxdg_output_manager_v1;
  std::unique_ptr<zwp_idle_inhibit_manager_v1> zwp_idle_inhibit_manager_v1;
  std::unique_ptr<wp_single_pixel_buffer_manager_v1>
      wp_single_pixel_buffer_manager_v1;
};

typedef void (*InterfaceRegistryCallback)(Globals*,
                                          wl_registry*,
                                          uint32_t,
                                          uint32_t);

#define REGISTRY_CALLBACK(field, t)                                         \
  {                                                                         \
#t, [](Globals* globals, wl_registry* registry,                           \
           uint32_t id, uint32_t version) {                                   \
      globals->field.reset(static_cast<t*>(                                   \
        wl_registry_bind(registry, id, &(t##_interface), version)));          \
    } \
  }

void RegistryHandler(void* data,
                     wl_registry* registry,
                     uint32_t id,
                     const char* interface,
                     uint32_t version) {
  Globals* globals = static_cast<Globals*>(data);
  if (globals->protocol_tested.length() == 0) {
    globals->protocols.push_back(interface);
    return;
  } else if (strcmp(interface, globals->protocol_tested.c_str()) == 0) {
    switch (globals->validity_type) {
      case ClientVersionTest::VersionValidityType::INVALID_NULL:
        version = 0;
        break;
      case ClientVersionTest::VersionValidityType::VALID_SKEWED:
        if (version > 1) {
          version = version - 1;
        }
        break;
      case ClientVersionTest::VersionValidityType::INVALID_UNSUPPORTED:
        version = version + 1;
        break;
      case ClientVersionTest::VersionValidityType::VALID_ADVERTISED:
      default:
        // Use advertised
        break;
    }
  }

  std::unordered_map<std::string, InterfaceRegistryCallback>
      interfaces_callbacks = {
          REGISTRY_CALLBACK(compositor, wl_compositor),
          REGISTRY_CALLBACK(wl_shm, wl_shm),
          REGISTRY_CALLBACK(wl_shell, wl_shell),
          REGISTRY_CALLBACK(wl_seat, wl_seat),
          REGISTRY_CALLBACK(wp_presentation, wp_presentation),
          REGISTRY_CALLBACK(zaura_output_manager, zaura_output_manager),
          REGISTRY_CALLBACK(zaura_output_manager_v2, zaura_output_manager_v2),
          REGISTRY_CALLBACK(zaura_shell, zaura_shell),
          REGISTRY_CALLBACK(zwp_linux_dmabuf_v1, zwp_linux_dmabuf_v1),
          REGISTRY_CALLBACK(wl_subcompositor, wl_subcompositor),
          REGISTRY_CALLBACK(zwp_input_timestamps_manager_v1,
                            zwp_input_timestamps_manager_v1),
          REGISTRY_CALLBACK(zwp_fullscreen_shell_v1, zwp_fullscreen_shell_v1),
          REGISTRY_CALLBACK(wl_output, wl_output),
          REGISTRY_CALLBACK(zwp_linux_explicit_synchronization_v1,
                            zwp_linux_explicit_synchronization_v1),
          REGISTRY_CALLBACK(zcr_vsync_feedback_v1, zcr_vsync_feedback_v1),
          REGISTRY_CALLBACK(wl_data_device_manager, wl_data_device_manager),
          REGISTRY_CALLBACK(wp_content_type_manager_v1,
                            wp_content_type_manager_v1),
          REGISTRY_CALLBACK(wp_fractional_scale_manager_v1,
                            wp_fractional_scale_manager_v1),
          REGISTRY_CALLBACK(wp_single_pixel_buffer_manager_v1,
                            wp_single_pixel_buffer_manager_v1),
          REGISTRY_CALLBACK(wp_viewporter, wp_viewporter),
          REGISTRY_CALLBACK(xdg_wm_base, xdg_wm_base),
          REGISTRY_CALLBACK(zwp_text_input_manager_v1,
                            zwp_text_input_manager_v1),
          REGISTRY_CALLBACK(zcr_secure_output_v1, zcr_secure_output_v1),
          REGISTRY_CALLBACK(zcr_alpha_compositing_v1, zcr_alpha_compositing_v1),
          REGISTRY_CALLBACK(zcr_stylus_v2, zcr_stylus_v2),
          REGISTRY_CALLBACK(zcr_color_manager_v1, zcr_color_manager_v1),
          REGISTRY_CALLBACK(zcr_cursor_shapes_v1, zcr_cursor_shapes_v1),
          REGISTRY_CALLBACK(zcr_gaming_input_v2, zcr_gaming_input_v2),
          REGISTRY_CALLBACK(zcr_keyboard_configuration_v1,
                            zcr_keyboard_configuration_v1),
          REGISTRY_CALLBACK(zcr_keyboard_extension_v1,
                            zcr_keyboard_extension_v1),
          REGISTRY_CALLBACK(zwp_keyboard_shortcuts_inhibit_manager_v1,
                            zwp_keyboard_shortcuts_inhibit_manager_v1),
          REGISTRY_CALLBACK(zcr_notification_shell_v1,
                            zcr_notification_shell_v1),
          REGISTRY_CALLBACK(zcr_remote_shell_v1, zcr_remote_shell_v1),
          REGISTRY_CALLBACK(zcr_remote_shell_v2, zcr_remote_shell_v2),
          REGISTRY_CALLBACK(zcr_stylus_tools_v1, zcr_stylus_tools_v1),
          REGISTRY_CALLBACK(zcr_text_input_extension_v1,
                            zcr_text_input_extension_v1),
          REGISTRY_CALLBACK(zcr_touchpad_haptics_v1, zcr_touchpad_haptics_v1),
          REGISTRY_CALLBACK(zcr_ui_controls_v1, zcr_ui_controls_v1),
          REGISTRY_CALLBACK(zwp_pointer_gestures_v1, zwp_pointer_gestures_v1),
          REGISTRY_CALLBACK(zwp_pointer_constraints_v1,
                            zwp_pointer_constraints_v1),
          REGISTRY_CALLBACK(zwp_relative_pointer_manager_v1,
                            zwp_relative_pointer_manager_v1),
          REGISTRY_CALLBACK(zxdg_decoration_manager_v1,
                            zxdg_decoration_manager_v1),
          REGISTRY_CALLBACK(zcr_extended_drag_v1, zcr_extended_drag_v1),
          REGISTRY_CALLBACK(zxdg_output_manager_v1, zxdg_output_manager_v1),
          REGISTRY_CALLBACK(surface_augmenter, surface_augmenter),
          REGISTRY_CALLBACK(overlay_prioritizer, overlay_prioritizer),
          REGISTRY_CALLBACK(zwp_idle_inhibit_manager_v1,
                            zwp_idle_inhibit_manager_v1),
      };
  if (interfaces_callbacks.find(interface) != interfaces_callbacks.end()) {
    interfaces_callbacks[interface](globals, registry, id, version);
  } else {
    LOG(ERROR) << "Unbound interface: " << interface;
  }
}

void RegistryRemover(void* data, wl_registry* registry, uint32_t id) {
  LOG(WARNING) << "Got a registry losing event for " << id;
}

wl_registry_listener g_registry_listener = {RegistryHandler, RegistryRemover};

}  // namespace

////////////////////////////////////////////////////////////////////////////////
// ClientVersionTest, public:

void ClientVersionTest::TestProtocol(const std::string& protocol,
                                     VersionValidityType validity_type) {
  auto globals = std::make_unique<Globals>();
  globals->protocol_tested = protocol;
  globals->validity_type = validity_type;
  std::unique_ptr<wl_display> display(wl_display_connect(nullptr));
  CHECK(display) << "wl_display_connect failed";

  std::unique_ptr<wl_registry> registry(wl_display_get_registry(display.get()));
  wl_registry_add_listener(registry.get(), &g_registry_listener, globals.get());

  wl_display_roundtrip(display.get());
  CHECK(globals->compositor) << "no compositor bound";

  std::unique_ptr<wl_surface> surface;
  surface.reset(static_cast<wl_surface*>(
      wl_compositor_create_surface(globals->compositor.get())));
  wl_surface_attach(surface.get(), nullptr, 0, 0);
  wl_display_roundtrip(display.get());

  int err = wl_display_get_error(display.get());
  if (validity_type == VersionValidityType::VALID_SKEWED ||
      validity_type == VersionValidityType::VALID_ADVERTISED) {
    DCHECK_EQ(err, 0) << "Couldn't bind valid version of " << protocol;
  } else {
    DCHECK_EQ(err, EPROTO) << "Invalid version bound for " << protocol;

    uint32_t errorcode;
    errorcode = wl_display_get_protocol_error(display.get(), nullptr, nullptr);
    DCHECK_EQ(errorcode, WL_DISPLAY_ERROR_INVALID_OBJECT)
        << "Invalid error code for invalid version of " << protocol;
  }

  globals.reset();
}

const std::vector<std::string> ClientVersionTest::Protocols() {
  Globals globals;
  std::unique_ptr<wl_display> display(wl_display_connect(nullptr));
  std::unique_ptr<wl_registry> registry(wl_display_get_registry(display.get()));
  wl_registry_add_listener(registry.get(), &g_registry_listener, &globals);
  wl_display_roundtrip(display.get());
  return std::move(globals.protocols);
}
}  // namespace exo::wayland::clients