Fix CI with libpipewire02, libdrm. Remove png.h unused import. Cleanup style.

This commit is contained in:
Dan Shick 2020-03-13 12:43:24 -04:00 committed by Simon Ser
parent c0da39f022
commit 45699637d1
15 changed files with 480 additions and 514 deletions

View file

@ -5,7 +5,8 @@ packages:
- meson - meson
- wayland - wayland
- wayland-protocols - wayland-protocols
- pipewire - libpipewire02
- libdrm
sources: sources:
- https://github.com/emersion/xdg-desktop-portal-wlr - https://github.com/emersion/xdg-desktop-portal-wlr
tasks: tasks:

2
.gitignore vendored
View file

@ -53,4 +53,4 @@ dkms.conf
# build folder # build folder
build/ build/
build-*/ build-*/

View file

@ -1,27 +1,20 @@
#ifndef LOGGER_H #ifndef LOGGER_H
#define LOGGER_H #define LOGGER_H
#include <stdarg.h>
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include <stdarg.h>
#include <string.h> #include <string.h>
#include <time.h> #include <time.h>
enum LOGLEVEL { enum LOGLEVEL { QUIET, ERROR, WARN, INFO, DEBUG, TRACE };
QUIET,
ERROR,
WARN,
INFO,
DEBUG,
TRACE
};
struct logger_properties{ struct logger_properties {
enum LOGLEVEL level; enum LOGLEVEL level;
FILE *__restrict__ dst; FILE *dst;
}; };
void init_logger(FILE *__restrict__ dst, enum LOGLEVEL level); void init_logger(FILE *dst, enum LOGLEVEL level);
enum LOGLEVEL get_loglevel(const char *level); enum LOGLEVEL get_loglevel(const char *level);
void logprint(enum LOGLEVEL level, char *msg, ...); void logprint(enum LOGLEVEL level, char *msg, ...);

View file

@ -1,6 +1,7 @@
#ifndef SCREENCAST_COMMON_H #ifndef SCREENCAST_COMMON_H
#define SCREENCAST_COMMON_H #define SCREENCAST_COMMON_H
#include <string.h>
#include <pipewire/pipewire.h> #include <pipewire/pipewire.h>
#include <spa/param/video/format-utils.h> #include <spa/param/video/format-utils.h>
#include <libdrm/drm_fourcc.h> #include <libdrm/drm_fourcc.h>
@ -24,7 +25,6 @@ struct simple_frame {
uint32_t tv_nsec; uint32_t tv_nsec;
enum wl_shm_format format; enum wl_shm_format format;
struct damage *damage; struct damage *damage;
struct wl_buffer *buffer; struct wl_buffer *buffer;
void *data; void *data;
}; };
@ -38,7 +38,6 @@ struct pwr_type {
}; };
struct screencast_context { struct screencast_context {
// pipewire // pipewire
struct pwr_type type; struct pwr_type type;
struct pw_loop *loop; struct pw_loop *loop;
@ -97,6 +96,5 @@ struct wayland_output {
}; };
uint32_t pipewire_from_wl_shm(void *data); uint32_t pipewire_from_wl_shm(void *data);
char *strdup(const char *src);
#endif /* SCREENCAST_COMMON_H */ #endif /* SCREENCAST_COMMON_H */

View file

@ -5,7 +5,6 @@
#include "xdg-output-unstable-v1-client-protocol.h" #include "xdg-output-unstable-v1-client-protocol.h"
#include <fcntl.h> #include <fcntl.h>
#include <limits.h> #include <limits.h>
#include <png.h>
#include <stdbool.h> #include <stdbool.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
@ -27,9 +26,10 @@ void wlr_frame_free(struct xdpw_state *state);
int wlr_screencopy_init(struct xdpw_state *state); int wlr_screencopy_init(struct xdpw_state *state);
void wlr_screencopy_uninit(struct screencast_context *ctx); void wlr_screencopy_uninit(struct screencast_context *ctx);
struct wayland_output *wlr_output_find_by_name(struct wl_list *output_list, const char* name); struct wayland_output *wlr_output_find_by_name(struct wl_list *output_list,
const char *name);
struct wayland_output *wlr_output_find(struct screencast_context *ctx, struct wayland_output *wlr_output_find(struct screencast_context *ctx,
struct wl_output *out, uint32_t id); struct wl_output *out, uint32_t id);
struct wayland_output *wlr_output_first(struct wl_list *output_list); struct wayland_output *wlr_output_first(struct wl_list *output_list);
void wlr_register_cb(struct xdpw_state *state); void wlr_register_cb(struct xdpw_state *state);

View file

@ -8,7 +8,6 @@ project(
'c_std=c11', 'c_std=c11',
'warning_level=2', 'warning_level=2',
'werror=true', 'werror=true',
'debug=true',
], ],
) )

View file

@ -47,4 +47,4 @@ lib_wl_protos = static_library(
wlr_protos = declare_dependency( wlr_protos = declare_dependency(
link_with: lib_wl_protos, link_with: lib_wl_protos,
sources: wl_protos_headers, sources: wl_protos_headers,
) )

View file

@ -1,207 +1,207 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<protocol name="wlr_screencopy_unstable_v1"> <protocol name="wlr_screencopy_unstable_v1">
<copyright> <copyright>
Copyright © 2018 Simon Ser Copyright © 2018 Simon Ser
Copyright © 2019 Andri Yngvason Copyright © 2019 Andri Yngvason
Permission is hereby granted, free of charge, to any person obtaining a Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and associated documentation files (the "Software"), copy of this software and associated documentation files (the "Software"),
to deal in the Software without restriction, including without limitation to deal in the Software without restriction, including without limitation
the rights to use, copy, modify, merge, publish, distribute, sublicense, the rights to use, copy, modify, merge, publish, distribute, sublicense,
and/or sell copies of the Software, and to permit persons to whom the and/or sell copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following conditions: Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice (including the next The above copyright notice and this permission notice (including the next
paragraph) shall be included in all copies or substantial portions of the paragraph) shall be included in all copies or substantial portions of the
Software. Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE. DEALINGS IN THE SOFTWARE.
</copyright> </copyright>
<description summary="screen content capturing on client buffers"> <description summary="screen content capturing on client buffers">
This protocol allows clients to ask the compositor to copy part of the This protocol allows clients to ask the compositor to copy part of the
screen content to a client buffer. screen content to a client buffer.
Warning! The protocol described in this file is experimental and Warning! The protocol described in this file is experimental and
backward incompatible changes may be made. Backward compatible changes backward incompatible changes may be made. Backward compatible changes
may be added together with the corresponding interface version bump. may be added together with the corresponding interface version bump.
Backward incompatible changes are done by bumping the version number in Backward incompatible changes are done by bumping the version number in
the protocol and interface names and resetting the interface version. the protocol and interface names and resetting the interface version.
Once the protocol is to be declared stable, the 'z' prefix and the Once the protocol is to be declared stable, the 'z' prefix and the
version number in the protocol and interface names are removed and the version number in the protocol and interface names are removed and the
interface version number is reset. interface version number is reset.
</description> </description>
<interface name="zwlr_screencopy_manager_v1" version="2"> <interface name="zwlr_screencopy_manager_v1" version="2">
<description summary="manager to inform clients and begin capturing"> <description summary="manager to inform clients and begin capturing">
This object is a manager which offers requests to start capturing from a This object is a manager which offers requests to start capturing from a
source. source.
</description> </description>
<request name="capture_output"> <request name="capture_output">
<description summary="capture an output"> <description summary="capture an output">
Capture the next frame of an entire output. Capture the next frame of an entire output.
</description> </description>
<arg name="frame" type="new_id" interface="zwlr_screencopy_frame_v1"/> <arg name="frame" type="new_id" interface="zwlr_screencopy_frame_v1"/>
<arg name="overlay_cursor" type="int" <arg name="overlay_cursor" type="int"
summary="composite cursor onto the frame"/> summary="composite cursor onto the frame"/>
<arg name="output" type="object" interface="wl_output"/> <arg name="output" type="object" interface="wl_output"/>
</request> </request>
<request name="capture_output_region"> <request name="capture_output_region">
<description summary="capture an output's region"> <description summary="capture an output's region">
Capture the next frame of an output's region. Capture the next frame of an output's region.
The region is given in output logical coordinates, see The region is given in output logical coordinates, see
xdg_output.logical_size. The region will be clipped to the output's xdg_output.logical_size. The region will be clipped to the output's
extents. extents.
</description> </description>
<arg name="frame" type="new_id" interface="zwlr_screencopy_frame_v1"/> <arg name="frame" type="new_id" interface="zwlr_screencopy_frame_v1"/>
<arg name="overlay_cursor" type="int" <arg name="overlay_cursor" type="int"
summary="composite cursor onto the frame"/> summary="composite cursor onto the frame"/>
<arg name="output" type="object" interface="wl_output"/> <arg name="output" type="object" interface="wl_output"/>
<arg name="x" type="int"/> <arg name="x" type="int"/>
<arg name="y" type="int"/> <arg name="y" type="int"/>
<arg name="width" type="int"/> <arg name="width" type="int"/>
<arg name="height" type="int"/> <arg name="height" type="int"/>
</request> </request>
<request name="destroy" type="destructor"> <request name="destroy" type="destructor">
<description summary="destroy the manager"> <description summary="destroy the manager">
All objects created by the manager will still remain valid, until their All objects created by the manager will still remain valid, until their
appropriate destroy request has been called. appropriate destroy request has been called.
</description> </description>
</request> </request>
</interface> </interface>
<interface name="zwlr_screencopy_frame_v1" version="2"> <interface name="zwlr_screencopy_frame_v1" version="2">
<description summary="a frame ready for copy"> <description summary="a frame ready for copy">
This object represents a single frame. This object represents a single frame.
When created, a "buffer" event will be sent. The client will then be able When created, a "buffer" event will be sent. The client will then be able
to send a "copy" request. If the capture is successful, the compositor to send a "copy" request. If the capture is successful, the compositor
will send a "flags" followed by a "ready" event. will send a "flags" followed by a "ready" event.
If the capture failed, the "failed" event is sent. This can happen anytime If the capture failed, the "failed" event is sent. This can happen anytime
before the "ready" event. before the "ready" event.
Once either a "ready" or a "failed" event is received, the client should Once either a "ready" or a "failed" event is received, the client should
destroy the frame. destroy the frame.
</description> </description>
<event name="buffer"> <event name="buffer">
<description summary="buffer information"> <description summary="buffer information">
Provides information about the frame's buffer. This event is sent once Provides information about the frame's buffer. This event is sent once
as soon as the frame is created. as soon as the frame is created.
The client should then create a buffer with the provided attributes, and The client should then create a buffer with the provided attributes, and
send a "copy" request. send a "copy" request.
</description> </description>
<arg name="format" type="uint" summary="buffer format"/> <arg name="format" type="uint" summary="buffer format"/>
<arg name="width" type="uint" summary="buffer width"/> <arg name="width" type="uint" summary="buffer width"/>
<arg name="height" type="uint" summary="buffer height"/> <arg name="height" type="uint" summary="buffer height"/>
<arg name="stride" type="uint" summary="buffer stride"/> <arg name="stride" type="uint" summary="buffer stride"/>
</event> </event>
<request name="copy"> <request name="copy">
<description summary="copy the frame"> <description summary="copy the frame">
Copy the frame to the supplied buffer. The buffer must have a the Copy the frame to the supplied buffer. The buffer must have a the
correct size, see zwlr_screencopy_frame_v1.buffer. The buffer needs to correct size, see zwlr_screencopy_frame_v1.buffer. The buffer needs to
have a supported format. have a supported format.
If the frame is successfully copied, a "flags" and a "ready" events are If the frame is successfully copied, a "flags" and a "ready" events are
sent. Otherwise, a "failed" event is sent. sent. Otherwise, a "failed" event is sent.
</description> </description>
<arg name="buffer" type="object" interface="wl_buffer"/> <arg name="buffer" type="object" interface="wl_buffer"/>
</request> </request>
<enum name="error"> <enum name="error">
<entry name="already_used" value="0" <entry name="already_used" value="0"
summary="the object has already been used to copy a wl_buffer"/> summary="the object has already been used to copy a wl_buffer"/>
<entry name="invalid_buffer" value="1" <entry name="invalid_buffer" value="1"
summary="buffer attributes are invalid"/> summary="buffer attributes are invalid"/>
</enum> </enum>
<enum name="flags" bitfield="true"> <enum name="flags" bitfield="true">
<entry name="y_invert" value="1" summary="contents are y-inverted"/> <entry name="y_invert" value="1" summary="contents are y-inverted"/>
</enum> </enum>
<event name="flags"> <event name="flags">
<description summary="frame flags"> <description summary="frame flags">
Provides flags about the frame. This event is sent once before the Provides flags about the frame. This event is sent once before the
"ready" event. "ready" event.
</description> </description>
<arg name="flags" type="uint" enum="flags" summary="frame flags"/> <arg name="flags" type="uint" enum="flags" summary="frame flags"/>
</event> </event>
<event name="ready"> <event name="ready">
<description summary="indicates frame is available for reading"> <description summary="indicates frame is available for reading">
Called as soon as the frame is copied, indicating it is available Called as soon as the frame is copied, indicating it is available
for reading. This event includes the time at which presentation happened for reading. This event includes the time at which presentation happened
at. at.
The timestamp is expressed as tv_sec_hi, tv_sec_lo, tv_nsec triples, The timestamp is expressed as tv_sec_hi, tv_sec_lo, tv_nsec triples,
each component being an unsigned 32-bit value. Whole seconds are in each component being an unsigned 32-bit value. Whole seconds are in
tv_sec which is a 64-bit value combined from tv_sec_hi and tv_sec_lo, tv_sec which is a 64-bit value combined from tv_sec_hi and tv_sec_lo,
and the additional fractional part in tv_nsec as nanoseconds. Hence, and the additional fractional part in tv_nsec as nanoseconds. Hence,
for valid timestamps tv_nsec must be in [0, 999999999]. The seconds part for valid timestamps tv_nsec must be in [0, 999999999]. The seconds part
may have an arbitrary offset at start. may have an arbitrary offset at start.
After receiving this event, the client should destroy the object. After receiving this event, the client should destroy the object.
</description> </description>
<arg name="tv_sec_hi" type="uint" <arg name="tv_sec_hi" type="uint"
summary="high 32 bits of the seconds part of the timestamp"/> summary="high 32 bits of the seconds part of the timestamp"/>
<arg name="tv_sec_lo" type="uint" <arg name="tv_sec_lo" type="uint"
summary="low 32 bits of the seconds part of the timestamp"/> summary="low 32 bits of the seconds part of the timestamp"/>
<arg name="tv_nsec" type="uint" <arg name="tv_nsec" type="uint"
summary="nanoseconds part of the timestamp"/> summary="nanoseconds part of the timestamp"/>
</event> </event>
<event name="failed"> <event name="failed">
<description summary="frame copy failed"> <description summary="frame copy failed">
This event indicates that the attempted frame copy has failed. This event indicates that the attempted frame copy has failed.
After receiving this event, the client should destroy the object. After receiving this event, the client should destroy the object.
</description> </description>
</event> </event>
<request name="destroy" type="destructor"> <request name="destroy" type="destructor">
<description summary="delete this object, used or not"> <description summary="delete this object, used or not">
Destroys the frame. This request can be sent at any time by the client. Destroys the frame. This request can be sent at any time by the client.
</description> </description>
</request> </request>
<!-- Version 2 additions --> <!-- Version 2 additions -->
<request name="copy_with_damage" since="2"> <request name="copy_with_damage" since="2">
<description summary="copy the frame when it's damaged"> <description summary="copy the frame when it's damaged">
Same as copy, except it waits until there is damage to copy. Same as copy, except it waits until there is damage to copy.
</description> </description>
<arg name="buffer" type="object" interface="wl_buffer"/> <arg name="buffer" type="object" interface="wl_buffer"/>
</request> </request>
<event name="damage" since="2"> <event name="damage" since="2">
<description summary="carries the coordinates of the damaged region"> <description summary="carries the coordinates of the damaged region">
This event is sent right before the ready event when copy_with_damage is This event is sent right before the ready event when copy_with_damage is
requested. It may be generated multiple times for each copy_with_damage requested. It may be generated multiple times for each copy_with_damage
request. request.
The arguments describe a box around an area that has changed since the The arguments describe a box around an area that has changed since the
last copy request that was derived from the current screencopy manager last copy request that was derived from the current screencopy manager
instance. instance.
The union of all regions received between the call to copy_with_damage The union of all regions received between the call to copy_with_damage
and a ready event is the total damage since the prior ready event. and a ready event is the total damage since the prior ready event.
</description> </description>
<arg name="x" type="uint" summary="damaged x coordinates"/> <arg name="x" type="uint" summary="damaged x coordinates"/>
<arg name="y" type="uint" summary="damaged y coordinates"/> <arg name="y" type="uint" summary="damaged y coordinates"/>
<arg name="width" type="uint" summary="current width"/> <arg name="width" type="uint" summary="current width"/>
<arg name="height" type="uint" summary="current height"/> <arg name="height" type="uint" summary="current height"/>
</event> </event>
</interface> </interface>
</protocol> </protocol>

View file

@ -1,119 +1,119 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<protocol name="xdg_output_unstable_v1"> <protocol name="xdg_output_unstable_v1">
<copyright> <copyright>
Copyright © 2017 Red Hat Inc. Copyright © 2017 Red Hat Inc.
Permission is hereby granted, free of charge, to any person obtaining a Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and associated documentation files (the "Software"), copy of this software and associated documentation files (the "Software"),
to deal in the Software without restriction, including without limitation to deal in the Software without restriction, including without limitation
the rights to use, copy, modify, merge, publish, distribute, sublicense, the rights to use, copy, modify, merge, publish, distribute, sublicense,
and/or sell copies of the Software, and to permit persons to whom the and/or sell copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following conditions: Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice (including the next The above copyright notice and this permission notice (including the next
paragraph) shall be included in all copies or substantial portions of the paragraph) shall be included in all copies or substantial portions of the
Software. Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE. DEALINGS IN THE SOFTWARE.
</copyright> </copyright>
<description summary="Protocol to describe output regions"> <description summary="Protocol to describe output regions">
This protocol aims at describing outputs in a way which is more in line This protocol aims at describing outputs in a way which is more in line
with the concept of an output on desktop oriented systems. with the concept of an output on desktop oriented systems.
Some information are more specific to the concept of an output for Some information are more specific to the concept of an output for
a desktop oriented system and may not make sense in other applications, a desktop oriented system and may not make sense in other applications,
such as IVI systems for example. such as IVI systems for example.
Typically, the global compositor space on a desktop system is made of Typically, the global compositor space on a desktop system is made of
a contiguous or overlapping set of rectangular regions. a contiguous or overlapping set of rectangular regions.
Some of the information provided in this protocol might be identical Some of the information provided in this protocol might be identical
to their counterparts already available from wl_output, in which case to their counterparts already available from wl_output, in which case
the information provided by this protocol should be preferred to their the information provided by this protocol should be preferred to their
equivalent in wl_output. The goal is to move the desktop specific equivalent in wl_output. The goal is to move the desktop specific
concepts (such as output location within the global compositor space, concepts (such as output location within the global compositor space,
the connector name and types, etc.) out of the core wl_output protocol. the connector name and types, etc.) out of the core wl_output protocol.
Warning! The protocol described in this file is experimental and Warning! The protocol described in this file is experimental and
backward incompatible changes may be made. Backward compatible backward incompatible changes may be made. Backward compatible
changes may be added together with the corresponding interface changes may be added together with the corresponding interface
version bump. version bump.
Backward incompatible changes are done by bumping the version Backward incompatible changes are done by bumping the version
number in the protocol and interface names and resetting the number in the protocol and interface names and resetting the
interface version. Once the protocol is to be declared stable, interface version. Once the protocol is to be declared stable,
the 'z' prefix and the version number in the protocol and the 'z' prefix and the version number in the protocol and
interface names are removed and the interface version number is interface names are removed and the interface version number is
reset. reset.
</description> </description>
<interface name="zxdg_output_manager_v1" version="3"> <interface name="zxdg_output_manager_v1" version="3">
<description summary="manage xdg_output objects"> <description summary="manage xdg_output objects">
A global factory interface for xdg_output objects. A global factory interface for xdg_output objects.
</description> </description>
<request name="destroy" type="destructor"> <request name="destroy" type="destructor">
<description summary="destroy the xdg_output_manager object"> <description summary="destroy the xdg_output_manager object">
Using this request a client can tell the server that it is not Using this request a client can tell the server that it is not
going to use the xdg_output_manager object anymore. going to use the xdg_output_manager object anymore.
Any objects already created through this instance are not affected. Any objects already created through this instance are not affected.
</description> </description>
</request> </request>
<request name="get_xdg_output"> <request name="get_xdg_output">
<description summary="create an xdg output from a wl_output"> <description summary="create an xdg output from a wl_output">
This creates a new xdg_output object for the given wl_output. This creates a new xdg_output object for the given wl_output.
</description> </description>
<arg name="id" type="new_id" interface="zxdg_output_v1"/> <arg name="id" type="new_id" interface="zxdg_output_v1"/>
<arg name="output" type="object" interface="wl_output"/> <arg name="output" type="object" interface="wl_output"/>
</request> </request>
</interface> </interface>
<interface name="zxdg_output_v1" version="3"> <interface name="zxdg_output_v1" version="3">
<description summary="compositor logical output region"> <description summary="compositor logical output region">
An xdg_output describes part of the compositor geometry. An xdg_output describes part of the compositor geometry.
This typically corresponds to a monitor that displays part of the This typically corresponds to a monitor that displays part of the
compositor space. compositor space.
For objects version 3 onwards, after all xdg_output properties have been For objects version 3 onwards, after all xdg_output properties have been
sent (when the object is created and when properties are updated), a sent (when the object is created and when properties are updated), a
wl_output.done event is sent. This allows changes to the output wl_output.done event is sent. This allows changes to the output
properties to be seen as atomic, even if they happen via multiple events. properties to be seen as atomic, even if they happen via multiple events.
</description> </description>
<request name="destroy" type="destructor"> <request name="destroy" type="destructor">
<description summary="destroy the xdg_output object"> <description summary="destroy the xdg_output object">
Using this request a client can tell the server that it is not Using this request a client can tell the server that it is not
going to use the xdg_output object anymore. going to use the xdg_output object anymore.
</description> </description>
</request> </request>
<event name="logical_position"> <event name="logical_position">
<description summary="position of the output within the global compositor space"> <description summary="position of the output within the global compositor space">
The position event describes the location of the wl_output within The position event describes the location of the wl_output within
the global compositor space. the global compositor space.
The logical_position event is sent after creating an xdg_output The logical_position event is sent after creating an xdg_output
(see xdg_output_manager.get_xdg_output) and whenever the location (see xdg_output_manager.get_xdg_output) and whenever the location
of the output changes within the global compositor space. of the output changes within the global compositor space.
</description> </description>
<arg name="x" type="int" <arg name="x" type="int"
summary="x position within the global compositor space"/> summary="x position within the global compositor space"/>
<arg name="y" type="int" <arg name="y" type="int"
summary="y position within the global compositor space"/> summary="y position within the global compositor space"/>
</event> </event>
<event name="logical_size"> <event name="logical_size">
<description summary="size of the output in the global compositor space"> <description summary="size of the output in the global compositor space">
The logical_size event describes the size of the output in the The logical_size event describes the size of the output in the
global compositor space. global compositor space.
@ -132,13 +132,13 @@
For example, for a wl_output mode 3840×2160 and a scale factor 2: For example, for a wl_output mode 3840×2160 and a scale factor 2:
- A compositor not scaling the surface buffers will advertise a - A compositor not scaling the surface buffers will advertise a
logical size of 3840×2160, logical size of 3840×2160,
- A compositor automatically scaling the surface buffers will - A compositor automatically scaling the surface buffers will
advertise a logical size of 1920×1080, advertise a logical size of 1920×1080,
- A compositor using a fractional scale of 1.5 will advertise a - A compositor using a fractional scale of 1.5 will advertise a
logical size to 2560×1620. logical size to 2560×1620.
For example, for a wl_output mode 1920×1080 and a 90 degree rotation, For example, for a wl_output mode 1920×1080 and a 90 degree rotation,
the compositor will advertise a logical size of 1080x1920. the compositor will advertise a logical size of 1080x1920.
@ -148,15 +148,15 @@
size of the output changes, either as a result of a change in the size of the output changes, either as a result of a change in the
applied scale or because of a change in the corresponding output applied scale or because of a change in the corresponding output
mode(see wl_output.mode) or transform (see wl_output.transform). mode(see wl_output.mode) or transform (see wl_output.transform).
</description> </description>
<arg name="width" type="int" <arg name="width" type="int"
summary="width in global compositor space"/> summary="width in global compositor space"/>
<arg name="height" type="int" <arg name="height" type="int"
summary="height in global compositor space"/> summary="height in global compositor space"/>
</event> </event>
<event name="done"> <event name="done">
<description summary="all information about the output have been sent"> <description summary="all information about the output have been sent">
This event is sent after all other properties of an xdg_output This event is sent after all other properties of an xdg_output
have been sent. have been sent.
@ -166,13 +166,13 @@
For objects version 3 onwards, this event is deprecated. Compositors For objects version 3 onwards, this event is deprecated. Compositors
are not required to send it anymore and must send wl_output.done are not required to send it anymore and must send wl_output.done
instead. instead.
</description> </description>
</event> </event>
<!-- Version 2 additions --> <!-- Version 2 additions -->
<event name="name" since="2"> <event name="name" since="2">
<description summary="name of this output"> <description summary="name of this output">
Many compositors will assign names to their outputs, show them to the Many compositors will assign names to their outputs, show them to the
user, allow them to be configured by name, etc. The client may wish to user, allow them to be configured by name, etc. The client may wish to
know this name as well to offer the user similar behaviors. know this name as well to offer the user similar behaviors.
@ -191,12 +191,12 @@
xdg_output_manager.get_xdg_output). This event is only sent once per xdg_output_manager.get_xdg_output). This event is only sent once per
xdg_output, and the name does not change over the lifetime of the xdg_output, and the name does not change over the lifetime of the
wl_output global. wl_output global.
</description> </description>
<arg name="name" type="string" summary="output name"/> <arg name="name" type="string" summary="output name"/>
</event> </event>
<event name="description" since="2"> <event name="description" since="2">
<description summary="human-readable description of this output"> <description summary="human-readable description of this output">
Many compositors can produce human-readable descriptions of their Many compositors can produce human-readable descriptions of their
outputs. The client may wish to know this description as well, to outputs. The client may wish to know this description as well, to
communicate the user for various purposes. communicate the user for various purposes.
@ -212,9 +212,9 @@
For objects of version 2 and lower, this event is only sent once per For objects of version 2 and lower, this event is only sent once per
xdg_output, and the description does not change over the lifetime of xdg_output, and the description does not change over the lifetime of
the wl_output global. the wl_output global.
</description> </description>
<arg name="description" type="string" summary="output description"/> <arg name="description" type="string" summary="output description"/>
</event> </event>
</interface> </interface>
</protocol> </protocol>

View file

@ -13,29 +13,31 @@ static const char *loglevels[] = {
static struct logger_properties logprops; static struct logger_properties logprops;
void init_logger(FILE *__restrict__ dst, enum LOGLEVEL level){ void init_logger(FILE *dst, enum LOGLEVEL level) {
logprops.dst = dst; logprops.dst = dst;
logprops.level = level; logprops.level = level;
} }
enum LOGLEVEL get_loglevel(const char *level){ enum LOGLEVEL get_loglevel(const char *level) {
int i; int i;
for(i = 0; i < NUM_LEVELS; i++){ for (i = 0; i < NUM_LEVELS; i++) {
if(!strcmp(level, loglevels[i])) return (enum LOGLEVEL) i; if (!strcmp(level, loglevels[i])) {
return (enum LOGLEVEL) i;
}
} }
fprintf(stderr, "Could not understand log level %s\n", level); fprintf(stderr, "Could not understand log level %s\n", level);
exit(EXIT_FAILURE); abort();
} }
void logprint(enum LOGLEVEL level, char *msg, ...){ void logprint(enum LOGLEVEL level, char *msg, ...) {
if (!logprops.dst) {
if(!logprops.dst){
fprintf(stderr, "Logger has been called, but was not initialized\n"); fprintf(stderr, "Logger has been called, but was not initialized\n");
exit(EXIT_FAILURE); abort();
} }
if(level > logprops.level || level == QUIET) return ; if (level > logprops.level || level == QUIET) {
return;
}
va_list args; va_list args;
char timestr[200]; char timestr[200];
@ -44,15 +46,12 @@ void logprint(enum LOGLEVEL level, char *msg, ...){
if (strftime(timestr, sizeof(timestr), "%Y/%m/%d %H:%M:%S", tmp) == 0) { if (strftime(timestr, sizeof(timestr), "%Y/%m/%d %H:%M:%S", tmp) == 0) {
fprintf(stderr, "strftime returned 0"); fprintf(stderr, "strftime returned 0");
exit(EXIT_FAILURE); abort();
} }
fprintf(logprops.dst, "%s", timestr); fprintf(logprops.dst, "%s", timestr);
fprintf(logprops.dst, " "); fprintf(logprops.dst, " ");
fprintf(logprops.dst, "[%s]", loglevels[level]); fprintf(logprops.dst, "[%s]", loglevels[level]);
fprintf(logprops.dst, " - "); fprintf(logprops.dst, " - ");
va_start(args, msg); va_start(args, msg);
@ -62,5 +61,4 @@ void logprint(enum LOGLEVEL level, char *msg, ...){
fprintf(logprops.dst, "\n"); fprintf(logprops.dst, "\n");
fflush(logprops.dst); fflush(logprops.dst);
}
}

View file

@ -14,26 +14,23 @@ enum event_loop_fd {
static const char service_name[] = "org.freedesktop.impl.portal.desktop.wlr"; static const char service_name[] = "org.freedesktop.impl.portal.desktop.wlr";
int xdpw_usage(FILE* stream, int rc) int xdpw_usage(FILE* stream, int rc) {
{
static const char* usage = static const char* usage =
"Usage: xdg-desktop-portal-wlr [options]\n" "Usage: xdg-desktop-portal-wlr [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"
" -o, --output=<name> Select output to capture.\n" " -o, --output=<name> Select output to capture.\n"
" -p,--pixelformat=BGRx|RGBx Force a pixelformat in pipewire\n" " -p,--pixelformat=BGRx|RGBx Force a pixelformat in pipewire\n"
" metadata (performs no conversion).\n" " metadata (performs no conversion).\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;
} }
int main(int argc, char *argv[]) { int main(int argc, char *argv[]) {
const char* output_name = NULL; const char* output_name = NULL;
const char* forced_pixelformat = NULL; const char* forced_pixelformat = NULL;
enum LOGLEVEL loglevel = ERROR; enum LOGLEVEL loglevel = ERROR;

View file

@ -1,6 +1,6 @@
#include "pipewire_screencast.h" #include "pipewire_screencast.h"
static inline void init_type(struct pwr_type *type, struct pw_type *map) { static void init_type(struct pwr_type *type, struct pw_type *map) {
pw_type_get(map, SPA_TYPE__MediaType, &type->media_type); pw_type_get(map, SPA_TYPE__MediaType, &type->media_type);
pw_type_get(map, SPA_TYPE__MediaSubtype, &type->media_subtype); pw_type_get(map, SPA_TYPE__MediaSubtype, &type->media_subtype);
pw_type_get(map, SPA_TYPE_FORMAT__Video, &type->format_video); pw_type_get(map, SPA_TYPE_FORMAT__Video, &type->format_video);
@ -9,8 +9,7 @@ static inline void init_type(struct pwr_type *type, struct pw_type *map) {
}; };
static void writeFrameData(void *pwFramePointer, void *wlrFramePointer, static void writeFrameData(void *pwFramePointer, void *wlrFramePointer,
uint32_t height, uint32_t stride, bool inverted) { uint32_t height, uint32_t stride, bool inverted) {
if (!inverted) { if (!inverted) {
memcpy(pwFramePointer, wlrFramePointer, height * stride); memcpy(pwFramePointer, wlrFramePointer, height * stride);
return; return;
@ -21,6 +20,7 @@ static void writeFrameData(void *pwFramePointer, void *wlrFramePointer,
void *pwRowPointer = pwFramePointer + (i * stride); void *pwRowPointer = pwFramePointer + (i * stride);
memcpy(pwRowPointer, flippedWlrRowPointer, stride); memcpy(pwRowPointer, flippedWlrRowPointer, stride);
} }
return; return;
} }
@ -63,7 +63,7 @@ static void pwr_on_event(void *data, uint64_t expirations) {
d[0].fd = -1; d[0].fd = -1;
writeFrameData(d[0].data, ctx->simple_frame.data, ctx->simple_frame.height, writeFrameData(d[0].data, ctx->simple_frame.data, ctx->simple_frame.height,
ctx->simple_frame.stride, ctx->simple_frame.y_invert); ctx->simple_frame.stride, ctx->simple_frame.y_invert);
logprint(TRACE, "pipewire: pointer %p", d[0].data); logprint(TRACE, "pipewire: pointer %p", d[0].data);
logprint(TRACE, "pipewire: size %d", d[0].maxsize); logprint(TRACE, "pipewire: size %d", d[0].maxsize);
@ -76,17 +76,15 @@ static void pwr_on_event(void *data, uint64_t expirations) {
pw_stream_queue_buffer(ctx->stream, pw_buf); pw_stream_queue_buffer(ctx->stream, pw_buf);
wlr_frame_free(state); wlr_frame_free(state);
} }
static void pwr_handle_stream_state_changed(void *data, static void pwr_handle_stream_state_changed(void *data,
enum pw_stream_state old, enum pw_stream_state old, enum pw_stream_state state, const char *error) {
enum pw_stream_state state,
const char *error) {
struct screencast_context *ctx = data; struct screencast_context *ctx = data;
ctx->node_id = pw_stream_get_node_id(ctx->stream); ctx->node_id = pw_stream_get_node_id(ctx->stream);
logprint(INFO, "pipewire: stream state changed to \"%s\"", pw_stream_state_as_string(state)); logprint(INFO, "pipewire: stream state changed to \"%s\"",
pw_stream_state_as_string(state));
logprint(INFO, "pipewire: node id is %d", ctx->node_id); logprint(INFO, "pipewire: node id is %d", ctx->node_id);
switch (state) { switch (state) {
@ -100,45 +98,46 @@ static void pwr_handle_stream_state_changed(void *data,
} }
static void pwr_handle_stream_format_changed(void *data, static void pwr_handle_stream_format_changed(void *data,
const struct spa_pod *format) { const struct spa_pod *format) {
struct screencast_context *ctx = data; struct screencast_context *ctx = data;
struct pw_stream *stream = ctx->stream; struct pw_stream *stream = ctx->stream;
struct pw_type *t = ctx->t; struct pw_type *t = ctx->t;
uint8_t params_buffer[1024]; uint8_t params_buffer[1024];
struct spa_pod_builder b = struct spa_pod_builder b =
SPA_POD_BUILDER_INIT(params_buffer, sizeof(params_buffer)); SPA_POD_BUILDER_INIT(params_buffer, sizeof(params_buffer));
const struct spa_pod *params[2]; const struct spa_pod *params[2];
if (format == NULL) { if (format == NULL) {
pw_stream_finish_format(stream, 0, NULL, 0); pw_stream_finish_format(stream, 0, NULL, 0);
return; return;
} }
spa_format_video_raw_parse(format, &ctx->pwr_format, &ctx->type.format_video); spa_format_video_raw_parse(format, &ctx->pwr_format, &ctx->type.format_video);
params[0] = spa_pod_builder_object( params[0] = spa_pod_builder_object(
&b, t->param.idBuffers, t->param_buffers.Buffers, ":", &b, t->param.idBuffers, t->param_buffers.Buffers, ":",
t->param_buffers.size, "i", ctx->simple_frame.size, ":", t->param_buffers.size, "i", ctx->simple_frame.size, ":",
t->param_buffers.stride, "i", ctx->simple_frame.stride, ":", t->param_buffers.stride, "i", ctx->simple_frame.stride, ":",
t->param_buffers.buffers, "iru", BUFFERS, SPA_POD_PROP_MIN_MAX(1, 32), t->param_buffers.buffers, "iru", BUFFERS, SPA_POD_PROP_MIN_MAX(1, 32),
":", t->param_buffers.align, "i", ALIGN); ":", t->param_buffers.align, "i", ALIGN);
params[1] = spa_pod_builder_object(&b, t->param.idMeta, t->param_meta.Meta, params[1] = spa_pod_builder_object(&b,
":", t->param_meta.type, "I", t->param.idMeta, t->param_meta.Meta,
t->meta.Header, ":", t->param_meta.size, ":", t->param_meta.type, "I",
"i", sizeof(struct spa_meta_header)); t->meta.Header, ":", t->param_meta.size,
"i", sizeof(struct spa_meta_header));
pw_stream_finish_format(stream, 0, params, 2); pw_stream_finish_format(stream, 0, params, 2);
} }
static const struct pw_stream_events pwr_stream_events = { static const struct pw_stream_events pwr_stream_events = {
PW_VERSION_STREAM_EVENTS, PW_VERSION_STREAM_EVENTS,
.state_changed = pwr_handle_stream_state_changed, .state_changed = pwr_handle_stream_state_changed,
.format_changed = pwr_handle_stream_format_changed, .format_changed = pwr_handle_stream_format_changed,
}; };
static void pwr_handle_state_changed(void *data, enum pw_remote_state old, static void pwr_handle_state_changed(void *data, enum pw_remote_state old,
enum pw_remote_state pwr_remote_state, enum pw_remote_state pwr_remote_state, const char *error) {
const char *error) {
struct xdpw_state *state = data; struct xdpw_state *state = data;
struct screencast_context *ctx = &state->screencast; struct screencast_context *ctx = &state->screencast;
struct pw_remote *remote = ctx->remote; struct pw_remote *remote = ctx->remote;
@ -146,7 +145,7 @@ static void pwr_handle_state_changed(void *data, enum pw_remote_state old,
switch (pwr_remote_state) { switch (pwr_remote_state) {
case PW_REMOTE_STATE_ERROR: case PW_REMOTE_STATE_ERROR:
logprint(INFO, "pipewire: remote state changed to \"%s\"", logprint(INFO, "pipewire: remote state changed to \"%s\"",
pw_remote_state_as_string(pwr_remote_state)); pw_remote_state_as_string(pwr_remote_state));
logprint(ERROR, "pipewire: remote error: %s", error); logprint(ERROR, "pipewire: remote error: %s", error);
pw_loop_leave(state->pw_loop); pw_loop_leave(state->pw_loop);
pw_loop_destroy(state->pw_loop); pw_loop_destroy(state->pw_loop);
@ -159,47 +158,47 @@ static void pwr_handle_state_changed(void *data, enum pw_remote_state old,
struct spa_pod_builder b = SPA_POD_BUILDER_INIT(buffer, sizeof(buffer)); struct spa_pod_builder b = SPA_POD_BUILDER_INIT(buffer, sizeof(buffer));
logprint(INFO, "pipewire: remote state changed to \"%s\"", logprint(INFO, "pipewire: remote state changed to \"%s\"",
pw_remote_state_as_string(pwr_remote_state)); pw_remote_state_as_string(pwr_remote_state));
ctx->stream = pw_stream_new( ctx->stream = pw_stream_new(
remote, "wlr_screeencopy", remote, "wlr_screeencopy",
pw_properties_new("media.class", "Video/Source", PW_NODE_PROP_MEDIA, pw_properties_new("media.class",
"Video", PW_NODE_PROP_CATEGORY, "Source", "Video/Source", PW_NODE_PROP_MEDIA,
PW_NODE_PROP_ROLE, "Screen", NULL)); "Video", PW_NODE_PROP_CATEGORY, "Source",
PW_NODE_PROP_ROLE, "Screen", NULL));
params[0] = spa_pod_builder_object( params[0] = spa_pod_builder_object(
&b, ctx->t->param.idEnumFormat, ctx->t->spa_format, "I", &b, ctx->t->param.idEnumFormat, ctx->t->spa_format, "I",
ctx->type.media_type.video, "I", ctx->type.media_subtype.raw, ":", ctx->type.media_type.video, "I", ctx->type.media_subtype.raw, ":",
ctx->type.format_video.format, "I", pipewire_from_wl_shm(ctx), ":", ctx->type.format_video.format, "I", pipewire_from_wl_shm(ctx), ":",
ctx->type.format_video.size, "Rru", ctx->type.format_video.size, "Rru",
&SPA_RECTANGLE(ctx->simple_frame.width, ctx->simple_frame.height), &SPA_RECTANGLE(ctx->simple_frame.width, ctx->simple_frame.height),
SPA_POD_PROP_MIN_MAX(&SPA_RECTANGLE(1, 1), &SPA_RECTANGLE(4096, 4096)), SPA_POD_PROP_MIN_MAX(&SPA_RECTANGLE(1, 1), &SPA_RECTANGLE(4096, 4096)),
":", ctx->type.format_video.framerate, "F", ":", ctx->type.format_video.framerate, "F",
// specify variable framerate // specify variable framerate
&SPA_FRACTION(0, 1), &SPA_FRACTION(0, 1),
":", ctx->type.format_video.max_framerate, "F", ":", ctx->type.format_video.max_framerate, "F",
// with a maximum at the wlroots specified hardware framerate // with a maximum at the wlroots specified hardware framerate
&SPA_FRACTION(ctx->framerate, 1)); &SPA_FRACTION(ctx->framerate, 1));
pw_stream_add_listener(ctx->stream, &ctx->stream_listener, pw_stream_add_listener(ctx->stream, &ctx->stream_listener,
&pwr_stream_events, ctx); &pwr_stream_events, ctx);
pw_stream_connect(ctx->stream, PW_DIRECTION_OUTPUT, NULL, pw_stream_connect(ctx->stream, PW_DIRECTION_OUTPUT, NULL,
PW_STREAM_FLAG_DRIVER | PW_STREAM_FLAG_MAP_BUFFERS, PW_STREAM_FLAG_DRIVER | PW_STREAM_FLAG_MAP_BUFFERS, params, 1);
params, 1);
break; break;
} }
default: default:
logprint(INFO, "pipewire: remote state changed to \"%s\"", logprint(INFO, "pipewire: remote state changed to \"%s\"",
pw_remote_state_as_string(pwr_remote_state)); pw_remote_state_as_string(pwr_remote_state));
break; break;
} }
} }
static const struct pw_remote_events pwr_remote_events = { static const struct pw_remote_events pwr_remote_events = {
PW_VERSION_REMOTE_EVENTS, PW_VERSION_REMOTE_EVENTS,
.state_changed = pwr_handle_state_changed, .state_changed = pwr_handle_state_changed,
}; };
void *pwr_start(struct xdpw_state *state) { void *pwr_start(struct xdpw_state *state) {
@ -216,10 +215,10 @@ void *pwr_start(struct xdpw_state *state) {
/* make an event to signal frame ready */ /* make an event to signal frame ready */
ctx->event = ctx->event =
pw_loop_add_event(state->pw_loop, pwr_on_event, state); pw_loop_add_event(state->pw_loop, pwr_on_event, state);
pw_remote_add_listener(ctx->remote, &ctx->remote_listener, &pwr_remote_events, pw_remote_add_listener(ctx->remote, &ctx->remote_listener, &pwr_remote_events,
state); state);
pw_remote_connect(ctx->remote); pw_remote_connect(ctx->remote);
return NULL; return NULL;

View file

@ -9,8 +9,8 @@ int setup_outputs(struct xdpw_state *state) {
struct wayland_output *output, *tmp_o; struct wayland_output *output, *tmp_o;
wl_list_for_each_reverse_safe(output, tmp_o, &ctx->output_list, link) { wl_list_for_each_reverse_safe(output, tmp_o, &ctx->output_list, link) {
logprint(INFO, "wlroots: capturable output: %s model: %s: id: %i name: %s", output->make, logprint(INFO, "wlroots: capturable output: %s model: %s: id: %i name: %s",
output->model, output->id, output->name); output->make, output->model, output->id, output->name);
} }
struct wayland_output *out; struct wayland_output *out;
@ -18,13 +18,13 @@ int setup_outputs(struct xdpw_state *state) {
out = wlr_output_find_by_name(&ctx->output_list, ctx->output_name); out = wlr_output_find_by_name(&ctx->output_list, ctx->output_name);
if (!out) { if (!out) {
logprint(ERROR, "wlroots: no such output"); logprint(ERROR, "wlroots: no such output");
exit(EXIT_FAILURE); abort();
} }
} else { } else {
out = wlr_output_first(&ctx->output_list); out = wlr_output_first(&ctx->output_list);
if (!out) { if (!out) {
logprint(ERROR, "wlroots: no output found"); logprint(ERROR, "wlroots: no output found");
exit(EXIT_FAILURE); abort();
} }
} }
@ -39,12 +39,10 @@ int setup_outputs(struct xdpw_state *state) {
} }
void *start_screencast(void *data){ void *start_screencast(void *data) {
struct xdpw_state *state = data; struct xdpw_state *state = data;
wlr_register_cb(state); wlr_register_cb(state);
// process at least one frame so that we know // process at least one frame so that we know
// some of the metadata required for the pipewire // some of the metadata required for the pipewire
// remote state connected event // remote state connected event
@ -54,7 +52,6 @@ void *start_screencast(void *data){
pwr_start(state); pwr_start(state);
return NULL; return NULL;
} }
static int method_screencast_create_session(sd_bus_message *msg, void *data, static int method_screencast_create_session(sd_bus_message *msg, void *data,
@ -80,13 +77,13 @@ static int method_screencast_create_session(sd_bus_message *msg, void *data,
char* key; char* key;
int innerRet = 0; int innerRet = 0;
while((ret = sd_bus_message_enter_container(msg, 'e', "sv")) > 0) { while ((ret = sd_bus_message_enter_container(msg, 'e', "sv")) > 0) {
innerRet = sd_bus_message_read(msg, "s", &key); innerRet = sd_bus_message_read(msg, "s", &key);
if (innerRet < 0) { if (innerRet < 0) {
return innerRet; return innerRet;
} }
if(strcmp(key, "session_handle_token") == 0) { if (strcmp(key, "session_handle_token") == 0) {
char* token; char* token;
sd_bus_message_read(msg, "v", "s", &token); sd_bus_message_read(msg, "v", "s", &token);
logprint(INFO, "dbus: option token: %s", token); logprint(INFO, "dbus: option token: %s", token);
@ -142,8 +139,8 @@ static int method_screencast_create_session(sd_bus_message *msg, void *data,
} }
static int method_screencast_select_sources(sd_bus_message *msg, void *data, sd_bus_error *ret_error) { static int method_screencast_select_sources(sd_bus_message *msg, void *data,
sd_bus_error *ret_error) {
struct xdpw_state *state = data; struct xdpw_state *state = data;
int ret = 0; int ret = 0;
@ -168,20 +165,20 @@ static int method_screencast_select_sources(sd_bus_message *msg, void *data, sd_
char* key; char* key;
int innerRet = 0; int innerRet = 0;
while((ret = sd_bus_message_enter_container(msg, 'e', "sv")) > 0) { while ((ret = sd_bus_message_enter_container(msg, 'e', "sv")) > 0) {
innerRet = sd_bus_message_read(msg, "s", &key); innerRet = sd_bus_message_read(msg, "s", &key);
if (innerRet < 0) { if (innerRet < 0) {
return innerRet; return innerRet;
} }
if(strcmp(key, "multiple") == 0) { if (strcmp(key, "multiple") == 0) {
bool multiple; bool multiple;
sd_bus_message_read(msg, "v", "b", &multiple); sd_bus_message_read(msg, "v", "b", &multiple);
logprint(INFO, "dbus: option multiple: %x", multiple); logprint(INFO, "dbus: option multiple: %x", multiple);
} else if(strcmp(key, "types") == 0) { } else if (strcmp(key, "types") == 0) {
uint32_t mask; uint32_t mask;
sd_bus_message_read(msg, "v", "u", &mask); sd_bus_message_read(msg, "v", "u", &mask);
logprint(INFO, "dbus: option types: %x", mask); logprint(INFO, "dbus: option types:%x", mask);
} else { } else {
logprint(WARN, "dbus: unknown option %s", key); logprint(WARN, "dbus: unknown option %s", key);
sd_bus_message_skip(msg, "v"); sd_bus_message_skip(msg, "v");
@ -219,8 +216,8 @@ static int method_screencast_select_sources(sd_bus_message *msg, void *data, sd_
return 0; return 0;
} }
static int method_screencast_start(sd_bus_message *msg, void *data, sd_bus_error *ret_error) { static int method_screencast_start(sd_bus_message *msg, void *data,
sd_bus_error *ret_error) {
struct xdpw_state *state = data; struct xdpw_state *state = data;
struct screencast_context *ctx = &state->screencast; struct screencast_context *ctx = &state->screencast;
@ -247,7 +244,7 @@ static int method_screencast_start(sd_bus_message *msg, void *data, sd_bus_error
char* key; char* key;
int innerRet = 0; int innerRet = 0;
while((ret = sd_bus_message_enter_container(msg, 'e', "sv")) > 0) { while ((ret = sd_bus_message_enter_container(msg, 'e', "sv")) > 0) {
innerRet = sd_bus_message_read(msg, "s", &key); innerRet = sd_bus_message_read(msg, "s", &key);
if (innerRet < 0) { if (innerRet < 0) {
return innerRet; return innerRet;
@ -275,19 +272,19 @@ static int method_screencast_start(sd_bus_message *msg, void *data, sd_bus_error
return ret; return ret;
} }
while(ctx->node_id == 0){ while (ctx->node_id == 0) {
int ret = pw_loop_iterate(state->pw_loop, 0); int ret = pw_loop_iterate(state->pw_loop, 0);
if (ret < 0) { if (ret < 0) {
logprint(ERROR, "pipewire_loop_iterate failed: %s", logprint(ERROR, "pipewire_loop_iterate failed: %s", spa_strerror(ret));
spa_strerror(ret));
} }
} }
ret = sd_bus_message_append(reply, "ua{sv}", PORTAL_RESPONSE_SUCCESS, 1, ret = sd_bus_message_append(reply, "ua{sv}", PORTAL_RESPONSE_SUCCESS, 1,
"streams", "a(ua{sv})", 1, "streams", "a(ua{sv})", 1,
ctx->node_id, 2, ctx->node_id, 2,
"position", "(ii)", 0, 0, "position", "(ii)", 0, 0,
"size", "(ii)", ctx->simple_frame.width, ctx->simple_frame.height); "size", "(ii)", ctx->simple_frame.width, ctx->simple_frame.height);
if (ret < 0) { if (ret < 0) {
return ret; return ret;
} }
@ -303,9 +300,12 @@ static int method_screencast_start(sd_bus_message *msg, void *data, sd_bus_error
static const sd_bus_vtable screencast_vtable[] = { static const sd_bus_vtable screencast_vtable[] = {
SD_BUS_VTABLE_START(0), SD_BUS_VTABLE_START(0),
SD_BUS_METHOD("CreateSession", "oosa{sv}", "ua{sv}", method_screencast_create_session, SD_BUS_VTABLE_UNPRIVILEGED), SD_BUS_METHOD("CreateSession", "oosa{sv}", "ua{sv}",
SD_BUS_METHOD("SelectSources", "oosa{sv}", "ua{sv}", method_screencast_select_sources, SD_BUS_VTABLE_UNPRIVILEGED), method_screencast_create_session, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("Start", "oossa{sv}", "ua{sv}", method_screencast_start, SD_BUS_VTABLE_UNPRIVILEGED), SD_BUS_METHOD("SelectSources", "oosa{sv}", "ua{sv}",
method_screencast_select_sources, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("Start", "oossa{sv}", "ua{sv}",
method_screencast_start, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_VTABLE_END SD_BUS_VTABLE_END
}; };
@ -316,11 +316,10 @@ int init_screencast(struct xdpw_state *state, const char *output_name, const cha
state->screencast = (struct screencast_context) { 0 }; state->screencast = (struct screencast_context) { 0 };
state->screencast.forced_pixelformat = forced_pixelformat; state->screencast.forced_pixelformat = forced_pixelformat;
state->screencast.output_name = output_name; state->screencast.output_name = output_name;
state->screencast.simple_frame = (struct simple_frame){0}; state->screencast.simple_frame = (struct simple_frame) { 0 };
state->screencast.simple_frame.damage = &(struct damage){0}; state->screencast.simple_frame.damage = &(struct damage) { 0 };
int err; int err = wlr_screencopy_init(state);
err = wlr_screencopy_init(state);
if (err) { if (err) {
goto end; goto end;
} }
@ -328,7 +327,7 @@ int init_screencast(struct xdpw_state *state, const char *output_name, const cha
return sd_bus_add_object_vtable(state->bus, &slot, object_path, interface_name, return sd_bus_add_object_vtable(state->bus, &slot, object_path, interface_name,
screencast_vtable, state); screencast_vtable, state);
end: end:
wlr_screencopy_uninit(&state->screencast); wlr_screencopy_uninit(&state->screencast);
return err; return err;
} }

View file

@ -1,45 +1,37 @@
#include "screencast_common.h" #include "screencast_common.h"
char *strdup(const char *src) {
char *dst = malloc(strlen(src) + 1); // Space for length plus nul
if (dst == NULL)
return NULL; // No memory
strcpy(dst, src); // Copy the characters
return dst; // Return the new string
}
uint32_t pipewire_from_wl_shm(void *data) { uint32_t pipewire_from_wl_shm(void *data) {
struct screencast_context *ctx = data; struct screencast_context *ctx = data;
if(ctx->forced_pixelformat){ if (ctx->forced_pixelformat) {
if(strcmp(ctx->forced_pixelformat, "BGRx") == 0) { if (strcmp(ctx->forced_pixelformat, "BGRx") == 0) {
return ctx->type.video_format.BGRx; return ctx->type.video_format.BGRx;
} }
if(strcmp(ctx->forced_pixelformat, "RGBx") == 0){ if (strcmp(ctx->forced_pixelformat, "RGBx") == 0) {
return ctx->type.video_format.RGBx; return ctx->type.video_format.RGBx;
} }
} }
switch (ctx->simple_frame.format) { switch (ctx->simple_frame.format) {
case WL_SHM_FORMAT_ARGB8888: case WL_SHM_FORMAT_ARGB8888:
return ctx->type.video_format.BGRA; return ctx->type.video_format.BGRA;
case WL_SHM_FORMAT_XRGB8888: case WL_SHM_FORMAT_XRGB8888:
return ctx->type.video_format.BGRx; return ctx->type.video_format.BGRx;
case WL_SHM_FORMAT_RGBA8888: case WL_SHM_FORMAT_RGBA8888:
return ctx->type.video_format.ABGR; return ctx->type.video_format.ABGR;
case WL_SHM_FORMAT_RGBX8888: case WL_SHM_FORMAT_RGBX8888:
return ctx->type.video_format.xBGR; return ctx->type.video_format.xBGR;
case WL_SHM_FORMAT_ABGR8888: case WL_SHM_FORMAT_ABGR8888:
return ctx->type.video_format.RGBA; return ctx->type.video_format.RGBA;
case WL_SHM_FORMAT_XBGR8888: case WL_SHM_FORMAT_XBGR8888:
return ctx->type.video_format.RGBx; return ctx->type.video_format.RGBx;
case WL_SHM_FORMAT_BGRA8888: case WL_SHM_FORMAT_BGRA8888:
return ctx->type.video_format.ARGB; return ctx->type.video_format.ARGB;
case WL_SHM_FORMAT_BGRX8888: case WL_SHM_FORMAT_BGRX8888:
return ctx->type.video_format.xRGB; return ctx->type.video_format.xRGB;
case WL_SHM_FORMAT_NV12: case WL_SHM_FORMAT_NV12:
return ctx->type.video_format.NV12; return ctx->type.video_format.NV12;
default: default:
exit(EXIT_FAILURE); abort();
} }
} }

View file

@ -1,23 +1,22 @@
#define _POSIX_C_SOURCE 200809L
#include "wlr_screencast.h" #include "wlr_screencast.h"
#include "xdpw.h" #include "xdpw.h"
void wlr_frame_free(struct xdpw_state *state) { void wlr_frame_free(struct xdpw_state *state) {
zwlr_screencopy_frame_v1_destroy(state->screencast.wlr_frame); zwlr_screencopy_frame_v1_destroy(state->screencast.wlr_frame);
munmap(state->screencast.simple_frame.data, state->screencast.simple_frame.size); munmap(state->screencast.simple_frame.data, state->screencast.simple_frame.size);
wl_buffer_destroy(state->screencast.simple_frame.buffer); wl_buffer_destroy(state->screencast.simple_frame.buffer);
logprint(TRACE, "wlroots: frame destroyed"); logprint(TRACE, "wlroots: frame destroyed");
if(!state->screencast.quit && !state->screencast.err){ if (!state->screencast.quit && !state->screencast.err) {
wlr_register_cb(state); wlr_register_cb(state);
} }
} }
static struct wl_buffer *create_shm_buffer(struct screencast_context *ctx, static struct wl_buffer *create_shm_buffer(struct screencast_context *ctx,
enum wl_shm_format fmt, int width, enum wl_shm_format fmt, int width, int height, int stride,
int height, int stride, void **data_out) {
void **data_out) {
int size = stride * height; int size = stride * height;
const char shm_name[] = "/wlroots-screencopy"; const char shm_name[] = "/wlroots-screencopy";
@ -47,7 +46,7 @@ static struct wl_buffer *create_shm_buffer(struct screencast_context *ctx,
struct wl_shm_pool *pool = wl_shm_create_pool(ctx->shm, fd, size); struct wl_shm_pool *pool = wl_shm_create_pool(ctx->shm, fd, size);
close(fd); close(fd);
struct wl_buffer *buffer = struct wl_buffer *buffer =
wl_shm_pool_create_buffer(pool, 0, width, height, stride, fmt); wl_shm_pool_create_buffer(pool, 0, width, height, stride, fmt);
wl_shm_pool_destroy(pool); wl_shm_pool_destroy(pool);
*data_out = data; *data_out = data;
@ -55,8 +54,7 @@ static struct wl_buffer *create_shm_buffer(struct screencast_context *ctx,
} }
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 format, uint32_t width, uint32_t height, uint32_t stride) {
uint32_t stride) {
struct xdpw_state *state = data; struct xdpw_state *state = data;
struct screencast_context *ctx = &state->screencast; struct screencast_context *ctx = &state->screencast;
@ -68,10 +66,11 @@ static void wlr_frame_buffer(void *data, struct zwlr_screencopy_frame_v1 *frame,
ctx->simple_frame.size = stride * height; ctx->simple_frame.size = stride * height;
ctx->simple_frame.format = format; ctx->simple_frame.format = format;
ctx->simple_frame.buffer = create_shm_buffer(ctx, format, width, height, ctx->simple_frame.buffer = create_shm_buffer(ctx, format, width, height,
stride, &ctx->simple_frame.data); stride, &ctx->simple_frame.data);
if (ctx->simple_frame.buffer == NULL) { if (ctx->simple_frame.buffer == NULL) {
logprint(ERROR, "wlroots: failed to create buffer"); logprint(ERROR, "wlroots: failed to create buffer");
exit(EXIT_FAILURE); abort();
} }
zwlr_screencopy_frame_v1_copy_with_damage(frame, ctx->simple_frame.buffer); zwlr_screencopy_frame_v1_copy_with_damage(frame, ctx->simple_frame.buffer);
@ -79,18 +78,16 @@ static void wlr_frame_buffer(void *data, struct zwlr_screencopy_frame_v1 *frame,
} }
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_state *state = data; struct xdpw_state *state = data;
struct screencast_context *ctx = &state->screencast; struct screencast_context *ctx = &state->screencast;
logprint(TRACE, "wlroots: flags event handler"); logprint(TRACE, "wlroots: flags event handler");
ctx->simple_frame.y_invert = flags & ZWLR_SCREENCOPY_FRAME_V1_FLAGS_Y_INVERT; ctx->simple_frame.y_invert = flags & ZWLR_SCREENCOPY_FRAME_V1_FLAGS_Y_INVERT;
} }
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_sec_hi, uint32_t tv_sec_lo, uint32_t tv_nsec) {
uint32_t tv_nsec) {
struct xdpw_state *state = data; struct xdpw_state *state = data;
struct screencast_context *ctx = &state->screencast; struct screencast_context *ctx = &state->screencast;
@ -105,11 +102,10 @@ static void wlr_frame_ready(void *data, struct zwlr_screencopy_frame_v1 *frame,
} }
wlr_frame_free(state); wlr_frame_free(state);
} }
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_state *state = data; struct xdpw_state *state = data;
struct screencast_context *ctx = &state->screencast; struct screencast_context *ctx = &state->screencast;
@ -120,8 +116,7 @@ static void wlr_frame_failed(void *data,
} }
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 x, uint32_t y, uint32_t width, uint32_t height) {
uint32_t height) {
struct xdpw_state *state = data; struct xdpw_state *state = data;
struct screencast_context *ctx = &state->screencast; struct screencast_context *ctx = &state->screencast;
@ -134,38 +129,34 @@ static void wlr_frame_damage(void *data, struct zwlr_screencopy_frame_v1 *frame,
} }
static const struct zwlr_screencopy_frame_v1_listener wlr_frame_listener = { static const struct zwlr_screencopy_frame_v1_listener wlr_frame_listener = {
.buffer = wlr_frame_buffer, .buffer = wlr_frame_buffer,
.flags = wlr_frame_flags, .flags = wlr_frame_flags,
.ready = wlr_frame_ready, .ready = wlr_frame_ready,
.failed = wlr_frame_failed, .failed = wlr_frame_failed,
.damage = wlr_frame_damage, .damage = wlr_frame_damage,
}; };
void wlr_register_cb(struct xdpw_state *state) { void wlr_register_cb(struct xdpw_state *state) {
struct screencast_context *ctx = &state->screencast; struct screencast_context *ctx = &state->screencast;
ctx->frame_callback = zwlr_screencopy_manager_v1_capture_output( ctx->frame_callback = zwlr_screencopy_manager_v1_capture_output(
ctx->screencopy_manager, ctx->with_cursor, ctx->target_output->output); ctx->screencopy_manager, ctx->with_cursor, ctx->target_output->output);
zwlr_screencopy_frame_v1_add_listener(ctx->frame_callback, zwlr_screencopy_frame_v1_add_listener(ctx->frame_callback,
&wlr_frame_listener, state); &wlr_frame_listener, state);
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 x, int32_t y, int32_t phys_width, int32_t phys_height,
int32_t phys_height, int32_t subpixel, int32_t subpixel, const char *make, const char *model, int32_t transform) {
const char *make, const char *model,
int32_t transform) {
struct wayland_output *output = data; struct wayland_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, uint32_t flags, int32_t width, int32_t height, int32_t refresh) {
int32_t height, int32_t refresh) {
if (flags & WL_OUTPUT_MODE_CURRENT) { if (flags & WL_OUTPUT_MODE_CURRENT) {
struct wayland_output *output = data; struct wayland_output *output = data;
output->framerate = (float)refresh/1000; output->framerate = (float)refresh/1000;
@ -177,49 +168,49 @@ static void wlr_output_handle_done(void *data, struct wl_output *wl_output) {
} }
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) { int32_t factor) {
/* Nothing to do */ /* Nothing to do */
} }
static const struct wl_output_listener wlr_output_listener = { static const struct wl_output_listener wlr_output_listener = {
.geometry = wlr_output_handle_geometry, .geometry = wlr_output_handle_geometry,
.mode = wlr_output_handle_mode, .mode = wlr_output_handle_mode,
.done = wlr_output_handle_done, .done = wlr_output_handle_done,
.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 wayland_output *output = data; struct wayland_output *output = data;
output->name = strdup(name); output->name = strdup(name);
}; };
static void dontcare(){ static void noop() {
// This space intentionally left blank
} }
static const struct zxdg_output_v1_listener wlr_xdg_output_listener = { static const struct zxdg_output_v1_listener wlr_xdg_output_listener = {
.logical_position = dontcare, .logical_position = noop,
.logical_size = dontcare, .logical_size = noop,
.done = NULL, /* Deprecated */ .done = NULL, /* Deprecated */
.description = dontcare, .description = noop,
.name = wlr_xdg_output_name, .name = wlr_xdg_output_name,
}; };
void wlr_add_xdg_output_listener(struct wayland_output *output, void wlr_add_xdg_output_listener(struct wayland_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_outputs(struct screencast_context *ctx){ static void wlr_init_xdg_outputs(struct screencast_context *ctx) {
struct wayland_output *output, *tmp; struct wayland_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) {
struct zxdg_output_v1 *xdg_output = struct zxdg_output_v1 *xdg_output =
zxdg_output_manager_v1_get_xdg_output( zxdg_output_manager_v1_get_xdg_output( ctx->xdg_output_manager,
ctx->xdg_output_manager, output->output); output->output);
wlr_add_xdg_output_listener(output, xdg_output); wlr_add_xdg_output_listener(output, xdg_output);
} }
} }
@ -232,19 +223,19 @@ struct wayland_output *wlr_output_first(struct wl_list *output_list) {
return NULL; return NULL;
} }
struct wayland_output *wlr_output_find_by_name(struct wl_list *output_list, const char* name) { struct wayland_output *wlr_output_find_by_name(struct wl_list *output_list,
const char* name) {
struct wayland_output *output, *tmp; struct wayland_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) {
return output; return output;
} }
} }
return NULL; return NULL;
} }
struct wayland_output *wlr_output_find(struct screencast_context *ctx, struct wayland_output *wlr_output_find(struct screencast_context *ctx,
struct wl_output *out, uint32_t id) { struct wl_output *out, uint32_t id) {
struct wayland_output *output, *tmp; struct wayland_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)) {
@ -259,8 +250,7 @@ static void wlr_remove_output(struct wayland_output *out) {
} }
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 id, const char *interface, uint32_t ver) {
uint32_t ver) {
struct screencast_context *ctx = data; struct screencast_context *ctx = data;
if (!strcmp(interface, wl_output_interface.name)) { if (!strcmp(interface, wl_output_interface.name)) {
@ -275,7 +265,7 @@ static void wlr_registry_handle_add(void *data, struct wl_registry *reg,
if (!strcmp(interface, zwlr_screencopy_manager_v1_interface.name)) { if (!strcmp(interface, zwlr_screencopy_manager_v1_interface.name)) {
ctx->screencopy_manager = wl_registry_bind( ctx->screencopy_manager = wl_registry_bind(
reg, id, &zwlr_screencopy_manager_v1_interface, SC_MANAGER_VERSION); reg, id, &zwlr_screencopy_manager_v1_interface, SC_MANAGER_VERSION);
} }
if (strcmp(interface, wl_shm_interface.name) == 0) { if (strcmp(interface, wl_shm_interface.name) == 0) {
@ -289,14 +279,14 @@ 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) {
wlr_remove_output( wlr_remove_output(
wlr_output_find((struct screencast_context *)data, NULL, id)); wlr_output_find((struct screencast_context *)data, NULL, id));
} }
static const struct wl_registry_listener wlr_registry_listener = { static const struct wl_registry_listener wlr_registry_listener = {
.global = wlr_registry_handle_add, .global = wlr_registry_handle_add,
.global_remove = wlr_registry_handle_remove, .global_remove = wlr_registry_handle_remove,
}; };
int wlr_screencopy_init(struct xdpw_state *state) { int wlr_screencopy_init(struct xdpw_state *state) {
@ -330,7 +320,7 @@ int 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;
} }