forked from quickshell/quickshell
hyprland/ipc: implement toplevel address association
This commit is contained in:
parent
ee570ec623
commit
dcd9e3aed8
6 changed files with 384 additions and 0 deletions
|
@ -17,6 +17,21 @@ install_qml_module(quickshell-hyprland-ipc)
|
||||||
|
|
||||||
target_link_libraries(quickshell-hyprland-ipc PRIVATE Qt::Quick)
|
target_link_libraries(quickshell-hyprland-ipc PRIVATE Qt::Quick)
|
||||||
|
|
||||||
|
if (WAYLAND_TOPLEVEL_MANAGEMENT)
|
||||||
|
target_sources(quickshell-hyprland-ipc PRIVATE
|
||||||
|
toplevel_mapping.cpp
|
||||||
|
hyprland_toplevel.cpp
|
||||||
|
)
|
||||||
|
|
||||||
|
wl_proto(wlp-hyprland-toplevel-mapping hyprland-toplevel-mapping-v1 "${CMAKE_CURRENT_SOURCE_DIR}")
|
||||||
|
|
||||||
|
target_link_libraries(quickshell-hyprland-ipc PRIVATE
|
||||||
|
Qt::WaylandClient Qt::WaylandClientPrivate wayland-client
|
||||||
|
wlp-hyprland-toplevel-mapping
|
||||||
|
wlp-foreign-toplevel
|
||||||
|
)
|
||||||
|
endif()
|
||||||
|
|
||||||
qs_module_pch(quickshell-hyprland-ipc SET large)
|
qs_module_pch(quickshell-hyprland-ipc SET large)
|
||||||
|
|
||||||
target_link_libraries(quickshell PRIVATE quickshell-hyprland-ipcplugin)
|
target_link_libraries(quickshell PRIVATE quickshell-hyprland-ipcplugin)
|
||||||
|
|
113
src/wayland/hyprland/ipc/hyprland-toplevel-mapping-v1.xml
Normal file
113
src/wayland/hyprland/ipc/hyprland-toplevel-mapping-v1.xml
Normal file
|
@ -0,0 +1,113 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<protocol name="hyprland_toplevel_mapping_v1">
|
||||||
|
<copyright>
|
||||||
|
Copyright © 2025 WhySoBad
|
||||||
|
All rights reserved.
|
||||||
|
|
||||||
|
Redistribution and use in source and binary forms, with or without
|
||||||
|
modification, are permitted provided that the following conditions are met:
|
||||||
|
|
||||||
|
1. Redistributions of source code must retain the above copyright notice, this
|
||||||
|
list of conditions and the following disclaimer.
|
||||||
|
|
||||||
|
2. Redistributions in binary form must reproduce the above copyright notice,
|
||||||
|
this list of conditions and the following disclaimer in the documentation
|
||||||
|
and/or other materials provided with the distribution.
|
||||||
|
|
||||||
|
3. Neither the name of the copyright holder nor the names of its
|
||||||
|
contributors may be used to endorse or promote products derived from
|
||||||
|
this software without specific prior written permission.
|
||||||
|
|
||||||
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||||
|
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||||
|
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||||
|
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||||
|
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||||
|
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||||
|
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||||
|
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||||
|
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
|
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
</copyright>
|
||||||
|
|
||||||
|
<description summary="mapping of toplevels to windows">
|
||||||
|
This protocol allows clients to retrieve the mapping of toplevels to hyprland window addresses.
|
||||||
|
</description>
|
||||||
|
|
||||||
|
<interface name="hyprland_toplevel_mapping_manager_v1" version="1">
|
||||||
|
<description summary="manager to request toplevel mappings">
|
||||||
|
This object is a manager which offers requests to retrieve a window address
|
||||||
|
for a toplevel.
|
||||||
|
</description>
|
||||||
|
|
||||||
|
<request name="get_window_for_toplevel">
|
||||||
|
<description summary="">
|
||||||
|
This request has been edited to remove a compile dep.
|
||||||
|
</description>
|
||||||
|
</request>
|
||||||
|
|
||||||
|
<request name="get_window_for_toplevel_wlr">
|
||||||
|
<description summary="get window address for wlr toplevel">
|
||||||
|
Get the window address for a wlr toplevel.
|
||||||
|
</description>
|
||||||
|
<arg
|
||||||
|
name="handle"
|
||||||
|
type="new_id"
|
||||||
|
interface="hyprland_toplevel_window_mapping_handle_v1"
|
||||||
|
/>
|
||||||
|
<arg
|
||||||
|
name="toplevel"
|
||||||
|
type="object"
|
||||||
|
interface="zwlr_foreign_toplevel_handle_v1"
|
||||||
|
summary="wlr toplevel to get the window address for"
|
||||||
|
/>
|
||||||
|
</request>
|
||||||
|
|
||||||
|
<request name="destroy" type="destructor">
|
||||||
|
<description summary="destroy the manager">
|
||||||
|
All objects created by the manager will still remain valid, until their appropriate destroy
|
||||||
|
request has been called.
|
||||||
|
</description>
|
||||||
|
</request>
|
||||||
|
</interface>
|
||||||
|
|
||||||
|
<interface name="hyprland_toplevel_window_mapping_handle_v1" version="1">
|
||||||
|
<description summary="toplevel to window mapping">
|
||||||
|
This object represents a mapping of a (wlr) toplevel to a window address.
|
||||||
|
|
||||||
|
Once created, the `window_address` event will be sent containing the address of the window
|
||||||
|
associated with the toplevel.
|
||||||
|
Should the mapping fail, the `failed` event will be sent.
|
||||||
|
</description>
|
||||||
|
|
||||||
|
<event name="window_address">
|
||||||
|
<description summary="address of the window">
|
||||||
|
The full 64bit window address. The `address` field contains the lower 32 bits whilst the
|
||||||
|
`address_hi` contains the upper 32 bits
|
||||||
|
</description>
|
||||||
|
<arg
|
||||||
|
name="address_hi"
|
||||||
|
type="uint"
|
||||||
|
summary="upper 32 bits of the window address"
|
||||||
|
/>
|
||||||
|
<arg
|
||||||
|
name="address"
|
||||||
|
type="uint"
|
||||||
|
summary="lower 32 bits of the window address"
|
||||||
|
/>
|
||||||
|
</event>
|
||||||
|
|
||||||
|
<event name="failed">
|
||||||
|
<description summary="mapping failed">
|
||||||
|
The mapping of the toplevel to a window address failed. Most likely the window does not
|
||||||
|
exist (anymore).
|
||||||
|
</description>
|
||||||
|
</event>
|
||||||
|
|
||||||
|
<request name="destroy" type="destructor">
|
||||||
|
<description summary="destroy handle">
|
||||||
|
Destroy the handle. This request can be sent at any time by the client.
|
||||||
|
</description>
|
||||||
|
</request>
|
||||||
|
</interface>
|
||||||
|
</protocol>
|
53
src/wayland/hyprland/ipc/hyprland_toplevel.cpp
Normal file
53
src/wayland/hyprland/ipc/hyprland_toplevel.cpp
Normal file
|
@ -0,0 +1,53 @@
|
||||||
|
#include "hyprland_toplevel.hpp"
|
||||||
|
|
||||||
|
#include <qcontainerfwd.h>
|
||||||
|
#include <qobject.h>
|
||||||
|
#include <qtmetamacros.h>
|
||||||
|
#include <qtypes.h>
|
||||||
|
|
||||||
|
#include "toplevel_mapping.hpp"
|
||||||
|
#include "../../toplevel_management/handle.hpp"
|
||||||
|
#include "../../toplevel_management/qml.hpp"
|
||||||
|
|
||||||
|
using namespace qs::wayland::toplevel_management;
|
||||||
|
using namespace qs::wayland::toplevel_management::impl;
|
||||||
|
|
||||||
|
namespace qs::hyprland::ipc {
|
||||||
|
|
||||||
|
HyprlandToplevel::HyprlandToplevel(Toplevel* toplevel)
|
||||||
|
: QObject(toplevel)
|
||||||
|
, handle(toplevel->implHandle()) {
|
||||||
|
auto* instance = HyprlandToplevelMappingManager::instance();
|
||||||
|
auto addr = instance->getToplevelAddress(handle);
|
||||||
|
|
||||||
|
if (addr != 0) this->setAddress(addr);
|
||||||
|
else {
|
||||||
|
QObject::connect(
|
||||||
|
instance,
|
||||||
|
&HyprlandToplevelMappingManager::toplevelAddressed,
|
||||||
|
this,
|
||||||
|
&HyprlandToplevel::onToplevelAddressed
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void HyprlandToplevel::onToplevelAddressed(ToplevelHandle* handle, quint64 address) {
|
||||||
|
if (handle == this->handle) {
|
||||||
|
this->setAddress(address);
|
||||||
|
QObject::disconnect(HyprlandToplevelMappingManager::instance(), nullptr, this, nullptr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void HyprlandToplevel::setAddress(quint64 address) {
|
||||||
|
this->mAddress = QString::number(address, 16);
|
||||||
|
emit this->addressChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
HyprlandToplevel* HyprlandToplevel::qmlAttachedProperties(QObject* object) {
|
||||||
|
if (auto* toplevel = qobject_cast<Toplevel*>(object)) {
|
||||||
|
return new HyprlandToplevel(toplevel);
|
||||||
|
} else {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} // namespace qs::hyprland::ipc
|
50
src/wayland/hyprland/ipc/hyprland_toplevel.hpp
Normal file
50
src/wayland/hyprland/ipc/hyprland_toplevel.hpp
Normal file
|
@ -0,0 +1,50 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <qcontainerfwd.h>
|
||||||
|
#include <qobject.h>
|
||||||
|
#include <qqmlintegration.h>
|
||||||
|
#include <qtmetamacros.h>
|
||||||
|
#include <qtypes.h>
|
||||||
|
|
||||||
|
#include "../../toplevel_management/handle.hpp"
|
||||||
|
#include "../../toplevel_management/qml.hpp"
|
||||||
|
|
||||||
|
namespace qs::hyprland::ipc {
|
||||||
|
|
||||||
|
//! Exposes Hyprland window address for a Toplevel
|
||||||
|
/// Attached object of @@Quickshell.Wayland.Toplevel which exposes
|
||||||
|
/// a Hyprland window address for the window.
|
||||||
|
class HyprlandToplevel: public QObject {
|
||||||
|
Q_OBJECT;
|
||||||
|
QML_ELEMENT;
|
||||||
|
QML_UNCREATABLE("");
|
||||||
|
QML_ATTACHED(HyprlandToplevel);
|
||||||
|
/// Hexadecimal Hyprland window address. Will be an empty string until
|
||||||
|
/// the address is reported.
|
||||||
|
Q_PROPERTY(QString address READ address NOTIFY addressChanged);
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit HyprlandToplevel(qs::wayland::toplevel_management::Toplevel* toplevel);
|
||||||
|
|
||||||
|
[[nodiscard]] QString address() { return this->mAddress; }
|
||||||
|
|
||||||
|
static HyprlandToplevel* qmlAttachedProperties(QObject* object);
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void addressChanged();
|
||||||
|
|
||||||
|
private slots:
|
||||||
|
void onToplevelAddressed(
|
||||||
|
qs::wayland::toplevel_management::impl::ToplevelHandle* handle,
|
||||||
|
quint64 address
|
||||||
|
);
|
||||||
|
|
||||||
|
private:
|
||||||
|
void setAddress(quint64 address);
|
||||||
|
|
||||||
|
QString mAddress;
|
||||||
|
// doesn't have to be nulled on destroy, only used for comparison
|
||||||
|
qs::wayland::toplevel_management::impl::ToplevelHandle* handle;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace qs::hyprland::ipc
|
83
src/wayland/hyprland/ipc/toplevel_mapping.cpp
Normal file
83
src/wayland/hyprland/ipc/toplevel_mapping.cpp
Normal file
|
@ -0,0 +1,83 @@
|
||||||
|
#include "toplevel_mapping.hpp"
|
||||||
|
|
||||||
|
#include <qlogging.h>
|
||||||
|
#include <qobject.h>
|
||||||
|
#include <qtmetamacros.h>
|
||||||
|
#include <qtypes.h>
|
||||||
|
#include <qwaylandclientextension.h>
|
||||||
|
|
||||||
|
#include "../../toplevel_management/manager.hpp"
|
||||||
|
|
||||||
|
using namespace qs::wayland::toplevel_management::impl;
|
||||||
|
|
||||||
|
namespace qs::hyprland::ipc {
|
||||||
|
|
||||||
|
HyprlandToplevelMappingHandle::~HyprlandToplevelMappingHandle() {
|
||||||
|
if (this->isInitialized()) this->destroy();
|
||||||
|
}
|
||||||
|
|
||||||
|
void HyprlandToplevelMappingHandle::hyprland_toplevel_window_mapping_handle_v1_window_address(
|
||||||
|
quint32 addressHi,
|
||||||
|
quint32 addressLo
|
||||||
|
) {
|
||||||
|
auto address = static_cast<quint64>(addressHi) << 32 | addressLo;
|
||||||
|
HyprlandToplevelMappingManager::instance()->assignAddress(this->handle, address);
|
||||||
|
delete this;
|
||||||
|
}
|
||||||
|
|
||||||
|
void HyprlandToplevelMappingHandle::hyprland_toplevel_window_mapping_handle_v1_failed() {
|
||||||
|
delete this;
|
||||||
|
}
|
||||||
|
|
||||||
|
HyprlandToplevelMappingManager::HyprlandToplevelMappingManager()
|
||||||
|
: QWaylandClientExtensionTemplate(1) {
|
||||||
|
this->initialize();
|
||||||
|
|
||||||
|
if (!this->isInitialized()) {
|
||||||
|
qWarning() << "Compositor does not support hyprland-toplevel-mapping-v1."
|
||||||
|
"It will not be possible to derive hyprland addresses from toplevels.";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
QObject::connect(
|
||||||
|
ToplevelManager::instance(),
|
||||||
|
&ToplevelManager::toplevelReady,
|
||||||
|
this,
|
||||||
|
&HyprlandToplevelMappingManager::onToplevelReady
|
||||||
|
);
|
||||||
|
|
||||||
|
for (auto* toplevel: ToplevelManager::instance()->readyToplevels()) {
|
||||||
|
this->onToplevelReady(toplevel);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void HyprlandToplevelMappingManager::onToplevelReady(ToplevelHandle* handle) {
|
||||||
|
QObject::connect(
|
||||||
|
handle,
|
||||||
|
&QObject::destroyed,
|
||||||
|
this,
|
||||||
|
&HyprlandToplevelMappingManager::onToplevelDestroyed
|
||||||
|
);
|
||||||
|
|
||||||
|
new HyprlandToplevelMappingHandle(handle, this->get_window_for_toplevel_wlr(handle->object()));
|
||||||
|
}
|
||||||
|
|
||||||
|
void HyprlandToplevelMappingManager::assignAddress(ToplevelHandle* handle, quint64 address) {
|
||||||
|
this->addresses.insert(handle, address);
|
||||||
|
emit this->toplevelAddressed(handle, address);
|
||||||
|
}
|
||||||
|
|
||||||
|
void HyprlandToplevelMappingManager::onToplevelDestroyed(QObject* object) {
|
||||||
|
this->addresses.remove(static_cast<ToplevelHandle*>(object)); // NOLINT
|
||||||
|
}
|
||||||
|
|
||||||
|
quint64 HyprlandToplevelMappingManager::getToplevelAddress(ToplevelHandle* handle) const {
|
||||||
|
return this->addresses.value(handle);
|
||||||
|
}
|
||||||
|
|
||||||
|
HyprlandToplevelMappingManager* HyprlandToplevelMappingManager::instance() {
|
||||||
|
static auto* instance = new HyprlandToplevelMappingManager();
|
||||||
|
return instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace qs::hyprland::ipc
|
70
src/wayland/hyprland/ipc/toplevel_mapping.hpp
Normal file
70
src/wayland/hyprland/ipc/toplevel_mapping.hpp
Normal file
|
@ -0,0 +1,70 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <qhash.h>
|
||||||
|
#include <qobject.h>
|
||||||
|
#include <qtclasshelpermacros.h>
|
||||||
|
#include <qtmetamacros.h>
|
||||||
|
#include <qtypes.h>
|
||||||
|
#include <qwayland-hyprland-toplevel-mapping-v1.h>
|
||||||
|
#include <qwaylandclientextension.h>
|
||||||
|
|
||||||
|
#include "../../toplevel_management/handle.hpp"
|
||||||
|
#include "wayland-hyprland-toplevel-mapping-v1-client-protocol.h"
|
||||||
|
|
||||||
|
namespace qs::hyprland::ipc {
|
||||||
|
|
||||||
|
class HyprlandToplevelMappingHandle: QtWayland::hyprland_toplevel_window_mapping_handle_v1 {
|
||||||
|
public:
|
||||||
|
explicit HyprlandToplevelMappingHandle(
|
||||||
|
qs::wayland::toplevel_management::impl::ToplevelHandle* handle,
|
||||||
|
::hyprland_toplevel_window_mapping_handle_v1* mapping
|
||||||
|
)
|
||||||
|
: QtWayland::hyprland_toplevel_window_mapping_handle_v1(mapping)
|
||||||
|
, handle(handle) {}
|
||||||
|
|
||||||
|
~HyprlandToplevelMappingHandle() override;
|
||||||
|
Q_DISABLE_COPY_MOVE(HyprlandToplevelMappingHandle);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void hyprland_toplevel_window_mapping_handle_v1_window_address(
|
||||||
|
quint32 addressHi,
|
||||||
|
quint32 addressLo
|
||||||
|
) override;
|
||||||
|
|
||||||
|
void hyprland_toplevel_window_mapping_handle_v1_failed() override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
qs::wayland::toplevel_management::impl::ToplevelHandle* handle;
|
||||||
|
};
|
||||||
|
|
||||||
|
class HyprlandToplevelMappingManager
|
||||||
|
: public QWaylandClientExtensionTemplate<HyprlandToplevelMappingManager>
|
||||||
|
, public QtWayland::hyprland_toplevel_mapping_manager_v1 {
|
||||||
|
Q_OBJECT;
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit HyprlandToplevelMappingManager();
|
||||||
|
|
||||||
|
static HyprlandToplevelMappingManager* instance();
|
||||||
|
|
||||||
|
[[nodiscard]] quint64
|
||||||
|
getToplevelAddress(qs::wayland::toplevel_management::impl::ToplevelHandle* handle) const;
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void toplevelAddressed(
|
||||||
|
qs::wayland::toplevel_management::impl::ToplevelHandle* handle,
|
||||||
|
quint64 address
|
||||||
|
);
|
||||||
|
|
||||||
|
private slots:
|
||||||
|
void onToplevelReady(qs::wayland::toplevel_management::impl::ToplevelHandle* handle);
|
||||||
|
void onToplevelDestroyed(QObject* object);
|
||||||
|
|
||||||
|
private:
|
||||||
|
void
|
||||||
|
assignAddress(qs::wayland::toplevel_management::impl::ToplevelHandle* handle, quint64 address);
|
||||||
|
QHash<wayland::toplevel_management::impl::ToplevelHandle*, quint64> addresses;
|
||||||
|
|
||||||
|
friend class HyprlandToplevelMappingHandle;
|
||||||
|
};
|
||||||
|
} // namespace qs::hyprland::ipc
|
Loading…
Add table
Add a link
Reference in a new issue