diff options
-rw-r--r-- | dbus/client/dbus_client.c | 269 | ||||
-rw-r--r-- | dbus/client/dbus_client.h | 36 | ||||
-rw-r--r-- | dbus/portal.h | 17 | ||||
-rw-r--r-- | dbus/protocol.h | 86 | ||||
-rw-r--r-- | dbus/server/dbus_server.c | 175 | ||||
-rw-r--r-- | include/capture/capture.h | 5 | ||||
-rw-r--r-- | include/dbus.h (renamed from dbus/dbus_impl.h) | 14 | ||||
-rw-r--r-- | include/pipewire_video.h | 4 | ||||
-rw-r--r-- | meson.build | 7 | ||||
-rw-r--r-- | project.conf | 2 | ||||
-rw-r--r-- | src/capture/portal.c | 95 | ||||
-rw-r--r-- | src/dbus.c (renamed from dbus/dbus_impl.c) | 2 | ||||
-rw-r--r-- | src/main.cpp | 32 | ||||
-rw-r--r-- | src/pipewire_video.c | 33 |
14 files changed, 147 insertions, 630 deletions
diff --git a/dbus/client/dbus_client.c b/dbus/client/dbus_client.c deleted file mode 100644 index de2df62..0000000 --- a/dbus/client/dbus_client.c +++ /dev/null @@ -1,269 +0,0 @@ -#include "dbus_client.h" -#include "../protocol.h" - -#include <sys/socket.h> -#include <sys/wait.h> -#include <sys/prctl.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 died 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]); - - /* Needed for NixOS for example, to make sure gsr-dbus-server doesn't inherit cap_sys_nice */ - prctl(PR_CAP_AMBIENT, PR_CAP_AMBIENT_CLEAR_ALL, 0, 0, 0); - - 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: failed to launch \"gsr-dbus-server\", 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 deleted file mode 100644 index 98a1ecf..0000000 --- a/dbus/client/dbus_client.h +++ /dev/null @@ -1,36 +0,0 @@ -#ifndef GSR_DBUS_CLIENT_H -#define GSR_DBUS_CLIENT_H - -/* - Using a client-server architecture is needed for dbus because cap_sys_nice breaks 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/dbus/portal.h b/dbus/portal.h deleted file mode 100644 index 6b93aa6..0000000 --- a/dbus/portal.h +++ /dev/null @@ -1,17 +0,0 @@ -#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 deleted file mode 100644 index 212358d..0000000 --- a/dbus/protocol.h +++ /dev/null @@ -1,86 +0,0 @@ -#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 deleted file mode 100644 index bde6acb..0000000 --- a/dbus/server/dbus_server.c +++ /dev/null @@ -1,175 +0,0 @@ -#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/include/capture/capture.h b/include/capture/capture.h index 634eee0..c2128c5 100644 --- a/include/capture/capture.h +++ b/include/capture/capture.h @@ -22,12 +22,13 @@ typedef struct { } gsr_capture_metadata; struct gsr_capture { - /* These methods should not be called manually. Call gsr_capture_* instead */ + /* These methods should not be called manually. Call gsr_capture_* instead. |capture_metdata->width| and |capture_metadata->height| should be set by this function */ int (*start)(gsr_capture *cap, gsr_capture_metadata *capture_metadata); void (*on_event)(gsr_capture *cap, gsr_egl *egl); /* can be NULL */ void (*tick)(gsr_capture *cap); /* can be NULL. If there is an event then |on_event| is called before this */ bool (*should_stop)(gsr_capture *cap, bool *err); /* can be NULL. If NULL, return false */ - int (*capture)(gsr_capture *cap, gsr_capture_metadata *capture_metadata, gsr_color_conversion *color_conversion); + bool (*capture_has_synchronous_task)(gsr_capture *cap); /* can be NULL. If this returns true then the time spent in |capture| is ignored for video/audio (capture is paused while the synchronous task happens) */ + int (*capture)(gsr_capture *cap, gsr_capture_metadata *capture_metadata, gsr_color_conversion *color_conversion); /* Return 0 if the frame was captured */ bool (*uses_external_image)(gsr_capture *cap); /* can be NULL. If NULL, return false */ bool (*set_hdr_metadata)(gsr_capture *cap, AVMasteringDisplayMetadata *mastering_display_metadata, AVContentLightMetadata *light_metadata); /* can be NULL. If NULL, return false */ uint64_t (*get_window_id)(gsr_capture *cap); /* can be NULL. Returns 0 if unknown */ diff --git a/dbus/dbus_impl.h b/include/dbus.h index c3f0751..229f7ea 100644 --- a/dbus/dbus_impl.h +++ b/include/dbus.h @@ -1,13 +1,25 @@ #ifndef GSR_DBUS_H #define GSR_DBUS_H -#include "portal.h" #include <stdbool.h> #include <stdint.h> #include <dbus/dbus.h> #define DBUS_RANDOM_STR_SIZE 16 +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; + typedef struct { DBusConnection *con; DBusError err; diff --git a/include/pipewire_video.h b/include/pipewire_video.h index 785f56f..d98e43d 100644 --- a/include/pipewire_video.h +++ b/include/pipewire_video.h @@ -95,6 +95,9 @@ typedef struct { uint64_t modifiers[GSR_PIPEWIRE_VIDEO_MAX_MODIFIERS]; size_t num_modifiers; + + bool paused; + double paused_start_secs; } gsr_pipewire_video; /* @@ -109,5 +112,6 @@ void gsr_pipewire_video_deinit(gsr_pipewire_video *self); bool gsr_pipewire_video_map_texture(gsr_pipewire_video *self, gsr_texture_map texture_map, gsr_pipewire_video_region *region, gsr_pipewire_video_region *cursor_region, gsr_pipewire_video_dmabuf_data *dmabuf_data, int *num_dmabuf_data, uint32_t *fourcc, uint64_t *modifiers, bool *using_external_image); bool gsr_pipewire_video_is_damaged(gsr_pipewire_video *self); void gsr_pipewire_video_clear_damage(gsr_pipewire_video *self); +bool gsr_pipewire_video_should_restart(gsr_pipewire_video *self); #endif /* GSR_PIPEWIRE_VIDEO_H */ diff --git a/meson.build b/meson.build index 43c429d..70f624b 100644 --- a/meson.build +++ b/meson.build @@ -76,7 +76,7 @@ uses_pipewire = false if get_option('portal') == true src += [ 'src/capture/portal.c', - 'dbus/client/dbus_client.c', + 'src/dbus.c', 'src/pipewire_video.c', ] add_project_arguments('-DGSR_PORTAL', language : ['c', 'cpp']) @@ -95,6 +95,7 @@ if uses_pipewire == true dep += [ dependency('libpipewire-0.3'), dependency('libspa-0.2'), + dependency('dbus-1'), ] endif @@ -103,10 +104,6 @@ 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/project.conf b/project.conf index 7cf013b..0c235d4 100644 --- a/project.conf +++ b/project.conf @@ -5,7 +5,7 @@ version = "5.5.10" platforms = ["posix"] [config] -ignore_dirs = ["kms/server", "build", "debug-build", "dbus/server"] +ignore_dirs = ["kms/server", "build", "debug-build"] #error_on_warning = "true" [define] diff --git a/src/capture/portal.c b/src/capture/portal.c index 581a2ed..d2217d1 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 "../../dbus/client/dbus_client.h" +#include "../../include/dbus.h" #include "../../include/pipewire_video.h" #include <stdlib.h> @@ -11,13 +11,22 @@ #include <limits.h> #include <assert.h> +#define PORTAL_CAPTURE_CANCELED_BY_USER_EXIT_CODE 60 + +typedef enum { + PORTAL_CAPTURE_SETUP_IDLE, + PORTAL_CAPTURE_SETUP_IN_PROGRESS, + PORTAL_CAPTURE_SETUP_FINISHED, + PORTAL_CAPTURE_SETUP_FAILED +} gsr_portal_capture_setup_state; + typedef struct { gsr_capture_portal_params params; gsr_texture_map texture_map; - gsr_dbus_client dbus_client; - char session_handle[128]; + gsr_dbus dbus; + char *session_handle; gsr_pipewire_video pipewire; vec2i capture_size; @@ -29,6 +38,9 @@ typedef struct { uint32_t pipewire_fourcc; uint64_t pipewire_modifiers; bool using_external_image; + + bool should_stop; + bool stop_is_error; } gsr_capture_portal; static void gsr_capture_portal_cleanup_plane_fds(gsr_capture_portal *self) { @@ -59,7 +71,7 @@ static void gsr_capture_portal_stop(gsr_capture_portal *self) { gsr_capture_portal_cleanup_plane_fds(self); gsr_pipewire_video_deinit(&self->pipewire); - gsr_dbus_client_deinit(&self->dbus_client); + gsr_dbus_deinit(&self->dbus); } static void gsr_capture_portal_create_input_textures(gsr_capture_portal *self) { @@ -183,36 +195,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_client_init(&self->dbus_client, restore_token)) + if(!gsr_dbus_init(&self->dbus, restore_token)) return -1; fprintf(stderr, "gsr info: gsr_capture_portal_setup_dbus: CreateSession\n"); - response_status = gsr_dbus_client_screencast_create_session(&self->dbus_client, self->session_handle, sizeof(self->session_handle)); + response_status = gsr_dbus_screencast_create_session(&self->dbus, &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_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); + 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); 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_client_screencast_start(&self->dbus_client, self->session_handle, pipewire_node); + response_status = gsr_dbus_screencast_start(&self->dbus, 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_client_screencast_get_restore_token(&self->dbus_client); + const char *screencast_restore_token = gsr_dbus_screencast_get_restore_token(&self->dbus); 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_client_screencast_open_pipewire_remote(&self->dbus_client, self->session_handle, pipewire_fd)) { + if(!gsr_dbus_screencast_open_pipewire_remote(&self->dbus, self->session_handle, pipewire_fd)) { fprintf(stderr, "gsr error: gsr_capture_portal_setup_dbus: OpenPipeWireRemote failed\n"); return -1; } @@ -239,45 +251,51 @@ static bool gsr_capture_portal_get_frame_dimensions(gsr_capture_portal *self) { return false; } -static int gsr_capture_portal_start(gsr_capture *cap, gsr_capture_metadata *capture_metadata) { - gsr_capture_portal *self = cap->priv; - +static int gsr_capture_portal_setup(gsr_capture_portal *self, int fps) { gsr_capture_portal_create_input_textures(self); int pipewire_fd = 0; uint32_t pipewire_node = 0; const int response_status = gsr_capture_portal_setup_dbus(self, &pipewire_fd, &pipewire_node); if(response_status != 0) { - gsr_capture_portal_stop(self); // Response status values: // 0: Success, the request is carried out // 1: The user cancelled the interaction // 2: The user interaction was ended in some other way // Response status value 2 happens usually if there was some kind of error in the desktop portal on the system if(response_status == 2) { - fprintf(stderr, "gsr error: gsr_capture_portal_start: desktop portal capture failed. Either you Wayland compositor doesn't support desktop portal capture or it's incorrectly setup on your system\n"); + fprintf(stderr, "gsr error: gsr_capture_portal_setup: desktop portal capture failed. Either you Wayland compositor doesn't support desktop portal capture or it's incorrectly setup on your system\n"); return 50; } else if(response_status == 1) { - fprintf(stderr, "gsr error: gsr_capture_portal_start: desktop portal capture failed. It seems like desktop portal capture was canceled by the user.\n"); - return 60; + fprintf(stderr, "gsr error: gsr_capture_portal_setup: desktop portal capture failed. It seems like desktop portal capture was canceled by the user.\n"); + return PORTAL_CAPTURE_CANCELED_BY_USER_EXIT_CODE; } else { return -1; } } - fprintf(stderr, "gsr info: gsr_capture_portal_start: setting up pipewire\n"); + fprintf(stderr, "gsr info: gsr_capture_portal_setup: setting up pipewire\n"); /* TODO: support hdr when pipewire supports it */ /* gsr_pipewire closes the pipewire fd, even on failure */ - if(!gsr_pipewire_video_init(&self->pipewire, pipewire_fd, pipewire_node, capture_metadata->fps, self->params.record_cursor, self->params.egl)) { - fprintf(stderr, "gsr error: gsr_capture_portal_start: failed to setup pipewire with fd: %d, node: %" PRIu32 "\n", pipewire_fd, pipewire_node); - gsr_capture_portal_stop(self); + if(!gsr_pipewire_video_init(&self->pipewire, pipewire_fd, pipewire_node, fps, self->params.record_cursor, self->params.egl)) { + fprintf(stderr, "gsr error: gsr_capture_portal_setup: failed to setup pipewire with fd: %d, node: %" PRIu32 "\n", pipewire_fd, pipewire_node); return -1; } - fprintf(stderr, "gsr info: gsr_capture_portal_start: pipewire setup finished\n"); + fprintf(stderr, "gsr info: gsr_capture_portal_setup: pipewire setup finished\n"); - if(!gsr_capture_portal_get_frame_dimensions(self)) { - gsr_capture_portal_stop(self); + if(!gsr_capture_portal_get_frame_dimensions(self)) return -1; + + return 0; +} + +static int gsr_capture_portal_start(gsr_capture *cap, gsr_capture_metadata *capture_metadata) { + gsr_capture_portal *self = cap->priv; + + const int result = gsr_capture_portal_setup(self, capture_metadata->fps); + if(result != 0) { + gsr_capture_portal_stop(self); + return result; } if(self->params.output_resolution.x == 0 && self->params.output_resolution.y == 0) { @@ -296,10 +314,29 @@ static int max_int(int a, int b) { return a > b ? a : b; } +static bool gsr_capture_portal_capture_has_synchronous_task(gsr_capture *cap) { + gsr_capture_portal *self = cap->priv; + return gsr_pipewire_video_should_restart(&self->pipewire); +} + static int gsr_capture_portal_capture(gsr_capture *cap, gsr_capture_metadata *capture_metadata, gsr_color_conversion *color_conversion) { (void)color_conversion; gsr_capture_portal *self = cap->priv; + if(self->should_stop) + return -1; + + if(gsr_pipewire_video_should_restart(&self->pipewire)) { + fprintf(stderr, "gsr info: gsr_capture_portal_capture: pipewire capture was paused, trying to start capture again\n"); + gsr_capture_portal_stop(self); + const int result = gsr_capture_portal_setup(self, capture_metadata->fps); + if(result != 0) { + self->stop_is_error = result != PORTAL_CAPTURE_CANCELED_BY_USER_EXIT_CODE; + self->should_stop = true; + } + return -1; + } + /* TODO: Handle formats other than RGB(A) */ if(self->num_dmabuf_data == 0) { if(gsr_pipewire_video_map_texture(&self->pipewire, self->texture_map, &self->region, &self->cursor_region, self->dmabuf_data, &self->num_dmabuf_data, &self->pipewire_fourcc, &self->pipewire_modifiers, &self->using_external_image)) { @@ -362,6 +399,13 @@ static bool gsr_capture_portal_uses_external_image(gsr_capture *cap) { return true; } +static bool gsr_capture_portal_should_stop(gsr_capture *cap, bool *err) { + gsr_capture_portal *self = cap->priv; + if(err) + *err = self->stop_is_error; + return self->should_stop; +} + static bool gsr_capture_portal_is_damaged(gsr_capture *cap) { gsr_capture_portal *self = cap->priv; return gsr_pipewire_video_is_damaged(&self->pipewire); @@ -403,7 +447,8 @@ gsr_capture* gsr_capture_portal_create(const gsr_capture_portal_params *params) *cap = (gsr_capture) { .start = gsr_capture_portal_start, .tick = NULL, - .should_stop = NULL, + .should_stop = gsr_capture_portal_should_stop, + .capture_has_synchronous_task = gsr_capture_portal_capture_has_synchronous_task, .capture = gsr_capture_portal_capture, .uses_external_image = gsr_capture_portal_uses_external_image, .is_damaged = gsr_capture_portal_is_damaged, diff --git a/dbus/dbus_impl.c b/src/dbus.c index 600fcc5..f12891f 100644 --- a/dbus/dbus_impl.c +++ b/src/dbus.c @@ -1,4 +1,4 @@ -#include "dbus_impl.h" +#include "../include/dbus.h" #include <sys/random.h> diff --git a/src/main.cpp b/src/main.cpp index 67619f9..f382dd0 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 "../dbus/client/dbus_client.h" +#include "../include/dbus.h" #endif #ifdef GSR_APP_AUDIO #include "../include/pipewire_audio.h" @@ -1847,15 +1847,15 @@ static void list_supported_capture_options(const gsr_window *window, const char if(!wayland) return; - gsr_dbus_client dbus_client; - if(!gsr_dbus_client_init(&dbus_client, NULL)) + gsr_dbus dbus; + if(!gsr_dbus_init(&dbus, NULL)) return; - char session_handle[128]; - if(gsr_dbus_client_screencast_create_session(&dbus_client, session_handle, sizeof(session_handle)) == 0) + char *session_handle = NULL; + if(gsr_dbus_screencast_create_session(&dbus, &session_handle) == 0) puts("portal"); - gsr_dbus_client_deinit(&dbus_client); + gsr_dbus_deinit(&dbus); #endif } @@ -3312,7 +3312,7 @@ int main(int argc, char **argv) { int damage_fps_counter = 0; bool paused = false; - double paused_time_offset = 0.0; + std::atomic<double> paused_time_offset(0.0); double paused_time_start = 0.0; bool replay_recording = false; RecordingStartResult replay_recording_start_result; @@ -3628,7 +3628,23 @@ int main(int argc, char **argv) { // TODO: Dont do this if no damage? egl.glClear(0); + + bool capture_has_synchronous_task = false; + if(capture->capture_has_synchronous_task) { + capture_has_synchronous_task = capture->capture_has_synchronous_task(capture); + if(capture_has_synchronous_task) { + paused_time_start = clock_get_monotonic_seconds(); + paused = true; + } + } + gsr_capture_capture(capture, &capture_metadata, &color_conversion); + + if(capture_has_synchronous_task) { + paused_time_offset = paused_time_offset + (clock_get_monotonic_seconds() - paused_time_start); + paused = false; + } + gsr_egl_swap_buffers(&egl); gsr_video_encoder_copy_textures_to_frame(video_encoder, video_frame, &color_conversion); @@ -3678,7 +3694,7 @@ int main(int argc, char **argv) { paused_time_start = clock_get_monotonic_seconds(); fprintf(stderr, "Paused\n"); } else { - paused_time_offset += (clock_get_monotonic_seconds() - paused_time_start); + paused_time_offset = paused_time_offset + (clock_get_monotonic_seconds() - paused_time_start); fprintf(stderr, "Unpaused\n"); } diff --git a/src/pipewire_video.c b/src/pipewire_video.c index 83b0bc3..277004c 100644 --- a/src/pipewire_video.c +++ b/src/pipewire_video.c @@ -280,13 +280,21 @@ static void on_param_changed_cb(void *user_data, uint32_t id, const struct spa_p self->negotiated = true; } -static void on_state_changed_cb(void *user_data, enum pw_stream_state old, enum pw_stream_state state, const char *error) { - (void)old; +static void on_state_changed_cb(void *user_data, enum pw_stream_state prev_state, enum pw_stream_state new_state, const char *error) { gsr_pipewire_video *self = user_data; - fprintf(stderr, "gsr info: pipewire: stream %p state: \"%s\" (error: %s)\n", - (void*)self->stream, pw_stream_state_as_string(state), + fprintf(stderr, "gsr info: pipewire: stream %p previous state: \"%s\", new state: \"%s\" (error: %s)\n", + (void*)self->stream, pw_stream_state_as_string(prev_state), pw_stream_state_as_string(new_state), error ? error : "none"); + + pthread_mutex_lock(&self->mutex); + if(new_state == PW_STREAM_STATE_PAUSED) { + self->paused_start_secs = clock_get_monotonic_seconds(); + self->paused = true; + } else { + self->paused = false; + } + pthread_mutex_unlock(&self->mutex); } static const struct pw_stream_events stream_events = { @@ -841,6 +849,9 @@ bool gsr_pipewire_video_map_texture(gsr_pipewire_video *self, gsr_texture_map te } bool gsr_pipewire_video_is_damaged(gsr_pipewire_video *self) { + if(!self->mutex_initialized) + return false; + bool damaged = false; pthread_mutex_lock(&self->mutex); damaged = self->damaged; @@ -849,7 +860,21 @@ bool gsr_pipewire_video_is_damaged(gsr_pipewire_video *self) { } void gsr_pipewire_video_clear_damage(gsr_pipewire_video *self) { + if(!self->mutex_initialized) + return; + pthread_mutex_lock(&self->mutex); self->damaged = false; pthread_mutex_unlock(&self->mutex); } + +bool gsr_pipewire_video_should_restart(gsr_pipewire_video *self) { + if(!self->mutex_initialized) + return false; + + bool should_restart = false; + pthread_mutex_lock(&self->mutex); + should_restart = self->paused && clock_get_monotonic_seconds() - self->paused_start_secs >= 3.0; + pthread_mutex_unlock(&self->mutex); + return should_restart; +} |