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]
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;

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

View file

@ -84,6 +84,7 @@ xdpw_files = files([
'src/screencast/wlr_screencast.c',
'src/screencast/pipewire_screencast.c',
'src/screencast/fps_limit.c',
'src/globalshortcuts/global_shortcuts.c'
])
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',
'wlr-screencopy-unstable-v1.xml',
'wlr-foreign-toplevel-management-unstable-v1.xml',
'hyprland-global-shortcuts-v1.xml',
]
wl_proto_files = []

View file

@ -1,288 +1,290 @@
#include <errno.h>
#include <stdlib.h>
#include <stdio.h>
#include <sys/timerfd.h>
#include <getopt.h>
#include <poll.h>
#include <pipewire/pipewire.h>
#include <poll.h>
#include <spa/utils/result.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/timerfd.h>
#include <unistd.h>
#include "xdpw.h"
#include "logger.h"
#include "xdpw.h"
enum event_loop_fd {
EVENT_LOOP_DBUS,
EVENT_LOOP_WAYLAND,
EVENT_LOOP_PIPEWIRE,
EVENT_LOOP_TIMER,
EVENT_LOOP_DBUS,
EVENT_LOOP_WAYLAND,
EVENT_LOOP_PIPEWIRE,
EVENT_LOOP_TIMER,
};
static const char service_name[] = "org.freedesktop.impl.portal.desktop.hyprland";
static int xdpw_usage(FILE *stream, int rc) {
static const char *usage =
"Usage: xdg-desktop-portal-hyprland [options]\n"
"\n"
" -l, --loglevel=<loglevel> Select log level (default is ERROR).\n"
" QUIET, ERROR, WARN, INFO, DEBUG, TRACE\n"
" -c, --config=<config file> Select config file.\n"
" (default is $XDG_CONFIG_HOME/xdg-desktop-portal-hyprland/config)\n"
" -r, --replace Replace a running instance.\n"
" -h, --help Get help (this text).\n"
"\n";
static const char *usage =
"Usage: xdg-desktop-portal-hyprland [options]\n"
"\n"
" -l, --loglevel=<loglevel> Select log level (default is ERROR).\n"
" QUIET, ERROR, WARN, INFO, DEBUG, TRACE\n"
" -c, --config=<config file> Select config file.\n"
" (default is $XDG_CONFIG_HOME/xdg-desktop-portal-hyprland/config)\n"
" -r, --replace Replace a running instance.\n"
" -h, --help Get help (this text).\n"
"\n";
fprintf(stream, "%s", usage);
return rc;
fprintf(stream, "%s", usage);
return rc;
}
static int handle_name_lost(sd_bus_message *m, void *userdata, sd_bus_error *ret_error) {
logprint(INFO, "dbus: lost name, closing connection");
sd_bus_close(sd_bus_message_get_bus(m));
return 1;
logprint(INFO, "dbus: lost name, closing connection");
sd_bus_close(sd_bus_message_get_bus(m));
return 1;
}
int main(int argc, char *argv[]) {
struct xdpw_config config = {0};
char *configfile = NULL;
enum LOGLEVEL loglevel = DEFAULT_LOGLEVEL;
bool replace = false;
struct xdpw_config config = {0};
char *configfile = NULL;
enum LOGLEVEL loglevel = DEFAULT_LOGLEVEL;
bool replace = false;
static const char *shortopts = "l:o:c:f:rh";
static const struct option longopts[] = {
{ "loglevel", required_argument, NULL, 'l' },
{ "config", required_argument, NULL, 'c' },
{ "replace", no_argument, NULL, 'r' },
{ "help", no_argument, NULL, 'h' },
{ NULL, 0, NULL, 0 }
};
static const char *shortopts = "l:o:c:f:rh";
static const struct option longopts[] = {{"loglevel", required_argument, NULL, 'l'},
{"config", required_argument, NULL, 'c'},
{"replace", no_argument, NULL, 'r'},
{"help", no_argument, NULL, 'h'},
{NULL, 0, NULL, 0}};
while (1) {
int c = getopt_long(argc, argv, shortopts, longopts, NULL);
if (c < 0) {
break;
}
while (1) {
int c = getopt_long(argc, argv, shortopts, longopts, NULL);
if (c < 0) {
break;
}
switch (c) {
case 'l':
loglevel = get_loglevel(optarg);
break;
case 'c':
configfile = strdup(optarg);
break;
case 'r':
replace = true;
break;
case 'h':
return xdpw_usage(stdout, EXIT_SUCCESS);
default:
return xdpw_usage(stderr, EXIT_FAILURE);
}
}
switch (c) {
case 'l':
loglevel = get_loglevel(optarg);
break;
case 'c':
configfile = strdup(optarg);
break;
case 'r':
replace = true;
break;
case 'h':
return xdpw_usage(stdout, EXIT_SUCCESS);
default:
return xdpw_usage(stderr, EXIT_FAILURE);
}
}
init_logger(stderr, loglevel);
init_config(&configfile, &config);
print_config(DEBUG, &config);
init_logger(stderr, loglevel);
init_config(&configfile, &config);
print_config(DEBUG, &config);
int ret = 0;
int ret = 0;
sd_bus *bus = NULL;
ret = sd_bus_open_user(&bus);
if (ret < 0) {
logprint(ERROR, "dbus: failed to connect to user bus: %s", strerror(-ret));
return EXIT_FAILURE;
}
logprint(DEBUG, "dbus: connected");
sd_bus *bus = NULL;
ret = sd_bus_open_user(&bus);
if (ret < 0) {
logprint(ERROR, "dbus: failed to connect to user bus: %s", strerror(-ret));
return EXIT_FAILURE;
}
logprint(DEBUG, "dbus: connected");
struct wl_display *wl_display = wl_display_connect(NULL);
if (!wl_display) {
logprint(ERROR, "wayland: failed to connect to display");
sd_bus_unref(bus);
return EXIT_FAILURE;
}
logprint(DEBUG, "wlroots: wl_display connected");
struct wl_display *wl_display = wl_display_connect(NULL);
if (!wl_display) {
logprint(ERROR, "wayland: failed to connect to display");
sd_bus_unref(bus);
return EXIT_FAILURE;
}
logprint(DEBUG, "wlroots: wl_display connected");
pw_init(NULL, NULL);
struct pw_loop *pw_loop = pw_loop_new(NULL);
if (!pw_loop) {
logprint(ERROR, "pipewire: failed to create loop");
wl_display_disconnect(wl_display);
sd_bus_unref(bus);
return EXIT_FAILURE;
}
logprint(DEBUG, "pipewire: pw_loop created");
pw_init(NULL, NULL);
struct pw_loop *pw_loop = pw_loop_new(NULL);
if (!pw_loop) {
logprint(ERROR, "pipewire: failed to create loop");
wl_display_disconnect(wl_display);
sd_bus_unref(bus);
return EXIT_FAILURE;
}
logprint(DEBUG, "pipewire: pw_loop created");
struct xdpw_state state = {
.bus = bus,
.wl_display = wl_display,
.pw_loop = pw_loop,
.screencast_source_types = MONITOR,
.screencast_cursor_modes = HIDDEN | EMBEDDED,
.screencast_version = XDP_CAST_PROTO_VER,
.screenshot_version = XDP_SHOT_PROTO_VER,
.config = &config,
};
struct xdpw_state state = {
.bus = bus,
.wl_display = wl_display,
.pw_loop = pw_loop,
.screencast_source_types = MONITOR,
.screencast_cursor_modes = HIDDEN | EMBEDDED,
.screencast_version = XDP_CAST_PROTO_VER,
.screenshot_version = XDP_SHOT_PROTO_VER,
.config = &config,
};
wl_list_init(&state.xdpw_sessions);
wl_list_init(&state.xdpw_sessions);
ret = xdpw_screenshot_init(&state);
if (ret < 0) {
logprint(ERROR, "xdpw: failed to initialize screenshot");
goto error;
}
initShortcutsInstance(&state, &state.shortcutsInstance);
ret = xdpw_screencast_init(&state);
if (ret < 0) {
logprint(ERROR, "xdpw: failed to initialize screencast");
goto error;
}
ret = xdpw_screenshot_init(&state);
if (ret < 0) {
logprint(ERROR, "xdpw: failed to initialize screenshot");
goto error;
}
uint64_t flags = SD_BUS_NAME_ALLOW_REPLACEMENT;
if (replace) {
flags |= SD_BUS_NAME_REPLACE_EXISTING;
}
ret = xdpw_screencast_init(&state);
if (ret < 0) {
logprint(ERROR, "xdpw: failed to initialize screencast");
goto error;
}
ret = sd_bus_request_name(bus, service_name, flags);
if (ret < 0) {
logprint(ERROR, "dbus: failed to acquire service name: %s", strerror(-ret));
goto error;
}
uint64_t flags = SD_BUS_NAME_ALLOW_REPLACEMENT;
if (replace) {
flags |= SD_BUS_NAME_REPLACE_EXISTING;
}
const char *unique_name;
ret = sd_bus_get_unique_name(bus, &unique_name);
if (ret < 0) {
logprint(ERROR, "dbus: failed to get unique bus name: %s", strerror(-ret));
goto error;
}
ret = sd_bus_request_name(bus, service_name, flags);
if (ret < 0) {
logprint(ERROR, "dbus: failed to acquire service name: %s", strerror(-ret));
goto error;
}
static char match[1024];
snprintf(match, sizeof(match), "sender='org.freedesktop.DBus',"
"type='signal',"
"interface='org.freedesktop.DBus',"
"member='NameOwnerChanged',"
"path='/org/freedesktop/DBus',"
"arg0='%s',"
"arg1='%s'",
service_name, unique_name);
const char *unique_name;
ret = sd_bus_get_unique_name(bus, &unique_name);
if (ret < 0) {
logprint(ERROR, "dbus: failed to get unique bus name: %s", strerror(-ret));
goto error;
}
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;
}
static char match[1024];
snprintf(match, sizeof(match),
"sender='org.freedesktop.DBus',"
"type='signal',"
"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[] = {
[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,
}
};
wl_list_init(&state.timers);
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) {
ret = poll(pollfds, sizeof(pollfds) / sizeof(pollfds[0]), -1);
if (ret < 0) {
logprint(ERROR, "poll failed: %s", strerror(errno));
goto error;
}
state.timer_poll_fd = pollfds[EVENT_LOOP_TIMER].fd;
if (pollfds[EVENT_LOOP_DBUS].revents & POLLHUP) {
logprint(INFO, "event-loop: disconnected from dbus");
break;
}
if (pollfds[EVENT_LOOP_WAYLAND].revents & POLLHUP) {
logprint(INFO, "event-loop: disconnected from wayland");
break;
}
if (pollfds[EVENT_LOOP_PIPEWIRE].revents & POLLHUP) {
logprint(INFO, "event-loop: disconnected from pipewire");
break;
}
while (1) {
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 & POLLIN) {
logprint(TRACE, "event-loop: got dbus event");
do {
ret = sd_bus_process(state.bus, NULL);
} while (ret > 0);
if (ret < 0) {
logprint(ERROR, "sd_bus_process failed: %s", strerror(-ret));
goto error;
}
}
if (pollfds[EVENT_LOOP_DBUS].revents & POLLHUP) {
logprint(INFO, "event-loop: disconnected from dbus");
break;
}
if (pollfds[EVENT_LOOP_WAYLAND].revents & POLLHUP) {
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_WAYLAND].revents & POLLIN) {
logprint(TRACE, "event-loop: got wayland event");
ret = wl_display_dispatch(state.wl_display);
if (ret < 0) {
logprint(ERROR, "wl_display_dispatch failed: %s", strerror(errno));
goto error;
}
}
if (pollfds[EVENT_LOOP_DBUS].revents & POLLIN) {
logprint(TRACE, "event-loop: got dbus event");
do {
ret = sd_bus_process(state.bus, NULL);
} while (ret > 0);
if (ret < 0) {
logprint(ERROR, "sd_bus_process failed: %s", strerror(-ret));
goto error;
}
}
if (pollfds[EVENT_LOOP_PIPEWIRE].revents & POLLIN) {
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;
}
}
if (pollfds[EVENT_LOOP_WAYLAND].revents & POLLIN) {
logprint(TRACE, "event-loop: got wayland event");
ret = wl_display_dispatch(state.wl_display);
if (ret < 0) {
logprint(ERROR, "wl_display_dispatch failed: %s", strerror(errno));
goto error;
}
}
if (pollfds[EVENT_LOOP_TIMER].revents & POLLIN) {
logprint(TRACE, "event-loop: got a timer event");
if (pollfds[EVENT_LOOP_PIPEWIRE].revents & POLLIN) {
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;
uint64_t expirations;
ssize_t n = read(timer_fd, &expirations, sizeof(expirations));
if (n < 0) {
logprint(ERROR, "failed to read from timer FD\n");
goto error;
}
if (pollfds[EVENT_LOOP_TIMER].revents & POLLIN) {
logprint(TRACE, "event-loop: got a timer event");
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);
int timer_fd = pollfds[EVENT_LOOP_TIMER].fd;
uint64_t expirations;
ssize_t n = read(timer_fd, &expirations, sizeof(expirations));
if (n < 0) {
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 {
ret = wl_display_dispatch_pending(state.wl_display);
wl_display_flush(state.wl_display);
} while (ret > 0);
func(user_data);
}
}
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
finish_config(&config);
free(configfile);
sd_bus_flush(state.bus);
}
return EXIT_SUCCESS;
// TODO: cleanup
finish_config(&config);
free(configfile);
return EXIT_SUCCESS;
error:
sd_bus_unref(bus);
pw_loop_leave(state.pw_loop);
pw_loop_destroy(state.pw_loop);
wl_display_disconnect(state.wl_display);
return EXIT_FAILURE;
sd_bus_unref(bus);
pw_loop_leave(state.pw_loop);
pw_loop_destroy(state.pw_loop);
wl_display_disconnect(state.wl_display);
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) {
strncpy(current->name, title, 255);
for (int i = 0; i < 255; ++i)
if (current->name[i] == '\"' || current->name[i] == '>' || current->name[i] == '\'')
current->name[i] = ' ';
if (current->name[i] == '\"' || current->name[i] == '>' || current->name[i] == '\'') current->name[i] = ' ';
current->name[255] = '\0';
break;
}
@ -57,8 +56,7 @@ void handleAppID(void *data, struct zwlr_foreign_toplevel_handle_v1 *handle, con
if (current->handle == handle) {
strncpy(current->clazz, app_id, 255);
for (int i = 0; i < 255; ++i)
if (current->clazz[i] == '\"' || current->clazz[i] == '>' || current->clazz[i] == '\'')
current->clazz[i] = ' ';
if (current->clazz[i] == '\"' || current->clazz[i] == '>' || current->clazz[i] == '\'') current->clazz[i] = ' ';
current->name[255] = '\0';
break;
}
@ -187,8 +185,7 @@ void xdpw_wlr_frame_finish(struct xdpw_screencast_instance *cast) {
xdpw_pwr_enqueue_buffer(cast);
uint64_t delay_ns = fps_limit_measure_end(&cast->fps_limit, cast->framerate);
if (delay_ns > 0) {
xdpw_add_timer(cast->ctx->state, delay_ns,
(xdpw_event_loop_timer_func_t)xdpw_wlr_frame_start, cast);
xdpw_add_timer(cast->ctx->state, delay_ns, (xdpw_event_loop_timer_func_t)xdpw_wlr_frame_start, cast);
return;
}
}
@ -211,11 +208,9 @@ void xdpw_wlr_frame_start(struct xdpw_screencast_instance *cast) {
xdpw_wlr_register_cb(cast);
}
static void wlr_frame_buffer_done(void *data,
struct zwlr_screencopy_frame_v1 *frame);
static void wlr_frame_buffer_done(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) {
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) {
struct xdpw_screencast_instance *cast = data;
if (!frame) {
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,
struct zwlr_screencopy_frame_v1 *frame,
uint32_t format, uint32_t width, uint32_t height) {
static void wlr_frame_linux_dmabuf(void *data, struct zwlr_screencopy_frame_v1 *frame, uint32_t format, uint32_t width, uint32_t height) {
struct xdpw_screencast_instance *cast = data;
if (!frame) {
return;
@ -250,8 +243,7 @@ static void wlr_frame_linux_dmabuf(void *data,
cast->screencopy_frame_info[DMABUF].format = format;
}
static void wlr_frame_buffer_done(void *data,
struct zwlr_screencopy_frame_v1 *frame) {
static void wlr_frame_buffer_done(void *data, struct zwlr_screencopy_frame_v1 *frame) {
struct xdpw_screencast_instance *cast = data;
if (!frame) {
return;
@ -266,7 +258,8 @@ static void wlr_frame_buffer_done(void *data,
// 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) &&
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.height != cast->screencopy_frame_info[cast->buffer_type].height) {
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);
// Check if dequeued buffer is compatible with announced buffer
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->stride[0] != cast->screencopy_frame_info[cast->buffer_type].stride)) ||
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->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->height != cast->screencopy_frame_info[cast->buffer_type].height) {
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);
}
static void wlr_frame_flags(void *data, struct zwlr_screencopy_frame_v1 *frame,
uint32_t flags) {
static void wlr_frame_flags(void *data, struct zwlr_screencopy_frame_v1 *frame, uint32_t flags) {
struct xdpw_screencast_instance *cast = data;
if (!frame) {
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;
}
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) {
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) {
struct xdpw_screencast_instance *cast = data;
if (!frame) {
return;
@ -331,8 +321,7 @@ static void wlr_frame_damage(void *data, struct zwlr_screencopy_frame_v1 *frame,
cast->current_frame.damage.height = height;
}
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) {
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) {
struct xdpw_screencast_instance *cast = data;
if (!frame) {
return;
@ -349,8 +338,7 @@ static void wlr_frame_ready(void *data, struct zwlr_screencopy_frame_v1 *frame,
xdpw_wlr_frame_finish(cast);
}
static void wlr_frame_failed(void *data,
struct zwlr_screencopy_frame_v1 *frame) {
static void wlr_frame_failed(void *data, struct zwlr_screencopy_frame_v1 *frame) {
struct xdpw_screencast_instance *cast = data;
if (!frame) {
return;
@ -373,11 +361,10 @@ static const struct zwlr_screencopy_frame_v1_listener wlr_frame_listener = {
.damage = wlr_frame_damage,
};
static void hyprland_frame_buffer_done(void *data,
struct hyprland_toplevel_export_frame_v1 *frame);
static void hyprland_frame_buffer_done(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 stride) {
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 stride) {
struct xdpw_screencast_instance *cast = data;
if (!frame) {
return;
@ -396,9 +383,8 @@ static void hyprland_frame_buffer(void *data, struct hyprland_toplevel_export_fr
hyprland_frame_buffer_done(cast, frame);
}
static void hyprland_frame_linux_dmabuf(void *data,
struct hyprland_toplevel_export_frame_v1 *frame,
uint32_t format, uint32_t width, uint32_t height) {
static void hyprland_frame_linux_dmabuf(void *data, struct hyprland_toplevel_export_frame_v1 *frame, uint32_t format, uint32_t width,
uint32_t height) {
struct xdpw_screencast_instance *cast = data;
if (!frame) {
return;
@ -411,8 +397,7 @@ static void hyprland_frame_linux_dmabuf(void *data,
cast->screencopy_frame_info[DMABUF].format = format;
}
static void hyprland_frame_buffer_done(void *data,
struct hyprland_toplevel_export_frame_v1 *frame) {
static void hyprland_frame_buffer_done(void *data, struct hyprland_toplevel_export_frame_v1 *frame) {
struct xdpw_screencast_instance *cast = data;
if (!frame) {
return;
@ -427,7 +412,8 @@ static void hyprland_frame_buffer_done(void *data,
// 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) &&
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.height != cast->screencopy_frame_info[cast->buffer_type].height) {
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);
// Check if dequeued buffer is compatible with announced buffer
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->stride[0] != cast->screencopy_frame_info[cast->buffer_type].stride)) ||
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->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->height != cast->screencopy_frame_info[cast->buffer_type].height) {
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);
}
static void hyprland_frame_flags(void *data, struct hyprland_toplevel_export_frame_v1 *frame,
uint32_t flags) {
static void hyprland_frame_flags(void *data, struct hyprland_toplevel_export_frame_v1 *frame, uint32_t flags) {
struct xdpw_screencast_instance *cast = data;
if (!frame) {
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;
}
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 height) {
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 height) {
struct xdpw_screencast_instance *cast = data;
if (!frame) {
return;
@ -492,8 +476,8 @@ static void hyprland_frame_damage(void *data, struct hyprland_toplevel_export_fr
cast->current_frame.damage.height = height;
}
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_nsec) {
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_nsec) {
struct xdpw_screencast_instance *cast = data;
if (!frame) {
return;
@ -510,8 +494,7 @@ static void hyprland_frame_ready(void *data, struct hyprland_toplevel_export_fra
xdpw_wlr_frame_finish(cast);
}
static void hyprland_frame_failed(void *data,
struct hyprland_toplevel_export_frame_v1 *frame) {
static void hyprland_frame_failed(void *data, struct hyprland_toplevel_export_frame_v1 *frame) {
struct xdpw_screencast_instance *cast = data;
if (!frame) {
return;
@ -524,24 +507,23 @@ static void hyprland_frame_failed(void *data,
xdpw_wlr_frame_finish(cast);
}
static const struct hyprland_toplevel_export_frame_v1_listener hyprland_frame_listener = {
.buffer = hyprland_frame_buffer,
.buffer_done = hyprland_frame_buffer_done,
.linux_dmabuf = hyprland_frame_linux_dmabuf,
.flags = hyprland_frame_flags,
.ready = hyprland_frame_ready,
.failed = hyprland_frame_failed,
.damage = hyprland_frame_damage};
static const struct hyprland_toplevel_export_frame_v1_listener hyprland_frame_listener = {.buffer = hyprland_frame_buffer,
.buffer_done = hyprland_frame_buffer_done,
.linux_dmabuf = hyprland_frame_linux_dmabuf,
.flags = hyprland_frame_flags,
.ready = hyprland_frame_ready,
.failed = hyprland_frame_failed,
.damage = hyprland_frame_damage};
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) {
// capture region
cast->frame_callback = zwlr_screencopy_manager_v1_capture_output_region(
cast->ctx->screencopy_manager, cast->with_cursor, cast->target.output->output, cast->target.x,
cast->target.y, cast->target.w, cast->target.h);
cast->frame_callback =
zwlr_screencopy_manager_v1_capture_output_region(cast->ctx->screencopy_manager, cast->with_cursor, cast->target.output->output,
cast->target.x, cast->target.y, cast->target.w, cast->target.h);
} else if (cast->target.window_handle == -1) {
cast->frame_callback = zwlr_screencopy_manager_v1_capture_output(
cast->ctx->screencopy_manager, cast->with_cursor, cast->target.output->output);
cast->frame_callback =
zwlr_screencopy_manager_v1_capture_output(cast->ctx->screencopy_manager, cast->with_cursor, cast->target.output->output);
} else {
// share window
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->ctx->hyprland_toplevel_manager, cast->with_cursor, entry->handle);
hyprland_toplevel_export_frame_v1_add_listener(cast->frame_callback_hyprland,
&hyprland_frame_listener, cast);
hyprland_toplevel_export_frame_v1_add_listener(cast->frame_callback_hyprland, &hyprland_frame_listener, cast);
logprint(TRACE, "hyprland: callbacks registered");
return;
}
zwlr_screencopy_frame_v1_add_listener(cast->frame_callback,
&wlr_frame_listener, cast);
zwlr_screencopy_frame_v1_add_listener(cast->frame_callback, &wlr_frame_listener, cast);
logprint(TRACE, "wlroots: callbacks registered");
}
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,
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 subpixel, const char *make, const char *model, int32_t transform) {
struct xdpw_wlr_output *output = data;
output->make = strdup(make);
output->model = strdup(model);
}
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) {
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) {
if (flags & WL_OUTPUT_MODE_CURRENT) {
struct xdpw_wlr_output *output = data;
output->framerate = (float)refresh / 1000;
}
}
static void wlr_output_handle_done(void *data, struct wl_output *wl_output) {
/* Nothing to do */
static void wlr_output_handle_done(void *data, struct wl_output *wl_output) { /* Nothing to do */
}
static void wlr_output_handle_scale(void *data, struct wl_output *wl_output,
int32_t factor) {
/* Nothing to do */
static void wlr_output_handle_scale(void *data, struct wl_output *wl_output, int32_t factor) { /* Nothing to do */
}
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,
};
static void wlr_xdg_output_name(void *data, struct zxdg_output_v1 *xdg_output,
const char *name) {
static void wlr_xdg_output_name(void *data, struct zxdg_output_v1 *xdg_output, const char *name) {
struct xdpw_wlr_output *output = data;
output->name = strdup(name);
@ -617,18 +591,13 @@ static const struct zxdg_output_v1_listener wlr_xdg_output_listener = {
.name = wlr_xdg_output_name,
};
static void wlr_add_xdg_output_listener(struct xdpw_wlr_output *output,
struct zxdg_output_v1 *xdg_output) {
static void wlr_add_xdg_output_listener(struct xdpw_wlr_output *output, struct zxdg_output_v1 *xdg_output) {
output->xdg_output = xdg_output;
zxdg_output_v1_add_listener(output->xdg_output, &wlr_xdg_output_listener,
output);
zxdg_output_v1_add_listener(output->xdg_output, &wlr_xdg_output_listener, output);
}
static void wlr_init_xdg_output(struct xdpw_screencast_context *ctx,
struct xdpw_wlr_output *output) {
struct zxdg_output_v1 *xdg_output =
zxdg_output_manager_v1_get_xdg_output(ctx->xdg_output_manager,
output->output);
static void wlr_init_xdg_output(struct xdpw_screencast_context *ctx, struct xdpw_wlr_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);
}
@ -682,8 +651,7 @@ char *getFormat(const char *fmt, ...) {
char *buildWindowList(struct xdpw_screencast_context *ctx) {
char *rolling = calloc(1, 1);
if (!ctx->wlroots_toplevel_manager)
return rolling;
if (!ctx->wlroots_toplevel_manager) return rolling;
struct SToplevelEntry *current;
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) {
if (rolling[i] == '\"')
rolling[i] = ' ';
if (rolling[i] == '\"') rolling[i] = ' ';
}
return rolling;
@ -713,7 +680,10 @@ struct xdpw_share xdpw_wlr_chooser(struct xdpw_screencast_context *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);
@ -756,8 +726,7 @@ struct xdpw_share xdpw_wlr_chooser(struct xdpw_screencast_context *ctx) {
free(display_name);
if (!found)
return res;
if (!found) return res;
res.output = out;
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 *output, *tmp;
wl_list_for_each_safe(output, tmp, output_list, link) {
return output;
}
wl_list_for_each_safe(output, tmp, output_list, link) { return output; }
return NULL;
}
struct xdpw_wlr_output *xdpw_wlr_output_find_by_name(struct wl_list *output_list,
const char *name) {
struct xdpw_wlr_output *xdpw_wlr_output_find_by_name(struct wl_list *output_list, const char *name) {
struct xdpw_wlr_output *output, *tmp;
wl_list_for_each_safe(output, tmp, output_list, link) {
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;
}
struct xdpw_wlr_output *xdpw_wlr_output_find(struct xdpw_screencast_context *ctx,
struct wl_output *out, uint32_t id) {
struct xdpw_wlr_output *xdpw_wlr_output_find(struct xdpw_screencast_context *ctx, struct wl_output *out, uint32_t id) {
struct xdpw_wlr_output *output, *tmp;
wl_list_for_each_safe(output, tmp, &ctx->output_list, link) {
if ((output->output == out) || (output->id == id)) {
@ -870,8 +835,7 @@ static void wlr_remove_output(struct xdpw_wlr_output *out) {
free(out);
}
static void wlr_format_modifier_pair_add(struct xdpw_screencast_context *ctx,
uint32_t format, uint64_t modifier) {
static void wlr_format_modifier_pair_add(struct xdpw_screencast_context *ctx, uint32_t format, uint64_t modifier) {
struct xdpw_format_modifier_pair *fm_pair;
wl_array_for_each(fm_pair, &ctx->format_modifier_pairs) {
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);
}
static void linux_dmabuf_handle_modifier(void *data,
struct zwp_linux_dmabuf_v1 *zwp_linux_dmabuf_v1,
uint32_t format, uint32_t modifier_hi, uint32_t modifier_lo) {
static void linux_dmabuf_handle_modifier(void *data, struct zwp_linux_dmabuf_v1 *zwp_linux_dmabuf_v1, uint32_t format, uint32_t modifier_hi,
uint32_t modifier_lo) {
struct xdpw_screencast_context *ctx = data;
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,
};
static void linux_dmabuf_feedback_handle_main_device(void *data,
struct zwp_linux_dmabuf_feedback_v1 *zwp_linux_dmabuf_feedback_v1, struct wl_array *device_arr) {
static void linux_dmabuf_feedback_handle_main_device(void *data, struct zwp_linux_dmabuf_feedback_v1 *zwp_linux_dmabuf_feedback_v1,
struct wl_array *device_arr) {
struct xdpw_screencast_context *ctx = data;
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);
}
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) {
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 xdpw_screencast_context *ctx = data;
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;
}
static void linux_dmabuf_feedback_handle_done(void *data,
struct zwp_linux_dmabuf_feedback_v1 *zwp_linux_dmabuf_feedback_v1) {
static void linux_dmabuf_feedback_handle_done(void *data, struct zwp_linux_dmabuf_feedback_v1 *zwp_linux_dmabuf_feedback_v1) {
struct xdpw_screencast_context *ctx = data;
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;
}
static void linux_dmabuf_feedback_tranche_target_devices(void *data,
struct zwp_linux_dmabuf_feedback_v1 *zwp_linux_dmabuf_feedback_v1, struct wl_array *device_arr) {
static void linux_dmabuf_feedback_tranche_target_devices(void *data, struct zwp_linux_dmabuf_feedback_v1 *zwp_linux_dmabuf_feedback_v1,
struct wl_array *device_arr) {
struct xdpw_screencast_context *ctx = data;
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,
struct zwp_linux_dmabuf_feedback_v1 *zwp_linux_dmabuf_feedback_v1, uint32_t flags) {
static void linux_dmabuf_feedback_tranche_flags(void *data, struct zwp_linux_dmabuf_feedback_v1 *zwp_linux_dmabuf_feedback_v1, uint32_t flags) {
logprint(DEBUG, "wlroots: linux_dmabuf_feedback_tranche_flags called");
}
static void linux_dmabuf_feedback_tranche_formats(void *data,
struct zwp_linux_dmabuf_feedback_v1 *zwp_linux_dmabuf_feedback_v1, struct wl_array *indices) {
static void linux_dmabuf_feedback_tranche_formats(void *data, struct zwp_linux_dmabuf_feedback_v1 *zwp_linux_dmabuf_feedback_v1,
struct wl_array *indices) {
struct xdpw_screencast_context *ctx = data;
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,
struct zwp_linux_dmabuf_feedback_v1 *zwp_linux_dmabuf_feedback_v1) {
static void linux_dmabuf_feedback_tranche_done(void *data, struct zwp_linux_dmabuf_feedback_v1 *zwp_linux_dmabuf_feedback_v1) {
struct xdpw_screencast_context *ctx = data;
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,
};
static void wlr_registry_handle_add(void *data, struct wl_registry *reg,
uint32_t id, const char *interface, uint32_t ver) {
static void wlr_registry_handle_add(void *data, struct wl_registry *reg, uint32_t id, const char *interface, uint32_t ver) {
struct xdpw_screencast_context *ctx = data;
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;
}
logprint(DEBUG, "wlroots: |-- registered to interface %s (Version %u)", interface, version);
ctx->screencopy_manager = wl_registry_bind(
reg, id, &zwlr_screencopy_manager_v1_interface, version);
ctx->screencopy_manager = wl_registry_bind(reg, id, &zwlr_screencopy_manager_v1_interface, version);
}
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) {
logprint(DEBUG, "wlroots: |-- registered to interface %s (Version %u)", interface, XDG_OUTPUT_MANAGER_VERSION);
ctx->xdg_output_manager =
wl_registry_bind(reg, id, &zxdg_output_manager_v1_interface, XDG_OUTPUT_MANAGER_VERSION);
ctx->xdg_output_manager = wl_registry_bind(reg, id, &zxdg_output_manager_v1_interface, XDG_OUTPUT_MANAGER_VERSION);
}
if (strcmp(interface, zwp_linux_dmabuf_v1_interface.name) == 0) {
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,
uint32_t id) {
static void wlr_registry_handle_remove(void *data, struct wl_registry *reg, uint32_t id) {
struct xdpw_screencast_context *ctx = data;
struct xdpw_wlr_output *output = xdpw_wlr_output_find(ctx, NULL, id);
if (output) {
@ -1164,8 +1119,7 @@ int xdpw_wlr_screencopy_init(struct xdpw_state *state) {
// make sure our wlroots supports xdg_output_manager
if (!ctx->xdg_output_manager) {
logprint(ERROR, "Compositor doesn't support %s!",
zxdg_output_manager_v1_interface.name);
logprint(ERROR, "Compositor doesn't support %s!", zxdg_output_manager_v1_interface.name);
return -1;
}
@ -1183,8 +1137,7 @@ int xdpw_wlr_screencopy_init(struct xdpw_state *state) {
// make sure our wlroots supports screencopy protocol
if (!ctx->screencopy_manager) {
logprint(ERROR, "Compositor doesn't support %s!",
zwlr_screencopy_manager_v1_interface.name);
logprint(ERROR, "Compositor doesn't support %s!", zwlr_screencopy_manager_v1_interface.name);
return -1;
}
@ -1210,9 +1163,7 @@ void xdpw_wlr_screencopy_finish(struct xdpw_screencast_context *ctx) {
}
struct xdpw_screencast_instance *cast, *tmp_c;
wl_list_for_each_safe(cast, tmp_c, &ctx->screencast_instances, link) {
cast->quit = true;
}
wl_list_for_each_safe(cast, tmp_c, &ctx->screencast_instances, link) { cast->quit = true; }
if (ctx->screencopy_manager) {
zwlr_screencopy_manager_v1_destroy(ctx->screencopy_manager);