diff options
author | dec05eba <dec05eba@protonmail.com> | 2025-05-10 17:10:59 +0200 |
---|---|---|
committer | dec05eba <dec05eba@protonmail.com> | 2025-05-10 17:10:59 +0200 |
commit | e3225bc62835c6accc18787c6038fc1dce2484fc (patch) | |
tree | 56c22deacd2a1101ad82ecc80ad300896e6c872f | |
parent | 811a14481dbd75fe2c2a072517e06add336fee4f (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.c | 265 | ||||
-rw-r--r-- | dbus/client/dbus_client.h | 36 | ||||
-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.h | 17 | ||||
-rw-r--r-- | dbus/protocol.h | 86 | ||||
-rw-r--r-- | dbus/server/dbus_server.c | 175 | ||||
-rwxr-xr-x | extra/meson_post_install.sh | 11 | ||||
-rw-r--r-- | kms/client/kms_client.c | 1 | ||||
-rw-r--r-- | kms/server/kms_server.c | 3 | ||||
-rw-r--r-- | meson.build | 7 | ||||
-rw-r--r-- | meson_options.txt | 2 | ||||
-rw-r--r-- | src/capture/portal.c | 27 | ||||
-rw-r--r-- | src/egl.c | 2 | ||||
-rw-r--r-- | src/main.cpp | 15 |
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; } @@ -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 } |