Add gamescope-pipewire protocol
Can be tested with: https://git.sr.ht/~emersion/gamescope-pipewire-demo
This commit is contained in:
parent
59e8aa4abc
commit
ef64b60cec
6 changed files with 126 additions and 32 deletions
|
|
@ -73,8 +73,6 @@ add_project_arguments(
|
|||
language: 'cpp',
|
||||
)
|
||||
|
||||
subdir('protocol')
|
||||
|
||||
src = [
|
||||
'src/steamcompmgr.cpp',
|
||||
'src/main.cpp',
|
||||
|
|
@ -86,13 +84,14 @@ src = [
|
|||
'src/rendervulkan.cpp',
|
||||
'src/log.cpp',
|
||||
spirv_shader,
|
||||
gamescope_xwayland_proto_files,
|
||||
]
|
||||
|
||||
if pipewire_dep.found()
|
||||
src += 'src/pipewire.cpp'
|
||||
endif
|
||||
|
||||
subdir('protocol')
|
||||
|
||||
executable(
|
||||
'gamescope',
|
||||
src,
|
||||
|
|
|
|||
45
protocol/gamescope-pipewire.xml
Normal file
45
protocol/gamescope-pipewire.xml
Normal file
|
|
@ -0,0 +1,45 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<protocol name="gamescope_pipewire">
|
||||
<copyright>
|
||||
Copyright © 2021 Valve Corporation
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a
|
||||
copy of this software and associated documentation files (the "Software"),
|
||||
to deal in the Software without restriction, including without limitation
|
||||
the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
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:
|
||||
|
||||
The above copyright notice and this permission notice (including the next
|
||||
paragraph) shall be included in all copies or substantial portions of the
|
||||
Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
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
|
||||
DEALINGS IN THE SOFTWARE.
|
||||
</copyright>
|
||||
|
||||
<description summary="gamescope-specific PipeWire protocol">
|
||||
This is a private Gamescope protocol. Regular Wayland clients must not use
|
||||
it.
|
||||
</description>
|
||||
|
||||
<interface name="gamescope_pipewire" version="1">
|
||||
<request name="destroy" type="destructor"></request>
|
||||
|
||||
<event name="stream_node">
|
||||
<description summary="pipewire stream node advertisement">
|
||||
This event advertises a PipeWire stream node identifier suitable for
|
||||
capturing the main output.
|
||||
|
||||
A roundtrip after binding to the gamescope_pipewire global ensures this
|
||||
event has been received.
|
||||
</description>
|
||||
<arg name="node_id" type="uint" summary="PipeWire stream node ID"/>
|
||||
</event>
|
||||
</interface>
|
||||
</protocol>
|
||||
|
|
@ -2,18 +2,25 @@ wayland_scanner_dep = dependency('wayland-scanner', native: true)
|
|||
wayland_scanner_path = wayland_scanner_dep.get_variable(pkgconfig: 'wayland_scanner')
|
||||
wayland_scanner = find_program(wayland_scanner_path, native: true)
|
||||
|
||||
code = custom_target(
|
||||
'gamescope-xwayland-protocol.c',
|
||||
input: 'gamescope-xwayland.xml',
|
||||
output: '@BASENAME@-protocol.c',
|
||||
command: [wayland_scanner, 'private-code', '@INPUT@', '@OUTPUT@'],
|
||||
)
|
||||
protocols = [
|
||||
'gamescope-xwayland',
|
||||
'gamescope-pipewire',
|
||||
]
|
||||
|
||||
server_header = custom_target(
|
||||
'gamescope-xwayland-protocol.h',
|
||||
input: 'gamescope-xwayland.xml',
|
||||
output: '@BASENAME@-protocol.h',
|
||||
command: [wayland_scanner, 'server-header', '@INPUT@', '@OUTPUT@'],
|
||||
)
|
||||
foreach name : protocols
|
||||
code = custom_target(
|
||||
name + '-protocol.c',
|
||||
input: name + '.xml',
|
||||
output: '@BASENAME@-protocol.c',
|
||||
command: [wayland_scanner, 'private-code', '@INPUT@', '@OUTPUT@'],
|
||||
)
|
||||
|
||||
gamescope_xwayland_proto_files = [code, server_header]
|
||||
server_header = custom_target(
|
||||
name + '-protocol.h',
|
||||
input: name + '.xml',
|
||||
output: '@BASENAME@-protocol.h',
|
||||
command: [wayland_scanner, 'server-header', '@INPUT@', '@OUTPUT@'],
|
||||
)
|
||||
|
||||
src += [code, server_header]
|
||||
endforeach
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@
|
|||
#include "main.hpp"
|
||||
#include "pipewire.hpp"
|
||||
|
||||
static struct pipewire_state pipewire_state = { .stream_node_id = SPA_ID_INVALID };
|
||||
static int nudgePipe[2] = { -1, -1 };
|
||||
|
||||
static std::atomic<struct pipewire_buffer *> out_buffer;
|
||||
|
|
@ -246,44 +247,44 @@ static void run_pipewire(struct pipewire_state *state)
|
|||
|
||||
bool init_pipewire(void)
|
||||
{
|
||||
pw_init(nullptr, nullptr);
|
||||
struct pipewire_state *state = &pipewire_state;
|
||||
|
||||
static struct pipewire_state state = { .stream_node_id = SPA_ID_INVALID };
|
||||
pw_init(nullptr, nullptr);
|
||||
|
||||
if (pipe2(nudgePipe, O_CLOEXEC | O_NONBLOCK) != 0) {
|
||||
perror("pipewire: pipe2 failed");
|
||||
return false;
|
||||
}
|
||||
|
||||
state.loop = pw_loop_new(nullptr);
|
||||
if (!state.loop) {
|
||||
state->loop = pw_loop_new(nullptr);
|
||||
if (!state->loop) {
|
||||
fprintf(stderr, "pipewire: pw_loop_new failed\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
state.context = pw_context_new(state.loop, nullptr, 0);
|
||||
if (!state.context) {
|
||||
state->context = pw_context_new(state->loop, nullptr, 0);
|
||||
if (!state->context) {
|
||||
fprintf(stderr, "pipewire: pw_context_new failed\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
state.core = pw_context_connect(state.context, nullptr, 0);
|
||||
if (!state.core) {
|
||||
state->core = pw_context_connect(state->context, nullptr, 0);
|
||||
if (!state->core) {
|
||||
fprintf(stderr, "pipewire: pw_context_connect failed\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
state.stream = pw_stream_new(state.core, "gamescope",
|
||||
state->stream = pw_stream_new(state->core, "gamescope",
|
||||
pw_properties_new(
|
||||
PW_KEY_MEDIA_CLASS, "Video/Source",
|
||||
nullptr));
|
||||
if (!state.stream) {
|
||||
if (!state->stream) {
|
||||
fprintf(stderr, "pipewire: pw_stream_new failed\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
static struct spa_hook stream_hook;
|
||||
pw_stream_add_listener(state.stream, &stream_hook, &stream_events, &state);
|
||||
pw_stream_add_listener(state->stream, &stream_hook, &stream_events, state);
|
||||
|
||||
uint8_t buf[1024];
|
||||
struct spa_pod_builder builder = SPA_POD_BUILDER_INIT(buf, sizeof(buf));
|
||||
|
|
@ -291,28 +292,33 @@ bool init_pipewire(void)
|
|||
|
||||
// TODO: PW_STREAM_FLAG_ALLOC_BUFFERS
|
||||
enum pw_stream_flags flags = (enum pw_stream_flags)(PW_STREAM_FLAG_DRIVER | PW_STREAM_FLAG_MAP_BUFFERS);
|
||||
int ret = pw_stream_connect(state.stream, PW_DIRECTION_OUTPUT, PW_ID_ANY, flags, &format_param, 1);
|
||||
int ret = pw_stream_connect(state->stream, PW_DIRECTION_OUTPUT, PW_ID_ANY, flags, &format_param, 1);
|
||||
if (ret != 0) {
|
||||
fprintf(stderr, "pipewire: pw_stream_connect failed\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
while (state.stream_node_id == SPA_ID_INVALID) {
|
||||
int ret = pw_loop_iterate(state.loop, -1);
|
||||
while (state->stream_node_id == SPA_ID_INVALID) {
|
||||
int ret = pw_loop_iterate(state->loop, -1);
|
||||
if (ret < 0) {
|
||||
fprintf(stderr, "pipewire: pw_loop_iterate failed\n");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
fprintf(stderr, "pipewire: stream available on node ID: %u\n", state.stream_node_id);
|
||||
fprintf(stderr, "pipewire: stream available on node ID: %u\n", state->stream_node_id);
|
||||
|
||||
std::thread thread(run_pipewire, &state);
|
||||
std::thread thread(run_pipewire, state);
|
||||
thread.detach();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
uint32_t get_pipewire_stream_node_id(void)
|
||||
{
|
||||
return pipewire_state.stream_node_id;
|
||||
}
|
||||
|
||||
struct pipewire_buffer *dequeue_pipewire_buffer(void)
|
||||
{
|
||||
return out_buffer.exchange(nullptr);
|
||||
|
|
|
|||
|
|
@ -26,6 +26,7 @@ struct pipewire_buffer {
|
|||
};
|
||||
|
||||
bool init_pipewire(void);
|
||||
uint32_t get_pipewire_stream_node_id(void);
|
||||
struct pipewire_buffer *dequeue_pipewire_buffer(void);
|
||||
void push_pipewire_buffer(struct pipewire_buffer *buffer);
|
||||
void nudge_pipewire(void);
|
||||
|
|
|
|||
|
|
@ -39,12 +39,17 @@ extern "C" {
|
|||
}
|
||||
|
||||
#include "gamescope-xwayland-protocol.h"
|
||||
#include "gamescope-pipewire-protocol.h"
|
||||
|
||||
#include "wlserver.hpp"
|
||||
#include "drm.hpp"
|
||||
#include "main.hpp"
|
||||
#include "steamcompmgr.hpp"
|
||||
|
||||
#if HAVE_PIPEWIRE
|
||||
#include "pipewire.hpp"
|
||||
#endif
|
||||
|
||||
#include "gpuvis_trace_utils.h"
|
||||
|
||||
static struct wlserver_t wlserver = {};
|
||||
|
|
@ -474,6 +479,33 @@ static void create_gamescope_xwayland( void )
|
|||
wl_global_create( wlserver.display, &gamescope_xwayland_interface, version, NULL, gamescope_xwayland_bind );
|
||||
}
|
||||
|
||||
static void gamescope_pipewire_handle_destroy( struct wl_client *client, struct wl_resource *resource )
|
||||
{
|
||||
wl_resource_destroy( resource );
|
||||
}
|
||||
|
||||
static const struct gamescope_pipewire_interface gamescope_pipewire_impl = {
|
||||
.destroy = gamescope_pipewire_handle_destroy,
|
||||
};
|
||||
|
||||
static void gamescope_pipewire_bind( struct wl_client *client, void *data, uint32_t version, uint32_t id )
|
||||
{
|
||||
struct wl_resource *resource = wl_resource_create( client, &gamescope_pipewire_interface, version, id );
|
||||
wl_resource_set_implementation( resource, &gamescope_pipewire_impl, NULL, NULL );
|
||||
|
||||
#if HAVE_PIPEWIRE
|
||||
gamescope_pipewire_send_stream_node( resource, get_pipewire_stream_node_id() );
|
||||
#else
|
||||
assert( 0 ); // unreachable
|
||||
#endif
|
||||
}
|
||||
|
||||
static void create_gamescope_pipewire( void )
|
||||
{
|
||||
uint32_t version = 1;
|
||||
wl_global_create( wlserver.display, &gamescope_pipewire_interface, version, NULL, gamescope_pipewire_bind );
|
||||
}
|
||||
|
||||
static void handle_session_active( struct wl_listener *listener, void *data )
|
||||
{
|
||||
if (wlserver.wlr.session->active) {
|
||||
|
|
@ -612,6 +644,10 @@ int wlserver_init(int argc, char **argv, bool bIsNested) {
|
|||
|
||||
create_gamescope_xwayland();
|
||||
|
||||
#if HAVE_PIPEWIRE
|
||||
create_gamescope_pipewire();
|
||||
#endif
|
||||
|
||||
int result = -1;
|
||||
int display_slot = 0;
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue