aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authordec05eba <dec05eba@protonmail.com>2025-05-10 17:10:59 +0200
committerdec05eba <dec05eba@protonmail.com>2025-05-10 17:10:59 +0200
commite3225bc62835c6accc18787c6038fc1dce2484fc (patch)
tree56c22deacd2a1101ad82ecc80ad300896e6c872f
parent811a14481dbd75fe2c2a072517e06add336fee4f (diff)
Move dbus code to a separate process to allow gpu-screen-recorder to use cap_sys_nice for better recording performance on amd
-rw-r--r--dbus/client/dbus_client.c265
-rw-r--r--dbus/client/dbus_client.h36
-rw-r--r--dbus/dbus_impl.c (renamed from src/dbus.c)24
-rw-r--r--dbus/dbus_impl.h (renamed from include/dbus.h)14
-rw-r--r--dbus/portal.h17
-rw-r--r--dbus/protocol.h86
-rw-r--r--dbus/server/dbus_server.c175
-rwxr-xr-xextra/meson_post_install.sh11
-rw-r--r--kms/client/kms_client.c1
-rw-r--r--kms/server/kms_server.c3
-rw-r--r--meson.build7
-rw-r--r--meson_options.txt2
-rw-r--r--src/capture/portal.c27
-rw-r--r--src/egl.c2
-rw-r--r--src/main.cpp15
15 files changed, 632 insertions, 53 deletions
diff --git a/dbus/client/dbus_client.c b/dbus/client/dbus_client.c
new file mode 100644
index 0000000..2fc9511
--- /dev/null
+++ b/dbus/client/dbus_client.c
@@ -0,0 +1,265 @@
+#include "dbus_client.h"
+#include "../protocol.h"
+
+#include <sys/socket.h>
+#include <sys/wait.h>
+#include <unistd.h>
+#include <poll.h>
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <errno.h>
+
+// TODO: Error checking for write/read
+
+static bool gsr_dbus_client_wait_for_startup(gsr_dbus_client *self) {
+ struct pollfd poll_fd = {
+ .fd = self->socket_pair[0],
+ .events = POLLIN,
+ .revents = 0
+ };
+ for(;;) {
+ int poll_res = poll(&poll_fd, 1, 100);
+ if(poll_res > 0 && (poll_fd.revents & POLLIN)) {
+ char msg;
+ read(self->socket_pair[0], &msg, 1);
+ return true;
+ } else {
+ int status = 0;
+ int wait_result = waitpid(self->pid, &status, WNOHANG);
+ if(wait_result != 0) {
+ int exit_code = -1;
+ if(WIFEXITED(status))
+ exit_code = WEXITSTATUS(status);
+ fprintf(stderr, "gsr error: gsr_dbus_client_init: server side or never started, exit code: %d\n", exit_code);
+ self->pid = 0;
+ return false;
+ }
+ }
+ }
+}
+
+bool gsr_dbus_client_init(gsr_dbus_client *self, const char *screencast_restore_token) {
+ memset(self, 0, sizeof(*self));
+
+ if(socketpair(AF_UNIX, SOCK_STREAM, 0, self->socket_pair) == -1) {
+ fprintf(stderr, "gsr error: gsr_dbus_client_init: socketpair failed, error: %s\n", strerror(errno));
+ return false;
+ }
+
+ if(screencast_restore_token) {
+ self->screencast_restore_token = strdup(screencast_restore_token);
+ if(!self->screencast_restore_token) {
+ fprintf(stderr, "gsr error: gsr_dbus_client_init: failed to clone restore token\n");
+ gsr_dbus_client_deinit(self);
+ return false;
+ }
+ }
+
+ self->pid = fork();
+ if(self->pid == -1) {
+ fprintf(stderr, "gsr error: gsr_dbus_client_init: failed to fork process\n");
+ gsr_dbus_client_deinit(self);
+ return false;
+ } else if(self->pid == 0) { /* child */
+ char socket_pair_server_str[32];
+ snprintf(socket_pair_server_str, sizeof(socket_pair_server_str), "%d", self->socket_pair[1]);
+
+ const char *args[] = { "gsr-dbus-server", socket_pair_server_str, self->screencast_restore_token ? self->screencast_restore_token : "", NULL };
+ execvp(args[0], (char *const*)args);
+
+ fprintf(stderr, "gsr error: gsr_dbus_client_init: execvp failed, error: %s\n", strerror(errno));
+ _exit(127);
+ } else { /* parent */
+ if(!gsr_dbus_client_wait_for_startup(self)) {
+ gsr_dbus_client_deinit(self);
+ return false;
+ }
+ }
+
+ return true;
+}
+
+void gsr_dbus_client_deinit(gsr_dbus_client *self) {
+ for(int i = 0; i < 2; ++i) {
+ if(self->socket_pair[i] > 0) {
+ close(self->socket_pair[i]);
+ self->socket_pair[i] = -1;
+ }
+ }
+
+ if(self->screencast_restore_token) {
+ free(self->screencast_restore_token);
+ self->screencast_restore_token = NULL;
+ }
+
+ if(self->pid > 0) {
+ kill(self->pid, SIGKILL);
+ int status = 0;
+ waitpid(self->pid, &status, 0);
+ self->pid = 0;
+ }
+}
+
+int gsr_dbus_client_screencast_create_session(gsr_dbus_client *self, char *session_handle, size_t session_handle_size) {
+ const gsr_dbus_request_message request = {
+ .protocol_version = GSR_DBUS_PROTOCOL_VERSION,
+ .type = GSR_DBUS_MESSAGE_REQ_CREATE_SESSION,
+ .create_session = (gsr_dbus_message_req_create_session) {}
+ };
+ write(self->socket_pair[0], &request, sizeof(request));
+
+ gsr_dbus_response_message response = {0};
+ read(self->socket_pair[0], &response, sizeof(response));
+
+ if(response.protocol_version != GSR_DBUS_PROTOCOL_VERSION) {
+ fprintf(stderr, "gsr error: gsr_dbus_client_screencast_create_session: server uses protocol version %d while the client is using protocol version %d", response.protocol_version, GSR_DBUS_PROTOCOL_VERSION);
+ return -1;
+ }
+
+ if(response.type == GSR_DBUS_MESSAGE_RESP_ERROR) {
+ fprintf(stderr, "gsr error: gsr_dbus_client_screencast_create_session: server return error: %s (%d)\n", response.error.message, (int)response.error.error_code);
+ return response.error.error_code;
+ }
+
+ if(response.type != GSR_DBUS_MESSAGE_RESP_CREATE_SESSION) {
+ fprintf(stderr, "gsr error: gsr_dbus_client_screencast_create_session: received incorrect response type. Expected %d got %d\n", GSR_DBUS_MESSAGE_RESP_CREATE_SESSION, response.type);
+ return -1;
+ }
+
+ snprintf(session_handle, session_handle_size, "%s", response.create_session.session_handle);
+ return 0;
+}
+
+int gsr_dbus_client_screencast_select_sources(gsr_dbus_client *self, const char *session_handle, uint32_t capture_type, uint32_t cursor_mode) {
+ gsr_dbus_request_message request = {
+ .protocol_version = GSR_DBUS_PROTOCOL_VERSION,
+ .type = GSR_DBUS_MESSAGE_REQ_SELECT_SOURCES,
+ .select_sources = (gsr_dbus_message_req_select_sources) {
+ .capture_type = capture_type,
+ .cursor_mode = cursor_mode
+ }
+ };
+ snprintf(request.select_sources.session_handle, sizeof(request.select_sources.session_handle), "%s", session_handle);
+ write(self->socket_pair[0], &request, sizeof(request));
+
+ gsr_dbus_response_message response = {0};
+ read(self->socket_pair[0], &response, sizeof(response));
+
+ if(response.protocol_version != GSR_DBUS_PROTOCOL_VERSION) {
+ fprintf(stderr, "gsr error: gsr_dbus_client_screencast_select_sources: server uses protocol version %d while the client is using protocol version %d", response.protocol_version, GSR_DBUS_PROTOCOL_VERSION);
+ return -1;
+ }
+
+ if(response.type == GSR_DBUS_MESSAGE_RESP_ERROR) {
+ fprintf(stderr, "gsr error: gsr_dbus_client_screencast_select_sources: server return error: %s (%d)\n", response.error.message, (int)response.error.error_code);
+ return response.error.error_code;
+ }
+
+ if(response.type != GSR_DBUS_MESSAGE_RESP_SELECT_SOURCES) {
+ fprintf(stderr, "gsr error: gsr_dbus_client_screencast_select_sources: received incorrect response type. Expected %d got %d\n", GSR_DBUS_MESSAGE_RESP_SELECT_SOURCES, response.type);
+ return -1;
+ }
+
+ return 0;
+}
+
+int gsr_dbus_client_screencast_start(gsr_dbus_client *self, const char *session_handle, uint32_t *pipewire_node) {
+ *pipewire_node = 0;
+
+ gsr_dbus_request_message request = {
+ .protocol_version = GSR_DBUS_PROTOCOL_VERSION,
+ .type = GSR_DBUS_MESSAGE_REQ_START,
+ .start = (gsr_dbus_message_req_start) {}
+ };
+ snprintf(request.start.session_handle, sizeof(request.start.session_handle), "%s", session_handle);
+ write(self->socket_pair[0], &request, sizeof(request));
+
+ gsr_dbus_response_message response = {0};
+ read(self->socket_pair[0], &response, sizeof(response));
+
+ if(response.protocol_version != GSR_DBUS_PROTOCOL_VERSION) {
+ fprintf(stderr, "gsr error: gsr_dbus_client_screencast_start: server uses protocol version %d while the client is using protocol version %d", response.protocol_version, GSR_DBUS_PROTOCOL_VERSION);
+ return -1;
+ }
+
+ if(response.type == GSR_DBUS_MESSAGE_RESP_ERROR) {
+ fprintf(stderr, "gsr error: gsr_dbus_client_screencast_start: server return error: %s (%d)\n", response.error.message, (int)response.error.error_code);
+ return response.error.error_code;
+ }
+
+ if(response.type != GSR_DBUS_MESSAGE_RESP_START) {
+ fprintf(stderr, "gsr error: gsr_dbus_client_screencast_start: received incorrect response type. Expected %d got %d\n", GSR_DBUS_MESSAGE_RESP_START, response.type);
+ return -1;
+ }
+
+ if(self->screencast_restore_token) {
+ free(self->screencast_restore_token);
+ if(response.start.restore_token[0] == '\0')
+ self->screencast_restore_token = NULL;
+ else
+ self->screencast_restore_token = strdup(response.start.restore_token);
+ }
+
+ *pipewire_node = response.start.pipewire_node;
+ return 0;
+}
+
+bool gsr_dbus_client_screencast_open_pipewire_remote(gsr_dbus_client *self, const char *session_handle, int *pipewire_fd) {
+ *pipewire_fd = 0;
+
+ gsr_dbus_request_message request = {
+ .protocol_version = GSR_DBUS_PROTOCOL_VERSION,
+ .type = GSR_DBUS_MESSAGE_REQ_OPEN_PIPEWIRE_REMOTE,
+ .open_pipewire_remote = (gsr_dbus_message_req_open_pipewire_remote) {}
+ };
+ snprintf(request.open_pipewire_remote.session_handle, sizeof(request.open_pipewire_remote.session_handle), "%s", session_handle);
+ write(self->socket_pair[0], &request, sizeof(request));
+
+ gsr_dbus_response_message response = {0};
+ struct iovec iov = {
+ .iov_base = &response,
+ .iov_len = sizeof(response)
+ };
+
+ char msg_control[CMSG_SPACE(sizeof(int))];
+
+ struct msghdr message = {
+ .msg_iov = &iov,
+ .msg_iovlen = 1,
+ .msg_control = msg_control,
+ .msg_controllen = sizeof(msg_control)
+ };
+
+ const int bla = recvmsg(self->socket_pair[0], &message, MSG_WAITALL);
+ (void)bla;
+
+ if(response.protocol_version != GSR_DBUS_PROTOCOL_VERSION) {
+ fprintf(stderr, "gsr error: gsr_dbus_client_screencast_open_pipewire_remote: server uses protocol version %d while the client is using protocol version %d", response.protocol_version, GSR_DBUS_PROTOCOL_VERSION);
+ return false;
+ }
+
+ if(response.type == GSR_DBUS_MESSAGE_RESP_ERROR) {
+ fprintf(stderr, "gsr error: gsr_dbus_client_screencast_open_pipewire_remote: server return error: %s (%d)\n", response.error.message, (int)response.error.error_code);
+ return false;
+ }
+
+ if(response.type != GSR_DBUS_MESSAGE_RESP_OPEN_PIPEWIRE_REMOTE) {
+ fprintf(stderr, "gsr error: gsr_dbus_client_screencast_open_pipewire_remote: received incorrect response type. Expected %d got %d\n", GSR_DBUS_MESSAGE_RESP_OPEN_PIPEWIRE_REMOTE, response.type);
+ return false;
+ }
+
+ struct cmsghdr *cmsg = CMSG_FIRSTHDR(&message);
+ if(!cmsg || cmsg->cmsg_type != SCM_RIGHTS) {
+ fprintf(stderr, "gsr error: gsr_dbus_client_screencast_open_pipewire_remote: returned message data is missing file descriptor\n");
+ return false;
+ }
+
+ memcpy(pipewire_fd, CMSG_DATA(cmsg), sizeof(*pipewire_fd));
+ return true;
+}
+
+const char* gsr_dbus_client_screencast_get_restore_token(gsr_dbus_client *self) {
+ return self->screencast_restore_token;
+}
diff --git a/dbus/client/dbus_client.h b/dbus/client/dbus_client.h
new file mode 100644
index 0000000..c9e8182
--- /dev/null
+++ b/dbus/client/dbus_client.h
@@ -0,0 +1,36 @@
+#ifndef GSR_DBUS_CLIENT_H
+#define GSR_DBUS_CLIENT_H
+
+/*
+ Using a client-server architecture is needed for dbus because cap_sys_nice doesn't work with desktop portal.
+ The main binary has cap_sys_nice and we launch a new child-process without it which uses uses desktop portal.
+*/
+
+#include "../portal.h"
+#include <stdbool.h>
+#include <stdint.h>
+#include <signal.h>
+
+typedef struct {
+ int socket_pair[2];
+ char *screencast_restore_token;
+ pid_t pid;
+} gsr_dbus_client;
+
+/* Blocking. TODO: Make non-blocking */
+bool gsr_dbus_client_init(gsr_dbus_client *self, const char *screencast_restore_token);
+void gsr_dbus_client_deinit(gsr_dbus_client *self);
+
+/* The follow functions should be called in order to setup ScreenCast properly */
+/* These functions that return an int return the response status code */
+int gsr_dbus_client_screencast_create_session(gsr_dbus_client *self, char *session_handle, size_t session_handle_size);
+/*
+ |capture_type| is a bitmask of gsr_portal_capture_type values. gsr_portal_capture_type values that are not supported by the desktop portal will be ignored.
+ |gsr_portal_cursor_mode| is a bitmask of gsr_portal_cursor_mode values. gsr_portal_cursor_mode values that are not supported will be ignored.
+*/
+int gsr_dbus_client_screencast_select_sources(gsr_dbus_client *self, const char *session_handle, uint32_t capture_type, uint32_t cursor_mode);
+int gsr_dbus_client_screencast_start(gsr_dbus_client *self, const char *session_handle, uint32_t *pipewire_node);
+bool gsr_dbus_client_screencast_open_pipewire_remote(gsr_dbus_client *self, const char *session_handle, int *pipewire_fd);
+const char* gsr_dbus_client_screencast_get_restore_token(gsr_dbus_client *self);
+
+#endif /* GSR_DBUS_CLIENT_H */
diff --git a/src/dbus.c b/dbus/dbus_impl.c
index 2ccdfb0..600fcc5 100644
--- a/src/dbus.c
+++ b/dbus/dbus_impl.c
@@ -1,5 +1,6 @@
-#include "../include/dbus.h"
-#include "../include/utils.h"
+#include "dbus_impl.h"
+
+#include <sys/random.h>
#include <stdio.h>
#include <string.h>
@@ -28,6 +29,25 @@ typedef struct {
};
} dict_entry;
+static bool generate_random_characters(char *buffer, int buffer_size, const char *alphabet, size_t alphabet_size) {
+ /* TODO: Use other functions on other platforms than linux */
+ if(getrandom(buffer, buffer_size, 0) < buffer_size) {
+ fprintf(stderr, "Failed to get random bytes, error: %s\n", strerror(errno));
+ return false;
+ }
+
+ for(int i = 0; i < buffer_size; ++i) {
+ unsigned char c = *(unsigned char*)&buffer[i];
+ buffer[i] = alphabet[c % alphabet_size];
+ }
+
+ return true;
+}
+
+static bool generate_random_characters_standard_alphabet(char *buffer, int buffer_size) {
+ return generate_random_characters(buffer, buffer_size, "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789", 62);
+}
+
static const char* dict_value_type_to_string(dict_value_type type) {
switch(type) {
case DICT_TYPE_STRING: return "string";
diff --git a/include/dbus.h b/dbus/dbus_impl.h
index 58edf3c..c3f0751 100644
--- a/include/dbus.h
+++ b/dbus/dbus_impl.h
@@ -1,6 +1,7 @@
#ifndef GSR_DBUS_H
#define GSR_DBUS_H
+#include "portal.h"
#include <stdbool.h>
#include <stdint.h>
#include <dbus/dbus.h>
@@ -17,19 +18,6 @@ typedef struct {
char *screencast_restore_token;
} gsr_dbus;
-typedef enum {
- GSR_PORTAL_CAPTURE_TYPE_MONITOR = 1 << 0,
- GSR_PORTAL_CAPTURE_TYPE_WINDOW = 1 << 1,
- GSR_PORTAL_CAPTURE_TYPE_VIRTUAL = 1 << 2,
- GSR_PORTAL_CAPTURE_TYPE_ALL = GSR_PORTAL_CAPTURE_TYPE_MONITOR | GSR_PORTAL_CAPTURE_TYPE_WINDOW | GSR_PORTAL_CAPTURE_TYPE_VIRTUAL
-} gsr_portal_capture_type;
-
-typedef enum {
- GSR_PORTAL_CURSOR_MODE_HIDDEN = 1 << 0,
- GSR_PORTAL_CURSOR_MODE_EMBEDDED = 1 << 1,
- GSR_PORTAL_CURSOR_MODE_METADATA = 1 << 2
-} gsr_portal_cursor_mode;
-
/* Blocking. TODO: Make non-blocking */
bool gsr_dbus_init(gsr_dbus *self, const char *screencast_restore_token);
void gsr_dbus_deinit(gsr_dbus *self);
diff --git a/dbus/portal.h b/dbus/portal.h
new file mode 100644
index 0000000..6b93aa6
--- /dev/null
+++ b/dbus/portal.h
@@ -0,0 +1,17 @@
+#ifndef GSR_PORTAL_H
+#define GSR_PORTAL_H
+
+typedef enum {
+ GSR_PORTAL_CAPTURE_TYPE_MONITOR = 1 << 0,
+ GSR_PORTAL_CAPTURE_TYPE_WINDOW = 1 << 1,
+ GSR_PORTAL_CAPTURE_TYPE_VIRTUAL = 1 << 2,
+ GSR_PORTAL_CAPTURE_TYPE_ALL = GSR_PORTAL_CAPTURE_TYPE_MONITOR | GSR_PORTAL_CAPTURE_TYPE_WINDOW | GSR_PORTAL_CAPTURE_TYPE_VIRTUAL
+} gsr_portal_capture_type;
+
+typedef enum {
+ GSR_PORTAL_CURSOR_MODE_HIDDEN = 1 << 0,
+ GSR_PORTAL_CURSOR_MODE_EMBEDDED = 1 << 1,
+ GSR_PORTAL_CURSOR_MODE_METADATA = 1 << 2
+} gsr_portal_cursor_mode;
+
+#endif /* GSR_PORTAL_H */
diff --git a/dbus/protocol.h b/dbus/protocol.h
new file mode 100644
index 0000000..212358d
--- /dev/null
+++ b/dbus/protocol.h
@@ -0,0 +1,86 @@
+#ifndef GSR_DBUS_PROTOCOL_H
+#define GSR_DBUS_PROTOCOL_H
+
+#include <stdint.h>
+
+#define GSR_DBUS_PROTOCOL_VERSION 1
+
+typedef enum {
+ GSR_DBUS_MESSAGE_REQ_CREATE_SESSION,
+ GSR_DBUS_MESSAGE_REQ_SELECT_SOURCES,
+ GSR_DBUS_MESSAGE_REQ_START,
+ GSR_DBUS_MESSAGE_REQ_OPEN_PIPEWIRE_REMOTE
+} gsr_dbus_message_req_type;
+
+typedef struct {
+
+} gsr_dbus_message_req_create_session;
+
+typedef struct {
+ char session_handle[128];
+ uint32_t capture_type;
+ uint32_t cursor_mode;
+} gsr_dbus_message_req_select_sources;
+
+typedef struct {
+ char session_handle[128];
+} gsr_dbus_message_req_start;
+
+typedef struct {
+ char session_handle[128];
+} gsr_dbus_message_req_open_pipewire_remote;
+
+typedef struct {
+ uint8_t protocol_version;
+ gsr_dbus_message_req_type type;
+ union {
+ gsr_dbus_message_req_create_session create_session;
+ gsr_dbus_message_req_select_sources select_sources;
+ gsr_dbus_message_req_start start;
+ gsr_dbus_message_req_open_pipewire_remote open_pipewire_remote;
+ };
+} gsr_dbus_request_message;
+
+typedef enum {
+ GSR_DBUS_MESSAGE_RESP_ERROR,
+ GSR_DBUS_MESSAGE_RESP_CREATE_SESSION,
+ GSR_DBUS_MESSAGE_RESP_SELECT_SOURCES,
+ GSR_DBUS_MESSAGE_RESP_START,
+ GSR_DBUS_MESSAGE_RESP_OPEN_PIPEWIRE_REMOTE
+} gsr_dbus_message_resp_type;
+
+typedef struct {
+ uint32_t error_code;
+ char message[128];
+} gsr_dbus_message_resp_error;
+
+typedef struct {
+ char session_handle[128];
+} gsr_dbus_message_resp_create_session;
+
+typedef struct {
+
+} gsr_dbus_message_resp_select_sources;
+
+typedef struct {
+ char restore_token[128];
+ uint32_t pipewire_node;
+} gsr_dbus_message_resp_start;
+
+typedef struct {
+
+} gsr_dbus_message_resp_open_pipewire_remote;
+
+typedef struct {
+ uint8_t protocol_version;
+ gsr_dbus_message_resp_type type;
+ union {
+ gsr_dbus_message_resp_error error;
+ gsr_dbus_message_resp_create_session create_session;
+ gsr_dbus_message_resp_select_sources select_sources;
+ gsr_dbus_message_resp_start start;
+ gsr_dbus_message_resp_open_pipewire_remote open_pipewire_remote;
+ };
+} gsr_dbus_response_message;
+
+#endif /* GSR_DBUS_PROTOCOL_H */
diff --git a/dbus/server/dbus_server.c b/dbus/server/dbus_server.c
new file mode 100644
index 0000000..bde6acb
--- /dev/null
+++ b/dbus/server/dbus_server.c
@@ -0,0 +1,175 @@
+#include "../dbus_impl.h"
+#include "../protocol.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <unistd.h>
+#include <sys/socket.h>
+
+/* TODO: Error check write/read */
+
+static int handle_create_session(gsr_dbus *dbus, int rpc_fd, const gsr_dbus_message_req_create_session *create_session) {
+ (void)create_session;
+ char *session_handle = NULL;
+ const int status = gsr_dbus_screencast_create_session(dbus, &session_handle);
+ if(status == 0) {
+ gsr_dbus_response_message response = {
+ .protocol_version = GSR_DBUS_PROTOCOL_VERSION,
+ .type = GSR_DBUS_MESSAGE_RESP_CREATE_SESSION,
+ .create_session = (gsr_dbus_message_resp_create_session) {}
+ };
+ snprintf(response.create_session.session_handle, sizeof(response.create_session.session_handle), "%s", session_handle);
+ free(session_handle);
+ write(rpc_fd, &response, sizeof(response));
+ }
+ return status;
+}
+
+static int handle_select_sources(gsr_dbus *dbus, int rpc_fd, const gsr_dbus_message_req_select_sources *select_sources) {
+ const int status = gsr_dbus_screencast_select_sources(dbus, select_sources->session_handle, select_sources->capture_type, select_sources->cursor_mode);
+ if(status == 0) {
+ gsr_dbus_response_message response = {
+ .protocol_version = GSR_DBUS_PROTOCOL_VERSION,
+ .type = GSR_DBUS_MESSAGE_RESP_SELECT_SOURCES,
+ .select_sources = (gsr_dbus_message_resp_select_sources) {}
+ };
+ write(rpc_fd, &response, sizeof(response));
+ }
+ return status;
+}
+
+static int handle_start(gsr_dbus *dbus, int rpc_fd, const gsr_dbus_message_req_start *start) {
+ uint32_t pipewire_node = 0;
+ const int status = gsr_dbus_screencast_start(dbus, start->session_handle, &pipewire_node);
+ if(status == 0) {
+ const char *screencast_restore_token = gsr_dbus_screencast_get_restore_token(dbus);
+ gsr_dbus_response_message response = {
+ .protocol_version = GSR_DBUS_PROTOCOL_VERSION,
+ .type = GSR_DBUS_MESSAGE_RESP_START,
+ .start = (gsr_dbus_message_resp_start) {
+ .pipewire_node = pipewire_node
+ }
+ };
+ snprintf(response.start.restore_token, sizeof(response.start.restore_token), "%s", screencast_restore_token ? screencast_restore_token : "");
+ write(rpc_fd, &response, sizeof(response));
+ }
+ return status;
+}
+
+static bool handle_open_pipewire_remote(gsr_dbus *dbus, int rpc_fd, const gsr_dbus_message_req_open_pipewire_remote *open_pipewire_remote) {
+ int pipewire_fd = 0;
+ const bool success = gsr_dbus_screencast_open_pipewire_remote(dbus, open_pipewire_remote->session_handle, &pipewire_fd);
+ if(success) {
+ gsr_dbus_response_message response = {
+ .protocol_version = GSR_DBUS_PROTOCOL_VERSION,
+ .type = GSR_DBUS_MESSAGE_RESP_OPEN_PIPEWIRE_REMOTE,
+ .open_pipewire_remote = (gsr_dbus_message_resp_open_pipewire_remote) {}
+ };
+
+ struct iovec iov = {
+ .iov_base = &response,
+ .iov_len = sizeof(response)
+ };
+
+ char msg_control[CMSG_SPACE(sizeof(int))];
+
+ struct msghdr message = {
+ .msg_iov = &iov,
+ .msg_iovlen = 1,
+ .msg_control = msg_control,
+ .msg_controllen = sizeof(msg_control)
+ };
+
+ struct cmsghdr *cmsg = CMSG_FIRSTHDR(&message);
+ cmsg->cmsg_level = SOL_SOCKET;
+ cmsg->cmsg_type = SCM_RIGHTS;
+ cmsg->cmsg_len = CMSG_LEN(sizeof(int));
+ int *fds = (int*)CMSG_DATA(cmsg);
+ fds[0] = pipewire_fd;
+ message.msg_controllen = cmsg->cmsg_len;
+ sendmsg(rpc_fd, &message, 0);
+ }
+ return success;
+}
+
+int main(int argc, char **argv) {
+ if(argc != 3) {
+ fprintf(stderr, "usage: gsr-dbus-server <rpc-fd> <screencast-restore-token>\n");
+ return 1;
+ }
+
+ const char *rpc_fd_str = argv[1];
+ const char *screencast_restore_token = argv[2];
+
+ int rpc_fd = -1;
+ if(sscanf(rpc_fd_str, "%d", &rpc_fd) != 1) {
+ fprintf(stderr, "gsr-dbus-server error: rpc-fd is not a number: %s\n", rpc_fd_str);
+ return 1;
+ }
+
+ if(screencast_restore_token[0] == '\0')
+ screencast_restore_token = NULL;
+
+ gsr_dbus dbus;
+ if(!gsr_dbus_init(&dbus, screencast_restore_token))
+ return 1;
+
+ /* Tell client we have started up */
+ write(rpc_fd, "S", 1);
+
+ gsr_dbus_request_message request;
+ for(;;) {
+ read(rpc_fd, &request, sizeof(request));
+
+ if(request.protocol_version != GSR_DBUS_PROTOCOL_VERSION) {
+ gsr_dbus_response_message response = {
+ .protocol_version = GSR_DBUS_PROTOCOL_VERSION,
+ .type = GSR_DBUS_MESSAGE_RESP_ERROR,
+ .error = (gsr_dbus_message_resp_error) {
+ .error_code = 1
+ }
+ };
+ snprintf(response.error.message, sizeof(response.error.message), "Client uses protocol version %d while the server is using protocol version %d", request.protocol_version, GSR_DBUS_PROTOCOL_VERSION);
+ fprintf(stderr, "gsr-dbus-server error: %s\n", response.error.message);
+ write(rpc_fd, &response, sizeof(response));
+ continue;
+ }
+
+ int status = 0;
+ switch(request.type) {
+ case GSR_DBUS_MESSAGE_REQ_CREATE_SESSION: {
+ status = handle_create_session(&dbus, rpc_fd, &request.create_session);
+ break;
+ }
+ case GSR_DBUS_MESSAGE_REQ_SELECT_SOURCES: {
+ status = handle_select_sources(&dbus, rpc_fd, &request.select_sources);
+ break;
+ }
+ case GSR_DBUS_MESSAGE_REQ_START: {
+ status = handle_start(&dbus, rpc_fd, &request.start);
+ break;
+ }
+ case GSR_DBUS_MESSAGE_REQ_OPEN_PIPEWIRE_REMOTE: {
+ if(!handle_open_pipewire_remote(&dbus, rpc_fd, &request.open_pipewire_remote))
+ status = -1;
+ break;
+ }
+ }
+
+ if(status != 0) {
+ gsr_dbus_response_message response = {
+ .protocol_version = GSR_DBUS_PROTOCOL_VERSION,
+ .type = GSR_DBUS_MESSAGE_RESP_ERROR,
+ .error = (gsr_dbus_message_resp_error) {
+ .error_code = status
+ }
+ };
+ snprintf(response.error.message, sizeof(response.error.message), "%s", "Failed to handle request");
+ write(rpc_fd, &response, sizeof(response));
+ }
+ }
+
+ gsr_dbus_deinit(&dbus);
+ return 0;
+} \ No newline at end of file
diff --git a/extra/meson_post_install.sh b/extra/meson_post_install.sh
index 7bf0d96..8a01b18 100755
--- a/extra/meson_post_install.sh
+++ b/extra/meson_post_install.sh
@@ -4,14 +4,7 @@
/usr/sbin/setcap cap_sys_admin+ep ${MESON_INSTALL_DESTDIR_PREFIX}/bin/gsr-kms-server \
|| echo "\n!!! Please re-run install as root\n"
-# Cant do this because it breaks desktop portal (create session)!!!.
-# For some reason the desktop portal tries to access /proc/gpu-screen-recorder-pid/root from the portal process
-# which doesn't work because for some reason CAP_SYS_NICE on a program makes /proc/self/root not readable by other processes.
-# The reason portal reads that file might be because portal seems to have a security feature where its able to identify the
-# process and if the session token is stolen by another application then it will ignore the session token as it wasn't that
-# application that created the session token.
-# ---
# This is needed (for EGL_CONTEXT_PRIORITY_HIGH_IMG) to allow gpu screen recorder to run faster than the heaviest application on AMD.
# For example when trying to record a game at 60 fps and the game drops to 45 fps in some place that would also make gpu screen recorder
-# drop to 45 fps unless this setcap is used.
-#/usr/sbin/setcap cap_sys_nice+ep ${MESON_INSTALL_DESTDIR_PREFIX}/bin/gpu-screen-recorder
+# drop to 45 fps unless this setcap is used. Recording would also drop to below 60 fps in some games even though they run above 60 fps.
+/usr/sbin/setcap cap_sys_nice+ep ${MESON_INSTALL_DESTDIR_PREFIX}/bin/gpu-screen-recorder
diff --git a/kms/client/kms_client.c b/kms/client/kms_client.c
index 018b25e..8335688 100644
--- a/kms/client/kms_client.c
+++ b/kms/client/kms_client.c
@@ -387,6 +387,7 @@ void cleanup_socket(gsr_kms_client *self, bool kill_server) {
if(kill_server && self->kms_server_pid > 0) {
kill(self->kms_server_pid, SIGKILL);
+ // TODO:
//int status;
//waitpid(self->kms_server_pid, &status, 0);
self->kms_server_pid = -1;
diff --git a/kms/server/kms_server.c b/kms/server/kms_server.c
index c585f24..070875b 100644
--- a/kms/server/kms_server.c
+++ b/kms/server/kms_server.c
@@ -8,6 +8,7 @@
#include <string.h>
#include <errno.h>
#include <stdlib.h>
+#include <locale.h>
#include <unistd.h>
#include <limits.h>
@@ -434,6 +435,8 @@ static double clock_get_monotonic_seconds(void) {
}
int main(int argc, char **argv) {
+ setlocale(LC_ALL, "C"); // Sigh... stupid C
+
int res = 0;
int socket_fd = 0;
gsr_drm drm;
diff --git a/meson.build b/meson.build
index 35c4984..e2945fa 100644
--- a/meson.build
+++ b/meson.build
@@ -76,10 +76,9 @@ uses_pipewire = false
if get_option('portal') == true
src += [
'src/capture/portal.c',
- 'src/dbus.c',
+ 'dbus/client/dbus_client.c',
'src/pipewire_video.c',
]
- dep += dependency('dbus-1')
add_project_arguments('-DGSR_PORTAL', language : ['c', 'cpp'])
uses_pipewire = true
endif
@@ -104,6 +103,10 @@ add_project_arguments('-DGSR_VERSION="' + meson.project_version() + '"', languag
executable('gsr-kms-server', 'kms/server/kms_server.c', dependencies : dependency('libdrm'), c_args : '-fstack-protector-all', install : true)
executable('gpu-screen-recorder', src, dependencies : dep, install : true)
+if get_option('portal') == true
+ executable('gsr-dbus-server', ['dbus/server/dbus_server.c', 'dbus/dbus_impl.c'], dependencies : dependency('dbus-1'), install : true)
+endif
+
if get_option('systemd') == true
install_data(files('extra/gpu-screen-recorder.service'), install_dir : 'lib/systemd/user')
endif
diff --git a/meson_options.txt b/meson_options.txt
index b1023c2..4adf715 100644
--- a/meson_options.txt
+++ b/meson_options.txt
@@ -1,5 +1,5 @@
option('systemd', type : 'boolean', value : true, description : 'Install systemd service file')
-option('capabilities', type : 'boolean', value : true, description : 'Set binary admin capability on gsr-kms-server binary to remove password prompt when recording monitor (without desktop portal option) on amd/intel or nvidia wayland')
+option('capabilities', type : 'boolean', value : true, description : 'Set binary admin capability on gsr-kms-server binary to remove password prompt when recording monitor (without desktop portal option) on amd/intel or nvidia wayland. Also sets nice capability on gpu-screen-recorder to allow it to run as a high priority graphics process for better performance')
option('nvidia_suspend_fix', type : 'boolean', value : true, description : 'Install nvidia modprobe config file to tell nvidia driver to preserve video memory on suspend. This is a workaround for an nvidia driver bug that breaks cuda (and gpu screen recorder) on suspend')
option('portal', type : 'boolean', value : true, description : 'Build with support for xdg desktop portal ScreenCast capture (wayland only) (-w portal option). Requires pipewire')
option('app_audio', type : 'boolean', value : true, description : 'Build with support for recording a single audio source (-a app: option). Requires pipewire')
diff --git a/src/capture/portal.c b/src/capture/portal.c
index 27f514f..a5e62af 100644
--- a/src/capture/portal.c
+++ b/src/capture/portal.c
@@ -2,7 +2,7 @@
#include "../../include/color_conversion.h"
#include "../../include/egl.h"
#include "../../include/utils.h"
-#include "../../include/dbus.h"
+#include "../../dbus/client/dbus_client.h"
#include "../../include/pipewire_video.h"
#include <stdlib.h>
@@ -16,8 +16,8 @@ typedef struct {
gsr_texture_map texture_map;
- gsr_dbus dbus;
- char *session_handle;
+ gsr_dbus_client dbus_client;
+ char session_handle[128];
gsr_pipewire_video pipewire;
vec2i capture_size;
@@ -52,15 +52,8 @@ static void gsr_capture_portal_stop(gsr_capture_portal *self) {
}
gsr_capture_portal_cleanup_plane_fds(self);
-
gsr_pipewire_video_deinit(&self->pipewire);
-
- if(self->session_handle) {
- free(self->session_handle);
- self->session_handle = NULL;
- }
-
- gsr_dbus_deinit(&self->dbus);
+ gsr_dbus_client_deinit(&self->dbus_client);
}
static void gsr_capture_portal_create_input_textures(gsr_capture_portal *self) {
@@ -195,36 +188,36 @@ static int gsr_capture_portal_setup_dbus(gsr_capture_portal *self, int *pipewire
if(self->params.restore_portal_session)
gsr_capture_portal_get_restore_token_from_cache(restore_token, sizeof(restore_token), self->params.portal_session_token_filepath);
- if(!gsr_dbus_init(&self->dbus, restore_token))
+ if(!gsr_dbus_client_init(&self->dbus_client, restore_token))
return -1;
fprintf(stderr, "gsr info: gsr_capture_portal_setup_dbus: CreateSession\n");
- response_status = gsr_dbus_screencast_create_session(&self->dbus, &self->session_handle);
+ response_status = gsr_dbus_client_screencast_create_session(&self->dbus_client, self->session_handle, sizeof(self->session_handle));
if(response_status != 0) {
fprintf(stderr, "gsr error: gsr_capture_portal_setup_dbus: CreateSession failed\n");
return response_status;
}
fprintf(stderr, "gsr info: gsr_capture_portal_setup_dbus: SelectSources\n");
- response_status = gsr_dbus_screencast_select_sources(&self->dbus, self->session_handle, GSR_PORTAL_CAPTURE_TYPE_ALL, self->params.record_cursor ? GSR_PORTAL_CURSOR_MODE_EMBEDDED : GSR_PORTAL_CURSOR_MODE_HIDDEN);
+ response_status = gsr_dbus_client_screencast_select_sources(&self->dbus_client, self->session_handle, GSR_PORTAL_CAPTURE_TYPE_ALL, self->params.record_cursor ? GSR_PORTAL_CURSOR_MODE_EMBEDDED : GSR_PORTAL_CURSOR_MODE_HIDDEN);
if(response_status != 0) {
fprintf(stderr, "gsr error: gsr_capture_portal_setup_dbus: SelectSources failed\n");
return response_status;
}
fprintf(stderr, "gsr info: gsr_capture_portal_setup_dbus: Start\n");
- response_status = gsr_dbus_screencast_start(&self->dbus, self->session_handle, pipewire_node);
+ response_status = gsr_dbus_client_screencast_start(&self->dbus_client, self->session_handle, pipewire_node);
if(response_status != 0) {
fprintf(stderr, "gsr error: gsr_capture_portal_setup_dbus: Start failed\n");
return response_status;
}
- const char *screencast_restore_token = gsr_dbus_screencast_get_restore_token(&self->dbus);
+ const char *screencast_restore_token = gsr_dbus_client_screencast_get_restore_token(&self->dbus_client);
if(screencast_restore_token)
gsr_capture_portal_save_restore_token(screencast_restore_token, self->params.portal_session_token_filepath);
fprintf(stderr, "gsr info: gsr_capture_portal_setup_dbus: OpenPipeWireRemote\n");
- if(!gsr_dbus_screencast_open_pipewire_remote(&self->dbus, self->session_handle, pipewire_fd)) {
+ if(!gsr_dbus_client_screencast_open_pipewire_remote(&self->dbus_client, self->session_handle, pipewire_fd)) {
fprintf(stderr, "gsr error: gsr_capture_portal_setup_dbus: OpenPipeWireRemote failed\n");
return -1;
}
diff --git a/src/egl.c b/src/egl.c
index 76ab9ec..8e38589 100644
--- a/src/egl.c
+++ b/src/egl.c
@@ -60,7 +60,7 @@ static bool gsr_egl_create_window(gsr_egl *self) {
const int32_t ctxattr[] = {
EGL_CONTEXT_CLIENT_VERSION, 2,
- //EGL_CONTEXT_PRIORITY_LEVEL_IMG, EGL_CONTEXT_PRIORITY_HIGH_IMG, /* requires cap_sys_nice, ignored otherwise */
+ EGL_CONTEXT_PRIORITY_LEVEL_IMG, EGL_CONTEXT_PRIORITY_HIGH_IMG, /* requires cap_sys_nice, ignored otherwise */
EGL_NONE, EGL_NONE
};
diff --git a/src/main.cpp b/src/main.cpp
index 4a4fc0c..d716fcd 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -5,7 +5,7 @@ extern "C" {
#include "../include/capture/kms.h"
#ifdef GSR_PORTAL
#include "../include/capture/portal.h"
-#include "../include/dbus.h"
+#include "../dbus/client/dbus_client.h"
#endif
#ifdef GSR_APP_AUDIO
#include "../include/pipewire_audio.h"
@@ -1843,16 +1843,15 @@ static void list_supported_capture_options(const gsr_window *window, const char
if(!wayland)
return;
- gsr_dbus dbus;
- if(!gsr_dbus_init(&dbus, NULL))
+ gsr_dbus_client dbus_client;
+ if(!gsr_dbus_client_init(&dbus_client, NULL))
return;
- char *session_handle = NULL;
- if(gsr_dbus_screencast_create_session(&dbus, &session_handle) == 0) {
- free(session_handle);
+ char session_handle[128];
+ if(gsr_dbus_client_screencast_create_session(&dbus_client, session_handle, sizeof(session_handle)) == 0)
puts("portal");
- }
- gsr_dbus_deinit(&dbus);
+
+ gsr_dbus_client_deinit(&dbus_client);
#endif
}