blob: 3965377939735bd94cbed69ca7978f23683c7a7f [file] [log] [blame] [edit]
/*
* Copyright © 2017-2019 Canonical Ltd.
*
* This program is free software: you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 3,
* as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* Authored by: Christopher James Halse Rogers <christopher.halse.rogers@canonical.com>
*/
#ifndef WLCS_IN_PROCESS_SERVER_H_
#define WLCS_IN_PROCESS_SERVER_H_
#include "generated/wayland-client.h"
#include "generated/xdg-shell-unstable-v6-client.h"
#include "generated/xdg-shell-client.h"
#include <gtest/gtest.h>
#include <memory>
#include <functional>
#include <experimental/optional>
#include <unordered_map>
#include <chrono>
#include "shared_library.h"
#include "wl_handle.h"
#include <wayland-client.h>
struct WlcsPointer;
struct WlcsTouch;
struct WlcsServerIntegration;
struct zwlr_layer_shell_v1;
namespace wlcs
{
class VersionSpecifier;
WLCS_CREATE_INTERFACE_DESCRIPTOR(wl_surface)
WLCS_CREATE_INTERFACE_DESCRIPTOR(wl_subsurface)
/* We need a manually-specified descriptor for wl_output,
* as wl_output only has a destructor for version >= 3
*/
namespace
{
void send_release_if_supported(wl_output* to_destroy)
{
if (wl_output_get_version(to_destroy) >= WL_OUTPUT_RELEASE_SINCE_VERSION)
{
wl_output_release(to_destroy);
}
else
{
wl_output_destroy(to_destroy);
}
}
}
template<>
struct WlInterfaceDescriptor<wl_output>
{
static constexpr bool const has_specialisation = true;
static constexpr wl_interface const* const interface = &wl_output_interface;
static constexpr void (* const destructor)(wl_output*) = &send_release_if_supported;
};
class Pointer
{
public:
~Pointer();
Pointer(Pointer&&);
void move_to(int x, int y);
void move_by(int dx, int dy);
void button_down(int button);
void button_up(int button);
void click(int button);
void left_button_down();
void left_button_up();
void left_click();
private:
friend class Server;
template<typename Proxy>
Pointer(
WlcsPointer* raw_device,
std::shared_ptr<Proxy> const& proxy,
std::shared_ptr<void const> const& keep_dso_loaded);
class Impl;
std::unique_ptr<Impl> impl;
};
class Touch
{
public:
~Touch();
Touch(Touch&&);
void down_at(int x, int y);
void move_to(int x, int y);
void up();
private:
friend class Server;
template<typename Proxy>
Touch(
WlcsTouch* raw_device,
std::shared_ptr<Proxy> const& proxy,
std::shared_ptr<void const> const& keep_dso_loaded);
class Impl;
std::unique_ptr<Impl> impl;
};
class Surface;
class Server
{
public:
Server(
std::shared_ptr<WlcsServerIntegration const> const& module,
int argc,
char const** argv);
~Server();
int create_client_socket();
Pointer create_pointer();
Touch create_touch();
void move_surface_to(Surface& surface, int x, int y);
void start();
void stop();
std::shared_ptr<const std::unordered_map<std::string, uint32_t>> supported_extensions();
private:
class Impl;
std::unique_ptr<Impl> const impl;
};
class Client;
class Surface
{
public:
explicit Surface(Client& client);
virtual ~Surface();
Surface(Surface&& other);
operator wl_surface*() const;
void attach_buffer(int width, int height);
void add_frame_callback(std::function<void(int)> const& on_frame);
void attach_visible_buffer(int width, int height);
void run_on_destruction(std::function<void()> callback);
Client& owner() const;
auto current_outputs() -> std::set<wl_output*> const&;
private:
class Impl;
std::unique_ptr<Impl> impl;
};
class Subsurface: public Surface
{
public:
static Subsurface create_visible(Surface& parent, int x, int y, int width, int height);
Subsurface(Surface& parent);
Subsurface(Subsurface &&);
~Subsurface();
operator wl_subsurface*() const;
Surface& parent() const;
private:
class Impl;
std::unique_ptr<Impl> impl;
};
class ShmBuffer
{
public:
ShmBuffer(Client& client, int width, int height);
~ShmBuffer();
ShmBuffer(ShmBuffer&& other);
operator wl_buffer*() const;
void add_release_listener(std::function<bool()> const &on_release);
private:
class Impl;
std::unique_ptr<Impl> impl;
};
struct OutputState
{
OutputState(wl_output* output)
: output{output}
{
}
wl_output* output;
std::experimental::optional<std::pair<int, int>> geometry_position;
std::experimental::optional<std::pair<int, int>> mode_size;
std::experimental::optional<int> scale;
};
class Client
{
public:
Client(Server& server);
~Client();
// Accessors
operator wl_display*() const;
wl_compositor* compositor() const;
wl_subcompositor* subcompositor() const;
wl_shm* shm() const;
wl_seat* seat() const;
void run_on_destruction(std::function<void()> callback);
ShmBuffer const& create_buffer(int width, int height);
Surface create_wl_shell_surface(int width, int height);
Surface create_xdg_shell_v6_surface(int width, int height);
Surface create_xdg_shell_stable_surface(int width, int height);
Surface create_visible_surface(int width, int height);
size_t output_count() const;
OutputState output_state(size_t index) const;
void add_output_done_notifier(size_t index, std::function<void()> const& notifier);
wl_shell* shell() const;
wl_pointer* the_pointer() const;
zxdg_shell_v6* xdg_shell_v6() const;
xdg_wm_base* xdg_shell_stable() const;
wl_surface* keyboard_focused_window() const;
wl_surface* window_under_cursor() const;
wl_surface* touched_window() const;
std::pair<wl_fixed_t, wl_fixed_t> pointer_position() const;
std::pair<wl_fixed_t, wl_fixed_t> touch_position() const;
std::experimental::optional<uint32_t> latest_serial() const;
using PointerEnterNotifier =
std::function<bool(wl_surface*, wl_fixed_t x, wl_fixed_t y)>;
using PointerLeaveNotifier =
std::function<bool(wl_surface*)>;
using PointerMotionNotifier =
std::function<bool(wl_fixed_t x, wl_fixed_t y)>;
using PointerButtonNotifier =
std::function<bool(uint32_t serial, uint32_t button, bool is_down)>;
void add_pointer_enter_notification(PointerEnterNotifier const& on_enter);
void add_pointer_leave_notification(PointerLeaveNotifier const& on_leave);
void add_pointer_motion_notification(PointerMotionNotifier const& on_motion);
void add_pointer_button_notification(PointerButtonNotifier const& on_button);
void dispatch_until(
std::function<bool()> const& predicate,
std::chrono::seconds timeout = std::chrono::seconds{10});
template<typename WlType>
auto bind_if_supported(VersionSpecifier const& version) -> WlHandle<WlType>
{
return wrap_wl_object(
static_cast<WlType*>(bind_if_supported(*WlInterfaceDescriptor<WlType>::interface, version)));
}
auto bind_if_supported(wl_interface const& interface, VersionSpecifier const& version) const -> void*;
void roundtrip();
private:
class Impl;
std::unique_ptr<Impl> const impl;
};
class ProtocolError : public std::system_error
{
public:
ProtocolError(wl_interface const* interface, uint32_t code)
: std::system_error(EPROTO, std::system_category(), "Wayland protocol error"),
interface_{interface},
code_{code},
message{
std::string{"Wayland protocol error: "} +
std::to_string(code) +
" on interface " +
interface_->name +
" v" +
std::to_string(interface->version)}
{
}
char const* what() const noexcept override
{
return message.c_str();
}
uint32_t error_code() const
{
return code_;
}
wl_interface const* interface() const
{
return interface_;
}
private:
wl_interface const* const interface_;
uint32_t const code_;
std::string const message;
};
class ExtensionExpectedlyNotSupported : public std::runtime_error
{
public:
ExtensionExpectedlyNotSupported(char const* extension, VersionSpecifier const& version);
};
class Timeout : public std::runtime_error
{
public:
explicit Timeout(char const* message);
};
class InProcessServer : public testing::Test
{
public:
InProcessServer();
void SetUp() override;
void TearDown() override;
Server& the_server();
private:
Server server;
};
class StartedInProcessServer : public InProcessServer
{
public:
StartedInProcessServer() { InProcessServer::SetUp(); }
~StartedInProcessServer() { InProcessServer::TearDown(); }
void SetUp() override {}
void TearDown() override {}
};
}
#endif //WLCS_IN_PROCESS_SERVER_H_