mirror of
https://github.com/ApfelTeeSaft/vanilla.git
synced 2026-08-26 19:33:44 +00:00
Various Steam Deck Improvements (#229)
* initial commit * seems to work * try SDL_SetHintWithPriority * set hint with SDL_HINT_OVERRIDE * try setting hint before SDL_init * fix issue with signal handling * send sigterm to pipe * ensure SDL creates signal handlers? * fixed pipe issues not closing * ensure pipe is stopped ASAP when receiving SIGINT and SIGTERM The Steam Deck gives us extremely little time to clean up before getting SIGKILL'd, so make sure pipe is told to quit ASAP when receiving SIGINT or SIGTERM. * fix logic issue * removed things that aren't necessary for this PR * clarified some comments [skip ci] --------- Co-authored-by: MattKC <[email protected]>
This commit is contained in:
+35
-3
@@ -49,6 +49,26 @@ void *vpi_pipe_log_thread(void *data)
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct sigaction old_sigint_action;
|
||||
struct sigaction old_sigterm_action;
|
||||
void sigint_handler(int signal)
|
||||
{
|
||||
// On the Steam Deck, if the user selects "exit game", we get handed a
|
||||
// SIGINT and SIGTERM, but then get SIGKILL'd shortly afterwards, before we
|
||||
// can properly clean everything up and send `vanilla-pipe` a "quit" signal.
|
||||
// This means `vanilla-pipe` often gets stuck in limbo, and even worse, the
|
||||
// Deck's Wi-Fi interface remains under its control until reboot.
|
||||
//
|
||||
// To resolve this, we make sure the first thing we do upon receiving SIGINT
|
||||
// or SIGTERM is to tell the pipe to quit, so hopefully even if we are killed,
|
||||
// it can clean itself up without us.
|
||||
vpi_stop_pipe();
|
||||
|
||||
// vpi_stop_pipe should have restored the old sigaction (SDL's), so let's
|
||||
// follow that through
|
||||
raise(signal);
|
||||
}
|
||||
|
||||
int vpi_start_pipe()
|
||||
{
|
||||
if (pipe_pid != -1) {
|
||||
@@ -111,6 +131,13 @@ int vpi_start_pipe()
|
||||
// Continuation of parent
|
||||
int ret = VANILLA_ERR_GENERIC;
|
||||
|
||||
struct sigaction sa;
|
||||
sa.sa_handler = sigint_handler;
|
||||
sigemptyset(&sa.sa_mask);
|
||||
sa.sa_flags = 0;
|
||||
sigaction(SIGINT, &sa, &old_sigint_action);
|
||||
sigaction(SIGTERM, &sa, &old_sigterm_action);
|
||||
|
||||
char ready_buf[500];
|
||||
memset(ready_buf, 0, sizeof(ready_buf));
|
||||
size_t read_count = 0;
|
||||
@@ -135,7 +162,7 @@ int vpi_start_pipe()
|
||||
pthread_create(&pipe_log_thread, 0, vpi_pipe_log_thread, (void *) (intptr_t) pipe_err);
|
||||
} else {
|
||||
vpilog("GOT INVALID SIGNAL: %.*s\n", sizeof(ready_buf), ready_buf);
|
||||
|
||||
|
||||
// Kill seems to break a lot of things so I guess we'll just leave it orphaned
|
||||
// kill(pipe_pid, SIGKILL);
|
||||
close(in_pipes[1]);
|
||||
@@ -152,7 +179,8 @@ int vpi_start_pipe()
|
||||
void vpi_stop_pipe()
|
||||
{
|
||||
if (pipe_pid != -1) {
|
||||
// Signal to pipe to quit
|
||||
// Signal to pipe to quit. We must send it through stdin because the pipe
|
||||
// runs under root, so we have no permission to send it SIGINT or SIGTERM.
|
||||
ssize_t s = write(pipe_input, "QUIT\n", 5);
|
||||
close(pipe_input);
|
||||
|
||||
@@ -163,6 +191,10 @@ void vpi_stop_pipe()
|
||||
|
||||
waitpid(pipe_pid, 0, 0);
|
||||
pipe_pid = -1;
|
||||
|
||||
// Restore old sigaction
|
||||
sigaction(SIGINT, &old_sigint_action, NULL);
|
||||
sigaction(SIGTERM, &old_sigterm_action, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -173,4 +205,4 @@ void vpi_stop_pipe()
|
||||
{
|
||||
}
|
||||
|
||||
#endif
|
||||
#endif
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
#include <math.h>
|
||||
#include <SDL2/SDL.h>
|
||||
#include <SDL2/SDL_hints.h>
|
||||
#include <SDL_image.h>
|
||||
#include <SDL_power.h>
|
||||
#include <SDL_ttf.h>
|
||||
@@ -275,6 +276,10 @@ void mic_callback(void *userdata, Uint8 *stream, int len)
|
||||
|
||||
int vui_init_sdl(vui_context_t *ctx, int fullscreen)
|
||||
{
|
||||
// Enable Steam Deck gyroscopes even while Steam is open and in gaming mode
|
||||
SDL_SetHintWithPriority("SDL_GAMECONTROLLER_ALLOW_STEAM_VIRTUAL_GAMEPAD", "0", SDL_HINT_OVERRIDE);
|
||||
SDL_SetHintWithPriority(SDL_HINT_GAMECONTROLLER_IGNORE_DEVICES, "", SDL_HINT_OVERRIDE);
|
||||
|
||||
// Initialize SDL
|
||||
if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO | SDL_INIT_GAMECONTROLLER) < 0) {
|
||||
vpilog("Failed to initialize SDL: %s\n", SDL_GetError());
|
||||
|
||||
@@ -229,4 +229,11 @@ target_link_libraries(vanilla-pipe PRIVATE
|
||||
)
|
||||
|
||||
# Handle polkit files
|
||||
install(FILES "polkit/vanilla-polkit-install.sh" DESTINATION bin)
|
||||
set(PIPE_BIN "${CMAKE_INSTALL_PREFIX}/bin/vanilla-pipe")
|
||||
configure_file(
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/polkit/com.mattkc.vanilla.policy.in"
|
||||
"${CMAKE_CURRENT_BINARY_DIR}/polkit/com.mattkc.vanilla.policy"
|
||||
@ONLY
|
||||
)
|
||||
install(FILES "${CMAKE_CURRENT_BINARY_DIR}/polkit/com.mattkc.vanilla.policy" DESTINATION share/polkit-1/actions)
|
||||
install(FILES "${CMAKE_CURRENT_SOURCE_DIR}/polkit/com.mattkc.vanilla.rules" DESTINATION share/polkit-1/rules.d)
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE policyconfig PUBLIC "-//freedesktop//DTD PolicyKit Policy Configuration 1.0//EN" "http://www.freedesktop.org/standards/PolicyKit/1/policyconfig.dtd">
|
||||
<policyconfig>
|
||||
<vendor>MattKC</vendor>
|
||||
<vendor_url>https://mattkc.com</vendor_url>
|
||||
<action id="com.mattkc.vanilla">
|
||||
<description>Run Vanilla Pipe as root</description>
|
||||
<message>Authentication is required to run Vanilla Pipe as root</message>
|
||||
<defaults>
|
||||
<allow_any>auth_admin</allow_any>
|
||||
<allow_inactive>auth_admin</allow_inactive>
|
||||
<allow_active>auth_admin</allow_active>
|
||||
</defaults>
|
||||
<annotate key="org.freedesktop.policykit.exec.path">@PIPE_BIN@</annotate>
|
||||
<annotate key="org.freedesktop.policykit.exec.allow_gui">true</annotate>
|
||||
</action>
|
||||
</policyconfig>
|
||||
@@ -0,0 +1,12 @@
|
||||
/**
|
||||
* Allow all users to run vanilla-pipe as root without having to enter a password
|
||||
*
|
||||
* This provides convenience, especially on platforms more suited for touch
|
||||
* controls, however it could be dangerous to allow a program unrestricted
|
||||
* administrator access, so it should be used with caution.
|
||||
*/
|
||||
polkit.addRule(function(action, subject) {
|
||||
if (action.id == "com.mattkc.vanilla") {
|
||||
return polkit.Result.YES;
|
||||
}
|
||||
});
|
||||
+147
-30
@@ -36,15 +36,15 @@
|
||||
#include <utils/common.h>
|
||||
#include <wpa_supplicant_i.h>
|
||||
|
||||
const char *wpa_ctrl_interface = "/var/run/wpa_supplicant_drc";
|
||||
static const char *wpa_ctrl_interface = "/var/run/wpa_supplicant_drc";
|
||||
|
||||
pthread_mutex_t running_mutex;
|
||||
pthread_mutex_t main_loop_mutex;
|
||||
pthread_mutex_t action_mutex;
|
||||
pthread_mutex_t relay_mutex;
|
||||
int running = 0;
|
||||
int main_loop = 0;
|
||||
int relay_running = 0;
|
||||
static pthread_mutex_t running_mutex;
|
||||
static pthread_mutex_t main_loop_mutex;
|
||||
static pthread_mutex_t action_mutex;
|
||||
static pthread_mutex_t relay_mutex;
|
||||
static int running = 0;
|
||||
static int main_loop = 0;
|
||||
static int relay_running = 0;
|
||||
|
||||
typedef union {
|
||||
struct sockaddr_in in;
|
||||
@@ -166,27 +166,53 @@ void sigint_handler(int signum)
|
||||
nlprint("RECEIVED TERMINATE SIGNAL");
|
||||
}
|
||||
quit_loop();
|
||||
signal(signum, SIG_DFL);
|
||||
}
|
||||
|
||||
void *read_stdin(void *arg)
|
||||
{
|
||||
char *line = NULL;
|
||||
size_t size = 0;
|
||||
char line[256];
|
||||
ssize_t read_size = 0;
|
||||
fd_set fds;
|
||||
struct timeval tv = {0, 10000}; // 10ms
|
||||
|
||||
while ((read_size = getline(&line, &size, stdin)) != -1) {
|
||||
if (read_size == 0) {
|
||||
continue;
|
||||
}
|
||||
pthread_mutex_lock(&main_loop_mutex);
|
||||
while (main_loop) {
|
||||
pthread_mutex_unlock(&main_loop_mutex);
|
||||
|
||||
line[read_size-1] = '\0';
|
||||
if (!strcasecmp(line, "quit") || !strcasecmp(line, "exit") || !strcasecmp(line, "bye")) {
|
||||
quit_loop();
|
||||
}
|
||||
}
|
||||
FD_ZERO(&fds);
|
||||
FD_SET(STDIN_FILENO, &fds);
|
||||
|
||||
int sel = select(STDIN_FILENO + 1, &fds, NULL, NULL, &tv);
|
||||
if (sel > 0) {
|
||||
char c;
|
||||
ssize_t s = read(STDIN_FILENO, &c, 1);
|
||||
if (s > 0) {
|
||||
if (c == '\n') {
|
||||
// Add null terminator
|
||||
line[read_size] = 0;
|
||||
|
||||
// Parse line
|
||||
if (!strcasecmp(line, "quit") || !strcasecmp(line, "exit") || !strcasecmp(line, "bye")) {
|
||||
quit_loop();
|
||||
}
|
||||
|
||||
read_size = 0;
|
||||
} else if (c == EOF) {
|
||||
break;
|
||||
} else {
|
||||
line[read_size] = c;
|
||||
read_size = (read_size + 1) % sizeof(line);
|
||||
}
|
||||
} else if (s < 0) {
|
||||
perror("read()");
|
||||
}
|
||||
} else if (sel < 0) {
|
||||
perror("select()");
|
||||
}
|
||||
pthread_mutex_lock(&main_loop_mutex);
|
||||
}
|
||||
pthread_mutex_unlock(&main_loop_mutex);
|
||||
|
||||
free(line);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
@@ -195,12 +221,97 @@ void *start_wpa(void *arg)
|
||||
return THREADRESULT(wpa_supplicant_run((struct wpa_global *) arg));
|
||||
}
|
||||
|
||||
ssize_t run_process_and_read_stdout(const char **args, char *read_buffer, size_t read_buffer_len)
|
||||
{
|
||||
// Create pipe so we can read from the forked child
|
||||
int pipefd[2];
|
||||
pipe(pipefd);
|
||||
|
||||
// Perform fork
|
||||
pid_t pid = fork();
|
||||
|
||||
// Handle fork
|
||||
switch (pid) {
|
||||
case -1:
|
||||
// Fork failed for some reason, report the errno
|
||||
nlprint("Failed to fork to run process: %i", errno);
|
||||
return -1;
|
||||
case 0:
|
||||
// We are the child process, go ahead and run exec
|
||||
close(pipefd[0]); // Close reading pipe
|
||||
|
||||
dup2(pipefd[1], STDOUT_FILENO); // Set stdout to write
|
||||
// dup2(pipefd[1], STDERR_FILENO); // Set stderr to write
|
||||
|
||||
close(pipefd[1]); // Done with this for now
|
||||
|
||||
execvp(args[0], (char * const *) args);
|
||||
|
||||
// Exit immediately if for some reason execvp failed
|
||||
_exit(0);
|
||||
default:
|
||||
// We are the parent process. Read from stdout until EOF.
|
||||
|
||||
close(pipefd[1]); // Close write, don't need it
|
||||
|
||||
ssize_t read_len = 0;
|
||||
char *read_buffer_now = read_buffer;
|
||||
char * const read_buffer_end = read_buffer + read_buffer_len;
|
||||
if (read_buffer && read_buffer_len) {
|
||||
while (read_buffer_now != read_buffer_end) {
|
||||
read_len = read(pipefd[0], read_buffer_now, read_buffer_end - read_buffer_now);
|
||||
if (read_len <= 0) {
|
||||
break;
|
||||
}
|
||||
read_buffer_now += read_len;
|
||||
}
|
||||
}
|
||||
|
||||
close(pipefd[0]); // Close read, we're done reading
|
||||
|
||||
int status;
|
||||
if (waitpid(pid, &status, 0) == -1) {
|
||||
nlprint("Failed to waitpid: %i", errno);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (WIFEXITED(status) && WEXITSTATUS(status) == 0) {
|
||||
return read_buffer_now - read_buffer;
|
||||
} else {
|
||||
nlprint("Subprocess failed with status: 0x%x", status);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void set_signals()
|
||||
{
|
||||
struct sigaction sa;
|
||||
sa.sa_handler = sigint_handler;
|
||||
sigemptyset(&sa.sa_mask);
|
||||
sa.sa_flags = 0;
|
||||
sigaction(SIGINT, &sa, NULL);
|
||||
sigaction(SIGTERM, &sa, NULL);
|
||||
}
|
||||
|
||||
void *wpa_setup_environment(void *data)
|
||||
{
|
||||
void *ret = THREADRESULT(VANILLA_ERR_GENERIC);
|
||||
|
||||
struct sync_args *args = (struct sync_args *) data;
|
||||
|
||||
// If this is the Steam Deck, we must switch the backend from `iwd` to `wpa_supplicant`
|
||||
int sd_wifi_backend_changed = 0;
|
||||
{
|
||||
char sd_wifi_backend_buf[100] = {0};
|
||||
ssize_t ret = run_process_and_read_stdout((const char *[]) {"steamos-wifi-set-backend", "--check", NULL}, sd_wifi_backend_buf, sizeof(sd_wifi_backend_buf));
|
||||
if (ret > 0 && !strcmp("iwd\n", sd_wifi_backend_buf)) {
|
||||
nlprint("STEAM DECK: SETTING WIFI BACKEND TO WPA_SUPPLICANT");
|
||||
sd_wifi_backend_changed = 1;
|
||||
run_process_and_read_stdout((const char *[]) {"steamos-wifi-set-backend", "wpa_supplicant", NULL}, 0, 0);
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef USE_LIBNM
|
||||
// Check status of interface with NetworkManager
|
||||
GError *nm_err;
|
||||
@@ -239,6 +350,9 @@ void *wpa_setup_environment(void *data)
|
||||
goto die_and_reenable_managed;
|
||||
}
|
||||
|
||||
// wpa_supplicant may have replaced our signals, so lets re-set them
|
||||
set_signals();
|
||||
|
||||
struct wpa_supplicant *wpa_s = wpa_supplicant_add_iface(wpa, &interface, NULL);
|
||||
if (!wpa_s) {
|
||||
nlprint("FAILED TO ADD WPA IFACE");
|
||||
@@ -273,7 +387,7 @@ die_and_close:
|
||||
wpa_ctrl_close(ctrl);
|
||||
|
||||
die_and_kill:
|
||||
pthread_kill(wpa_thread, SIGINT);
|
||||
pthread_cancel(wpa_thread);
|
||||
pthread_join(wpa_thread, NULL);
|
||||
wpa_supplicant_deinit(wpa);
|
||||
|
||||
@@ -290,6 +404,12 @@ die_and_close_nmcli:
|
||||
}
|
||||
#endif
|
||||
|
||||
if (sd_wifi_backend_changed) {
|
||||
// Restore iwd
|
||||
nlprint("STEAM DECK: SETTING WIFI BACKEND TO IWD");
|
||||
run_process_and_read_stdout((const char *[]) {"steamos-wifi-set-backend", "iwd", NULL}, 0, 0);
|
||||
}
|
||||
|
||||
die:
|
||||
return ret;
|
||||
}
|
||||
@@ -989,11 +1109,10 @@ void pipe_listen(int local, const char *wireless_interface, const char *log_file
|
||||
pthread_mutex_init(&action_mutex, NULL);
|
||||
pthread_mutex_init(&main_loop_mutex, NULL);
|
||||
|
||||
signal(SIGINT, sigint_handler);
|
||||
signal(SIGTERM, sigint_handler);
|
||||
pthread_t stdin_thread;
|
||||
pthread_create(&stdin_thread, NULL, read_stdin, NULL);
|
||||
|
||||
pthread_t stdin_thread;
|
||||
pthread_create(&stdin_thread, NULL, read_stdin, NULL);
|
||||
set_signals();
|
||||
|
||||
main_loop = 1;
|
||||
|
||||
@@ -1065,10 +1184,8 @@ repeat_loop:
|
||||
pthread_mutex_lock(&action_mutex);
|
||||
pthread_mutex_unlock(&action_mutex);
|
||||
|
||||
// Interrupt our stdin thread
|
||||
signal(SIGINT, SIG_DFL);
|
||||
signal(SIGTERM, SIG_DFL);
|
||||
pthread_kill(stdin_thread, SIGINT);
|
||||
// Wait for stdin thread
|
||||
pthread_join(stdin_thread, NULL);
|
||||
|
||||
pthread_mutex_destroy(&main_loop_mutex);
|
||||
pthread_mutex_destroy(&action_mutex);
|
||||
|
||||
@@ -6,7 +6,6 @@
|
||||
#include <sys/types.h>
|
||||
|
||||
struct wpa_ctrl;
|
||||
extern const char *wpa_ctrl_interface;
|
||||
|
||||
void wpa_ctrl_command(struct wpa_ctrl *ctrl, const char *cmd, char *buf, size_t *buf_len);
|
||||
int start_wpa_supplicant(const char *wireless_interface, const char *config_file, pid_t *pid);
|
||||
|
||||
Reference in New Issue
Block a user