[WIP] Global Shortcuts impl (#35)

Implements the `GlobalShortcuts` portal via `hyprland-global-shortcuts-v1`

---------

Co-authored-by: Mihai Fufezan <fufexan@protonmail.com>
This commit is contained in:
Vaxry 2023-04-09 13:47:05 +01:00 committed by GitHub
parent 803c00db11
commit 510257c0e9
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
10 changed files with 924 additions and 403 deletions

View file

@ -7,11 +7,11 @@
] ]
}, },
"locked": { "locked": {
"lastModified": 1671839510, "lastModified": 1680997116,
"narHash": "sha256-+PY1qqJfmZzzROgcIY4I7AkCwpnC+qBIYk2eFoA9RWc=", "narHash": "sha256-nNyoatiHmTMczrCoHCH2LIRfSF8n9ZPZ1O7WNMxcbR4=",
"owner": "hyprwm", "owner": "hyprwm",
"repo": "hyprland-protocols", "repo": "hyprland-protocols",
"rev": "b8f55e02a328c47ed373133c52483bbfa20a1b75", "rev": "d7d403b711b60e8136295b0d4229e89a115e80cc",
"type": "github" "type": "github"
}, },
"original": { "original": {
@ -22,11 +22,11 @@
}, },
"nixpkgs": { "nixpkgs": {
"locked": { "locked": {
"lastModified": 1669969257, "lastModified": 1680945546,
"narHash": "sha256-mOS13sK3v+kfgP+1Mh56ohiG8uVhLHAo7m/q9kqAehc=", "narHash": "sha256-8FuaH5t/aVi/pR1XxnF0qi4WwMYC+YxlfdsA0V+TEuQ=",
"owner": "NixOS", "owner": "NixOS",
"repo": "nixpkgs", "repo": "nixpkgs",
"rev": "b72b8b94cf0c012b0252a9100a636cad69696666", "rev": "d9f759f2ea8d265d974a6e1259bd510ac5844c5d",
"type": "github" "type": "github"
}, },
"original": { "original": {

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,31 @@
#pragma once
#include <stdbool.h>
#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;
bool bound;
};
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

@ -15,6 +15,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',
hl_protocol_dir / 'protocols/hyprland-toplevel-export-v1.xml', hl_protocol_dir / 'protocols/hyprland-toplevel-export-v1.xml',
hl_protocol_dir / 'protocols/hyprland-global-shortcuts-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',
] ]

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 | METADATA, .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,537 @@
#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 == NULL) {
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 int method_gs_create_session(sd_bus_message *msg, void *data, sd_bus_error *ret_error);
static int method_gs_bind_shortcuts(sd_bus_message *msg, void *data, sd_bus_error *ret_error);
static int method_gs_list_shortcuts(sd_bus_message *msg, void *data, sd_bus_error *ret_error);
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;
bool found = false;
struct globalShortcut *curr;
struct globalShortcutsClient *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) {
found = true;
goto found;
}
}
}
found:
if (!found) return;
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;
bool found = false;
struct globalShortcut *curr;
struct globalShortcutsClient *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) {
found = true;
goto found;
}
}
}
found:
if (!found) return;
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;
char *option_token;
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) {
sd_bus_message_read(msg, "v", "s", &option_token);
logprint(INFO, "dbus: option token: %s", option_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);
ret = sd_bus_message_enter_container(msg, 'a', "{sv}");
if (ret < 0) {
return ret;
}
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, "description") == 0) {
sd_bus_message_read(msg, "v", "s", &key);
shortcut->description = malloc(strlen(key) + 1);
strcpy(shortcut->description, key);
} else {
sd_bus_message_skip(msg, "v");
}
innerRet = sd_bus_message_exit_container(msg);
if (innerRet < 0) {
return innerRet;
}
}
sd_bus_message_exit_container(msg);
if (shortcut->description == NULL) {
shortcut->description = calloc(1, 1);
}
wl_list_insert(&client->shortcuts, &shortcut->link);
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;
}
client->session = sess;
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);
curr->hlShortcut = hyprland_global_shortcuts_manager_v1_register_shortcut(
state->shortcutsInstance.manager, curr->name, (strlen(app_id) == 0 ? option_token : app_id), curr->description, "");
hyprland_global_shortcut_v1_add_listener(curr->hlShortcut, &shortcutListener, state);
curr->bound = true;
}
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);
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);
ret = sd_bus_message_enter_container(msg, 'a', "{sv}");
if (ret < 0) {
return ret;
}
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, "description") == 0) {
sd_bus_message_read(msg, "v", "s", &key);
shortcut->description = malloc(strlen(key) + 1);
strcpy(shortcut->description, key);
} else {
sd_bus_message_skip(msg, "v");
}
innerRet = sd_bus_message_exit_container(msg);
if (innerRet < 0) {
return innerRet;
}
}
sd_bus_message_exit_container(msg);
if (shortcut->description == NULL) {
shortcut->description = calloc(1, 1);
}
wl_list_insert(&client->shortcuts, &shortcut->link);
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);
if (!curr->bound) {
curr->hlShortcut = hyprland_global_shortcuts_manager_v1_register_shortcut(state->shortcutsInstance.manager, curr->name, 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);
bool foundClient = false;
struct globalShortcutsClient *client, *tmp_client;
wl_list_for_each(client, &state->shortcutsInstance.shortcutClients, link) {
if (strcmp(client->session, session_handle) == 0) {
logprint(DEBUG, "dbus: bind shortcuts: found matching client %s", client->session);
foundClient = true;
break;
}
}
if (!foundClient || !client->sentShortcuts) return 0;
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 == NULL) {
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);

@ -1 +1 @@
Subproject commit b8f55e02a328c47ed373133c52483bbfa20a1b75 Subproject commit d7d403b711b60e8136295b0d4229e89a115e80cc