mirror of
https://github.com/noctalia-dev/noctalia-shell.git
synced 2026-05-11 17:08:27 +08:00
723 lines
27 KiB
C++
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;
|
|
}
|