duckstation

duckstation, but archived from the revision just before upstream changed it to a proprietary software project, this version is the libre one
git clone https://git.neptards.moe/u3shit/duckstation.git
Log | Files | Refs | README | LICENSE

platform_misc_unix.cpp (5625B)


      1 // SPDX-FileCopyrightText: 2019-2024 Connor McLaughlin <stenzek@gmail.com> and contributors.
      2 // SPDX-License-Identifier: (GPL-3.0 OR CC-BY-NC-ND-4.0)
      3 
      4 #include "input_manager.h"
      5 #include "platform_misc.h"
      6 
      7 #include "common/error.h"
      8 #include "common/log.h"
      9 #include "common/path.h"
     10 #include "common/scoped_guard.h"
     11 #include "common/small_string.h"
     12 
     13 #include <cinttypes>
     14 #include <dbus/dbus.h>
     15 #include <signal.h>
     16 #include <spawn.h>
     17 #include <unistd.h>
     18 
     19 Log_SetChannel(PlatformMisc);
     20 
     21 bool PlatformMisc::InitializeSocketSupport(Error* error)
     22 {
     23   // Ignore SIGPIPE, we handle errors ourselves.
     24   if (signal(SIGPIPE, SIG_IGN) == SIG_ERR)
     25   {
     26     Error::SetErrno(error, "signal(SIGPIPE, SIG_IGN) failed: ", errno);
     27     return false;
     28   }
     29 
     30   return true;
     31 }
     32 
     33 static bool SetScreensaverInhibitDBus(const bool inhibit_requested, const char* program_name, const char* reason)
     34 {
     35   static dbus_uint32_t s_cookie;
     36   const char* bus_method = (inhibit_requested) ? "Inhibit" : "UnInhibit";
     37   DBusError error;
     38   DBusConnection* connection = nullptr;
     39   static DBusConnection* s_comparison_connection;
     40   DBusMessage* message = nullptr;
     41   DBusMessage* response = nullptr;
     42   DBusMessageIter message_itr;
     43 
     44   ScopedGuard cleanup = [&]() {
     45     if (dbus_error_is_set(&error))
     46     {
     47       ERROR_LOG("SetScreensaverInhibitDBus error: {}", error.message);
     48       dbus_error_free(&error);
     49     }
     50     if (message)
     51       dbus_message_unref(message);
     52     if (response)
     53       dbus_message_unref(response);
     54   };
     55 
     56   dbus_error_init(&error);
     57   // Calling dbus_bus_get() after the first time returns a pointer to the existing connection.
     58   connection = dbus_bus_get(DBUS_BUS_SESSION, &error);
     59   if (!connection || (dbus_error_is_set(&error)))
     60     return false;
     61   if (s_comparison_connection != connection)
     62   {
     63     dbus_connection_set_exit_on_disconnect(connection, false);
     64     s_cookie = 0;
     65     s_comparison_connection = connection;
     66   }
     67   message = dbus_message_new_method_call("org.freedesktop.ScreenSaver", "/org/freedesktop/ScreenSaver",
     68                                          "org.freedesktop.ScreenSaver", bus_method);
     69   if (!message)
     70     return false;
     71   // Initialize an append iterator for the message, gets freed with the message.
     72   dbus_message_iter_init_append(message, &message_itr);
     73   if (inhibit_requested)
     74   {
     75     // Guard against repeat inhibitions which would add extra inhibitors each generating a different cookie.
     76     if (s_cookie)
     77       return false;
     78     // Append process/window name.
     79     if (!dbus_message_iter_append_basic(&message_itr, DBUS_TYPE_STRING, &program_name))
     80       return false;
     81     // Append reason for inhibiting the screensaver.
     82     if (!dbus_message_iter_append_basic(&message_itr, DBUS_TYPE_STRING, &reason))
     83       return false;
     84   }
     85   else
     86   {
     87     // Only Append the cookie.
     88     if (!dbus_message_iter_append_basic(&message_itr, DBUS_TYPE_UINT32, &s_cookie))
     89       return false;
     90   }
     91   // Send message and get response.
     92   response = dbus_connection_send_with_reply_and_block(connection, message, DBUS_TIMEOUT_USE_DEFAULT, &error);
     93   if (!response || dbus_error_is_set(&error))
     94     return false;
     95   s_cookie = 0;
     96   if (inhibit_requested)
     97   {
     98     // Get the cookie from the response message.
     99     if (!dbus_message_get_args(response, &error, DBUS_TYPE_UINT32, &s_cookie, DBUS_TYPE_INVALID) ||
    100         dbus_error_is_set(&error))
    101       return false;
    102   }
    103   return true;
    104 }
    105 
    106 static bool SetScreensaverInhibit(bool inhibit)
    107 {
    108   return SetScreensaverInhibitDBus(inhibit, "DuckStation", "DuckStation VM is running.");
    109 }
    110 
    111 static bool s_screensaver_suspended;
    112 
    113 void PlatformMisc::SuspendScreensaver()
    114 {
    115   if (s_screensaver_suspended)
    116     return;
    117 
    118   if (!SetScreensaverInhibit(true))
    119   {
    120     ERROR_LOG("Failed to suspend screensaver.");
    121     return;
    122   }
    123 
    124   s_screensaver_suspended = true;
    125 }
    126 
    127 void PlatformMisc::ResumeScreensaver()
    128 {
    129   if (!s_screensaver_suspended)
    130     return;
    131 
    132   if (!SetScreensaverInhibit(false))
    133     ERROR_LOG("Failed to resume screensaver.");
    134 
    135   s_screensaver_suspended = false;
    136 }
    137 
    138 size_t PlatformMisc::GetRuntimePageSize()
    139 {
    140   int res = sysconf(_SC_PAGESIZE);
    141   return (res > 0) ? static_cast<size_t>(res) : 0;
    142 }
    143 
    144 bool PlatformMisc::PlaySoundAsync(const char* path)
    145 {
    146 #ifdef __linux__
    147   // This is... pretty awful. But I can't think of a better way without linking to e.g. gstreamer.
    148   const char* cmdname = "aplay";
    149   const char* argv[] = {cmdname, path, nullptr};
    150   pid_t pid;
    151 
    152   // Since we set SA_NOCLDWAIT in Qt, we don't need to wait here.
    153   int res = posix_spawnp(&pid, cmdname, nullptr, nullptr, const_cast<char**>(argv), environ);
    154   if (res == 0)
    155     return true;
    156 
    157   // Try gst-play-1.0.
    158   const char* gst_play_cmdname = "gst-play-1.0";
    159   const char* gst_play_argv[] = {cmdname, path, nullptr};
    160   res = posix_spawnp(&pid, gst_play_cmdname, nullptr, nullptr, const_cast<char**>(gst_play_argv), environ);
    161   if (res == 0)
    162     return true;
    163 
    164   // gst-launch? Bit messier for sure.
    165   TinyString location_str = TinyString::from_format("location={}", path);
    166   TinyString parse_str = TinyString::from_format("{}parse", Path::GetExtension(path));
    167   const char* gst_launch_cmdname = "gst-launch-1.0";
    168   const char* gst_launch_argv[] = {gst_launch_cmdname, "filesrc", location_str.c_str(), "!",
    169                                    parse_str.c_str(),  "!",       "alsasink",           nullptr};
    170   res = posix_spawnp(&pid, gst_launch_cmdname, nullptr, nullptr, const_cast<char**>(gst_launch_argv), environ);
    171   if (res == 0)
    172     return true;
    173 
    174   ERROR_LOG("Failed to play sound effect {}. Make sure you have aplay, gst-play-1.0, or gst-launch-1.0 available.",
    175             path);
    176   return false;
    177 #else
    178   return false;
    179 #endif
    180 }