mirror of
https://github.com/hyprwm/xdg-desktop-portal-hyprland.git
synced 2024-11-22 06:35:57 +01:00
Toplevel sharing Rev2 impl (#3)
* toplevel sharing rev2 * nix: fix build * updated protocols Co-authored-by: Mihai Fufezan <fufexan@protonmail.com>
This commit is contained in:
parent
1fa106cfdf
commit
3c4c9969cc
8 changed files with 532 additions and 31 deletions
|
@ -3,11 +3,11 @@
|
||||||
"hyprland-protocols": {
|
"hyprland-protocols": {
|
||||||
"flake": false,
|
"flake": false,
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1670185345,
|
"lastModified": 1670703428,
|
||||||
"narHash": "sha256-hxWGqlPecqEsE6nOHDV29KFBKePbY2Ipeac6lrChMKY=",
|
"narHash": "sha256-4KUW5SKR0Y9uaYGcYwy53YJ3B/sgiprCL4fRGO+mpOA=",
|
||||||
"owner": "hyprwm",
|
"owner": "hyprwm",
|
||||||
"repo": "hyprland-protocols",
|
"repo": "hyprland-protocols",
|
||||||
"rev": "4623a404c091e64743ba310199bb380ec52f1936",
|
"rev": "d0d6db8cb5bef6d93ca3ad8fb2124964173396da",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
|
|
|
@ -16,6 +16,7 @@
|
||||||
#include <stdexcept>
|
#include <stdexcept>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <array>
|
#include <array>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
std::string execAndGet(const char* cmd) {
|
std::string execAndGet(const char* cmd) {
|
||||||
std::array<char, 128> buffer;
|
std::array<char, 128> buffer;
|
||||||
|
@ -31,12 +32,58 @@ std::string execAndGet(const char* cmd) {
|
||||||
}
|
}
|
||||||
|
|
||||||
QApplication* pickerPtr = nullptr;
|
QApplication* pickerPtr = nullptr;
|
||||||
|
MainPicker* mainPickerPtr = nullptr;
|
||||||
|
|
||||||
|
struct SWindowEntry {
|
||||||
|
std::string name;
|
||||||
|
std::string clazz;
|
||||||
|
int id = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
std::vector<SWindowEntry> getWindows(const char* env) {
|
||||||
|
std::vector<SWindowEntry> result;
|
||||||
|
|
||||||
|
if (!env)
|
||||||
|
return result;
|
||||||
|
|
||||||
|
std::string rolling = env;
|
||||||
|
|
||||||
|
while (!rolling.empty()) {
|
||||||
|
// ID
|
||||||
|
const auto IDSEPPOS = rolling.find("[HC\011]");
|
||||||
|
const auto IDSTR = rolling.substr(0, IDSEPPOS);
|
||||||
|
|
||||||
|
// class
|
||||||
|
const auto CLASSSEPPOS = rolling.find("[HT\011]");
|
||||||
|
const auto CLASSSTR = rolling.substr(IDSEPPOS + 5, CLASSSEPPOS - IDSEPPOS - 5);
|
||||||
|
|
||||||
|
// title
|
||||||
|
const auto TITLESEPPOS = rolling.find("[HE\011]");
|
||||||
|
const auto TITLESTR = rolling.substr(CLASSSEPPOS + 5, TITLESEPPOS - 5 - CLASSSEPPOS);
|
||||||
|
|
||||||
|
try {
|
||||||
|
result.push_back({TITLESTR, CLASSSTR, std::stoi(IDSTR)});
|
||||||
|
} catch (...) {
|
||||||
|
std::cout << "err\n"; // silent err
|
||||||
|
}
|
||||||
|
|
||||||
|
rolling = rolling.substr(TITLESEPPOS + 5);
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
int main(int argc, char *argv[]) {
|
int main(int argc, char *argv[]) {
|
||||||
qputenv("QT_LOGGING_RULES", "qml=false");
|
qputenv("QT_LOGGING_RULES", "qml=false");
|
||||||
|
|
||||||
|
const char* WINDOWLISTSTR = getenv("XDPH_WINDOW_SHARING_LIST");
|
||||||
|
|
||||||
|
const auto WINDOWLIST = getWindows(WINDOWLISTSTR);
|
||||||
|
|
||||||
QApplication picker(argc, argv);
|
QApplication picker(argc, argv);
|
||||||
pickerPtr = &picker;
|
pickerPtr = &picker;
|
||||||
MainPicker w;
|
MainPicker w;
|
||||||
|
mainPickerPtr = &w;
|
||||||
|
|
||||||
// get the tabwidget
|
// get the tabwidget
|
||||||
const auto TABWIDGET = (QTabWidget*)w.children()[1]->children()[0];
|
const auto TABWIDGET = (QTabWidget*)w.children()[1]->children()[0];
|
||||||
|
@ -82,29 +129,18 @@ int main(int argc, char *argv[]) {
|
||||||
|
|
||||||
// loop over them
|
// loop over them
|
||||||
int windowIterator = 0;
|
int windowIterator = 0;
|
||||||
while (windowsList.find("Window ") != std::string::npos) {
|
for (auto& window : WINDOWLIST) {
|
||||||
auto windowPropLen = windowsList.find("Window ", windowsList.find("\n\n") + 2);
|
|
||||||
if (windowPropLen == std::string::npos)
|
|
||||||
windowPropLen = windowsList.length();
|
|
||||||
const std::string windowProp = windowsList.substr(0, windowPropLen);
|
|
||||||
windowsList = windowsList.substr(windowPropLen);
|
|
||||||
|
|
||||||
// get window name
|
QString text = QString::fromStdString(window.clazz + ": " + window.name);
|
||||||
auto windowName = windowProp.substr(windowProp.find(" -> ") + 4);
|
|
||||||
windowName = windowName.substr(0, windowName.find_first_of('\n') - 1);
|
|
||||||
|
|
||||||
auto windowHandle = windowProp.substr(7, windowProp.find(" -> ") - 7);
|
|
||||||
|
|
||||||
QString text = QString::fromStdString("Window " + windowHandle + ": " + windowName);
|
|
||||||
|
|
||||||
QPushButton* button = new QPushButton(text, (QWidget*)WINDOWS_SCROLL_AREA_CONTENTS);
|
QPushButton* button = new QPushButton(text, (QWidget*)WINDOWS_SCROLL_AREA_CONTENTS);
|
||||||
button->move(9, 5 + (BUTTON_HEIGHT + BUTTON_PAD) * windowIterator);
|
button->move(9, 5 + (BUTTON_HEIGHT + BUTTON_PAD) * windowIterator);
|
||||||
button->resize(BUTTON_WIDTH, BUTTON_HEIGHT);
|
button->resize(BUTTON_WIDTH, BUTTON_HEIGHT);
|
||||||
QObject::connect(button, &QPushButton::clicked, [=] () {
|
|
||||||
std::string HANDLE = button->text().toStdString();
|
|
||||||
HANDLE = HANDLE.substr(7, HANDLE.find_first_of(':') - 7);
|
|
||||||
|
|
||||||
std::cout << "window:" << HANDLE << "\n";
|
mainPickerPtr->windowIDs[button] = window.id;
|
||||||
|
|
||||||
|
QObject::connect(button, &QPushButton::clicked, [=] () {
|
||||||
|
std::cout << "window:" << mainPickerPtr->windowIDs[button] << "\n";
|
||||||
pickerPtr->quit();
|
pickerPtr->quit();
|
||||||
return 0;
|
return 0;
|
||||||
});
|
});
|
||||||
|
|
|
@ -4,6 +4,7 @@
|
||||||
#include <QMainWindow>
|
#include <QMainWindow>
|
||||||
#include <QObject>
|
#include <QObject>
|
||||||
#include <QEvent>
|
#include <QEvent>
|
||||||
|
#include <unordered_map>
|
||||||
|
|
||||||
QT_BEGIN_NAMESPACE
|
QT_BEGIN_NAMESPACE
|
||||||
namespace Ui { class MainPicker; }
|
namespace Ui { class MainPicker; }
|
||||||
|
@ -19,6 +20,8 @@ public:
|
||||||
|
|
||||||
void onMonitorButtonClicked(QObject* target, QEvent* event);
|
void onMonitorButtonClicked(QObject* target, QEvent* event);
|
||||||
|
|
||||||
|
std::unordered_map<void*, int> windowIDs; // button -> id
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Ui::MainPicker *ui;
|
Ui::MainPicker *ui;
|
||||||
};
|
};
|
||||||
|
|
|
@ -126,6 +126,8 @@ struct xdpw_screencast_context {
|
||||||
|
|
||||||
// hyprland
|
// hyprland
|
||||||
struct hyprland_toplevel_export_manager_v1 *hyprland_toplevel_manager;
|
struct hyprland_toplevel_export_manager_v1 *hyprland_toplevel_manager;
|
||||||
|
struct zwlr_foreign_toplevel_manager_v1 *wlroots_toplevel_manager;
|
||||||
|
struct wl_list toplevel_resource_list;
|
||||||
|
|
||||||
// gbm
|
// gbm
|
||||||
struct gbm_device *gbm;
|
struct gbm_device *gbm;
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
Subproject commit 4623a404c091e64743ba310199bb380ec52f1936
|
Subproject commit 301733ae466b229066ba15a53e6d8b91c5dcef5b
|
|
@ -14,6 +14,7 @@ client_protocols = [
|
||||||
wl_protocol_dir / 'unstable/linux-dmabuf/linux-dmabuf-unstable-v1.xml',
|
wl_protocol_dir / 'unstable/linux-dmabuf/linux-dmabuf-unstable-v1.xml',
|
||||||
wl_protocol_dir / 'unstable/xdg-output/xdg-output-unstable-v1.xml',
|
wl_protocol_dir / 'unstable/xdg-output/xdg-output-unstable-v1.xml',
|
||||||
'wlr-screencopy-unstable-v1.xml',
|
'wlr-screencopy-unstable-v1.xml',
|
||||||
|
'wlr-foreign-toplevel-management-unstable-v1.xml',
|
||||||
'hyprland-protocols/protocols/hyprland-toplevel-export-v1.xml'
|
'hyprland-protocols/protocols/hyprland-toplevel-export-v1.xml'
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
270
protocols/wlr-foreign-toplevel-management-unstable-v1.xml
Normal file
270
protocols/wlr-foreign-toplevel-management-unstable-v1.xml
Normal file
|
@ -0,0 +1,270 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<protocol name="wlr_foreign_toplevel_management_unstable_v1">
|
||||||
|
<copyright>
|
||||||
|
Copyright © 2018 Ilia Bozhinov
|
||||||
|
|
||||||
|
Permission to use, copy, modify, distribute, and sell this
|
||||||
|
software and its documentation for any purpose is hereby granted
|
||||||
|
without fee, provided that the above copyright notice appear in
|
||||||
|
all copies and that both that copyright notice and this permission
|
||||||
|
notice appear in supporting documentation, and that the name of
|
||||||
|
the copyright holders not be used in advertising or publicity
|
||||||
|
pertaining to distribution of the software without specific,
|
||||||
|
written prior permission. The copyright holders make no
|
||||||
|
representations about the suitability of this software for any
|
||||||
|
purpose. It is provided "as is" without express or implied
|
||||||
|
warranty.
|
||||||
|
|
||||||
|
THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS
|
||||||
|
SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
||||||
|
FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||||
|
SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||||
|
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
|
||||||
|
AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
|
||||||
|
ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
|
||||||
|
THIS SOFTWARE.
|
||||||
|
</copyright>
|
||||||
|
|
||||||
|
<interface name="zwlr_foreign_toplevel_manager_v1" version="3">
|
||||||
|
<description summary="list and control opened apps">
|
||||||
|
The purpose of this protocol is to enable the creation of taskbars
|
||||||
|
and docks by providing them with a list of opened applications and
|
||||||
|
letting them request certain actions on them, like maximizing, etc.
|
||||||
|
|
||||||
|
After a client binds the zwlr_foreign_toplevel_manager_v1, each opened
|
||||||
|
toplevel window will be sent via the toplevel event
|
||||||
|
</description>
|
||||||
|
|
||||||
|
<event name="toplevel">
|
||||||
|
<description summary="a toplevel has been created">
|
||||||
|
This event is emitted whenever a new toplevel window is created. It
|
||||||
|
is emitted for all toplevels, regardless of the app that has created
|
||||||
|
them.
|
||||||
|
|
||||||
|
All initial details of the toplevel(title, app_id, states, etc.) will
|
||||||
|
be sent immediately after this event via the corresponding events in
|
||||||
|
zwlr_foreign_toplevel_handle_v1.
|
||||||
|
</description>
|
||||||
|
<arg name="toplevel" type="new_id" interface="zwlr_foreign_toplevel_handle_v1"/>
|
||||||
|
</event>
|
||||||
|
|
||||||
|
<request name="stop">
|
||||||
|
<description summary="stop sending events">
|
||||||
|
Indicates the client no longer wishes to receive events for new toplevels.
|
||||||
|
However the compositor may emit further toplevel_created events, until
|
||||||
|
the finished event is emitted.
|
||||||
|
|
||||||
|
The client must not send any more requests after this one.
|
||||||
|
</description>
|
||||||
|
</request>
|
||||||
|
|
||||||
|
<event name="finished" type="destructor">
|
||||||
|
<description summary="the compositor has finished with the toplevel manager">
|
||||||
|
This event indicates that the compositor is done sending events to the
|
||||||
|
zwlr_foreign_toplevel_manager_v1. The server will destroy the object
|
||||||
|
immediately after sending this request, so it will become invalid and
|
||||||
|
the client should free any resources associated with it.
|
||||||
|
</description>
|
||||||
|
</event>
|
||||||
|
</interface>
|
||||||
|
|
||||||
|
<interface name="zwlr_foreign_toplevel_handle_v1" version="3">
|
||||||
|
<description summary="an opened toplevel">
|
||||||
|
A zwlr_foreign_toplevel_handle_v1 object represents an opened toplevel
|
||||||
|
window. Each app may have multiple opened toplevels.
|
||||||
|
|
||||||
|
Each toplevel has a list of outputs it is visible on, conveyed to the
|
||||||
|
client with the output_enter and output_leave events.
|
||||||
|
</description>
|
||||||
|
|
||||||
|
<event name="title">
|
||||||
|
<description summary="title change">
|
||||||
|
This event is emitted whenever the title of the toplevel changes.
|
||||||
|
</description>
|
||||||
|
<arg name="title" type="string"/>
|
||||||
|
</event>
|
||||||
|
|
||||||
|
<event name="app_id">
|
||||||
|
<description summary="app-id change">
|
||||||
|
This event is emitted whenever the app-id of the toplevel changes.
|
||||||
|
</description>
|
||||||
|
<arg name="app_id" type="string"/>
|
||||||
|
</event>
|
||||||
|
|
||||||
|
<event name="output_enter">
|
||||||
|
<description summary="toplevel entered an output">
|
||||||
|
This event is emitted whenever the toplevel becomes visible on
|
||||||
|
the given output. A toplevel may be visible on multiple outputs.
|
||||||
|
</description>
|
||||||
|
<arg name="output" type="object" interface="wl_output"/>
|
||||||
|
</event>
|
||||||
|
|
||||||
|
<event name="output_leave">
|
||||||
|
<description summary="toplevel left an output">
|
||||||
|
This event is emitted whenever the toplevel stops being visible on
|
||||||
|
the given output. It is guaranteed that an entered-output event
|
||||||
|
with the same output has been emitted before this event.
|
||||||
|
</description>
|
||||||
|
<arg name="output" type="object" interface="wl_output"/>
|
||||||
|
</event>
|
||||||
|
|
||||||
|
<request name="set_maximized">
|
||||||
|
<description summary="requests that the toplevel be maximized">
|
||||||
|
Requests that the toplevel be maximized. If the maximized state actually
|
||||||
|
changes, this will be indicated by the state event.
|
||||||
|
</description>
|
||||||
|
</request>
|
||||||
|
|
||||||
|
<request name="unset_maximized">
|
||||||
|
<description summary="requests that the toplevel be unmaximized">
|
||||||
|
Requests that the toplevel be unmaximized. If the maximized state actually
|
||||||
|
changes, this will be indicated by the state event.
|
||||||
|
</description>
|
||||||
|
</request>
|
||||||
|
|
||||||
|
<request name="set_minimized">
|
||||||
|
<description summary="requests that the toplevel be minimized">
|
||||||
|
Requests that the toplevel be minimized. If the minimized state actually
|
||||||
|
changes, this will be indicated by the state event.
|
||||||
|
</description>
|
||||||
|
</request>
|
||||||
|
|
||||||
|
<request name="unset_minimized">
|
||||||
|
<description summary="requests that the toplevel be unminimized">
|
||||||
|
Requests that the toplevel be unminimized. If the minimized state actually
|
||||||
|
changes, this will be indicated by the state event.
|
||||||
|
</description>
|
||||||
|
</request>
|
||||||
|
|
||||||
|
<request name="activate">
|
||||||
|
<description summary="activate the toplevel">
|
||||||
|
Request that this toplevel be activated on the given seat.
|
||||||
|
There is no guarantee the toplevel will be actually activated.
|
||||||
|
</description>
|
||||||
|
<arg name="seat" type="object" interface="wl_seat"/>
|
||||||
|
</request>
|
||||||
|
|
||||||
|
<enum name="state">
|
||||||
|
<description summary="types of states on the toplevel">
|
||||||
|
The different states that a toplevel can have. These have the same meaning
|
||||||
|
as the states with the same names defined in xdg-toplevel
|
||||||
|
</description>
|
||||||
|
|
||||||
|
<entry name="maximized" value="0" summary="the toplevel is maximized"/>
|
||||||
|
<entry name="minimized" value="1" summary="the toplevel is minimized"/>
|
||||||
|
<entry name="activated" value="2" summary="the toplevel is active"/>
|
||||||
|
<entry name="fullscreen" value="3" summary="the toplevel is fullscreen" since="2"/>
|
||||||
|
</enum>
|
||||||
|
|
||||||
|
<event name="state">
|
||||||
|
<description summary="the toplevel state changed">
|
||||||
|
This event is emitted immediately after the zlw_foreign_toplevel_handle_v1
|
||||||
|
is created and each time the toplevel state changes, either because of a
|
||||||
|
compositor action or because of a request in this protocol.
|
||||||
|
</description>
|
||||||
|
|
||||||
|
<arg name="state" type="array"/>
|
||||||
|
</event>
|
||||||
|
|
||||||
|
<event name="done">
|
||||||
|
<description summary="all information about the toplevel has been sent">
|
||||||
|
This event is sent after all changes in the toplevel state have been
|
||||||
|
sent.
|
||||||
|
|
||||||
|
This allows changes to the zwlr_foreign_toplevel_handle_v1 properties
|
||||||
|
to be seen as atomic, even if they happen via multiple events.
|
||||||
|
</description>
|
||||||
|
</event>
|
||||||
|
|
||||||
|
<request name="close">
|
||||||
|
<description summary="request that the toplevel be closed">
|
||||||
|
Send a request to the toplevel to close itself. The compositor would
|
||||||
|
typically use a shell-specific method to carry out this request, for
|
||||||
|
example by sending the xdg_toplevel.close event. However, this gives
|
||||||
|
no guarantees the toplevel will actually be destroyed. If and when
|
||||||
|
this happens, the zwlr_foreign_toplevel_handle_v1.closed event will
|
||||||
|
be emitted.
|
||||||
|
</description>
|
||||||
|
</request>
|
||||||
|
|
||||||
|
<request name="set_rectangle">
|
||||||
|
<description summary="the rectangle which represents the toplevel">
|
||||||
|
The rectangle of the surface specified in this request corresponds to
|
||||||
|
the place where the app using this protocol represents the given toplevel.
|
||||||
|
It can be used by the compositor as a hint for some operations, e.g
|
||||||
|
minimizing. The client is however not required to set this, in which
|
||||||
|
case the compositor is free to decide some default value.
|
||||||
|
|
||||||
|
If the client specifies more than one rectangle, only the last one is
|
||||||
|
considered.
|
||||||
|
|
||||||
|
The dimensions are given in surface-local coordinates.
|
||||||
|
Setting width=height=0 removes the already-set rectangle.
|
||||||
|
</description>
|
||||||
|
|
||||||
|
<arg name="surface" type="object" interface="wl_surface"/>
|
||||||
|
<arg name="x" type="int"/>
|
||||||
|
<arg name="y" type="int"/>
|
||||||
|
<arg name="width" type="int"/>
|
||||||
|
<arg name="height" type="int"/>
|
||||||
|
</request>
|
||||||
|
|
||||||
|
<enum name="error">
|
||||||
|
<entry name="invalid_rectangle" value="0"
|
||||||
|
summary="the provided rectangle is invalid"/>
|
||||||
|
</enum>
|
||||||
|
|
||||||
|
<event name="closed">
|
||||||
|
<description summary="this toplevel has been destroyed">
|
||||||
|
This event means the toplevel has been destroyed. It is guaranteed there
|
||||||
|
won't be any more events for this zwlr_foreign_toplevel_handle_v1. The
|
||||||
|
toplevel itself becomes inert so any requests will be ignored except the
|
||||||
|
destroy request.
|
||||||
|
</description>
|
||||||
|
</event>
|
||||||
|
|
||||||
|
<request name="destroy" type="destructor">
|
||||||
|
<description summary="destroy the zwlr_foreign_toplevel_handle_v1 object">
|
||||||
|
Destroys the zwlr_foreign_toplevel_handle_v1 object.
|
||||||
|
|
||||||
|
This request should be called either when the client does not want to
|
||||||
|
use the toplevel anymore or after the closed event to finalize the
|
||||||
|
destruction of the object.
|
||||||
|
</description>
|
||||||
|
</request>
|
||||||
|
|
||||||
|
<!-- Version 2 additions -->
|
||||||
|
|
||||||
|
<request name="set_fullscreen" since="2">
|
||||||
|
<description summary="request that the toplevel be fullscreened">
|
||||||
|
Requests that the toplevel be fullscreened on the given output. If the
|
||||||
|
fullscreen state and/or the outputs the toplevel is visible on actually
|
||||||
|
change, this will be indicated by the state and output_enter/leave
|
||||||
|
events.
|
||||||
|
|
||||||
|
The output parameter is only a hint to the compositor. Also, if output
|
||||||
|
is NULL, the compositor should decide which output the toplevel will be
|
||||||
|
fullscreened on, if at all.
|
||||||
|
</description>
|
||||||
|
<arg name="output" type="object" interface="wl_output" allow-null="true"/>
|
||||||
|
</request>
|
||||||
|
|
||||||
|
<request name="unset_fullscreen" since="2">
|
||||||
|
<description summary="request that the toplevel be unfullscreened">
|
||||||
|
Requests that the toplevel be unfullscreened. If the fullscreen state
|
||||||
|
actually changes, this will be indicated by the state event.
|
||||||
|
</description>
|
||||||
|
</request>
|
||||||
|
|
||||||
|
<!-- Version 3 additions -->
|
||||||
|
|
||||||
|
<event name="parent" since="3">
|
||||||
|
<description summary="parent change">
|
||||||
|
This event is emitted whenever the parent of the toplevel changes.
|
||||||
|
|
||||||
|
No event is emitted when the parent handle is destroyed by the client.
|
||||||
|
</description>
|
||||||
|
<arg name="parent" type="object" interface="zwlr_foreign_toplevel_handle_v1" allow-null="true"/>
|
||||||
|
</event>
|
||||||
|
</interface>
|
||||||
|
</protocol>
|
|
@ -3,6 +3,7 @@
|
||||||
#include "linux-dmabuf-unstable-v1-client-protocol.h"
|
#include "linux-dmabuf-unstable-v1-client-protocol.h"
|
||||||
#include "wlr-screencopy-unstable-v1-client-protocol.h"
|
#include "wlr-screencopy-unstable-v1-client-protocol.h"
|
||||||
#include "xdg-output-unstable-v1-client-protocol.h"
|
#include "xdg-output-unstable-v1-client-protocol.h"
|
||||||
|
#include "wlr-foreign-toplevel-management-unstable-v1-client-protocol.h"
|
||||||
#include "hyprland-toplevel-export-v1-client-protocol.h"
|
#include "hyprland-toplevel-export-v1-client-protocol.h"
|
||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
#include <limits.h>
|
#include <limits.h>
|
||||||
|
@ -24,6 +25,117 @@
|
||||||
#include "xdpw.h"
|
#include "xdpw.h"
|
||||||
#include "logger.h"
|
#include "logger.h"
|
||||||
#include "fps_limit.h"
|
#include "fps_limit.h"
|
||||||
|
//
|
||||||
|
|
||||||
|
struct SToplevelEntry {
|
||||||
|
struct zwlr_foreign_toplevel_handle_v1 *handle;
|
||||||
|
char name[256];
|
||||||
|
char clazz[256];
|
||||||
|
struct wl_list link;
|
||||||
|
};
|
||||||
|
|
||||||
|
void handleTitle(void *data, struct zwlr_foreign_toplevel_handle_v1 *handle, const char *title) {
|
||||||
|
struct xdpw_screencast_context *ctx = data;
|
||||||
|
|
||||||
|
struct SToplevelEntry *current;
|
||||||
|
wl_list_for_each(current, &ctx->toplevel_resource_list, link) {
|
||||||
|
if (current->handle == handle) {
|
||||||
|
strncpy(current->name, title, 255);
|
||||||
|
current->name[255] = '\0';
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void handleAppID(void *data, struct zwlr_foreign_toplevel_handle_v1 *handle, const char *app_id) {
|
||||||
|
struct xdpw_screencast_context *ctx = data;
|
||||||
|
|
||||||
|
struct SToplevelEntry *current;
|
||||||
|
wl_list_for_each(current, &ctx->toplevel_resource_list, link) {
|
||||||
|
if (current->handle == handle) {
|
||||||
|
strncpy(current->clazz, app_id, 255);
|
||||||
|
current->name[255] = '\0';
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void handleOutputEnter(void *data, struct zwlr_foreign_toplevel_handle_v1 *handle, struct wl_output *output) {
|
||||||
|
; // noop
|
||||||
|
}
|
||||||
|
|
||||||
|
void handleOutputLeave(void *data, struct zwlr_foreign_toplevel_handle_v1 *handle, struct wl_output *output) {
|
||||||
|
; // noop
|
||||||
|
}
|
||||||
|
|
||||||
|
void handleState(void *data, struct zwlr_foreign_toplevel_handle_v1 *handle, struct wl_array *state) {
|
||||||
|
; // noop
|
||||||
|
}
|
||||||
|
|
||||||
|
void handleDone(void *data, struct zwlr_foreign_toplevel_handle_v1 *handle) {
|
||||||
|
; // noop
|
||||||
|
}
|
||||||
|
|
||||||
|
void handleClosed(void *data, struct zwlr_foreign_toplevel_handle_v1 *handle) {
|
||||||
|
struct xdpw_screencast_context *ctx = data;
|
||||||
|
|
||||||
|
struct SToplevelEntry *current;
|
||||||
|
wl_list_for_each(current, &ctx->toplevel_resource_list, link) {
|
||||||
|
if (current->handle == handle) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
wl_list_remove(¤t->link);
|
||||||
|
}
|
||||||
|
|
||||||
|
void handleParent(void *data, struct zwlr_foreign_toplevel_handle_v1 *handle, struct zwlr_foreign_toplevel_handle_v1 *parent) {
|
||||||
|
; // noop
|
||||||
|
}
|
||||||
|
|
||||||
|
struct zwlr_foreign_toplevel_handle_v1_listener toplevelHandleListener = {
|
||||||
|
.title = handleTitle,
|
||||||
|
.app_id = handleAppID,
|
||||||
|
.output_enter = handleOutputEnter,
|
||||||
|
.output_leave = handleOutputLeave,
|
||||||
|
.state = handleState,
|
||||||
|
.done = handleDone,
|
||||||
|
.closed = handleClosed,
|
||||||
|
.parent = handleParent,
|
||||||
|
};
|
||||||
|
|
||||||
|
void handleToplevel(void *data, struct zwlr_foreign_toplevel_manager_v1 *manager, struct zwlr_foreign_toplevel_handle_v1 *toplevel) {
|
||||||
|
struct xdpw_screencast_context *ctx = data;
|
||||||
|
|
||||||
|
struct SToplevelEntry* entry = malloc(sizeof(struct SToplevelEntry));
|
||||||
|
|
||||||
|
entry->handle = toplevel;
|
||||||
|
|
||||||
|
wl_list_insert(&ctx->toplevel_resource_list, &entry->link);
|
||||||
|
|
||||||
|
zwlr_foreign_toplevel_handle_v1_add_listener(toplevel, &toplevelHandleListener, ctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
void handleFinished(void *data, struct zwlr_foreign_toplevel_manager_v1 *zwlr_foreign_toplevel_manager_v1) {
|
||||||
|
; // noop
|
||||||
|
}
|
||||||
|
|
||||||
|
struct zwlr_foreign_toplevel_manager_v1_listener toplevelListener = {
|
||||||
|
.toplevel = handleToplevel,
|
||||||
|
.finished = handleFinished,
|
||||||
|
};
|
||||||
|
|
||||||
|
struct SToplevelEntry* toplevelEntryFromID(struct xdpw_screencast_context *ctx, uint32_t id) {
|
||||||
|
struct SToplevelEntry *current;
|
||||||
|
wl_list_for_each(current, &ctx->toplevel_resource_list, link) {
|
||||||
|
if (((uint64_t)current->handle & 0xFFFFFFFF) == id) {
|
||||||
|
return current;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
///
|
||||||
|
|
||||||
void wlr_frame_free(struct xdpw_screencast_instance *cast) {
|
void wlr_frame_free(struct xdpw_screencast_instance *cast) {
|
||||||
if (!cast->wlr_frame) {
|
if (!cast->wlr_frame) {
|
||||||
|
@ -423,8 +535,15 @@ void xdpw_wlr_register_cb(struct xdpw_screencast_instance *cast) {
|
||||||
cast->ctx->screencopy_manager, cast->with_cursor, cast->target.output->output);
|
cast->ctx->screencopy_manager, cast->with_cursor, cast->target.output->output);
|
||||||
} else {
|
} else {
|
||||||
// share window
|
// share window
|
||||||
cast->frame_callback_hyprland = hyprland_toplevel_export_manager_v1_capture_toplevel(
|
struct SToplevelEntry* entry = toplevelEntryFromID(cast->ctx, cast->target.window_handle);
|
||||||
cast->ctx->hyprland_toplevel_manager, cast->with_cursor, cast->target.window_handle);
|
|
||||||
|
if (!entry) {
|
||||||
|
logprint(DEBUG, "hyprland: error in getting entry");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
cast->frame_callback_hyprland = hyprland_toplevel_export_manager_v1_capture_toplevel_with_wlr_toplevel_handle(
|
||||||
|
cast->ctx->hyprland_toplevel_manager, cast->with_cursor, entry->handle);
|
||||||
|
|
||||||
hyprland_toplevel_export_frame_v1_add_listener(cast->frame_callback_hyprland,
|
hyprland_toplevel_export_frame_v1_add_listener(cast->frame_callback_hyprland,
|
||||||
&hyprland_frame_listener, cast);
|
&hyprland_frame_listener, cast);
|
||||||
|
@ -514,6 +633,65 @@ static void wlr_init_xdg_outputs(struct xdpw_screencast_context *ctx) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// stolen from LLVM cuz it wouldnt include lol
|
||||||
|
static inline int vasprintf(char **strp, const char *fmt, va_list ap) {
|
||||||
|
const size_t buff_size = 256;
|
||||||
|
if ((*strp = (char *)malloc(buff_size)) == NULL) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
va_list ap_copy;
|
||||||
|
// va_copy may not be provided by the C library in C++ 03 mode.
|
||||||
|
#if defined(_LIBCPP_CXX03_LANG) && __has_builtin(__builtin_va_copy)
|
||||||
|
__builtin_va_copy(ap_copy, ap);
|
||||||
|
#else
|
||||||
|
va_copy(ap_copy, ap);
|
||||||
|
#endif
|
||||||
|
int str_size = vsnprintf(*strp, buff_size, fmt, ap_copy);
|
||||||
|
va_end(ap_copy);
|
||||||
|
|
||||||
|
if ((size_t)str_size >= buff_size) {
|
||||||
|
if ((*strp = (char *)realloc(*strp, str_size + 1)) == NULL) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
str_size = vsnprintf(*strp, str_size + 1, fmt, ap);
|
||||||
|
}
|
||||||
|
return str_size;
|
||||||
|
}
|
||||||
|
|
||||||
|
char *getFormat(const char *fmt, ...) {
|
||||||
|
char *outputStr = NULL;
|
||||||
|
|
||||||
|
va_list args;
|
||||||
|
va_start(args, fmt);
|
||||||
|
vasprintf(&outputStr, fmt, args);
|
||||||
|
va_end(args);
|
||||||
|
|
||||||
|
return outputStr;
|
||||||
|
}
|
||||||
|
|
||||||
|
char* buildWindowList(struct xdpw_screencast_context *ctx) {
|
||||||
|
|
||||||
|
char* rolling = calloc(1, 1);
|
||||||
|
|
||||||
|
struct SToplevelEntry* current;
|
||||||
|
wl_list_for_each(current, &ctx->toplevel_resource_list, link) {
|
||||||
|
|
||||||
|
char* oldRolling = rolling;
|
||||||
|
|
||||||
|
rolling = getFormat("%s%u[HC\011]%s[HT\011]%s[HE\011]", rolling, (uint32_t)(((uint64_t)current->handle) & 0xFFFFFFFF), current->clazz, current->name);
|
||||||
|
|
||||||
|
free(oldRolling);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (size_t i = 0; i < strlen(rolling); ++i) {
|
||||||
|
if (rolling[i] == '\"')
|
||||||
|
rolling[i] = ' ';
|
||||||
|
}
|
||||||
|
|
||||||
|
return rolling;
|
||||||
|
}
|
||||||
|
|
||||||
struct xdpw_share xdpw_wlr_chooser(struct xdpw_screencast_context *ctx) {
|
struct xdpw_share xdpw_wlr_chooser(struct xdpw_screencast_context *ctx) {
|
||||||
char result[1024] = {0};
|
char result[1024] = {0};
|
||||||
FILE *fp;
|
FILE *fp;
|
||||||
|
@ -523,13 +701,11 @@ struct xdpw_share xdpw_wlr_chooser(struct xdpw_screencast_context *ctx) {
|
||||||
const char *XCURSOR_SIZE = getenv("XCURSOR_SIZE");
|
const char *XCURSOR_SIZE = getenv("XCURSOR_SIZE");
|
||||||
const char *HYPRLAND_INSTANCE_SIGNATURE = getenv("HYPRLAND_INSTANCE_SIGNATURE");
|
const char *HYPRLAND_INSTANCE_SIGNATURE = getenv("HYPRLAND_INSTANCE_SIGNATURE");
|
||||||
|
|
||||||
char cmd[256] = "WAYLAND_DISPLAY=";
|
char* windowList = buildWindowList(ctx);
|
||||||
strcat(cmd, WAYLAND_DISPLAY);
|
|
||||||
strcat(cmd, " XCURSOR_SIZE=");
|
char *cmd = getFormat("WAYLAND_DISPLAY=%s XCURSOR_SIZE=%s HYPRLAND_INSTANCE_SIGNATURE=%s XDPH_WINDOW_SHARING_LIST=\"%s\" hyprland-share-picker", WAYLAND_DISPLAY, XCURSOR_SIZE ? XCURSOR_SIZE : "24", HYPRLAND_INSTANCE_SIGNATURE ? HYPRLAND_INSTANCE_SIGNATURE : "0", windowList);
|
||||||
strcat(cmd, XCURSOR_SIZE ? XCURSOR_SIZE : "24");
|
|
||||||
strcat(cmd, " HYPRLAND_INSTANCE_SIGNATURE=");
|
free(windowList);
|
||||||
strcat(cmd, HYPRLAND_INSTANCE_SIGNATURE ? HYPRLAND_INSTANCE_SIGNATURE : "0");
|
|
||||||
strcat(cmd, " QT_QPA_PLATFORM=wayland hyprland-share-picker");
|
|
||||||
|
|
||||||
logprint(DEBUG, "Screencast: Picker: Running command \"%s\"", cmd);
|
logprint(DEBUG, "Screencast: Picker: Running command \"%s\"", cmd);
|
||||||
|
|
||||||
|
@ -545,6 +721,8 @@ struct xdpw_share xdpw_wlr_chooser(struct xdpw_screencast_context *ctx) {
|
||||||
|
|
||||||
pclose(fp);
|
pclose(fp);
|
||||||
|
|
||||||
|
free(cmd);
|
||||||
|
|
||||||
// great, let's parse it.
|
// great, let's parse it.
|
||||||
|
|
||||||
struct xdpw_share res = {NULL, -1, -1, -1, -1, -1};
|
struct xdpw_share res = {NULL, -1, -1, -1, -1, -1};
|
||||||
|
@ -630,7 +808,7 @@ struct xdpw_share xdpw_wlr_chooser(struct xdpw_screencast_context *ctx) {
|
||||||
strncpy(display_name, result + 7, strlen(result) - 8);
|
strncpy(display_name, result + 7, strlen(result) - 8);
|
||||||
display_name[strlen(result) - 8] = 0;
|
display_name[strlen(result) - 8] = 0;
|
||||||
|
|
||||||
res.window_handle = strtol(display_name, NULL, 16);
|
res.window_handle = strtol(display_name, NULL, 10);
|
||||||
|
|
||||||
free(display_name);
|
free(display_name);
|
||||||
return res;
|
return res;
|
||||||
|
@ -882,6 +1060,17 @@ static void wlr_registry_handle_add(void *data, struct wl_registry *reg,
|
||||||
ctx->hyprland_toplevel_manager = wl_registry_bind(reg, id, &hyprland_toplevel_export_manager_v1_interface, version);
|
ctx->hyprland_toplevel_manager = wl_registry_bind(reg, id, &hyprland_toplevel_export_manager_v1_interface, version);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!strcmp(interface, zwlr_foreign_toplevel_manager_v1_interface.name)) {
|
||||||
|
uint32_t version = ver;
|
||||||
|
|
||||||
|
logprint(DEBUG, "hyprland: |-- registered to interface %s (Version %u)", interface, version);
|
||||||
|
|
||||||
|
ctx->wlroots_toplevel_manager = wl_registry_bind(reg, id, &zwlr_foreign_toplevel_manager_v1_interface, version);
|
||||||
|
wl_list_init(&ctx->toplevel_resource_list);
|
||||||
|
|
||||||
|
zwlr_foreign_toplevel_manager_v1_add_listener(ctx->wlroots_toplevel_manager, &toplevelListener, ctx);
|
||||||
|
}
|
||||||
|
|
||||||
if (strcmp(interface, wl_shm_interface.name) == 0) {
|
if (strcmp(interface, wl_shm_interface.name) == 0) {
|
||||||
logprint(DEBUG, "wlroots: |-- registered to interface %s (Version %u)", interface, WL_SHM_VERSION);
|
logprint(DEBUG, "wlroots: |-- registered to interface %s (Version %u)", interface, WL_SHM_VERSION);
|
||||||
ctx->shm = wl_registry_bind(reg, id, &wl_shm_interface, WL_SHM_VERSION);
|
ctx->shm = wl_registry_bind(reg, id, &wl_shm_interface, WL_SHM_VERSION);
|
||||||
|
|
Loading…
Reference in a new issue