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)
|
||||
|
||||
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)
|
||||
|
||||
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