Files
noctalia-shell/src/compositors/compositor_platform.cpp
T

723 lines
27 KiB
C++

#include "compositors/compositor_platform.h"
#include "compositors/compositor_detect.h"
#include "compositors/ext_workspace/ext_workspace_output_backend.h"
#include "compositors/hyprland/hyprland_keyboard_backend.h"
#include "compositors/hyprland/hyprland_output_backend.h"
#include "compositors/mango/mango_keyboard_backend.h"
#include "compositors/mango/mango_output_backend.h"
#include "compositors/niri/niri_keyboard_backend.h"
#include "compositors/niri/niri_output_backend.h"
#include "compositors/niri/niri_workspace_backend.h"
#include "compositors/sway/sway_keyboard_backend.h"
#include "compositors/sway/sway_output_backend.h"
#include "wayland/wayland_connection.h"
#include "wayland/wayland_workspaces.h"
#include <algorithm>
#include <optional>
#include <string>
#include <string_view>
#include <utility>
namespace compositors {
class FocusedOutputBackend {
public:
virtual ~FocusedOutputBackend() = default;
[[nodiscard]] virtual std::optional<std::string> focusedOutputName() const = 0;
};
class OutputPowerBackend {
public:
virtual ~OutputPowerBackend() = default;
[[nodiscard]] virtual bool setOutputPower(WaylandConnection& wayland, bool on) const = 0;
};
class WorkspaceMetadataBackend {
public:
using ChangeCallback = std::function<void()>;
virtual ~WorkspaceMetadataBackend() = default;
virtual void setChangeCallback(ChangeCallback callback) = 0;
[[nodiscard]] virtual int pollFd() const noexcept { return -1; }
[[nodiscard]] virtual short pollEvents() const noexcept { return POLLIN | POLLHUP | POLLERR; }
[[nodiscard]] virtual int pollTimeoutMs() const noexcept { return -1; }
virtual void dispatchPoll(short /*revents*/) {}
virtual void apply(std::vector<Workspace>& /*workspaces*/, const std::string& /*outputName*/ = {}) const {}
[[nodiscard]] virtual std::vector<std::string> workspaceKeys(const std::string& /*outputName*/ = {}) const {
return {};
}
[[nodiscard]] virtual std::unordered_map<std::string, std::vector<std::string>>
appIdsByWorkspace(const std::string& /*outputName*/ = {}) const {
return {};
}
[[nodiscard]] virtual std::vector<WorkspaceWindow> workspaceWindows(const std::string& /*outputName*/ = {}) const {
return {};
}
[[nodiscard]] virtual bool canTrackOverviewState() const noexcept { return false; }
[[nodiscard]] virtual bool hasOverviewState() const noexcept { return false; }
[[nodiscard]] virtual bool isOverviewOpen() const noexcept { return true; }
virtual void cleanup() = 0;
};
} // namespace compositors
namespace {
template <typename BackendT> class FocusedOutputAdapter final : public compositors::FocusedOutputBackend {
public:
explicit FocusedOutputAdapter(std::string_view compositorHint) : m_backend(compositorHint) {}
[[nodiscard]] std::optional<std::string> focusedOutputName() const override {
if (!m_backend.isAvailable()) {
return std::nullopt;
}
return m_backend.focusedOutputName();
}
private:
BackendT m_backend;
};
template <typename BackendT> class KeyboardLayoutBackendAdapter final : public KeyboardLayoutBackend {
public:
explicit KeyboardLayoutBackendAdapter(std::string_view compositorHint) : m_backend(compositorHint) {}
[[nodiscard]] bool isAvailable() const noexcept override { return m_backend.isAvailable(); }
[[nodiscard]] bool cycleLayout() const override { return m_backend.cycleLayout(); }
[[nodiscard]] std::optional<KeyboardLayoutState> layoutState() const override { return m_backend.layoutState(); }
[[nodiscard]] std::optional<std::string> currentLayoutName() const override {
return m_backend.currentLayoutName();
}
bool connectSocket() override {
if constexpr (requires { m_backend.connectSocket(); }) {
return m_backend.connectSocket();
}
return false;
}
void setChangeCallback(ChangeCallback callback) override {
if constexpr (requires { m_backend.setChangeCallback(std::move(callback)); }) {
m_backend.setChangeCallback(std::move(callback));
}
}
[[nodiscard]] int pollFd() const noexcept override {
if constexpr (requires { m_backend.pollFd(); }) {
return m_backend.pollFd();
}
return -1;
}
void dispatchPoll(short revents) override {
if constexpr (requires { m_backend.dispatchPoll(revents); }) {
m_backend.dispatchPoll(revents);
}
}
void cleanup() override {
if constexpr (requires { m_backend.cleanup(); }) {
m_backend.cleanup();
}
}
private:
BackendT m_backend;
};
class LambdaOutputPowerBackend final : public compositors::OutputPowerBackend {
public:
using Callback = bool (*)(WaylandConnection&, bool);
explicit LambdaOutputPowerBackend(Callback callback) : m_callback(callback) {}
[[nodiscard]] bool setOutputPower(WaylandConnection& wayland, bool on) const override {
return m_callback != nullptr && m_callback(wayland, on);
}
private:
Callback m_callback = nullptr;
};
class NiriWorkspaceMetadataBackend final : public compositors::WorkspaceMetadataBackend {
public:
explicit NiriWorkspaceMetadataBackend(std::string_view compositorHint) : m_backend(compositorHint) {}
void setChangeCallback(ChangeCallback callback) override { m_backend.setChangeCallback(std::move(callback)); }
[[nodiscard]] int pollFd() const noexcept override { return m_backend.pollFd(); }
[[nodiscard]] short pollEvents() const noexcept override { return m_backend.pollEvents(); }
[[nodiscard]] int pollTimeoutMs() const noexcept override { return m_backend.pollTimeoutMs(); }
void dispatchPoll(short revents) override { m_backend.dispatchPoll(revents); }
void apply(std::vector<Workspace>& workspaces, const std::string& outputName = {}) const override {
m_backend.apply(workspaces, outputName);
}
[[nodiscard]] std::vector<std::string> workspaceKeys(const std::string& outputName = {}) const override {
return m_backend.workspaceKeys(outputName);
}
[[nodiscard]] std::unordered_map<std::string, std::vector<std::string>>
appIdsByWorkspace(const std::string& outputName = {}) const override {
return m_backend.appIdsByWorkspace(outputName);
}
[[nodiscard]] std::vector<WorkspaceWindow> workspaceWindows(const std::string& outputName = {}) const override {
return m_backend.workspaceWindows(outputName);
}
[[nodiscard]] bool canTrackOverviewState() const noexcept override { return m_backend.canTrackOverviewState(); }
[[nodiscard]] bool hasOverviewState() const noexcept override { return m_backend.hasOverviewState(); }
[[nodiscard]] bool isOverviewOpen() const noexcept override { return m_backend.isOverviewOpen(); }
void cleanup() override { m_backend.cleanup(); }
private:
NiriWorkspaceBackend m_backend;
};
[[nodiscard]] bool setHyprlandOutputPower(WaylandConnection& /*wayland*/, bool on) {
return compositors::hyprland::setOutputPower(on);
}
[[nodiscard]] bool setNiriOutputPower(WaylandConnection& /*wayland*/, bool on) {
return compositors::niri::setOutputPower(on);
}
[[nodiscard]] bool setSwayOutputPower(WaylandConnection& /*wayland*/, bool on) {
return compositors::sway::setOutputPower(on);
}
[[nodiscard]] bool setMangoOutputPower(WaylandConnection& wayland, bool on) {
return compositors::mango::setOutputPower(wayland, on);
}
[[nodiscard]] bool setGenericOutputPower(WaylandConnection& /*wayland*/, bool on) {
return compositors::ext_workspace::setOutputPower(on);
}
[[nodiscard]] std::unique_ptr<compositors::OutputPowerBackend> createOutputPowerBackend() {
switch (compositors::detect()) {
case compositors::CompositorKind::Hyprland:
return std::make_unique<LambdaOutputPowerBackend>(&setHyprlandOutputPower);
case compositors::CompositorKind::Niri:
return std::make_unique<LambdaOutputPowerBackend>(&setNiriOutputPower);
case compositors::CompositorKind::Sway:
return std::make_unique<LambdaOutputPowerBackend>(&setSwayOutputPower);
case compositors::CompositorKind::Mango:
return std::make_unique<LambdaOutputPowerBackend>(&setMangoOutputPower);
case compositors::CompositorKind::Unknown:
return std::make_unique<LambdaOutputPowerBackend>(&setGenericOutputPower);
}
return nullptr;
}
[[nodiscard]] std::unique_ptr<compositors::WorkspaceMetadataBackend>
createWorkspaceMetadataBackend(std::string_view compositorHint) {
switch (compositors::detect()) {
case compositors::CompositorKind::Niri:
return std::make_unique<NiriWorkspaceMetadataBackend>(compositorHint);
case compositors::CompositorKind::Hyprland:
case compositors::CompositorKind::Sway:
case compositors::CompositorKind::Mango:
case compositors::CompositorKind::Unknown:
break;
}
return nullptr;
}
[[nodiscard]] std::unique_ptr<KeyboardLayoutBackend> createKeyboardLayoutBackend(std::string_view compositorHint) {
switch (compositors::detect()) {
case compositors::CompositorKind::Niri:
return std::make_unique<KeyboardLayoutBackendAdapter<NiriKeyboardBackend>>(compositorHint);
case compositors::CompositorKind::Hyprland:
return std::make_unique<KeyboardLayoutBackendAdapter<HyprlandKeyboardBackend>>(compositorHint);
case compositors::CompositorKind::Mango:
return std::make_unique<KeyboardLayoutBackendAdapter<MangoKeyboardBackend>>(compositorHint);
case compositors::CompositorKind::Sway:
return std::make_unique<KeyboardLayoutBackendAdapter<SwayKeyboardBackend>>(compositorHint);
case compositors::CompositorKind::Unknown:
break;
}
return nullptr;
}
} // namespace
CompositorPlatform::CompositorPlatform(WaylandConnection& wayland)
: m_wayland(wayland), m_workspaces(std::make_unique<WaylandWorkspaces>()) {
const std::string compositorHint(compositors::envHint());
m_workspaceMetadataBackend = createWorkspaceMetadataBackend(compositorHint);
m_focusedOutputBackends.push_back(std::make_unique<FocusedOutputAdapter<NiriOutputBackend>>(compositorHint));
m_focusedOutputBackends.push_back(std::make_unique<FocusedOutputAdapter<SwayOutputBackend>>(compositorHint));
m_outputPowerBackend = createOutputPowerBackend();
m_keyboardLayoutBackend = createKeyboardLayoutBackend(compositorHint);
m_workspaces->setOutputNameResolver([this](wl_output* output) { return connectorNameForOutput(output); });
m_wayland.setWorkspaceManagerCallbacks([this](ext_workspace_manager_v1* manager) { bindExtWorkspace(manager); },
[this](zdwl_ipc_manager_v2* manager) { bindDwlIpcWorkspace(manager); });
m_wayland.setOutputLifecycleCallbacks([this](wl_output* output) { onOutputAdded(output); },
[this](wl_output* output) { onOutputRemoved(output); });
}
CompositorPlatform::~CompositorPlatform() {
cleanup();
m_wayland.setOutputLifecycleCallbacks({}, {});
m_wayland.setWorkspaceManagerCallbacks({}, {});
}
void CompositorPlatform::initialize() {
if (m_initialized) {
return;
}
m_initialized = true;
m_workspaces->initialize(compositors::envHint());
for (const auto& output : m_wayland.outputs()) {
if (output.output != nullptr) {
m_workspaces->onOutputAdded(output.output);
}
}
}
void CompositorPlatform::cleanup() {
if (m_workspaceMetadataBackend != nullptr) {
m_workspaceMetadataBackend->cleanup();
}
if (m_workspaces != nullptr) {
m_workspaces->cleanup();
}
m_initialized = false;
}
wl_display* CompositorPlatform::display() const noexcept { return m_wayland.display(); }
bool CompositorPlatform::hasXdgShell() const noexcept { return m_wayland.hasXdgShell(); }
bool CompositorPlatform::hasGammaControl() const noexcept { return m_wayland.hasGammaControl(); }
const std::vector<WaylandOutput>& CompositorPlatform::outputs() const noexcept { return m_wayland.outputs(); }
const WaylandOutput* CompositorPlatform::findOutputByWl(wl_output* output) const {
return m_wayland.findOutputByWl(output);
}
wl_output* CompositorPlatform::outputForSurface(wl_surface* surface) const noexcept {
return m_wayland.outputForSurface(surface);
}
FocusGrabService* CompositorPlatform::focusGrabService() const noexcept { return m_wayland.focusGrabService(); }
wl_surface* CompositorPlatform::lastPointerSurface() const noexcept { return m_wayland.lastPointerSurface(); }
wl_surface* CompositorPlatform::lastKeyboardSurface() const noexcept { return m_wayland.lastKeyboardSurface(); }
double CompositorPlatform::lastPointerX() const noexcept { return m_wayland.lastPointerX(); }
double CompositorPlatform::lastPointerY() const noexcept { return m_wayland.lastPointerY(); }
std::uint32_t CompositorPlatform::lastInputSerial() const noexcept { return m_wayland.lastInputSerial(); }
zwlr_layer_surface_v1* CompositorPlatform::layerSurfaceFor(wl_surface* surface) const noexcept {
return m_wayland.layerSurfaceFor(surface);
}
void CompositorPlatform::stopKeyRepeat() { m_wayland.stopKeyRepeat(); }
void CompositorPlatform::setCursorShape(std::uint32_t serial, std::uint32_t shape) {
m_wayland.setCursorShape(serial, shape);
}
wl_output* CompositorPlatform::preferredInteractiveOutput(std::chrono::milliseconds pointerMaxAge) const {
for (const auto& backend : m_focusedOutputBackends) {
if (backend == nullptr) {
continue;
}
if (const auto focusedName = backend->focusedOutputName(); focusedName.has_value()) {
if (wl_output* output = resolveOutputName(*focusedName); output != nullptr) {
return output;
}
}
}
if (wl_output* output = m_wayland.activeToplevelOutput(); output != nullptr) {
return output;
}
if (wl_surface* keyboardSurface = m_wayland.lastKeyboardSurface(); keyboardSurface != nullptr) {
if (wl_output* output = m_wayland.outputForSurface(keyboardSurface); output != nullptr) {
return output;
}
}
if (m_wayland.hasFreshPointerOutput(pointerMaxAge)) {
return m_wayland.lastPointerOutput();
}
const auto& outputs = m_wayland.outputs();
return !outputs.empty() ? outputs.front().output : nullptr;
}
std::optional<ActiveToplevel> CompositorPlatform::activeToplevel() const { return m_wayland.activeToplevel(); }
wl_output* CompositorPlatform::activeToplevelOutput() const { return m_wayland.activeToplevelOutput(); }
std::vector<std::string> CompositorPlatform::runningAppIds(wl_output* outputFilter) const {
return m_wayland.runningAppIds(outputFilter);
}
std::vector<ToplevelInfo> CompositorPlatform::windowsForApp(const std::string& idLower, const std::string& wmClassLower,
wl_output* outputFilter) const {
return m_wayland.windowsForApp(idLower, wmClassLower, outputFilter);
}
void CompositorPlatform::activateToplevel(zwlr_foreign_toplevel_handle_v1* handle) {
m_wayland.activateToplevel(handle);
}
void CompositorPlatform::closeToplevel(zwlr_foreign_toplevel_handle_v1* handle) { m_wayland.closeToplevel(handle); }
void CompositorPlatform::setWorkspaceChangeCallback(ChangeCallback callback) {
m_workspaceChangeCallback = std::move(callback);
m_lastWorkspaceModelSnapshot = workspaceModelSnapshot();
auto wrapper = [this]() {
auto nextSnapshot = workspaceModelSnapshot();
if (sameWorkspaceModelSnapshot(nextSnapshot, m_lastWorkspaceModelSnapshot)) {
return;
}
m_lastWorkspaceModelSnapshot = std::move(nextSnapshot);
if (m_workspaceChangeCallback) {
m_workspaceChangeCallback();
}
};
if (m_workspaces != nullptr) {
m_workspaces->setChangeCallback(wrapper);
}
if (m_workspaceMetadataBackend != nullptr) {
m_workspaceMetadataBackend->setChangeCallback(wrapper);
}
}
void CompositorPlatform::activateWorkspace(const std::string& id) {
if (m_workspaces != nullptr) {
m_workspaces->activate(id);
}
}
void CompositorPlatform::activateWorkspace(wl_output* output, const std::string& id) {
if (m_workspaces != nullptr) {
m_workspaces->activateForOutput(output, id);
}
}
void CompositorPlatform::activateWorkspace(wl_output* output, const Workspace& workspace) {
if (m_workspaces != nullptr) {
m_workspaces->activateForOutput(output, workspace);
}
}
std::size_t CompositorPlatform::addWorkspacePollFds(std::vector<pollfd>& fds) const {
const auto start = fds.size();
if (m_workspaces != nullptr && m_workspaces->pollFd() >= 0) {
fds.push_back({.fd = m_workspaces->pollFd(), .events = m_workspaces->pollEvents(), .revents = 0});
}
if (m_workspaceMetadataBackend != nullptr && m_workspaceMetadataBackend->pollFd() >= 0) {
fds.push_back(
{.fd = m_workspaceMetadataBackend->pollFd(), .events = m_workspaceMetadataBackend->pollEvents(), .revents = 0});
}
return start;
}
int CompositorPlatform::workspacePollTimeoutMs() const noexcept {
int timeout = m_workspaces != nullptr ? m_workspaces->pollTimeoutMs() : -1;
if (m_workspaceMetadataBackend != nullptr) {
const int trackerTimeout = m_workspaceMetadataBackend->pollTimeoutMs();
if (trackerTimeout >= 0 && (timeout < 0 || trackerTimeout < timeout)) {
timeout = trackerTimeout;
}
}
return timeout;
}
void CompositorPlatform::dispatchWorkspacePoll(const std::vector<pollfd>& fds, std::size_t startIdx) {
std::size_t index = startIdx;
if (m_workspaces != nullptr && m_workspaces->pollFd() >= 0 && index < fds.size() &&
fds[index].fd == m_workspaces->pollFd()) {
m_workspaces->dispatchPoll(fds[index].revents);
++index;
}
if (m_workspaceMetadataBackend != nullptr) {
short revents = 0;
if (m_workspaceMetadataBackend->pollFd() >= 0 && index < fds.size() &&
fds[index].fd == m_workspaceMetadataBackend->pollFd()) {
revents = fds[index].revents;
}
m_workspaceMetadataBackend->dispatchPoll(revents);
}
}
std::vector<Workspace> CompositorPlatform::workspaces() const {
auto current = m_workspaces != nullptr ? m_workspaces->all() : std::vector<Workspace>{};
if (m_workspaceMetadataBackend != nullptr) {
m_workspaceMetadataBackend->apply(current);
}
return current;
}
std::vector<Workspace> CompositorPlatform::workspaces(wl_output* output) const {
auto current = m_workspaces != nullptr ? m_workspaces->forOutput(output) : std::vector<Workspace>{};
if (m_workspaceMetadataBackend != nullptr) {
m_workspaceMetadataBackend->apply(current, connectorNameForOutput(output));
}
return current;
}
std::unordered_map<std::string, std::vector<std::string>>
CompositorPlatform::appIdsByWorkspace(wl_output* outputFilter) const {
if (m_workspaceMetadataBackend != nullptr) {
const auto fromMetadata = m_workspaceMetadataBackend->appIdsByWorkspace(connectorNameForOutput(outputFilter));
if (!fromMetadata.empty()) {
return fromMetadata;
}
}
return m_workspaces != nullptr ? m_workspaces->appIdsByWorkspace(outputFilter)
: std::unordered_map<std::string, std::vector<std::string>>{};
}
std::vector<std::string> CompositorPlatform::workspaceDisplayKeys(wl_output* outputFilter) const {
if (m_workspaceMetadataBackend == nullptr) {
return {};
}
return m_workspaceMetadataBackend->workspaceKeys(connectorNameForOutput(outputFilter));
}
std::vector<WorkspaceWindowAssignment> CompositorPlatform::workspaceWindowAssignments(wl_output* outputFilter) const {
std::vector<WorkspaceWindow> windows;
if (m_workspaceMetadataBackend != nullptr) {
windows = m_workspaceMetadataBackend->workspaceWindows(connectorNameForOutput(outputFilter));
}
if (windows.empty() && m_workspaces != nullptr) {
windows = m_workspaces->workspaceWindows(outputFilter);
}
std::vector<WorkspaceWindowAssignment> result;
result.reserve(windows.size());
for (const auto& window : windows) {
result.push_back(WorkspaceWindowAssignment{
.windowId = window.windowId,
.workspaceKey = window.workspaceKey,
.appId = window.appId,
.title = window.title,
.x = window.x,
.y = window.y,
});
}
return result;
}
TaskbarAssignmentMode CompositorPlatform::taskbarAssignmentMode() const noexcept {
return m_workspaces != nullptr ? m_workspaces->taskbarAssignmentMode() : TaskbarAssignmentMode::Generic;
}
std::unordered_map<std::uintptr_t, WorkspaceWindow>
CompositorPlatform::assignTaskbarWindows(const std::vector<TaskbarWindowCandidate>& windows,
wl_output* outputFilter) const {
return m_workspaces != nullptr ? m_workspaces->assignTaskbarWindows(windows, outputFilter)
: std::unordered_map<std::uintptr_t, WorkspaceWindow>{};
}
const char* CompositorPlatform::workspaceBackendName() const noexcept {
return m_workspaces != nullptr ? m_workspaces->backendName() : "none";
}
bool CompositorPlatform::cycleKeyboardLayout() const {
return m_keyboardLayoutBackend != nullptr && m_keyboardLayoutBackend->cycleLayout();
}
bool CompositorPlatform::hasKeyboardLayoutBackend() const noexcept {
return m_keyboardLayoutBackend != nullptr && m_keyboardLayoutBackend->isAvailable();
}
std::optional<KeyboardLayoutState> CompositorPlatform::keyboardLayoutState() const {
if (m_keyboardLayoutBackend == nullptr) {
return std::nullopt;
}
return m_keyboardLayoutBackend->layoutState();
}
std::string CompositorPlatform::currentKeyboardLayoutName() const {
if (m_keyboardLayoutBackend != nullptr) {
if (auto backendName = m_keyboardLayoutBackend->currentLayoutName();
backendName.has_value() && !backendName->empty()) {
return *backendName;
}
}
return m_wayland.currentKeyboardLayoutName();
}
std::vector<std::string> CompositorPlatform::keyboardLayoutNames() const {
if (m_keyboardLayoutBackend != nullptr) {
if (const auto state = m_keyboardLayoutBackend->layoutState(); state.has_value() && !state->names.empty()) {
return state->names;
}
}
return m_wayland.keyboardLayoutNames();
}
void CompositorPlatform::setKeyboardLayoutChangeCallback(ChangeCallback callback) {
m_keyboardLayoutChangeCallback = std::move(callback);
if (m_keyboardLayoutBackend != nullptr) {
m_keyboardLayoutBackend->setChangeCallback(m_keyboardLayoutChangeCallback);
m_keyboardLayoutBackend->connectSocket();
}
}
void CompositorPlatform::addKeyboardLayoutPollFds(std::vector<pollfd>& fds) const {
if (m_keyboardLayoutBackend != nullptr && m_keyboardLayoutBackend->pollFd() >= 0) {
fds.push_back(
{.fd = m_keyboardLayoutBackend->pollFd(), .events = m_keyboardLayoutBackend->pollEvents(), .revents = 0});
}
}
void CompositorPlatform::dispatchKeyboardLayoutPoll(const std::vector<pollfd>& fds, std::size_t startIdx) {
if (m_keyboardLayoutBackend != nullptr && m_keyboardLayoutBackend->pollFd() >= 0 && startIdx < fds.size()) {
m_keyboardLayoutBackend->dispatchPoll(fds[startIdx].revents);
}
}
bool CompositorPlatform::setOutputPower(bool on) const {
return m_outputPowerBackend != nullptr && m_outputPowerBackend->setOutputPower(m_wayland, on);
}
bool CompositorPlatform::tracksOverviewState() const noexcept {
return m_workspaceMetadataBackend != nullptr && m_workspaceMetadataBackend->canTrackOverviewState();
}
bool CompositorPlatform::hasOverviewState() const noexcept {
return m_workspaceMetadataBackend != nullptr && m_workspaceMetadataBackend->hasOverviewState();
}
bool CompositorPlatform::isOverviewOpen() const noexcept {
if (m_workspaceMetadataBackend == nullptr || !m_workspaceMetadataBackend->hasOverviewState()) {
return true;
}
return m_workspaceMetadataBackend->isOverviewOpen();
}
void CompositorPlatform::bindExtWorkspace(ext_workspace_manager_v1* manager) {
if (m_workspaces != nullptr) {
m_workspaces->bindExtWorkspace(manager);
}
}
void CompositorPlatform::bindDwlIpcWorkspace(zdwl_ipc_manager_v2* manager) {
if (m_workspaces != nullptr) {
m_workspaces->bindDwlIpcWorkspace(manager);
}
}
void CompositorPlatform::onOutputAdded(wl_output* output) {
if (m_workspaces != nullptr) {
m_workspaces->onOutputAdded(output);
}
}
void CompositorPlatform::onOutputRemoved(wl_output* output) {
if (m_workspaces != nullptr) {
m_workspaces->onOutputRemoved(output);
}
}
wl_output* CompositorPlatform::resolveOutputName(const std::string& outputName) const {
if (outputName.empty()) {
return nullptr;
}
for (const auto& output : m_wayland.outputs()) {
if (output.output == nullptr) {
continue;
}
if (output.connectorName == outputName || output.description == outputName) {
return output.output;
}
}
return nullptr;
}
std::string CompositorPlatform::connectorNameForOutput(wl_output* output) const {
if (output == nullptr) {
return {};
}
for (const auto& candidate : m_wayland.outputs()) {
if (candidate.output == output) {
return candidate.connectorName;
}
}
return {};
}
std::vector<CompositorPlatform::WorkspaceModelSnapshot> CompositorPlatform::workspaceModelSnapshot() const {
auto sortedAssignments = [](std::vector<WorkspaceWindowAssignment> assignments) {
std::sort(assignments.begin(), assignments.end(), [](const auto& lhs, const auto& rhs) {
if (lhs.windowId != rhs.windowId) {
return lhs.windowId < rhs.windowId;
}
if (lhs.workspaceKey != rhs.workspaceKey) {
return lhs.workspaceKey < rhs.workspaceKey;
}
return lhs.appId < rhs.appId;
});
return assignments;
};
auto makeSnapshot = [&](const WaylandOutput* output) {
auto* wlOutput = output != nullptr ? output->output : nullptr;
return WorkspaceModelSnapshot{
.outputName = output != nullptr ? output->name : 0,
.workspaces = workspaces(wlOutput),
.assignments = sortedAssignments(workspaceWindowAssignments(wlOutput)),
};
};
std::vector<WorkspaceModelSnapshot> snapshot;
const auto& outputs = m_wayland.outputs();
if (outputs.empty()) {
snapshot.push_back(makeSnapshot(nullptr));
return snapshot;
}
snapshot.reserve(outputs.size());
for (const auto& output : outputs) {
snapshot.push_back(makeSnapshot(&output));
}
return snapshot;
}
bool CompositorPlatform::sameWorkspaceModelSnapshot(const std::vector<WorkspaceModelSnapshot>& lhs,
const std::vector<WorkspaceModelSnapshot>& rhs) {
auto sameWorkspace = [](const Workspace& a, const Workspace& b) {
return a.id == b.id && a.name == b.name && a.coordinates == b.coordinates && a.active == b.active &&
a.urgent == b.urgent && a.occupied == b.occupied;
};
auto sameAssignment = [](const WorkspaceWindowAssignment& a, const WorkspaceWindowAssignment& b) {
return a.windowId == b.windowId && a.workspaceKey == b.workspaceKey && a.appId == b.appId && a.title == b.title &&
a.x == b.x && a.y == b.y;
};
if (lhs.size() != rhs.size()) {
return false;
}
for (std::size_t i = 0; i < lhs.size(); ++i) {
if (lhs[i].outputName != rhs[i].outputName || lhs[i].workspaces.size() != rhs[i].workspaces.size() ||
lhs[i].assignments.size() != rhs[i].assignments.size()) {
return false;
}
for (std::size_t w = 0; w < lhs[i].workspaces.size(); ++w) {
if (!sameWorkspace(lhs[i].workspaces[w], rhs[i].workspaces[w])) {
return false;
}
}
for (std::size_t a = 0; a < lhs[i].assignments.size(); ++a) {
if (!sameAssignment(lhs[i].assignments[a], rhs[i].assignments[a])) {
return false;
}
}
}
return true;
}