initial impl

This commit is contained in:
vaxerski 2023-03-26 00:20:47 +00:00
parent 85f49f4d6c
commit d16feeefbd
9 changed files with 944 additions and 396 deletions

View File

@ -1,4 +1,4 @@
[portal] [portal]
DBusName=org.freedesktop.impl.portal.desktop.hyprland DBusName=org.freedesktop.impl.portal.desktop.hyprland
Interfaces=org.freedesktop.impl.portal.Screenshot;org.freedesktop.impl.portal.ScreenCast; Interfaces=org.freedesktop.impl.portal.Screenshot;org.freedesktop.impl.portal.ScreenCast;org.freedesktop.impl.portal.GlobalShortcuts;
UseIn=wlroots;Hyprland;sway;Wayfire;river; UseIn=wlroots;Hyprland;sway;Wayfire;river;

View File

@ -0,0 +1,28 @@
#pragma once
#include "protocols/hyprland-global-shortcuts-v1-client-protocol.h"
struct xdpw_state;
struct xdpw_session;
struct globalShortcut {
char* name;
char* description;
struct wl_list link;
struct hyprland_global_shortcut_v1* hlShortcut;
};
struct globalShortcutsClient {
struct xdpw_session* session;
struct wl_list shortcuts; // struct globalShortcut*
struct wl_list link;
char* parent_window;
bool sentShortcuts;
};
struct globalShortcutsInstance {
struct hyprland_global_shortcuts_manager_v1* manager;
struct wl_list shortcutClients; // struct globalShortcutsClient*
};
void initShortcutsInstance(struct xdpw_state* state, struct globalShortcutsInstance* instance);

View File

@ -10,52 +10,51 @@
#include <basu/sd-bus.h> #include <basu/sd-bus.h>
#endif #endif
#include "config.h"
#include "global_shortcuts.h"
#include "screencast_common.h" #include "screencast_common.h"
#include "screenshot_common.h" #include "screenshot_common.h"
#include "config.h"
struct xdpw_state { struct xdpw_state {
struct wl_list xdpw_sessions; struct wl_list xdpw_sessions;
sd_bus *bus; sd_bus *bus;
struct wl_display *wl_display; struct wl_display *wl_display;
struct pw_loop *pw_loop; struct pw_loop *pw_loop;
struct xdpw_screencast_context screencast; struct xdpw_screencast_context screencast;
uint32_t screencast_source_types; // bitfield of enum source_types uint32_t screencast_source_types; // bitfield of enum source_types
uint32_t screencast_cursor_modes; // bitfield of enum cursor_modes uint32_t screencast_cursor_modes; // bitfield of enum cursor_modes
uint32_t screencast_version; uint32_t screencast_version;
uint32_t screenshot_version; uint32_t screenshot_version;
struct xdpw_config *config; struct xdpw_config *config;
int timer_poll_fd; int timer_poll_fd;
struct wl_list timers; struct wl_list timers;
struct xdpw_timer *next_timer; struct xdpw_timer *next_timer;
struct globalShortcutsInstance shortcutsInstance;
}; };
struct xdpw_request { struct xdpw_request {
sd_bus_slot *slot; sd_bus_slot *slot;
}; };
struct xdpw_session { struct xdpw_session {
struct wl_list link; struct wl_list link;
sd_bus_slot *slot; sd_bus_slot *slot;
char *session_handle; char *session_handle;
struct xdpw_screencast_instance *screencast_instance; char *app_id;
struct xdpw_screencast_instance *screencast_instance;
}; };
typedef void (*xdpw_event_loop_timer_func_t)(void *data); typedef void (*xdpw_event_loop_timer_func_t)(void *data);
struct xdpw_timer { struct xdpw_timer {
struct xdpw_state *state; struct xdpw_state *state;
xdpw_event_loop_timer_func_t func; xdpw_event_loop_timer_func_t func;
void *user_data; void *user_data;
struct timespec at; struct timespec at;
struct wl_list link; // xdpw_state::timers struct wl_list link; // xdpw_state::timers
}; };
enum { enum { PORTAL_RESPONSE_SUCCESS = 0, PORTAL_RESPONSE_CANCELLED = 1, PORTAL_RESPONSE_ENDED = 2 };
PORTAL_RESPONSE_SUCCESS = 0,
PORTAL_RESPONSE_CANCELLED = 1,
PORTAL_RESPONSE_ENDED = 2
};
int xdpw_screenshot_init(struct xdpw_state *state); int xdpw_screenshot_init(struct xdpw_state *state);
int xdpw_screencast_init(struct xdpw_state *state); int xdpw_screencast_init(struct xdpw_state *state);
@ -66,8 +65,7 @@ void xdpw_request_destroy(struct xdpw_request *req);
struct xdpw_session *xdpw_session_create(struct xdpw_state *state, sd_bus *bus, char *object_path); struct xdpw_session *xdpw_session_create(struct xdpw_state *state, sd_bus *bus, char *object_path);
void xdpw_session_destroy(struct xdpw_session *req); void xdpw_session_destroy(struct xdpw_session *req);
struct xdpw_timer *xdpw_add_timer(struct xdpw_state *state, struct xdpw_timer *xdpw_add_timer(struct xdpw_state *state, uint64_t delay_ns, xdpw_event_loop_timer_func_t func, void *data);
uint64_t delay_ns, xdpw_event_loop_timer_func_t func, void *data);
void xdpw_destroy_timer(struct xdpw_timer *timer); void xdpw_destroy_timer(struct xdpw_timer *timer);

View File

@ -84,6 +84,7 @@ xdpw_files = files([
'src/screencast/wlr_screencast.c', 'src/screencast/wlr_screencast.c',
'src/screencast/pipewire_screencast.c', 'src/screencast/pipewire_screencast.c',
'src/screencast/fps_limit.c', 'src/screencast/fps_limit.c',
'src/globalshortcuts/global_shortcuts.c'
]) ])
executable( executable(

View File

@ -0,0 +1,112 @@
<?xml version="1.0" encoding="UTF-8"?>
<protocol name="hyprland_global_shortcuts_v1">
<copyright>
Copyright © 2022 Vaxry
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="registering global shortcuts">
This protocol allows a client to register triggerable actions,
meant to be global shortcuts.
</description>
<interface name="hyprland_global_shortcuts_manager_v1" version="1">
<description summary="manager to register global shortcuts">
This object is a manager which offers requests to create global shortcuts.
</description>
<request name="register_shortcut">
<description summary="register a shortcut">
Register a new global shortcut.
A global shortcut is anonymous, meaning the app does not know what key(s) trigger it.
The shortcut's keybinding shall be dealt with by the compositor.
In the case of a duplicate app_id + id combination, the already_taken protocol error is raised.
</description>
<arg name="shortcut" type="new_id" interface="hyprland_global_shortcut_v1"/>
<arg name="id" type="string" summary="a unique id for the shortcut"/>
<arg name="app_id" type="string" summary="the app_id of the application requesting the shortcut"/>
<arg name="description" type="string" summary="user-readable text describing what the shortcut does."/>
<arg name="trigger_description" type="string" summary="user-readable text describing how to trigger the shortcut for the client to render."/>
</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>
<enum name="error">
<entry name="already_taken" value="0"
summary="the app_id + id combination has already been registered."/>
</enum>
</interface>
<interface name="hyprland_global_shortcut_v1" version="1">
<description summary="a shortcut">
This object represents a single shortcut.
</description>
<event name="pressed">
<description summary="keystroke pressed">
The keystroke was pressed.
tv_ values hold the timestamp of the occurrence.
</description>
<arg name="tv_sec_hi" type="uint"
summary="high 32 bits of the seconds part of the timestamp"/>
<arg name="tv_sec_lo" type="uint"
summary="low 32 bits of the seconds part of the timestamp"/>
<arg name="tv_nsec" type="uint"
summary="nanoseconds part of the timestamp"/>
</event>
<event name="released">
<description summary="keystroke released">
The keystroke was released.
tv_ values hold the timestamp of the occurrence.
</description>
<arg name="tv_sec_hi" type="uint"
summary="high 32 bits of the seconds part of the timestamp"/>
<arg name="tv_sec_lo" type="uint"
summary="low 32 bits of the seconds part of the timestamp"/>
<arg name="tv_nsec" type="uint"
summary="nanoseconds part of the timestamp"/>
</event>
<request name="destroy" type="destructor">
<description summary="delete this object, used or not">
Destroys the shortcut. Can be sent at any time by the client.
</description>
</request>
</interface>
</protocol>

View File

@ -17,6 +17,7 @@ client_protocols = [
hl_protocol_dir / 'protocols/hyprland-toplevel-export-v1.xml', hl_protocol_dir / 'protocols/hyprland-toplevel-export-v1.xml',
'wlr-screencopy-unstable-v1.xml', 'wlr-screencopy-unstable-v1.xml',
'wlr-foreign-toplevel-management-unstable-v1.xml', 'wlr-foreign-toplevel-management-unstable-v1.xml',
'hyprland-global-shortcuts-v1.xml',
] ]
wl_proto_files = [] wl_proto_files = []

View File

@ -1,288 +1,290 @@
#include <errno.h> #include <errno.h>
#include <stdlib.h>
#include <stdio.h>
#include <sys/timerfd.h>
#include <getopt.h> #include <getopt.h>
#include <poll.h>
#include <pipewire/pipewire.h> #include <pipewire/pipewire.h>
#include <poll.h>
#include <spa/utils/result.h> #include <spa/utils/result.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/timerfd.h>
#include <unistd.h> #include <unistd.h>
#include "xdpw.h"
#include "logger.h" #include "logger.h"
#include "xdpw.h"
enum event_loop_fd { enum event_loop_fd {
EVENT_LOOP_DBUS, EVENT_LOOP_DBUS,
EVENT_LOOP_WAYLAND, EVENT_LOOP_WAYLAND,
EVENT_LOOP_PIPEWIRE, EVENT_LOOP_PIPEWIRE,
EVENT_LOOP_TIMER, EVENT_LOOP_TIMER,
}; };
static const char service_name[] = "org.freedesktop.impl.portal.desktop.hyprland"; static const char service_name[] = "org.freedesktop.impl.portal.desktop.hyprland";
static int xdpw_usage(FILE *stream, int rc) { static int xdpw_usage(FILE *stream, int rc) {
static const char *usage = static const char *usage =
"Usage: xdg-desktop-portal-hyprland [options]\n" "Usage: xdg-desktop-portal-hyprland [options]\n"
"\n" "\n"
" -l, --loglevel=<loglevel> Select log level (default is ERROR).\n" " -l, --loglevel=<loglevel> Select log level (default is ERROR).\n"
" QUIET, ERROR, WARN, INFO, DEBUG, TRACE\n" " QUIET, ERROR, WARN, INFO, DEBUG, TRACE\n"
" -c, --config=<config file> Select config file.\n" " -c, --config=<config file> Select config file.\n"
" (default is $XDG_CONFIG_HOME/xdg-desktop-portal-hyprland/config)\n" " (default is $XDG_CONFIG_HOME/xdg-desktop-portal-hyprland/config)\n"
" -r, --replace Replace a running instance.\n" " -r, --replace Replace a running instance.\n"
" -h, --help Get help (this text).\n" " -h, --help Get help (this text).\n"
"\n"; "\n";
fprintf(stream, "%s", usage); fprintf(stream, "%s", usage);
return rc; return rc;
} }
static int handle_name_lost(sd_bus_message *m, void *userdata, sd_bus_error *ret_error) { static int handle_name_lost(sd_bus_message *m, void *userdata, sd_bus_error *ret_error) {
logprint(INFO, "dbus: lost name, closing connection"); logprint(INFO, "dbus: lost name, closing connection");
sd_bus_close(sd_bus_message_get_bus(m)); sd_bus_close(sd_bus_message_get_bus(m));
return 1; return 1;
} }
int main(int argc, char *argv[]) { int main(int argc, char *argv[]) {
struct xdpw_config config = {0}; struct xdpw_config config = {0};
char *configfile = NULL; char *configfile = NULL;
enum LOGLEVEL loglevel = DEFAULT_LOGLEVEL; enum LOGLEVEL loglevel = DEFAULT_LOGLEVEL;
bool replace = false; bool replace = false;
static const char *shortopts = "l:o:c:f:rh"; static const char *shortopts = "l:o:c:f:rh";
static const struct option longopts[] = { static const struct option longopts[] = {{"loglevel", required_argument, NULL, 'l'},
{ "loglevel", required_argument, NULL, 'l' }, {"config", required_argument, NULL, 'c'},
{ "config", required_argument, NULL, 'c' }, {"replace", no_argument, NULL, 'r'},
{ "replace", no_argument, NULL, 'r' }, {"help", no_argument, NULL, 'h'},
{ "help", no_argument, NULL, 'h' }, {NULL, 0, NULL, 0}};
{ NULL, 0, NULL, 0 }
};
while (1) { while (1) {
int c = getopt_long(argc, argv, shortopts, longopts, NULL); int c = getopt_long(argc, argv, shortopts, longopts, NULL);
if (c < 0) { if (c < 0) {
break; break;
} }
switch (c) { switch (c) {
case 'l': case 'l':
loglevel = get_loglevel(optarg); loglevel = get_loglevel(optarg);
break; break;
case 'c': case 'c':
configfile = strdup(optarg); configfile = strdup(optarg);
break; break;
case 'r': case 'r':
replace = true; replace = true;
break; break;
case 'h': case 'h':
return xdpw_usage(stdout, EXIT_SUCCESS); return xdpw_usage(stdout, EXIT_SUCCESS);
default: default:
return xdpw_usage(stderr, EXIT_FAILURE); return xdpw_usage(stderr, EXIT_FAILURE);
} }
} }
init_logger(stderr, loglevel); init_logger(stderr, loglevel);
init_config(&configfile, &config); init_config(&configfile, &config);
print_config(DEBUG, &config); print_config(DEBUG, &config);
int ret = 0; int ret = 0;
sd_bus *bus = NULL; sd_bus *bus = NULL;
ret = sd_bus_open_user(&bus); ret = sd_bus_open_user(&bus);
if (ret < 0) { if (ret < 0) {
logprint(ERROR, "dbus: failed to connect to user bus: %s", strerror(-ret)); logprint(ERROR, "dbus: failed to connect to user bus: %s", strerror(-ret));
return EXIT_FAILURE; return EXIT_FAILURE;
} }
logprint(DEBUG, "dbus: connected"); logprint(DEBUG, "dbus: connected");
struct wl_display *wl_display = wl_display_connect(NULL); struct wl_display *wl_display = wl_display_connect(NULL);
if (!wl_display) { if (!wl_display) {
logprint(ERROR, "wayland: failed to connect to display"); logprint(ERROR, "wayland: failed to connect to display");
sd_bus_unref(bus); sd_bus_unref(bus);
return EXIT_FAILURE; return EXIT_FAILURE;
} }
logprint(DEBUG, "wlroots: wl_display connected"); logprint(DEBUG, "wlroots: wl_display connected");
pw_init(NULL, NULL); pw_init(NULL, NULL);
struct pw_loop *pw_loop = pw_loop_new(NULL); struct pw_loop *pw_loop = pw_loop_new(NULL);
if (!pw_loop) { if (!pw_loop) {
logprint(ERROR, "pipewire: failed to create loop"); logprint(ERROR, "pipewire: failed to create loop");
wl_display_disconnect(wl_display); wl_display_disconnect(wl_display);
sd_bus_unref(bus); sd_bus_unref(bus);
return EXIT_FAILURE; return EXIT_FAILURE;
} }
logprint(DEBUG, "pipewire: pw_loop created"); logprint(DEBUG, "pipewire: pw_loop created");
struct xdpw_state state = { struct xdpw_state state = {
.bus = bus, .bus = bus,
.wl_display = wl_display, .wl_display = wl_display,
.pw_loop = pw_loop, .pw_loop = pw_loop,
.screencast_source_types = MONITOR, .screencast_source_types = MONITOR,
.screencast_cursor_modes = HIDDEN | EMBEDDED, .screencast_cursor_modes = HIDDEN | EMBEDDED,
.screencast_version = XDP_CAST_PROTO_VER, .screencast_version = XDP_CAST_PROTO_VER,
.screenshot_version = XDP_SHOT_PROTO_VER, .screenshot_version = XDP_SHOT_PROTO_VER,
.config = &config, .config = &config,
}; };
wl_list_init(&state.xdpw_sessions); wl_list_init(&state.xdpw_sessions);
ret = xdpw_screenshot_init(&state); initShortcutsInstance(&state, &state.shortcutsInstance);
if (ret < 0) {
logprint(ERROR, "xdpw: failed to initialize screenshot");
goto error;
}
ret = xdpw_screencast_init(&state); ret = xdpw_screenshot_init(&state);
if (ret < 0) { if (ret < 0) {
logprint(ERROR, "xdpw: failed to initialize screencast"); logprint(ERROR, "xdpw: failed to initialize screenshot");
goto error; goto error;
} }
uint64_t flags = SD_BUS_NAME_ALLOW_REPLACEMENT; ret = xdpw_screencast_init(&state);
if (replace) { if (ret < 0) {
flags |= SD_BUS_NAME_REPLACE_EXISTING; logprint(ERROR, "xdpw: failed to initialize screencast");
} goto error;
}
ret = sd_bus_request_name(bus, service_name, flags); uint64_t flags = SD_BUS_NAME_ALLOW_REPLACEMENT;
if (ret < 0) { if (replace) {
logprint(ERROR, "dbus: failed to acquire service name: %s", strerror(-ret)); flags |= SD_BUS_NAME_REPLACE_EXISTING;
goto error; }
}
const char *unique_name; ret = sd_bus_request_name(bus, service_name, flags);
ret = sd_bus_get_unique_name(bus, &unique_name); if (ret < 0) {
if (ret < 0) { logprint(ERROR, "dbus: failed to acquire service name: %s", strerror(-ret));
logprint(ERROR, "dbus: failed to get unique bus name: %s", strerror(-ret)); goto error;
goto error; }
}
static char match[1024]; const char *unique_name;
snprintf(match, sizeof(match), "sender='org.freedesktop.DBus'," ret = sd_bus_get_unique_name(bus, &unique_name);
"type='signal'," if (ret < 0) {
"interface='org.freedesktop.DBus'," logprint(ERROR, "dbus: failed to get unique bus name: %s", strerror(-ret));
"member='NameOwnerChanged'," goto error;
"path='/org/freedesktop/DBus'," }
"arg0='%s',"
"arg1='%s'",
service_name, unique_name);
sd_bus_slot *slot; static char match[1024];
ret = sd_bus_add_match(bus, &slot, match, handle_name_lost, NULL); snprintf(match, sizeof(match),
if (ret < 0) { "sender='org.freedesktop.DBus',"
logprint(ERROR, "dbus: failed to add NameOwnerChanged signal match: %s", strerror(-ret)); "type='signal',"
goto error; "interface='org.freedesktop.DBus',"
} "member='NameOwnerChanged',"
"path='/org/freedesktop/DBus',"
"arg0='%s',"
"arg1='%s'",
service_name, unique_name);
wl_list_init(&state.timers); sd_bus_slot *slot;
ret = sd_bus_add_match(bus, &slot, match, handle_name_lost, NULL);
if (ret < 0) {
logprint(ERROR, "dbus: failed to add NameOwnerChanged signal match: %s", strerror(-ret));
goto error;
}
struct pollfd pollfds[] = { wl_list_init(&state.timers);
[EVENT_LOOP_DBUS] = {
.fd = sd_bus_get_fd(state.bus),
.events = POLLIN,
},
[EVENT_LOOP_WAYLAND] = {
.fd = wl_display_get_fd(state.wl_display),
.events = POLLIN,
},
[EVENT_LOOP_PIPEWIRE] = {
.fd = pw_loop_get_fd(state.pw_loop),
.events = POLLIN,
},
[EVENT_LOOP_TIMER] = {
.fd = timerfd_create(CLOCK_MONOTONIC, TFD_CLOEXEC),
.events = POLLIN,
}
};
state.timer_poll_fd = pollfds[EVENT_LOOP_TIMER].fd; struct pollfd pollfds[] = {[EVENT_LOOP_DBUS] =
{
.fd = sd_bus_get_fd(state.bus),
.events = POLLIN,
},
[EVENT_LOOP_WAYLAND] =
{
.fd = wl_display_get_fd(state.wl_display),
.events = POLLIN,
},
[EVENT_LOOP_PIPEWIRE] =
{
.fd = pw_loop_get_fd(state.pw_loop),
.events = POLLIN,
},
[EVENT_LOOP_TIMER] = {
.fd = timerfd_create(CLOCK_MONOTONIC, TFD_CLOEXEC),
.events = POLLIN,
}};
while (1) { state.timer_poll_fd = pollfds[EVENT_LOOP_TIMER].fd;
ret = poll(pollfds, sizeof(pollfds) / sizeof(pollfds[0]), -1);
if (ret < 0) {
logprint(ERROR, "poll failed: %s", strerror(errno));
goto error;
}
if (pollfds[EVENT_LOOP_DBUS].revents & POLLHUP) { while (1) {
logprint(INFO, "event-loop: disconnected from dbus"); ret = poll(pollfds, sizeof(pollfds) / sizeof(pollfds[0]), -1);
break; if (ret < 0) {
} logprint(ERROR, "poll failed: %s", strerror(errno));
if (pollfds[EVENT_LOOP_WAYLAND].revents & POLLHUP) { goto error;
logprint(INFO, "event-loop: disconnected from wayland"); }
break;
}
if (pollfds[EVENT_LOOP_PIPEWIRE].revents & POLLHUP) {
logprint(INFO, "event-loop: disconnected from pipewire");
break;
}
if (pollfds[EVENT_LOOP_DBUS].revents & POLLIN) { if (pollfds[EVENT_LOOP_DBUS].revents & POLLHUP) {
logprint(TRACE, "event-loop: got dbus event"); logprint(INFO, "event-loop: disconnected from dbus");
do { break;
ret = sd_bus_process(state.bus, NULL); }
} while (ret > 0); if (pollfds[EVENT_LOOP_WAYLAND].revents & POLLHUP) {
if (ret < 0) { logprint(INFO, "event-loop: disconnected from wayland");
logprint(ERROR, "sd_bus_process failed: %s", strerror(-ret)); break;
goto error; }
} if (pollfds[EVENT_LOOP_PIPEWIRE].revents & POLLHUP) {
} logprint(INFO, "event-loop: disconnected from pipewire");
break;
}
if (pollfds[EVENT_LOOP_WAYLAND].revents & POLLIN) { if (pollfds[EVENT_LOOP_DBUS].revents & POLLIN) {
logprint(TRACE, "event-loop: got wayland event"); logprint(TRACE, "event-loop: got dbus event");
ret = wl_display_dispatch(state.wl_display); do {
if (ret < 0) { ret = sd_bus_process(state.bus, NULL);
logprint(ERROR, "wl_display_dispatch failed: %s", strerror(errno)); } while (ret > 0);
goto error; if (ret < 0) {
} logprint(ERROR, "sd_bus_process failed: %s", strerror(-ret));
} goto error;
}
}
if (pollfds[EVENT_LOOP_PIPEWIRE].revents & POLLIN) { if (pollfds[EVENT_LOOP_WAYLAND].revents & POLLIN) {
logprint(TRACE, "event-loop: got pipewire event"); logprint(TRACE, "event-loop: got wayland event");
ret = pw_loop_iterate(state.pw_loop, 0); ret = wl_display_dispatch(state.wl_display);
if (ret < 0) { if (ret < 0) {
logprint(ERROR, "pw_loop_iterate failed: %s", spa_strerror(ret)); logprint(ERROR, "wl_display_dispatch failed: %s", strerror(errno));
goto error; goto error;
} }
} }
if (pollfds[EVENT_LOOP_TIMER].revents & POLLIN) { if (pollfds[EVENT_LOOP_PIPEWIRE].revents & POLLIN) {
logprint(TRACE, "event-loop: got a timer event"); logprint(TRACE, "event-loop: got pipewire event");
ret = pw_loop_iterate(state.pw_loop, 0);
if (ret < 0) {
logprint(ERROR, "pw_loop_iterate failed: %s", spa_strerror(ret));
goto error;
}
}
int timer_fd = pollfds[EVENT_LOOP_TIMER].fd; if (pollfds[EVENT_LOOP_TIMER].revents & POLLIN) {
uint64_t expirations; logprint(TRACE, "event-loop: got a timer event");
ssize_t n = read(timer_fd, &expirations, sizeof(expirations));
if (n < 0) {
logprint(ERROR, "failed to read from timer FD\n");
goto error;
}
struct xdpw_timer *timer = state.next_timer; int timer_fd = pollfds[EVENT_LOOP_TIMER].fd;
if (timer != NULL) { uint64_t expirations;
xdpw_event_loop_timer_func_t func = timer->func; ssize_t n = read(timer_fd, &expirations, sizeof(expirations));
void *user_data = timer->user_data; if (n < 0) {
xdpw_destroy_timer(timer); logprint(ERROR, "failed to read from timer FD\n");
goto error;
}
func(user_data); struct xdpw_timer *timer = state.next_timer;
} if (timer != NULL) {
} xdpw_event_loop_timer_func_t func = timer->func;
void *user_data = timer->user_data;
xdpw_destroy_timer(timer);
do { func(user_data);
ret = wl_display_dispatch_pending(state.wl_display); }
wl_display_flush(state.wl_display); }
} while (ret > 0);
sd_bus_flush(state.bus); do {
} ret = wl_display_dispatch_pending(state.wl_display);
wl_display_flush(state.wl_display);
} while (ret > 0);
// TODO: cleanup sd_bus_flush(state.bus);
finish_config(&config); }
free(configfile);
return EXIT_SUCCESS; // TODO: cleanup
finish_config(&config);
free(configfile);
return EXIT_SUCCESS;
error: error:
sd_bus_unref(bus); sd_bus_unref(bus);
pw_loop_leave(state.pw_loop); pw_loop_leave(state.pw_loop);
pw_loop_destroy(state.pw_loop); pw_loop_destroy(state.pw_loop);
wl_display_disconnect(state.wl_display); wl_display_disconnect(state.wl_display);
return EXIT_FAILURE; return EXIT_FAILURE;
} }

View File

@ -0,0 +1,455 @@
#include "include/global_shortcuts.h"
#include <string.h>
#include "include/xdpw.h"
static void wlr_registry_handle_add(void *data, struct wl_registry *reg, uint32_t id, const char *interface, uint32_t ver) {
struct globalShortcutsInstance *instance = data;
if (!strcmp(interface, hyprland_global_shortcuts_manager_v1_interface.name) && !instance->manager) {
uint32_t version = ver;
logprint(DEBUG, "hyprland: |-- registered to interface %s (Version %u)", interface, version);
instance->manager = wl_registry_bind(reg, id, &hyprland_global_shortcuts_manager_v1_interface, version);
}
}
static void wlr_registry_handle_remove(void *data, struct wl_registry *reg, uint32_t id) {
; // ignored
}
static const struct wl_registry_listener wlr_registry_listener = {
.global = wlr_registry_handle_add,
.global_remove = wlr_registry_handle_remove,
};
static const char object_path[] = "/org/freedesktop/portal/desktop";
static const char interface_name[] = "org.freedesktop.impl.portal.GlobalShortcuts";
static const sd_bus_vtable gs_vtable[] = {
SD_BUS_VTABLE_START(0),
SD_BUS_METHOD("CreateSession", "oosa{sv}", "ua{sv}", method_gs_create_session, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("BindShortcuts", "ooa(sa{sv})sa{sv}", "ua{sv}", method_gs_bind_shortcuts, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("ListShortcuts", "oo", "ua{sv}", method_gs_list_shortcuts, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_SIGNAL("Activated", "osta{sv}", NULL),
SD_BUS_SIGNAL("Deactivated", "osta{sv}", NULL),
SD_BUS_SIGNAL("ShortcutsChanged", "oa(sa{sv})", NULL),
SD_BUS_VTABLE_END};
static void handleActivated(void *data, struct hyprland_global_shortcut_v1 *hyprland_global_shortcut_v1, uint32_t tv_sec_hi, uint32_t tv_sec_lo,
uint32_t tv_nsec) {
struct xdpw_state *state = data;
struct globalShortcut *curr;
struct globalShortcutClient *currc;
wl_list_for_each(currc, &state->shortcutsInstance.shortcutClients, link) {
wl_list_for_each(curr, &currc->shortcuts, link) {
if (curr->hlShortcut == hyprland_global_shortcut_v1) {
goto found;
}
}
}
found:
sd_bus_emit_signal(state->bus, object_path, interface_name, "Activated", "osta{sv}", currc->session->session_handle, curr->name,
((uint64_t)tv_sec_hi << 32) | (uint64_t)(tv_sec_lo), 0);
}
static void handleDeactivated(void *data, struct hyprland_global_shortcut_v1 *hyprland_global_shortcut_v1, uint32_t tv_sec_hi, uint32_t tv_sec_lo,
uint32_t tv_nsec) {
struct xdpw_state *state = data;
struct globalShortcut *curr;
struct globalShortcutClient *currc;
wl_list_for_each(currc, &state->shortcutsInstance.shortcutClients, link) {
wl_list_for_each(curr, &currc->shortcuts, link) {
if (curr->hlShortcut == hyprland_global_shortcut_v1) {
goto found;
}
}
}
found:
sd_bus_emit_signal(state->bus, object_path, interface_name, "Deactivated", "osta{sv}", currc->session->session_handle, curr->name,
((uint64_t)tv_sec_hi << 32) | (uint64_t)(tv_sec_lo), 0);
}
static const struct hyprland_global_shortcut_v1_listener shortcutListener = {
.pressed = handleActivated,
.released = handleDeactivated,
};
static int method_gs_create_session(sd_bus_message *msg, void *data, sd_bus_error *ret_error) {
struct xdpw_state *state = data;
struct globalShortcutsClient *client = calloc(1, sizeof(struct globalShortcutsClient));
wl_list_insert(&state->shortcutsInstance.shortcutClients, &client->link);
wl_list_init(&client->shortcuts);
int ret = 0;
logprint(INFO, "dbus: create session method invoked");
char *request_handle, *session_handle, *app_id;
ret = sd_bus_message_read(msg, "oos", &request_handle, &session_handle, &app_id);
if (ret < 0) {
return ret;
}
ret = sd_bus_message_enter_container(msg, 'a', "{sv}");
if (ret < 0) {
return ret;
}
logprint(INFO, "dbus: request_handle: %s", request_handle);
logprint(INFO, "dbus: session_handle: %s", session_handle);
logprint(INFO, "dbus: app_id: %s", app_id);
char *key;
int innerRet = 0;
while ((ret = sd_bus_message_enter_container(msg, 'e', "sv")) > 0) {
innerRet = sd_bus_message_read(msg, "s", &key);
if (innerRet < 0) {
return innerRet;
}
if (strcmp(key, "session_handle_token") == 0) {
char *token;
sd_bus_message_read(msg, "v", "s", &token);
logprint(INFO, "dbus: option token: %s", token);
} else if (strcmp(key, "shortcuts") == 0) {
// init shortcuts
client->sentShortcuts = true;
innerRet = sd_bus_message_enter_container(msg, 'v', "a(sa{sv})");
if (innerRet < 0) {
return innerRet;
}
innerRet = sd_bus_message_enter_container(msg, 'a', "(sa{sv})");
while (innerRet > 0) {
char type;
char *container;
sd_bus_message_peek_type(msg, &type, &container);
if (type != 'r') break;
innerRet = sd_bus_message_enter_container(msg, 'r', "sa{sv}");
if (innerRet == -ENXIO) break;
sd_bus_message_peek_type(msg, &type, &container);
innerRet = sd_bus_message_read(msg, "s", &key);
if (innerRet == -ENXIO) break;
if (innerRet < 0) {
return innerRet;
}
logprint(DEBUG, "shortcut name %s", key);
struct globalShortcut *shortcut = calloc(1, sizeof(struct globalShortcut));
shortcut->name = malloc(strlen(key) + 1);
strcpy(shortcut->name, key);
shortcut->description = calloc(1, 1); // todo
wl_list_insert(&client->shortcuts, &shortcut->link);
// sd_bus_message_enter_container(msg, 'e', "sv");
// sd_bus_message_exit_container(msg);
sd_bus_message_skip(msg, "a{sv}");
sd_bus_message_exit_container(msg);
}
innerRet = sd_bus_message_exit_container(msg);
innerRet = sd_bus_message_exit_container(msg);
if (innerRet < 0) {
return innerRet;
}
} else {
logprint(WARN, "dbus: unknown option: %s", key);
sd_bus_message_skip(msg, "v");
}
innerRet = sd_bus_message_exit_container(msg);
if (innerRet < 0) {
return innerRet;
}
}
if (ret < 0) {
return ret;
}
ret = sd_bus_message_exit_container(msg);
if (ret < 0) {
return ret;
}
struct xdpw_request *req = xdpw_request_create(sd_bus_message_get_bus(msg), request_handle);
if (req == NULL) {
return -ENOMEM;
}
struct xdpw_session *sess = xdpw_session_create(state, sd_bus_message_get_bus(msg), strdup(session_handle));
if (sess == NULL) {
return -ENOMEM;
}
sess->app_id = malloc(strlen(app_id) + 1);
strcpy(sess->app_id, app_id);
sd_bus_message *reply = NULL;
ret = sd_bus_message_new_method_return(msg, &reply);
if (ret < 0) {
return ret;
}
ret = sd_bus_message_append(reply, "u", PORTAL_RESPONSE_SUCCESS, 0);
if (ret < 0) {
return ret;
}
ret = sd_bus_message_open_container(reply, 'a', "{sv}");
if (ret < 0) {
return ret;
}
sd_bus_message_open_container(reply, 'e', "sv");
sd_bus_message_append(reply, "s", "shortcuts");
sd_bus_message_open_container(reply, 'v', "a(sa{sv})");
sd_bus_message_open_container(reply, 'a', "(sa{sv})");
struct globalShortcut *curr;
wl_list_for_each(curr, &client->shortcuts, link) {
sd_bus_message_append(reply, "(sa{sv})", curr->name, 1, "description", "s", curr->description);
}
ret = sd_bus_message_close_container(reply);
ret = sd_bus_message_close_container(reply);
ret = sd_bus_message_close_container(reply);
ret = sd_bus_message_close_container(reply);
if (ret < 0) {
return ret;
}
ret = sd_bus_send(NULL, reply, NULL);
if (ret < 0) {
return ret;
}
sd_bus_message_unref(reply);
return 0;
}
static int method_gs_bind_shortcuts(sd_bus_message *msg, void *data, sd_bus_error *ret_error) {
struct xdpw_state *state = data;
int ret = 0;
logprint(INFO, "dbus: bind shortcuts invoked");
char *request_handle, *session_handle;
ret = sd_bus_message_read(msg, "oo", &request_handle, &session_handle);
if (ret < 0) {
return ret;
}
logprint(INFO, "dbus: request_handle: %s", request_handle);
logprint(INFO, "dbus: session_handle: %s", session_handle);
struct xdpw_session *session, *tmp_session;
wl_list_for_each_reverse_safe(session, tmp_session, &state->xdpw_sessions, link) {
if (strcmp(session->session_handle, session_handle) == 0) {
logprint(DEBUG, "dbus: bind shortcuts: found matching session %s", session->session_handle);
break;
}
}
struct globalShortcutsClient *client, *tmp_client;
wl_list_for_each_reverse_safe(client, tmp_client, &state->shortcutsInstance.shortcutClients, link) {
if (strcmp(client->session, session_handle) == 0) {
logprint(DEBUG, "dbus: bind shortcuts: found matching client %s", client->session_handle);
break;
}
}
if (!client->sentShortcuts) {
char *key;
int innerRet = 0;
client->sentShortcuts = true;
innerRet = sd_bus_message_enter_container(msg, 'a', "(sa{sv})");
while (innerRet > 0) {
char type;
char *container;
sd_bus_message_peek_type(msg, &type, &container);
if (type != 'r') break;
innerRet = sd_bus_message_enter_container(msg, 'r', "sa{sv}");
if (innerRet == -ENXIO) break;
sd_bus_message_peek_type(msg, &type, &container);
innerRet = sd_bus_message_read(msg, "s", &key);
if (innerRet == -ENXIO) break;
if (innerRet < 0) {
return innerRet;
}
logprint(DEBUG, "shortcut name %s", key);
struct globalShortcut *shortcut = calloc(1, sizeof(struct globalShortcut));
shortcut->name = malloc(strlen(key) + 1);
strcpy(shortcut->name, key);
shortcut->description = calloc(1, 1); // todo
wl_list_insert(&client->shortcuts, &shortcut->link);
// sd_bus_message_enter_container(msg, 'e', "sv");
// sd_bus_message_exit_container(msg);
sd_bus_message_skip(msg, "a{sv}");
sd_bus_message_exit_container(msg);
}
innerRet = sd_bus_message_exit_container(msg);
if (innerRet < 0) {
return innerRet;
}
}
ret = sd_bus_message_exit_container(msg);
if (ret < 0) {
return ret;
}
char *parent_window;
ret = sd_bus_message_read(msg, "s", &parent_window);
logprint(DEBUG, "dbus: parent_window %s", parent_window);
client->parent_window = malloc(strlen(parent_window) + 1);
strcpy(client->parent_window, parent_window);
sd_bus_message *reply = NULL;
ret = sd_bus_message_new_method_return(msg, &reply);
if (ret < 0) {
return ret;
}
ret = sd_bus_message_append(reply, "u", PORTAL_RESPONSE_SUCCESS, 0);
if (ret < 0) {
return ret;
}
ret = sd_bus_message_open_container(reply, 'a', "{sv}");
if (ret < 0) {
return ret;
}
sd_bus_message_open_container(reply, 'e', "sv");
sd_bus_message_append(reply, "s", "shortcuts");
sd_bus_message_open_container(reply, 'v', "a(sa{sv})");
sd_bus_message_open_container(reply, 'a', "(sa{sv})");
struct globalShortcut *curr;
wl_list_for_each(curr, &client->shortcuts, link) {
sd_bus_message_append(reply, "(sa{sv})", curr->name, 1, "description", "s", curr->description);
curr->hlShortcut = hyprland_global_shortcuts_manager_v1_register_shortcut(state->shortcutsInstance.manager, curr->name, client->parent_window,
curr->description, "");
hyprland_global_shortcut_v1_add_listener(curr->hlShortcut, &shortcutListener, state);
}
ret = sd_bus_message_close_container(reply);
ret = sd_bus_message_close_container(reply);
ret = sd_bus_message_close_container(reply);
ret = sd_bus_message_close_container(reply);
if (ret < 0) {
return ret;
}
ret = sd_bus_send(NULL, reply, NULL);
if (ret < 0) {
return ret;
}
return 0;
}
static int method_gs_list_shortcuts(sd_bus_message *msg, void *data, sd_bus_error *ret_error) {
struct xdpw_state *state = data;
int ret = 0;
logprint(INFO, "dbus: list shortcuts invoked");
char *request_handle, *session_handle;
ret = sd_bus_message_read(msg, "oo", &request_handle, &session_handle);
if (ret < 0) {
return ret;
}
logprint(INFO, "dbus: request_handle: %s", request_handle);
logprint(INFO, "dbus: session_handle: %s", session_handle);
struct globalShortcutsClient *client, *tmp_client;
wl_list_for_each_reverse_safe(client, tmp_client, &state->shortcutsInstance.shortcutClients, link) {
if (strcmp(client->session, session_handle) == 0) {
logprint(DEBUG, "dbus: bind shortcuts: found matching client %s", client->session_handle);
break;
}
}
sd_bus_message *reply = NULL;
ret = sd_bus_message_new_method_return(msg, &reply);
if (ret < 0) {
return ret;
}
ret = sd_bus_message_append(reply, "u", PORTAL_RESPONSE_SUCCESS, 0);
if (ret < 0) {
return ret;
}
ret = sd_bus_message_open_container(reply, 'a', "{sv}");
if (ret < 0) {
return ret;
}
sd_bus_message_open_container(reply, 'e', "sv");
sd_bus_message_append(reply, "s", "shortcuts");
sd_bus_message_open_container(reply, 'v', "a(sa{sv})");
sd_bus_message_open_container(reply, 'a', "(sa{sv})");
struct globalShortcut *curr;
wl_list_for_each(curr, &client->shortcuts, link) {
sd_bus_message_append(reply, "(sa{sv})", curr->name, 1, "description", "s", curr->description);
}
ret = sd_bus_message_close_container(reply);
ret = sd_bus_message_close_container(reply);
ret = sd_bus_message_close_container(reply);
ret = sd_bus_message_close_container(reply);
if (ret < 0) {
return ret;
}
ret = sd_bus_send(NULL, reply, NULL);
if (ret < 0) {
return ret;
}
return 0;
}
void initShortcutsInstance(struct xdpw_state *state, struct globalShortcutsInstance *instance) {
instance->manager = NULL;
wl_list_init(&instance->shortcutClients);
struct wl_registry *registry = wl_display_get_registry(state->wl_display);
wl_registry_add_listener(registry, &wlr_registry_listener, instance);
wl_display_roundtrip(state->wl_display);
if (!instance->manager) {
logprint(ERROR, "hyprland shortcut protocol unavailable!");
return;
}
sd_bus_slot *slot = NULL;
int ret = sd_bus_add_object_vtable(state->bus, &slot, object_path, interface_name, gs_vtable, state);
logprint(DEBUG, "dbus: gs: ret bind %d", ret);
// register to hl
}

View File

@ -41,8 +41,7 @@ void handleTitle(void *data, struct zwlr_foreign_toplevel_handle_v1 *handle, con
if (current->handle == handle) { if (current->handle == handle) {
strncpy(current->name, title, 255); strncpy(current->name, title, 255);
for (int i = 0; i < 255; ++i) for (int i = 0; i < 255; ++i)
if (current->name[i] == '\"' || current->name[i] == '>' || current->name[i] == '\'') if (current->name[i] == '\"' || current->name[i] == '>' || current->name[i] == '\'') current->name[i] = ' ';
current->name[i] = ' ';
current->name[255] = '\0'; current->name[255] = '\0';
break; break;
} }
@ -57,8 +56,7 @@ void handleAppID(void *data, struct zwlr_foreign_toplevel_handle_v1 *handle, con
if (current->handle == handle) { if (current->handle == handle) {
strncpy(current->clazz, app_id, 255); strncpy(current->clazz, app_id, 255);
for (int i = 0; i < 255; ++i) for (int i = 0; i < 255; ++i)
if (current->clazz[i] == '\"' || current->clazz[i] == '>' || current->clazz[i] == '\'') if (current->clazz[i] == '\"' || current->clazz[i] == '>' || current->clazz[i] == '\'') current->clazz[i] = ' ';
current->clazz[i] = ' ';
current->name[255] = '\0'; current->name[255] = '\0';
break; break;
} }
@ -187,8 +185,7 @@ void xdpw_wlr_frame_finish(struct xdpw_screencast_instance *cast) {
xdpw_pwr_enqueue_buffer(cast); xdpw_pwr_enqueue_buffer(cast);
uint64_t delay_ns = fps_limit_measure_end(&cast->fps_limit, cast->framerate); uint64_t delay_ns = fps_limit_measure_end(&cast->fps_limit, cast->framerate);
if (delay_ns > 0) { if (delay_ns > 0) {
xdpw_add_timer(cast->ctx->state, delay_ns, xdpw_add_timer(cast->ctx->state, delay_ns, (xdpw_event_loop_timer_func_t)xdpw_wlr_frame_start, cast);
(xdpw_event_loop_timer_func_t)xdpw_wlr_frame_start, cast);
return; return;
} }
} }
@ -211,11 +208,9 @@ void xdpw_wlr_frame_start(struct xdpw_screencast_instance *cast) {
xdpw_wlr_register_cb(cast); xdpw_wlr_register_cb(cast);
} }
static void wlr_frame_buffer_done(void *data, static void wlr_frame_buffer_done(void *data, struct zwlr_screencopy_frame_v1 *frame);
struct zwlr_screencopy_frame_v1 *frame);
static void wlr_frame_buffer(void *data, struct zwlr_screencopy_frame_v1 *frame, static void wlr_frame_buffer(void *data, struct zwlr_screencopy_frame_v1 *frame, uint32_t format, uint32_t width, uint32_t height, uint32_t stride) {
uint32_t format, uint32_t width, uint32_t height, uint32_t stride) {
struct xdpw_screencast_instance *cast = data; struct xdpw_screencast_instance *cast = data;
if (!frame) { if (!frame) {
return; return;
@ -235,9 +230,7 @@ static void wlr_frame_buffer(void *data, struct zwlr_screencopy_frame_v1 *frame,
} }
} }
static void wlr_frame_linux_dmabuf(void *data, static void wlr_frame_linux_dmabuf(void *data, struct zwlr_screencopy_frame_v1 *frame, uint32_t format, uint32_t width, uint32_t height) {
struct zwlr_screencopy_frame_v1 *frame,
uint32_t format, uint32_t width, uint32_t height) {
struct xdpw_screencast_instance *cast = data; struct xdpw_screencast_instance *cast = data;
if (!frame) { if (!frame) {
return; return;
@ -250,8 +243,7 @@ static void wlr_frame_linux_dmabuf(void *data,
cast->screencopy_frame_info[DMABUF].format = format; cast->screencopy_frame_info[DMABUF].format = format;
} }
static void wlr_frame_buffer_done(void *data, static void wlr_frame_buffer_done(void *data, struct zwlr_screencopy_frame_v1 *frame) {
struct zwlr_screencopy_frame_v1 *frame) {
struct xdpw_screencast_instance *cast = data; struct xdpw_screencast_instance *cast = data;
if (!frame) { if (!frame) {
return; return;
@ -266,7 +258,8 @@ static void wlr_frame_buffer_done(void *data,
// Check if announced screencopy information is compatible with pipewire meta // Check if announced screencopy information is compatible with pipewire meta
if ((cast->pwr_format.format != xdpw_format_pw_from_drm_fourcc(cast->screencopy_frame_info[cast->buffer_type].format) && if ((cast->pwr_format.format != xdpw_format_pw_from_drm_fourcc(cast->screencopy_frame_info[cast->buffer_type].format) &&
cast->pwr_format.format != xdpw_format_pw_strip_alpha(xdpw_format_pw_from_drm_fourcc(cast->screencopy_frame_info[cast->buffer_type].format))) || cast->pwr_format.format !=
xdpw_format_pw_strip_alpha(xdpw_format_pw_from_drm_fourcc(cast->screencopy_frame_info[cast->buffer_type].format))) ||
cast->pwr_format.size.width != cast->screencopy_frame_info[cast->buffer_type].width || cast->pwr_format.size.width != cast->screencopy_frame_info[cast->buffer_type].width ||
cast->pwr_format.size.height != cast->screencopy_frame_info[cast->buffer_type].height) { cast->pwr_format.size.height != cast->screencopy_frame_info[cast->buffer_type].height) {
logprint(DEBUG, "wlroots: pipewire and wlroots metadata are incompatible. Renegotiate stream"); logprint(DEBUG, "wlroots: pipewire and wlroots metadata are incompatible. Renegotiate stream");
@ -288,9 +281,8 @@ static void wlr_frame_buffer_done(void *data,
assert(cast->current_frame.xdpw_buffer); assert(cast->current_frame.xdpw_buffer);
// Check if dequeued buffer is compatible with announced buffer // Check if dequeued buffer is compatible with announced buffer
if ((cast->buffer_type == WL_SHM && if ((cast->buffer_type == WL_SHM && (cast->current_frame.xdpw_buffer->size[0] != cast->screencopy_frame_info[cast->buffer_type].size ||
(cast->current_frame.xdpw_buffer->size[0] != cast->screencopy_frame_info[cast->buffer_type].size || cast->current_frame.xdpw_buffer->stride[0] != cast->screencopy_frame_info[cast->buffer_type].stride)) ||
cast->current_frame.xdpw_buffer->stride[0] != cast->screencopy_frame_info[cast->buffer_type].stride)) ||
cast->current_frame.xdpw_buffer->width != cast->screencopy_frame_info[cast->buffer_type].width || cast->current_frame.xdpw_buffer->width != cast->screencopy_frame_info[cast->buffer_type].width ||
cast->current_frame.xdpw_buffer->height != cast->screencopy_frame_info[cast->buffer_type].height) { cast->current_frame.xdpw_buffer->height != cast->screencopy_frame_info[cast->buffer_type].height) {
logprint(DEBUG, "wlroots: pipewire buffer has wrong dimensions"); logprint(DEBUG, "wlroots: pipewire buffer has wrong dimensions");
@ -305,8 +297,7 @@ static void wlr_frame_buffer_done(void *data,
fps_limit_measure_start(&cast->fps_limit, cast->framerate); fps_limit_measure_start(&cast->fps_limit, cast->framerate);
} }
static void wlr_frame_flags(void *data, struct zwlr_screencopy_frame_v1 *frame, static void wlr_frame_flags(void *data, struct zwlr_screencopy_frame_v1 *frame, uint32_t flags) {
uint32_t flags) {
struct xdpw_screencast_instance *cast = data; struct xdpw_screencast_instance *cast = data;
if (!frame) { if (!frame) {
return; return;
@ -316,8 +307,7 @@ static void wlr_frame_flags(void *data, struct zwlr_screencopy_frame_v1 *frame,
cast->current_frame.y_invert = flags & ZWLR_SCREENCOPY_FRAME_V1_FLAGS_Y_INVERT; cast->current_frame.y_invert = flags & ZWLR_SCREENCOPY_FRAME_V1_FLAGS_Y_INVERT;
} }
static void wlr_frame_damage(void *data, struct zwlr_screencopy_frame_v1 *frame, static void wlr_frame_damage(void *data, struct zwlr_screencopy_frame_v1 *frame, uint32_t x, uint32_t y, uint32_t width, uint32_t height) {
uint32_t x, uint32_t y, uint32_t width, uint32_t height) {
struct xdpw_screencast_instance *cast = data; struct xdpw_screencast_instance *cast = data;
if (!frame) { if (!frame) {
return; return;
@ -331,8 +321,7 @@ static void wlr_frame_damage(void *data, struct zwlr_screencopy_frame_v1 *frame,
cast->current_frame.damage.height = height; cast->current_frame.damage.height = height;
} }
static void wlr_frame_ready(void *data, struct zwlr_screencopy_frame_v1 *frame, static void wlr_frame_ready(void *data, struct zwlr_screencopy_frame_v1 *frame, uint32_t tv_sec_hi, uint32_t tv_sec_lo, uint32_t tv_nsec) {
uint32_t tv_sec_hi, uint32_t tv_sec_lo, uint32_t tv_nsec) {
struct xdpw_screencast_instance *cast = data; struct xdpw_screencast_instance *cast = data;
if (!frame) { if (!frame) {
return; return;
@ -349,8 +338,7 @@ static void wlr_frame_ready(void *data, struct zwlr_screencopy_frame_v1 *frame,
xdpw_wlr_frame_finish(cast); xdpw_wlr_frame_finish(cast);
} }
static void wlr_frame_failed(void *data, static void wlr_frame_failed(void *data, struct zwlr_screencopy_frame_v1 *frame) {
struct zwlr_screencopy_frame_v1 *frame) {
struct xdpw_screencast_instance *cast = data; struct xdpw_screencast_instance *cast = data;
if (!frame) { if (!frame) {
return; return;
@ -373,11 +361,10 @@ static const struct zwlr_screencopy_frame_v1_listener wlr_frame_listener = {
.damage = wlr_frame_damage, .damage = wlr_frame_damage,
}; };
static void hyprland_frame_buffer_done(void *data, static void hyprland_frame_buffer_done(void *data, struct hyprland_toplevel_export_frame_v1 *frame);
struct hyprland_toplevel_export_frame_v1 *frame);
static void hyprland_frame_buffer(void *data, struct hyprland_toplevel_export_frame_v1 *frame, static void hyprland_frame_buffer(void *data, struct hyprland_toplevel_export_frame_v1 *frame, uint32_t format, uint32_t width, uint32_t height,
uint32_t format, uint32_t width, uint32_t height, uint32_t stride) { uint32_t stride) {
struct xdpw_screencast_instance *cast = data; struct xdpw_screencast_instance *cast = data;
if (!frame) { if (!frame) {
return; return;
@ -396,9 +383,8 @@ static void hyprland_frame_buffer(void *data, struct hyprland_toplevel_export_fr
hyprland_frame_buffer_done(cast, frame); hyprland_frame_buffer_done(cast, frame);
} }
static void hyprland_frame_linux_dmabuf(void *data, static void hyprland_frame_linux_dmabuf(void *data, struct hyprland_toplevel_export_frame_v1 *frame, uint32_t format, uint32_t width,
struct hyprland_toplevel_export_frame_v1 *frame, uint32_t height) {
uint32_t format, uint32_t width, uint32_t height) {
struct xdpw_screencast_instance *cast = data; struct xdpw_screencast_instance *cast = data;
if (!frame) { if (!frame) {
return; return;
@ -411,8 +397,7 @@ static void hyprland_frame_linux_dmabuf(void *data,
cast->screencopy_frame_info[DMABUF].format = format; cast->screencopy_frame_info[DMABUF].format = format;
} }
static void hyprland_frame_buffer_done(void *data, static void hyprland_frame_buffer_done(void *data, struct hyprland_toplevel_export_frame_v1 *frame) {
struct hyprland_toplevel_export_frame_v1 *frame) {
struct xdpw_screencast_instance *cast = data; struct xdpw_screencast_instance *cast = data;
if (!frame) { if (!frame) {
return; return;
@ -427,7 +412,8 @@ static void hyprland_frame_buffer_done(void *data,
// Check if announced screencopy information is compatible with pipewire meta // Check if announced screencopy information is compatible with pipewire meta
if ((cast->pwr_format.format != xdpw_format_pw_from_drm_fourcc(cast->screencopy_frame_info[cast->buffer_type].format) && if ((cast->pwr_format.format != xdpw_format_pw_from_drm_fourcc(cast->screencopy_frame_info[cast->buffer_type].format) &&
cast->pwr_format.format != xdpw_format_pw_strip_alpha(xdpw_format_pw_from_drm_fourcc(cast->screencopy_frame_info[cast->buffer_type].format))) || cast->pwr_format.format !=
xdpw_format_pw_strip_alpha(xdpw_format_pw_from_drm_fourcc(cast->screencopy_frame_info[cast->buffer_type].format))) ||
cast->pwr_format.size.width != cast->screencopy_frame_info[cast->buffer_type].width || cast->pwr_format.size.width != cast->screencopy_frame_info[cast->buffer_type].width ||
cast->pwr_format.size.height != cast->screencopy_frame_info[cast->buffer_type].height) { cast->pwr_format.size.height != cast->screencopy_frame_info[cast->buffer_type].height) {
logprint(DEBUG, "hyprland: pipewire and wlroots metadata are incompatible. Renegotiate stream"); logprint(DEBUG, "hyprland: pipewire and wlroots metadata are incompatible. Renegotiate stream");
@ -449,9 +435,8 @@ static void hyprland_frame_buffer_done(void *data,
assert(cast->current_frame.xdpw_buffer); assert(cast->current_frame.xdpw_buffer);
// Check if dequeued buffer is compatible with announced buffer // Check if dequeued buffer is compatible with announced buffer
if ((cast->buffer_type == WL_SHM && if ((cast->buffer_type == WL_SHM && (cast->current_frame.xdpw_buffer->size[0] != cast->screencopy_frame_info[cast->buffer_type].size ||
(cast->current_frame.xdpw_buffer->size[0] != cast->screencopy_frame_info[cast->buffer_type].size || cast->current_frame.xdpw_buffer->stride[0] != cast->screencopy_frame_info[cast->buffer_type].stride)) ||
cast->current_frame.xdpw_buffer->stride[0] != cast->screencopy_frame_info[cast->buffer_type].stride)) ||
cast->current_frame.xdpw_buffer->width != cast->screencopy_frame_info[cast->buffer_type].width || cast->current_frame.xdpw_buffer->width != cast->screencopy_frame_info[cast->buffer_type].width ||
cast->current_frame.xdpw_buffer->height != cast->screencopy_frame_info[cast->buffer_type].height) { cast->current_frame.xdpw_buffer->height != cast->screencopy_frame_info[cast->buffer_type].height) {
logprint(DEBUG, "hyprland: pipewire buffer has wrong dimensions"); logprint(DEBUG, "hyprland: pipewire buffer has wrong dimensions");
@ -466,8 +451,7 @@ static void hyprland_frame_buffer_done(void *data,
fps_limit_measure_start(&cast->fps_limit, cast->framerate); fps_limit_measure_start(&cast->fps_limit, cast->framerate);
} }
static void hyprland_frame_flags(void *data, struct hyprland_toplevel_export_frame_v1 *frame, static void hyprland_frame_flags(void *data, struct hyprland_toplevel_export_frame_v1 *frame, uint32_t flags) {
uint32_t flags) {
struct xdpw_screencast_instance *cast = data; struct xdpw_screencast_instance *cast = data;
if (!frame) { if (!frame) {
return; return;
@ -477,8 +461,8 @@ static void hyprland_frame_flags(void *data, struct hyprland_toplevel_export_fra
cast->current_frame.y_invert = flags & ZWLR_SCREENCOPY_FRAME_V1_FLAGS_Y_INVERT; cast->current_frame.y_invert = flags & ZWLR_SCREENCOPY_FRAME_V1_FLAGS_Y_INVERT;
} }
static void hyprland_frame_damage(void *data, struct hyprland_toplevel_export_frame_v1 *frame, static void hyprland_frame_damage(void *data, struct hyprland_toplevel_export_frame_v1 *frame, uint32_t x, uint32_t y, uint32_t width,
uint32_t x, uint32_t y, uint32_t width, uint32_t height) { uint32_t height) {
struct xdpw_screencast_instance *cast = data; struct xdpw_screencast_instance *cast = data;
if (!frame) { if (!frame) {
return; return;
@ -492,8 +476,8 @@ static void hyprland_frame_damage(void *data, struct hyprland_toplevel_export_fr
cast->current_frame.damage.height = height; cast->current_frame.damage.height = height;
} }
static void hyprland_frame_ready(void *data, struct hyprland_toplevel_export_frame_v1 *frame, static void hyprland_frame_ready(void *data, struct hyprland_toplevel_export_frame_v1 *frame, uint32_t tv_sec_hi, uint32_t tv_sec_lo,
uint32_t tv_sec_hi, uint32_t tv_sec_lo, uint32_t tv_nsec) { uint32_t tv_nsec) {
struct xdpw_screencast_instance *cast = data; struct xdpw_screencast_instance *cast = data;
if (!frame) { if (!frame) {
return; return;
@ -510,8 +494,7 @@ static void hyprland_frame_ready(void *data, struct hyprland_toplevel_export_fra
xdpw_wlr_frame_finish(cast); xdpw_wlr_frame_finish(cast);
} }
static void hyprland_frame_failed(void *data, static void hyprland_frame_failed(void *data, struct hyprland_toplevel_export_frame_v1 *frame) {
struct hyprland_toplevel_export_frame_v1 *frame) {
struct xdpw_screencast_instance *cast = data; struct xdpw_screencast_instance *cast = data;
if (!frame) { if (!frame) {
return; return;
@ -524,24 +507,23 @@ static void hyprland_frame_failed(void *data,
xdpw_wlr_frame_finish(cast); xdpw_wlr_frame_finish(cast);
} }
static const struct hyprland_toplevel_export_frame_v1_listener hyprland_frame_listener = { static const struct hyprland_toplevel_export_frame_v1_listener hyprland_frame_listener = {.buffer = hyprland_frame_buffer,
.buffer = hyprland_frame_buffer, .buffer_done = hyprland_frame_buffer_done,
.buffer_done = hyprland_frame_buffer_done, .linux_dmabuf = hyprland_frame_linux_dmabuf,
.linux_dmabuf = hyprland_frame_linux_dmabuf, .flags = hyprland_frame_flags,
.flags = hyprland_frame_flags, .ready = hyprland_frame_ready,
.ready = hyprland_frame_ready, .failed = hyprland_frame_failed,
.failed = hyprland_frame_failed, .damage = hyprland_frame_damage};
.damage = hyprland_frame_damage};
void xdpw_wlr_register_cb(struct xdpw_screencast_instance *cast) { void xdpw_wlr_register_cb(struct xdpw_screencast_instance *cast) {
if (cast->target.x != -1 && cast->target.y != -1 && cast->target.w != -1 && cast->target.h != -1 && cast->target.window_handle <= 0) { if (cast->target.x != -1 && cast->target.y != -1 && cast->target.w != -1 && cast->target.h != -1 && cast->target.window_handle <= 0) {
// capture region // capture region
cast->frame_callback = zwlr_screencopy_manager_v1_capture_output_region( cast->frame_callback =
cast->ctx->screencopy_manager, cast->with_cursor, cast->target.output->output, cast->target.x, zwlr_screencopy_manager_v1_capture_output_region(cast->ctx->screencopy_manager, cast->with_cursor, cast->target.output->output,
cast->target.y, cast->target.w, cast->target.h); cast->target.x, cast->target.y, cast->target.w, cast->target.h);
} else if (cast->target.window_handle == -1) { } else if (cast->target.window_handle == -1) {
cast->frame_callback = zwlr_screencopy_manager_v1_capture_output( cast->frame_callback =
cast->ctx->screencopy_manager, cast->with_cursor, cast->target.output->output); zwlr_screencopy_manager_v1_capture_output(cast->ctx->screencopy_manager, cast->with_cursor, cast->target.output->output);
} else { } else {
// share window // share window
struct SToplevelEntry *entry = toplevelEntryFromID(cast->ctx, cast->target.window_handle); struct SToplevelEntry *entry = toplevelEntryFromID(cast->ctx, cast->target.window_handle);
@ -554,41 +536,34 @@ void xdpw_wlr_register_cb(struct xdpw_screencast_instance *cast) {
cast->frame_callback_hyprland = hyprland_toplevel_export_manager_v1_capture_toplevel_with_wlr_toplevel_handle( 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); 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);
logprint(TRACE, "hyprland: callbacks registered"); logprint(TRACE, "hyprland: callbacks registered");
return; return;
} }
zwlr_screencopy_frame_v1_add_listener(cast->frame_callback, zwlr_screencopy_frame_v1_add_listener(cast->frame_callback, &wlr_frame_listener, cast);
&wlr_frame_listener, cast);
logprint(TRACE, "wlroots: callbacks registered"); logprint(TRACE, "wlroots: callbacks registered");
} }
static void wlr_output_handle_geometry(void *data, struct wl_output *wl_output, static void wlr_output_handle_geometry(void *data, struct wl_output *wl_output, int32_t x, int32_t y, int32_t phys_width, int32_t phys_height,
int32_t x, int32_t y, int32_t phys_width, int32_t phys_height,
int32_t subpixel, const char *make, const char *model, int32_t transform) { int32_t subpixel, const char *make, const char *model, int32_t transform) {
struct xdpw_wlr_output *output = data; struct xdpw_wlr_output *output = data;
output->make = strdup(make); output->make = strdup(make);
output->model = strdup(model); output->model = strdup(model);
} }
static void wlr_output_handle_mode(void *data, struct wl_output *wl_output, static void wlr_output_handle_mode(void *data, struct wl_output *wl_output, uint32_t flags, int32_t width, int32_t height, int32_t refresh) {
uint32_t flags, int32_t width, int32_t height, int32_t refresh) {
if (flags & WL_OUTPUT_MODE_CURRENT) { if (flags & WL_OUTPUT_MODE_CURRENT) {
struct xdpw_wlr_output *output = data; struct xdpw_wlr_output *output = data;
output->framerate = (float)refresh / 1000; output->framerate = (float)refresh / 1000;
} }
} }
static void wlr_output_handle_done(void *data, struct wl_output *wl_output) { static void wlr_output_handle_done(void *data, struct wl_output *wl_output) { /* Nothing to do */
/* Nothing to do */
} }
static void wlr_output_handle_scale(void *data, struct wl_output *wl_output, static void wlr_output_handle_scale(void *data, struct wl_output *wl_output, int32_t factor) { /* Nothing to do */
int32_t factor) {
/* Nothing to do */
} }
static const struct wl_output_listener wlr_output_listener = { static const struct wl_output_listener wlr_output_listener = {
@ -598,8 +573,7 @@ static const struct wl_output_listener wlr_output_listener = {
.scale = wlr_output_handle_scale, .scale = wlr_output_handle_scale,
}; };
static void wlr_xdg_output_name(void *data, struct zxdg_output_v1 *xdg_output, static void wlr_xdg_output_name(void *data, struct zxdg_output_v1 *xdg_output, const char *name) {
const char *name) {
struct xdpw_wlr_output *output = data; struct xdpw_wlr_output *output = data;
output->name = strdup(name); output->name = strdup(name);
@ -617,18 +591,13 @@ static const struct zxdg_output_v1_listener wlr_xdg_output_listener = {
.name = wlr_xdg_output_name, .name = wlr_xdg_output_name,
}; };
static void wlr_add_xdg_output_listener(struct xdpw_wlr_output *output, static void wlr_add_xdg_output_listener(struct xdpw_wlr_output *output, struct zxdg_output_v1 *xdg_output) {
struct zxdg_output_v1 *xdg_output) {
output->xdg_output = xdg_output; output->xdg_output = xdg_output;
zxdg_output_v1_add_listener(output->xdg_output, &wlr_xdg_output_listener, zxdg_output_v1_add_listener(output->xdg_output, &wlr_xdg_output_listener, output);
output);
} }
static void wlr_init_xdg_output(struct xdpw_screencast_context *ctx, static void wlr_init_xdg_output(struct xdpw_screencast_context *ctx, struct xdpw_wlr_output *output) {
struct xdpw_wlr_output *output) { struct zxdg_output_v1 *xdg_output = zxdg_output_manager_v1_get_xdg_output(ctx->xdg_output_manager, output->output);
struct zxdg_output_v1 *xdg_output =
zxdg_output_manager_v1_get_xdg_output(ctx->xdg_output_manager,
output->output);
wlr_add_xdg_output_listener(output, xdg_output); wlr_add_xdg_output_listener(output, xdg_output);
} }
@ -682,8 +651,7 @@ char *getFormat(const char *fmt, ...) {
char *buildWindowList(struct xdpw_screencast_context *ctx) { char *buildWindowList(struct xdpw_screencast_context *ctx) {
char *rolling = calloc(1, 1); char *rolling = calloc(1, 1);
if (!ctx->wlroots_toplevel_manager) if (!ctx->wlroots_toplevel_manager) return rolling;
return rolling;
struct SToplevelEntry *current; struct SToplevelEntry *current;
wl_list_for_each(current, &ctx->toplevel_resource_list, link) { wl_list_for_each(current, &ctx->toplevel_resource_list, link) {
@ -695,8 +663,7 @@ char *buildWindowList(struct xdpw_screencast_context *ctx) {
} }
for (size_t i = 0; i < strlen(rolling); ++i) { for (size_t i = 0; i < strlen(rolling); ++i) {
if (rolling[i] == '\"') if (rolling[i] == '\"') rolling[i] = ' ';
rolling[i] = ' ';
} }
return rolling; return rolling;
@ -713,7 +680,10 @@ struct xdpw_share xdpw_wlr_chooser(struct xdpw_screencast_context *ctx) {
char *windowList = buildWindowList(ctx); char *windowList = buildWindowList(ctx);
char *cmd = getFormat("WAYLAND_DISPLAY=%s QT_QPA_PLATFORM=\"wayland\" 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); char *cmd = getFormat(
"WAYLAND_DISPLAY=%s QT_QPA_PLATFORM=\"wayland\" 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);
free(windowList); free(windowList);
@ -756,8 +726,7 @@ struct xdpw_share xdpw_wlr_chooser(struct xdpw_screencast_context *ctx) {
free(display_name); free(display_name);
if (!found) if (!found) return res;
return res;
res.output = out; res.output = out;
return res; return res;
@ -832,14 +801,11 @@ struct xdpw_share xdpw_wlr_chooser(struct xdpw_screencast_context *ctx) {
struct xdpw_wlr_output *xdpw_wlr_output_first(struct wl_list *output_list) { struct xdpw_wlr_output *xdpw_wlr_output_first(struct wl_list *output_list) {
struct xdpw_wlr_output *output, *tmp; struct xdpw_wlr_output *output, *tmp;
wl_list_for_each_safe(output, tmp, output_list, link) { wl_list_for_each_safe(output, tmp, output_list, link) { return output; }
return output;
}
return NULL; return NULL;
} }
struct xdpw_wlr_output *xdpw_wlr_output_find_by_name(struct wl_list *output_list, struct xdpw_wlr_output *xdpw_wlr_output_find_by_name(struct wl_list *output_list, const char *name) {
const char *name) {
struct xdpw_wlr_output *output, *tmp; struct xdpw_wlr_output *output, *tmp;
wl_list_for_each_safe(output, tmp, output_list, link) { wl_list_for_each_safe(output, tmp, output_list, link) {
if (strcmp(output->name, name) == 0) { if (strcmp(output->name, name) == 0) {
@ -849,8 +815,7 @@ struct xdpw_wlr_output *xdpw_wlr_output_find_by_name(struct wl_list *output_list
return NULL; return NULL;
} }
struct xdpw_wlr_output *xdpw_wlr_output_find(struct xdpw_screencast_context *ctx, struct xdpw_wlr_output *xdpw_wlr_output_find(struct xdpw_screencast_context *ctx, struct wl_output *out, uint32_t id) {
struct wl_output *out, uint32_t id) {
struct xdpw_wlr_output *output, *tmp; struct xdpw_wlr_output *output, *tmp;
wl_list_for_each_safe(output, tmp, &ctx->output_list, link) { wl_list_for_each_safe(output, tmp, &ctx->output_list, link) {
if ((output->output == out) || (output->id == id)) { if ((output->output == out) || (output->id == id)) {
@ -870,8 +835,7 @@ static void wlr_remove_output(struct xdpw_wlr_output *out) {
free(out); free(out);
} }
static void wlr_format_modifier_pair_add(struct xdpw_screencast_context *ctx, static void wlr_format_modifier_pair_add(struct xdpw_screencast_context *ctx, uint32_t format, uint64_t modifier) {
uint32_t format, uint64_t modifier) {
struct xdpw_format_modifier_pair *fm_pair; struct xdpw_format_modifier_pair *fm_pair;
wl_array_for_each(fm_pair, &ctx->format_modifier_pairs) { wl_array_for_each(fm_pair, &ctx->format_modifier_pairs) {
if (fm_pair->fourcc == format && fm_pair->modifier == modifier) { if (fm_pair->fourcc == format && fm_pair->modifier == modifier) {
@ -886,9 +850,8 @@ static void wlr_format_modifier_pair_add(struct xdpw_screencast_context *ctx,
logprint(TRACE, "wlroots: format %u (%lu)", fm_pair->fourcc, fm_pair->modifier); logprint(TRACE, "wlroots: format %u (%lu)", fm_pair->fourcc, fm_pair->modifier);
} }
static void linux_dmabuf_handle_modifier(void *data, static void linux_dmabuf_handle_modifier(void *data, struct zwp_linux_dmabuf_v1 *zwp_linux_dmabuf_v1, uint32_t format, uint32_t modifier_hi,
struct zwp_linux_dmabuf_v1 *zwp_linux_dmabuf_v1, uint32_t modifier_lo) {
uint32_t format, uint32_t modifier_hi, uint32_t modifier_lo) {
struct xdpw_screencast_context *ctx = data; struct xdpw_screencast_context *ctx = data;
logprint(TRACE, "wlroots: linux_dmabuf_handle_modifier called"); logprint(TRACE, "wlroots: linux_dmabuf_handle_modifier called");
@ -902,8 +865,8 @@ static const struct zwp_linux_dmabuf_v1_listener linux_dmabuf_listener = {
.modifier = linux_dmabuf_handle_modifier, .modifier = linux_dmabuf_handle_modifier,
}; };
static void linux_dmabuf_feedback_handle_main_device(void *data, static void linux_dmabuf_feedback_handle_main_device(void *data, struct zwp_linux_dmabuf_feedback_v1 *zwp_linux_dmabuf_feedback_v1,
struct zwp_linux_dmabuf_feedback_v1 *zwp_linux_dmabuf_feedback_v1, struct wl_array *device_arr) { struct wl_array *device_arr) {
struct xdpw_screencast_context *ctx = data; struct xdpw_screencast_context *ctx = data;
logprint(DEBUG, "wlroots: linux_dmabuf_feedback_handle_main_device called"); logprint(DEBUG, "wlroots: linux_dmabuf_feedback_handle_main_device called");
@ -923,8 +886,7 @@ static void linux_dmabuf_feedback_handle_main_device(void *data,
ctx->gbm = xdpw_gbm_device_create(drmDev); ctx->gbm = xdpw_gbm_device_create(drmDev);
} }
static void linux_dmabuf_feedback_format_table(void *data, static void linux_dmabuf_feedback_format_table(void *data, struct zwp_linux_dmabuf_feedback_v1 *zwp_linux_dmabuf_feedback_v1, int fd, uint32_t size) {
struct zwp_linux_dmabuf_feedback_v1 *zwp_linux_dmabuf_feedback_v1, int fd, uint32_t size) {
struct xdpw_screencast_context *ctx = data; struct xdpw_screencast_context *ctx = data;
logprint(DEBUG, "wlroots: linux_dmabuf_feedback_format_table called"); logprint(DEBUG, "wlroots: linux_dmabuf_feedback_format_table called");
@ -941,8 +903,7 @@ static void linux_dmabuf_feedback_format_table(void *data,
ctx->feedback_data.format_table_size = size; ctx->feedback_data.format_table_size = size;
} }
static void linux_dmabuf_feedback_handle_done(void *data, static void linux_dmabuf_feedback_handle_done(void *data, struct zwp_linux_dmabuf_feedback_v1 *zwp_linux_dmabuf_feedback_v1) {
struct zwp_linux_dmabuf_feedback_v1 *zwp_linux_dmabuf_feedback_v1) {
struct xdpw_screencast_context *ctx = data; struct xdpw_screencast_context *ctx = data;
logprint(DEBUG, "wlroots: linux_dmabuf_feedback_handle_done called"); logprint(DEBUG, "wlroots: linux_dmabuf_feedback_handle_done called");
@ -954,8 +915,8 @@ static void linux_dmabuf_feedback_handle_done(void *data,
ctx->feedback_data.format_table_size = 0; ctx->feedback_data.format_table_size = 0;
} }
static void linux_dmabuf_feedback_tranche_target_devices(void *data, static void linux_dmabuf_feedback_tranche_target_devices(void *data, struct zwp_linux_dmabuf_feedback_v1 *zwp_linux_dmabuf_feedback_v1,
struct zwp_linux_dmabuf_feedback_v1 *zwp_linux_dmabuf_feedback_v1, struct wl_array *device_arr) { struct wl_array *device_arr) {
struct xdpw_screencast_context *ctx = data; struct xdpw_screencast_context *ctx = data;
logprint(DEBUG, "wlroots: linux_dmabuf_feedback_tranche_target_devices called"); logprint(DEBUG, "wlroots: linux_dmabuf_feedback_tranche_target_devices called");
@ -979,13 +940,12 @@ static void linux_dmabuf_feedback_tranche_target_devices(void *data,
} }
} }
static void linux_dmabuf_feedback_tranche_flags(void *data, static void linux_dmabuf_feedback_tranche_flags(void *data, struct zwp_linux_dmabuf_feedback_v1 *zwp_linux_dmabuf_feedback_v1, uint32_t flags) {
struct zwp_linux_dmabuf_feedback_v1 *zwp_linux_dmabuf_feedback_v1, uint32_t flags) {
logprint(DEBUG, "wlroots: linux_dmabuf_feedback_tranche_flags called"); logprint(DEBUG, "wlroots: linux_dmabuf_feedback_tranche_flags called");
} }
static void linux_dmabuf_feedback_tranche_formats(void *data, static void linux_dmabuf_feedback_tranche_formats(void *data, struct zwp_linux_dmabuf_feedback_v1 *zwp_linux_dmabuf_feedback_v1,
struct zwp_linux_dmabuf_feedback_v1 *zwp_linux_dmabuf_feedback_v1, struct wl_array *indices) { struct wl_array *indices) {
struct xdpw_screencast_context *ctx = data; struct xdpw_screencast_context *ctx = data;
logprint(DEBUG, "wlroots: linux_dmabuf_feedback_tranche_formats called"); logprint(DEBUG, "wlroots: linux_dmabuf_feedback_tranche_formats called");
@ -1012,8 +972,7 @@ static void linux_dmabuf_feedback_tranche_formats(void *data,
} }
} }
static void linux_dmabuf_feedback_tranche_done(void *data, static void linux_dmabuf_feedback_tranche_done(void *data, struct zwp_linux_dmabuf_feedback_v1 *zwp_linux_dmabuf_feedback_v1) {
struct zwp_linux_dmabuf_feedback_v1 *zwp_linux_dmabuf_feedback_v1) {
struct xdpw_screencast_context *ctx = data; struct xdpw_screencast_context *ctx = data;
logprint(DEBUG, "wlroots: linux_dmabuf_feedback_tranche_done called"); logprint(DEBUG, "wlroots: linux_dmabuf_feedback_tranche_done called");
@ -1031,8 +990,7 @@ static const struct zwp_linux_dmabuf_feedback_v1_listener linux_dmabuf_listener_
.tranche_done = linux_dmabuf_feedback_tranche_done, .tranche_done = linux_dmabuf_feedback_tranche_done,
}; };
static void wlr_registry_handle_add(void *data, struct wl_registry *reg, static void wlr_registry_handle_add(void *data, struct wl_registry *reg, uint32_t id, const char *interface, uint32_t ver) {
uint32_t id, const char *interface, uint32_t ver) {
struct xdpw_screencast_context *ctx = data; struct xdpw_screencast_context *ctx = data;
logprint(DEBUG, "wlroots: interface to register %s (Version: %u)", interface, ver); logprint(DEBUG, "wlroots: interface to register %s (Version: %u)", interface, ver);
@ -1058,8 +1016,7 @@ static void wlr_registry_handle_add(void *data, struct wl_registry *reg,
version = SC_MANAGER_VERSION_MIN; version = SC_MANAGER_VERSION_MIN;
} }
logprint(DEBUG, "wlroots: |-- registered to interface %s (Version %u)", interface, version); logprint(DEBUG, "wlroots: |-- registered to interface %s (Version %u)", interface, version);
ctx->screencopy_manager = wl_registry_bind( ctx->screencopy_manager = wl_registry_bind(reg, id, &zwlr_screencopy_manager_v1_interface, version);
reg, id, &zwlr_screencopy_manager_v1_interface, version);
} }
if (!strcmp(interface, hyprland_toplevel_export_manager_v1_interface.name) && !ctx->hyprland_toplevel_manager) { if (!strcmp(interface, hyprland_toplevel_export_manager_v1_interface.name) && !ctx->hyprland_toplevel_manager) {
@ -1088,8 +1045,7 @@ static void wlr_registry_handle_add(void *data, struct wl_registry *reg,
if (strcmp(interface, zxdg_output_manager_v1_interface.name) == 0) { if (strcmp(interface, zxdg_output_manager_v1_interface.name) == 0) {
logprint(DEBUG, "wlroots: |-- registered to interface %s (Version %u)", interface, XDG_OUTPUT_MANAGER_VERSION); logprint(DEBUG, "wlroots: |-- registered to interface %s (Version %u)", interface, XDG_OUTPUT_MANAGER_VERSION);
ctx->xdg_output_manager = ctx->xdg_output_manager = wl_registry_bind(reg, id, &zxdg_output_manager_v1_interface, XDG_OUTPUT_MANAGER_VERSION);
wl_registry_bind(reg, id, &zxdg_output_manager_v1_interface, XDG_OUTPUT_MANAGER_VERSION);
} }
if (strcmp(interface, zwp_linux_dmabuf_v1_interface.name) == 0) { if (strcmp(interface, zwp_linux_dmabuf_v1_interface.name) == 0) {
uint32_t version = ver; uint32_t version = ver;
@ -1111,8 +1067,7 @@ static void wlr_registry_handle_add(void *data, struct wl_registry *reg,
} }
} }
static void wlr_registry_handle_remove(void *data, struct wl_registry *reg, static void wlr_registry_handle_remove(void *data, struct wl_registry *reg, uint32_t id) {
uint32_t id) {
struct xdpw_screencast_context *ctx = data; struct xdpw_screencast_context *ctx = data;
struct xdpw_wlr_output *output = xdpw_wlr_output_find(ctx, NULL, id); struct xdpw_wlr_output *output = xdpw_wlr_output_find(ctx, NULL, id);
if (output) { if (output) {
@ -1164,8 +1119,7 @@ int xdpw_wlr_screencopy_init(struct xdpw_state *state) {
// make sure our wlroots supports xdg_output_manager // make sure our wlroots supports xdg_output_manager
if (!ctx->xdg_output_manager) { if (!ctx->xdg_output_manager) {
logprint(ERROR, "Compositor doesn't support %s!", logprint(ERROR, "Compositor doesn't support %s!", zxdg_output_manager_v1_interface.name);
zxdg_output_manager_v1_interface.name);
return -1; return -1;
} }
@ -1183,8 +1137,7 @@ int xdpw_wlr_screencopy_init(struct xdpw_state *state) {
// make sure our wlroots supports screencopy protocol // make sure our wlroots supports screencopy protocol
if (!ctx->screencopy_manager) { if (!ctx->screencopy_manager) {
logprint(ERROR, "Compositor doesn't support %s!", logprint(ERROR, "Compositor doesn't support %s!", zwlr_screencopy_manager_v1_interface.name);
zwlr_screencopy_manager_v1_interface.name);
return -1; return -1;
} }
@ -1210,9 +1163,7 @@ void xdpw_wlr_screencopy_finish(struct xdpw_screencast_context *ctx) {
} }
struct xdpw_screencast_instance *cast, *tmp_c; struct xdpw_screencast_instance *cast, *tmp_c;
wl_list_for_each_safe(cast, tmp_c, &ctx->screencast_instances, link) { wl_list_for_each_safe(cast, tmp_c, &ctx->screencast_instances, link) { cast->quit = true; }
cast->quit = true;
}
if (ctx->screencopy_manager) { if (ctx->screencopy_manager) {
zwlr_screencopy_manager_v1_destroy(ctx->screencopy_manager); zwlr_screencopy_manager_v1_destroy(ctx->screencopy_manager);