diff options
26 files changed, 371 insertions, 106 deletions
| diff --git a/src/audio_core/device/audio_buffers.h b/src/audio_core/device/audio_buffers.h index 3dae1a3b7..15082f6c6 100644 --- a/src/audio_core/device/audio_buffers.h +++ b/src/audio_core/device/audio_buffers.h @@ -91,9 +91,10 @@ public:       * @param core_timing - The CoreTiming instance       * @param session     - The device session       * -     * @return Is the buffer was released. +     * @return If any buffer was released.       */ -    bool ReleaseBuffers(const Core::Timing::CoreTiming& core_timing, const DeviceSession& session) { +    bool ReleaseBuffers(const Core::Timing::CoreTiming& core_timing, const DeviceSession& session, +                        bool force) {          std::scoped_lock l{lock};          bool buffer_released{false};          while (registered_count > 0) { @@ -103,7 +104,8 @@ public:              }              // Check with the backend if this buffer can be released yet. -            if (!session.IsBufferConsumed(buffers[index])) { +            // If we're shutting down, we don't care if it's been played or not. +            if (!force && !session.IsBufferConsumed(buffers[index])) {                  break;              } diff --git a/src/audio_core/device/device_session.cpp b/src/audio_core/device/device_session.cpp index 995060414..5a327a606 100644 --- a/src/audio_core/device/device_session.cpp +++ b/src/audio_core/device/device_session.cpp @@ -73,6 +73,12 @@ void DeviceSession::Stop() {      }  } +void DeviceSession::ClearBuffers() { +    if (stream) { +        stream->ClearQueue(); +    } +} +  void DeviceSession::AppendBuffers(std::span<const AudioBuffer> buffers) const {      for (const auto& buffer : buffers) {          Sink::SinkBuffer new_buffer{ diff --git a/src/audio_core/device/device_session.h b/src/audio_core/device/device_session.h index 74f4dc085..75f766c68 100644 --- a/src/audio_core/device/device_session.h +++ b/src/audio_core/device/device_session.h @@ -91,6 +91,11 @@ public:      void Stop();      /** +     * Clear out the underlying audio buffers in the backend stream. +     */ +    void ClearBuffers(); + +    /**       * Set this device session's volume.       *       * @param volume - New volume for this session. diff --git a/src/audio_core/in/audio_in_system.cpp b/src/audio_core/in/audio_in_system.cpp index 4324cafd8..934ef8c1c 100644 --- a/src/audio_core/in/audio_in_system.cpp +++ b/src/audio_core/in/audio_in_system.cpp @@ -23,7 +23,6 @@ System::~System() {  void System::Finalize() {      Stop();      session->Finalize(); -    buffer_event->Signal();  }  void System::StartSession() { @@ -102,6 +101,10 @@ Result System::Stop() {      if (state == State::Started) {          session->Stop();          session->SetVolume(0.0f); +        session->ClearBuffers(); +        if (buffers.ReleaseBuffers(system.CoreTiming(), *session, true)) { +            buffer_event->Signal(); +        }          state = State::Stopped;      } @@ -138,7 +141,7 @@ void System::RegisterBuffers() {  }  void System::ReleaseBuffers() { -    bool signal{buffers.ReleaseBuffers(system.CoreTiming(), *session)}; +    bool signal{buffers.ReleaseBuffers(system.CoreTiming(), *session, false)};      if (signal) {          // Signal if any buffer was released, or if none are registered, we need more. diff --git a/src/audio_core/out/audio_out_system.cpp b/src/audio_core/out/audio_out_system.cpp index a66208ed9..e096a1dac 100644 --- a/src/audio_core/out/audio_out_system.cpp +++ b/src/audio_core/out/audio_out_system.cpp @@ -24,7 +24,6 @@ System::~System() {  void System::Finalize() {      Stop();      session->Finalize(); -    buffer_event->Signal();  }  std::string_view System::GetDefaultOutputDeviceName() const { @@ -102,6 +101,10 @@ Result System::Stop() {      if (state == State::Started) {          session->Stop();          session->SetVolume(0.0f); +        session->ClearBuffers(); +        if (buffers.ReleaseBuffers(system.CoreTiming(), *session, true)) { +            buffer_event->Signal(); +        }          state = State::Stopped;      } @@ -138,7 +141,7 @@ void System::RegisterBuffers() {  }  void System::ReleaseBuffers() { -    bool signal{buffers.ReleaseBuffers(system.CoreTiming(), *session)}; +    bool signal{buffers.ReleaseBuffers(system.CoreTiming(), *session, false)};      if (signal) {          // Signal if any buffer was released, or if none are registered, we need more.          buffer_event->Signal(); diff --git a/src/common/settings.cpp b/src/common/settings.cpp index d8ffe34c3..149e621f9 100644 --- a/src/common/settings.cpp +++ b/src/common/settings.cpp @@ -40,6 +40,7 @@ void LogSettings() {      LOG_INFO(Config, "yuzu Configuration:");      log_setting("Controls_UseDockedMode", values.use_docked_mode.GetValue());      log_setting("System_RngSeed", values.rng_seed.GetValue().value_or(0)); +    log_setting("System_DeviceName", values.device_name.GetValue());      log_setting("System_CurrentUser", values.current_user.GetValue());      log_setting("System_LanguageIndex", values.language_index.GetValue());      log_setting("System_RegionIndex", values.region_index.GetValue()); diff --git a/src/common/settings.h b/src/common/settings.h index 7ce9ea23c..6b199af93 100644 --- a/src/common/settings.h +++ b/src/common/settings.h @@ -458,6 +458,7 @@ struct Values {      // System      SwitchableSetting<std::optional<u32>> rng_seed{std::optional<u32>(), "rng_seed"}; +    Setting<std::string> device_name{"Yuzu", "device_name"};      // Measured in seconds since epoch      std::optional<s64> custom_rtc;      // Set on game boot, reset on stop. Seconds difference between current time and `custom_rtc` diff --git a/src/core/hle/kernel/hle_ipc.cpp b/src/core/hle/kernel/hle_ipc.cpp index 06010b8d1..738b6d0f1 100644 --- a/src/core/hle/kernel/hle_ipc.cpp +++ b/src/core/hle/kernel/hle_ipc.cpp @@ -167,6 +167,9 @@ void HLERequestContext::ParseCommandBuffer(const KHandleTable& handle_table, u32          }          if (incoming) {              // Populate the object lists with the data in the IPC request. +            incoming_copy_handles.reserve(handle_descriptor_header->num_handles_to_copy); +            incoming_move_handles.reserve(handle_descriptor_header->num_handles_to_move); +              for (u32 handle = 0; handle < handle_descriptor_header->num_handles_to_copy; ++handle) {                  incoming_copy_handles.push_back(rp.Pop<Handle>());              } @@ -181,6 +184,11 @@ void HLERequestContext::ParseCommandBuffer(const KHandleTable& handle_table, u32          }      } +    buffer_x_desciptors.reserve(command_header->num_buf_x_descriptors); +    buffer_a_desciptors.reserve(command_header->num_buf_a_descriptors); +    buffer_b_desciptors.reserve(command_header->num_buf_b_descriptors); +    buffer_w_desciptors.reserve(command_header->num_buf_w_descriptors); +      for (u32 i = 0; i < command_header->num_buf_x_descriptors; ++i) {          buffer_x_desciptors.push_back(rp.PopRaw<IPC::BufferDescriptorX>());      } @@ -318,25 +326,23 @@ Result HLERequestContext::WriteToOutgoingCommandBuffer(KThread& requesting_threa  }  std::vector<u8> HLERequestContext::ReadBuffer(std::size_t buffer_index) const { -    std::vector<u8> buffer{};      const bool is_buffer_a{BufferDescriptorA().size() > buffer_index &&                             BufferDescriptorA()[buffer_index].Size()}; -      if (is_buffer_a) {          ASSERT_OR_EXECUTE_MSG( -            BufferDescriptorA().size() > buffer_index, { return buffer; }, +            BufferDescriptorA().size() > buffer_index, { return {}; },              "BufferDescriptorA invalid buffer_index {}", buffer_index); -        buffer.resize(BufferDescriptorA()[buffer_index].Size()); +        std::vector<u8> buffer(BufferDescriptorA()[buffer_index].Size());          memory.ReadBlock(BufferDescriptorA()[buffer_index].Address(), buffer.data(), buffer.size()); +        return buffer;      } else {          ASSERT_OR_EXECUTE_MSG( -            BufferDescriptorX().size() > buffer_index, { return buffer; }, +            BufferDescriptorX().size() > buffer_index, { return {}; },              "BufferDescriptorX invalid buffer_index {}", buffer_index); -        buffer.resize(BufferDescriptorX()[buffer_index].Size()); +        std::vector<u8> buffer(BufferDescriptorX()[buffer_index].Size());          memory.ReadBlock(BufferDescriptorX()[buffer_index].Address(), buffer.data(), buffer.size()); +        return buffer;      } - -    return buffer;  }  std::size_t HLERequestContext::WriteBuffer(const void* buffer, std::size_t size, diff --git a/src/core/hle/kernel/k_process.cpp b/src/core/hle/kernel/k_process.cpp index d1dc62401..a1abf5d68 100644 --- a/src/core/hle/kernel/k_process.cpp +++ b/src/core/hle/kernel/k_process.cpp @@ -285,6 +285,17 @@ void KProcess::UnregisterThread(KThread* thread) {      thread_list.remove(thread);  } +u64 KProcess::GetFreeThreadCount() const { +    if (resource_limit != nullptr) { +        const auto current_value = +            resource_limit->GetCurrentValue(LimitableResource::ThreadCountMax); +        const auto limit_value = resource_limit->GetLimitValue(LimitableResource::ThreadCountMax); +        return limit_value - current_value; +    } else { +        return 0; +    } +} +  Result KProcess::Reset() {      // Lock the process and the scheduler.      KScopedLightLock lk(state_lock); diff --git a/src/core/hle/kernel/k_process.h b/src/core/hle/kernel/k_process.h index 2e0cc3d0b..09bf2f1d0 100644 --- a/src/core/hle/kernel/k_process.h +++ b/src/core/hle/kernel/k_process.h @@ -304,6 +304,9 @@ public:      /// from this process' thread list.      void UnregisterThread(KThread* thread); +    /// Retrieves the number of available threads for this process. +    u64 GetFreeThreadCount() const; +      /// Clears the signaled state of the process if and only if it's signaled.      ///      /// @pre The process must not be already terminated. If this is called on a diff --git a/src/core/hle/kernel/svc.cpp b/src/core/hle/kernel/svc.cpp index e520cab47..788ee2160 100644 --- a/src/core/hle/kernel/svc.cpp +++ b/src/core/hle/kernel/svc.cpp @@ -784,63 +784,29 @@ static Result GetInfo(Core::System& system, u64* result, u64 info_id, Handle han      LOG_TRACE(Kernel_SVC, "called info_id=0x{:X}, info_sub_id=0x{:X}, handle=0x{:08X}", info_id,                info_sub_id, handle); -    enum class GetInfoType : u64 { -        // 1.0.0+ -        AllowedCPUCoreMask = 0, -        AllowedThreadPriorityMask = 1, -        MapRegionBaseAddr = 2, -        MapRegionSize = 3, -        HeapRegionBaseAddr = 4, -        HeapRegionSize = 5, -        TotalPhysicalMemoryAvailable = 6, -        TotalPhysicalMemoryUsed = 7, -        IsCurrentProcessBeingDebugged = 8, -        RegisterResourceLimit = 9, -        IdleTickCount = 10, -        RandomEntropy = 11, -        ThreadTickCount = 0xF0000002, -        // 2.0.0+ -        ASLRRegionBaseAddr = 12, -        ASLRRegionSize = 13, -        StackRegionBaseAddr = 14, -        StackRegionSize = 15, -        // 3.0.0+ -        SystemResourceSize = 16, -        SystemResourceUsage = 17, -        TitleId = 18, -        // 4.0.0+ -        PrivilegedProcessId = 19, -        // 5.0.0+ -        UserExceptionContextAddr = 20, -        // 6.0.0+ -        TotalPhysicalMemoryAvailableWithoutSystemResource = 21, -        TotalPhysicalMemoryUsedWithoutSystemResource = 22, - -        // Homebrew only -        MesosphereCurrentProcess = 65001, -    }; - -    const auto info_id_type = static_cast<GetInfoType>(info_id); +    const auto info_id_type = static_cast<InfoType>(info_id);      switch (info_id_type) { -    case GetInfoType::AllowedCPUCoreMask: -    case GetInfoType::AllowedThreadPriorityMask: -    case GetInfoType::MapRegionBaseAddr: -    case GetInfoType::MapRegionSize: -    case GetInfoType::HeapRegionBaseAddr: -    case GetInfoType::HeapRegionSize: -    case GetInfoType::ASLRRegionBaseAddr: -    case GetInfoType::ASLRRegionSize: -    case GetInfoType::StackRegionBaseAddr: -    case GetInfoType::StackRegionSize: -    case GetInfoType::TotalPhysicalMemoryAvailable: -    case GetInfoType::TotalPhysicalMemoryUsed: -    case GetInfoType::SystemResourceSize: -    case GetInfoType::SystemResourceUsage: -    case GetInfoType::TitleId: -    case GetInfoType::UserExceptionContextAddr: -    case GetInfoType::TotalPhysicalMemoryAvailableWithoutSystemResource: -    case GetInfoType::TotalPhysicalMemoryUsedWithoutSystemResource: { +    case InfoType::CoreMask: +    case InfoType::PriorityMask: +    case InfoType::AliasRegionAddress: +    case InfoType::AliasRegionSize: +    case InfoType::HeapRegionAddress: +    case InfoType::HeapRegionSize: +    case InfoType::AslrRegionAddress: +    case InfoType::AslrRegionSize: +    case InfoType::StackRegionAddress: +    case InfoType::StackRegionSize: +    case InfoType::TotalMemorySize: +    case InfoType::UsedMemorySize: +    case InfoType::SystemResourceSizeTotal: +    case InfoType::SystemResourceSizeUsed: +    case InfoType::ProgramId: +    case InfoType::UserExceptionContextAddress: +    case InfoType::TotalNonSystemMemorySize: +    case InfoType::UsedNonSystemMemorySize: +    case InfoType::IsApplication: +    case InfoType::FreeThreadCount: {          if (info_sub_id != 0) {              LOG_ERROR(Kernel_SVC, "Info sub id is non zero! info_id={}, info_sub_id={}", info_id,                        info_sub_id); @@ -856,79 +822,83 @@ static Result GetInfo(Core::System& system, u64* result, u64 info_id, Handle han          }          switch (info_id_type) { -        case GetInfoType::AllowedCPUCoreMask: +        case InfoType::CoreMask:              *result = process->GetCoreMask();              return ResultSuccess; -        case GetInfoType::AllowedThreadPriorityMask: +        case InfoType::PriorityMask:              *result = process->GetPriorityMask();              return ResultSuccess; -        case GetInfoType::MapRegionBaseAddr: +        case InfoType::AliasRegionAddress:              *result = process->PageTable().GetAliasRegionStart();              return ResultSuccess; -        case GetInfoType::MapRegionSize: +        case InfoType::AliasRegionSize:              *result = process->PageTable().GetAliasRegionSize();              return ResultSuccess; -        case GetInfoType::HeapRegionBaseAddr: +        case InfoType::HeapRegionAddress:              *result = process->PageTable().GetHeapRegionStart();              return ResultSuccess; -        case GetInfoType::HeapRegionSize: +        case InfoType::HeapRegionSize:              *result = process->PageTable().GetHeapRegionSize();              return ResultSuccess; -        case GetInfoType::ASLRRegionBaseAddr: +        case InfoType::AslrRegionAddress:              *result = process->PageTable().GetAliasCodeRegionStart();              return ResultSuccess; -        case GetInfoType::ASLRRegionSize: +        case InfoType::AslrRegionSize:              *result = process->PageTable().GetAliasCodeRegionSize();              return ResultSuccess; -        case GetInfoType::StackRegionBaseAddr: +        case InfoType::StackRegionAddress:              *result = process->PageTable().GetStackRegionStart();              return ResultSuccess; -        case GetInfoType::StackRegionSize: +        case InfoType::StackRegionSize:              *result = process->PageTable().GetStackRegionSize();              return ResultSuccess; -        case GetInfoType::TotalPhysicalMemoryAvailable: +        case InfoType::TotalMemorySize:              *result = process->GetTotalPhysicalMemoryAvailable();              return ResultSuccess; -        case GetInfoType::TotalPhysicalMemoryUsed: +        case InfoType::UsedMemorySize:              *result = process->GetTotalPhysicalMemoryUsed();              return ResultSuccess; -        case GetInfoType::SystemResourceSize: +        case InfoType::SystemResourceSizeTotal:              *result = process->GetSystemResourceSize();              return ResultSuccess; -        case GetInfoType::SystemResourceUsage: +        case InfoType::SystemResourceSizeUsed:              LOG_WARNING(Kernel_SVC, "(STUBBED) Attempted to query system resource usage");              *result = process->GetSystemResourceUsage();              return ResultSuccess; -        case GetInfoType::TitleId: +        case InfoType::ProgramId:              *result = process->GetProgramID();              return ResultSuccess; -        case GetInfoType::UserExceptionContextAddr: +        case InfoType::UserExceptionContextAddress:              *result = process->GetProcessLocalRegionAddress();              return ResultSuccess; -        case GetInfoType::TotalPhysicalMemoryAvailableWithoutSystemResource: +        case InfoType::TotalNonSystemMemorySize:              *result = process->GetTotalPhysicalMemoryAvailableWithoutSystemResource();              return ResultSuccess; -        case GetInfoType::TotalPhysicalMemoryUsedWithoutSystemResource: +        case InfoType::UsedNonSystemMemorySize:              *result = process->GetTotalPhysicalMemoryUsedWithoutSystemResource();              return ResultSuccess; +        case InfoType::FreeThreadCount: +            *result = process->GetFreeThreadCount(); +            return ResultSuccess; +          default:              break;          } @@ -937,11 +907,11 @@ static Result GetInfo(Core::System& system, u64* result, u64 info_id, Handle han          return ResultInvalidEnumValue;      } -    case GetInfoType::IsCurrentProcessBeingDebugged: +    case InfoType::DebuggerAttached:          *result = 0;          return ResultSuccess; -    case GetInfoType::RegisterResourceLimit: { +    case InfoType::ResourceLimit: {          if (handle != 0) {              LOG_ERROR(Kernel, "Handle is non zero! handle={:08X}", handle);              return ResultInvalidHandle; @@ -969,7 +939,7 @@ static Result GetInfo(Core::System& system, u64* result, u64 info_id, Handle han          return ResultSuccess;      } -    case GetInfoType::RandomEntropy: +    case InfoType::RandomEntropy:          if (handle != 0) {              LOG_ERROR(Kernel_SVC, "Process Handle is non zero, expected 0 result but got {:016X}",                        handle); @@ -985,13 +955,13 @@ static Result GetInfo(Core::System& system, u64* result, u64 info_id, Handle han          *result = system.Kernel().CurrentProcess()->GetRandomEntropy(info_sub_id);          return ResultSuccess; -    case GetInfoType::PrivilegedProcessId: +    case InfoType::InitialProcessIdRange:          LOG_WARNING(Kernel_SVC,                      "(STUBBED) Attempted to query privileged process id bounds, returned 0");          *result = 0;          return ResultSuccess; -    case GetInfoType::ThreadTickCount: { +    case InfoType::ThreadTickCount: {          constexpr u64 num_cpus = 4;          if (info_sub_id != 0xFFFFFFFFFFFFFFFF && info_sub_id >= num_cpus) {              LOG_ERROR(Kernel_SVC, "Core count is out of range, expected {} but got {}", num_cpus, @@ -1026,7 +996,7 @@ static Result GetInfo(Core::System& system, u64* result, u64 info_id, Handle han          *result = out_ticks;          return ResultSuccess;      } -    case GetInfoType::IdleTickCount: { +    case InfoType::IdleTickCount: {          // Verify the input handle is invalid.          R_UNLESS(handle == InvalidHandle, ResultInvalidHandle); @@ -1040,7 +1010,7 @@ static Result GetInfo(Core::System& system, u64* result, u64 info_id, Handle han          *result = system.Kernel().CurrentScheduler()->GetIdleThread()->GetCpuTime();          return ResultSuccess;      } -    case GetInfoType::MesosphereCurrentProcess: { +    case InfoType::MesosphereCurrentProcess: {          // Verify the input handle is invalid.          R_UNLESS(handle == InvalidHandle, ResultInvalidHandle); diff --git a/src/core/hle/service/set/set.cpp b/src/core/hle/service/set/set.cpp index 4f1a8d6b7..16c5eaf75 100644 --- a/src/core/hle/service/set/set.cpp +++ b/src/core/hle/service/set/set.cpp @@ -191,6 +191,13 @@ void SET::GetKeyCodeMap2(Kernel::HLERequestContext& ctx) {      GetKeyCodeMapImpl(ctx);  } +void SET::GetDeviceNickName(Kernel::HLERequestContext& ctx) { +    LOG_DEBUG(Service_SET, "called"); +    IPC::ResponseBuilder rb{ctx, 2}; +    rb.Push(ResultSuccess); +    ctx.WriteBuffer(Settings::values.device_name.GetValue()); +} +  SET::SET(Core::System& system_) : ServiceFramework{system_, "set"} {      // clang-format off      static const FunctionInfo functions[] = { @@ -205,7 +212,7 @@ SET::SET(Core::System& system_) : ServiceFramework{system_, "set"} {          {8, &SET::GetQuestFlag, "GetQuestFlag"},          {9, &SET::GetKeyCodeMap2, "GetKeyCodeMap2"},          {10, nullptr, "GetFirmwareVersionForDebug"}, -        {11, nullptr, "GetDeviceNickName"}, +        {11, &SET::GetDeviceNickName, "GetDeviceNickName"},      };      // clang-format on diff --git a/src/core/hle/service/set/set.h b/src/core/hle/service/set/set.h index 60cad3e6f..375975711 100644 --- a/src/core/hle/service/set/set.h +++ b/src/core/hle/service/set/set.h @@ -50,6 +50,7 @@ private:      void GetRegionCode(Kernel::HLERequestContext& ctx);      void GetKeyCodeMap(Kernel::HLERequestContext& ctx);      void GetKeyCodeMap2(Kernel::HLERequestContext& ctx); +    void GetDeviceNickName(Kernel::HLERequestContext& ctx);  };  } // namespace Service::Set diff --git a/src/core/hle/service/set/set_sys.cpp b/src/core/hle/service/set/set_sys.cpp index d7cea6aac..94c20edda 100644 --- a/src/core/hle/service/set/set_sys.cpp +++ b/src/core/hle/service/set/set_sys.cpp @@ -3,6 +3,7 @@  #include "common/assert.h"  #include "common/logging/log.h" +#include "common/settings.h"  #include "core/file_sys/errors.h"  #include "core/file_sys/system_archive/system_version.h"  #include "core/hle/ipc_helpers.h" @@ -176,6 +177,13 @@ void SET_SYS::GetSettingsItemValue(Kernel::HLERequestContext& ctx) {      rb.Push(response);  } +void SET_SYS::GetDeviceNickName(Kernel::HLERequestContext& ctx) { +    LOG_DEBUG(Service_SET, "called"); +    IPC::ResponseBuilder rb{ctx, 2}; +    rb.Push(ResultSuccess); +    ctx.WriteBuffer(::Settings::values.device_name.GetValue()); +} +  SET_SYS::SET_SYS(Core::System& system_) : ServiceFramework{system_, "set:sys"} {      // clang-format off      static const FunctionInfo functions[] = { @@ -253,7 +261,7 @@ SET_SYS::SET_SYS(Core::System& system_) : ServiceFramework{system_, "set:sys"} {          {74, nullptr, "SetWirelessLanEnableFlag"},          {75, nullptr, "GetInitialLaunchSettings"},          {76, nullptr, "SetInitialLaunchSettings"}, -        {77, nullptr, "GetDeviceNickName"}, +        {77, &SET_SYS::GetDeviceNickName, "GetDeviceNickName"},          {78, nullptr, "SetDeviceNickName"},          {79, nullptr, "GetProductModel"},          {80, nullptr, "GetLdnChannel"}, diff --git a/src/core/hle/service/set/set_sys.h b/src/core/hle/service/set/set_sys.h index 258ef8c57..464ac3da1 100644 --- a/src/core/hle/service/set/set_sys.h +++ b/src/core/hle/service/set/set_sys.h @@ -29,6 +29,7 @@ private:      void GetFirmwareVersion2(Kernel::HLERequestContext& ctx);      void GetColorSetId(Kernel::HLERequestContext& ctx);      void SetColorSetId(Kernel::HLERequestContext& ctx); +    void GetDeviceNickName(Kernel::HLERequestContext& ctx);      ColorSet color_set = ColorSet::BasicWhite;  }; diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.cpp b/src/video_core/renderer_vulkan/vk_rasterizer.cpp index a8b82abae..4b7126c30 100644 --- a/src/video_core/renderer_vulkan/vk_rasterizer.cpp +++ b/src/video_core/renderer_vulkan/vk_rasterizer.cpp @@ -658,8 +658,7 @@ void RasterizerVulkan::BeginTransformFeedback() {          return;      }      UNIMPLEMENTED_IF(regs.IsShaderConfigEnabled(Maxwell::ShaderType::TessellationInit) || -                     regs.IsShaderConfigEnabled(Maxwell::ShaderType::Tessellation) || -                     regs.IsShaderConfigEnabled(Maxwell::ShaderType::Geometry)); +                     regs.IsShaderConfigEnabled(Maxwell::ShaderType::Tessellation));      scheduler.Record(          [](vk::CommandBuffer cmdbuf) { cmdbuf.BeginTransformFeedbackEXT(0, 0, nullptr, nullptr); });  } diff --git a/src/yuzu/bootmanager.cpp b/src/yuzu/bootmanager.cpp index 09959d154..6afbdaf12 100644 --- a/src/yuzu/bootmanager.cpp +++ b/src/yuzu/bootmanager.cpp @@ -79,6 +79,11 @@ void EmuThread::run() {      system.GetCpuManager().OnGpuReady(); +    system.RegisterExitCallback([this]() { +        stop_source.request_stop(); +        SetRunning(false); +    }); +      // Holds whether the cpu was running during the last iteration,      // so that the DebugModeLeft signal can be emitted before the      // next execution step diff --git a/src/yuzu/bootmanager.h b/src/yuzu/bootmanager.h index 4dec2da2a..5bbcf61f7 100644 --- a/src/yuzu/bootmanager.h +++ b/src/yuzu/bootmanager.h @@ -84,9 +84,10 @@ public:      }      /** -     * Requests for the emulation thread to stop running +     * Requests for the emulation thread to immediately stop running       */ -    void RequestStop() { +    void ForceStop() { +        LOG_WARNING(Frontend, "Force stopping EmuThread");          stop_source.request_stop();          SetRunning(false);      } diff --git a/src/yuzu/configuration/config.cpp b/src/yuzu/configuration/config.cpp index 90fb4b0a4..a8d47a2f9 100644 --- a/src/yuzu/configuration/config.cpp +++ b/src/yuzu/configuration/config.cpp @@ -783,6 +783,8 @@ void Config::ReadSystemValues() {          }      } +    ReadBasicSetting(Settings::values.device_name); +      if (global) {          ReadBasicSetting(Settings::values.current_user);          Settings::values.current_user = std::clamp<int>(Settings::values.current_user.GetValue(), 0, @@ -1405,6 +1407,7 @@ void Config::SaveSystemValues() {                   Settings::values.rng_seed.UsingGlobal());      WriteSetting(QStringLiteral("rng_seed"), Settings::values.rng_seed.GetValue(global).value_or(0),                   0, Settings::values.rng_seed.UsingGlobal()); +    WriteBasicSetting(Settings::values.device_name);      if (global) {          WriteBasicSetting(Settings::values.current_user); diff --git a/src/yuzu/configuration/configure_system.cpp b/src/yuzu/configuration/configure_system.cpp index bc9d9d77a..9b14e5903 100644 --- a/src/yuzu/configuration/configure_system.cpp +++ b/src/yuzu/configuration/configure_system.cpp @@ -72,6 +72,8 @@ void ConfigureSystem::SetConfiguration() {      ui->custom_rtc_checkbox->setChecked(Settings::values.custom_rtc.has_value());      ui->custom_rtc_edit->setEnabled(Settings::values.custom_rtc.has_value());      ui->custom_rtc_edit->setDateTime(QDateTime::fromSecsSinceEpoch(rtc_time)); +    ui->device_name_edit->setText( +        QString::fromUtf8(Settings::values.device_name.GetValue().c_str()));      if (Settings::IsConfiguringGlobal()) {          ui->combo_language->setCurrentIndex(Settings::values.language_index.GetValue()); @@ -115,6 +117,8 @@ void ConfigureSystem::ApplyConfiguration() {          }      } +    Settings::values.device_name = ui->device_name_edit->text().toStdString(); +      if (!enabled) {          return;      } diff --git a/src/yuzu/configuration/configure_system.ui b/src/yuzu/configuration/configure_system.ui index b234ea87b..46892f5c1 100644 --- a/src/yuzu/configuration/configure_system.ui +++ b/src/yuzu/configuration/configure_system.ui @@ -432,6 +432,13 @@              </property>             </widget>            </item> +          <item row="7" column="0"> +           <widget class="QLabel" name="device_name_label"> +            <property name="text"> +             <string>Device Name</string> +            </property> +           </widget> +          </item>            <item row="3" column="1">             <widget class="QComboBox" name="combo_sound">              <item> @@ -476,6 +483,13 @@              </property>             </widget>            </item> +          <item row="7" column="1"> +           <widget class="QLineEdit" name="device_name_edit"> +            <property name="maxLength"> +             <number>128</number> +            </property> +           </widget> +          </item>            <item row="6" column="1">             <widget class="QLineEdit" name="rng_seed_edit">              <property name="sizePolicy"> diff --git a/src/yuzu/game_list.cpp b/src/yuzu/game_list.cpp index 5c33c1b0f..22aa19c56 100644 --- a/src/yuzu/game_list.cpp +++ b/src/yuzu/game_list.cpp @@ -554,6 +554,12 @@ void GameList::AddGamePopup(QMenu& context_menu, u64 program_id, const std::stri      QAction* dump_romfs_sdmc = dump_romfs_menu->addAction(tr("Dump RomFS to SDMC"));      QAction* copy_tid = context_menu.addAction(tr("Copy Title ID to Clipboard"));      QAction* navigate_to_gamedb_entry = context_menu.addAction(tr("Navigate to GameDB entry")); +#ifndef WIN32 +    QMenu* shortcut_menu = context_menu.addMenu(tr("Create Shortcut")); +    QAction* create_desktop_shortcut = shortcut_menu->addAction(tr("Add to Desktop")); +    QAction* create_applications_menu_shortcut = +        shortcut_menu->addAction(tr("Add to Applications Menu")); +#endif      context_menu.addSeparator();      QAction* properties = context_menu.addAction(tr("Properties")); @@ -619,6 +625,14 @@ void GameList::AddGamePopup(QMenu& context_menu, u64 program_id, const std::stri      connect(navigate_to_gamedb_entry, &QAction::triggered, [this, program_id]() {          emit NavigateToGamedbEntryRequested(program_id, compatibility_list);      }); +#ifndef WIN32 +    connect(create_desktop_shortcut, &QAction::triggered, [this, program_id, path]() { +        emit CreateShortcut(program_id, path, GameListShortcutTarget::Desktop); +    }); +    connect(create_applications_menu_shortcut, &QAction::triggered, [this, program_id, path]() { +        emit CreateShortcut(program_id, path, GameListShortcutTarget::Applications); +    }); +#endif      connect(properties, &QAction::triggered,              [this, path]() { emit OpenPerGameGeneralRequested(path); });  }; diff --git a/src/yuzu/game_list.h b/src/yuzu/game_list.h index cdf085019..f7ff93ed9 100644 --- a/src/yuzu/game_list.h +++ b/src/yuzu/game_list.h @@ -52,6 +52,11 @@ enum class DumpRomFSTarget {      SDMC,  }; +enum class GameListShortcutTarget { +    Desktop, +    Applications, +}; +  enum class InstalledEntryType {      Game,      Update, @@ -108,6 +113,8 @@ signals:                               const std::string& game_path);      void DumpRomFSRequested(u64 program_id, const std::string& game_path, DumpRomFSTarget target);      void CopyTIDRequested(u64 program_id); +    void CreateShortcut(u64 program_id, const std::string& game_path, +                        GameListShortcutTarget target);      void NavigateToGamedbEntryRequested(u64 program_id,                                          const CompatibilityList& compatibility_list);      void OpenPerGameGeneralRequested(const std::string& file); diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp index 885e24990..70552bdb8 100644 --- a/src/yuzu/main.cpp +++ b/src/yuzu/main.cpp @@ -4,6 +4,8 @@  #include <cinttypes>  #include <clocale>  #include <cmath> +#include <fstream> +#include <iostream>  #include <memory>  #include <thread>  #ifdef __APPLE__ @@ -1249,6 +1251,7 @@ void GMainWindow::ConnectWidgetEvents() {      connect(game_list, &GameList::CopyTIDRequested, this, &GMainWindow::OnGameListCopyTID);      connect(game_list, &GameList::NavigateToGamedbEntryRequested, this,              &GMainWindow::OnGameListNavigateToGamedbEntry); +    connect(game_list, &GameList::CreateShortcut, this, &GMainWindow::OnGameListCreateShortcut);      connect(game_list, &GameList::AddDirectory, this, &GMainWindow::OnGameListAddDirectory);      connect(game_list_placeholder, &GameListPlaceholder::AddDirectory, this,              &GMainWindow::OnGameListAddDirectory); @@ -1707,9 +1710,6 @@ void GMainWindow::BootGame(const QString& filename, u64 program_id, std::size_t      system->RegisterExecuteProgramCallback(          [this](std::size_t program_index_) { render_window->ExecuteProgram(program_index_); }); -    // Register an Exit callback such that Core can exit the currently running application. -    system->RegisterExitCallback([this]() { render_window->Exit(); }); -      connect(render_window, &GRenderWindow::Closed, this, &GMainWindow::OnStopGame);      connect(render_window, &GRenderWindow::MouseActivity, this, &GMainWindow::OnMouseActivity);      // BlockingQueuedConnection is important here, it makes sure we've finished refreshing our views @@ -1796,12 +1796,16 @@ void GMainWindow::ShutdownGame() {      system->SetShuttingDown(true);      system->DetachDebugger();      discord_rpc->Pause(); -    emu_thread->RequestStop(); + +    RequestGameExit();      emit EmulationStopping();      // Wait for emulation thread to complete and delete it -    emu_thread->wait(); +    if (!emu_thread->wait(5000)) { +        emu_thread->ForceStop(); +        emu_thread->wait(); +    }      emu_thread = nullptr;      emulation_running = false; @@ -2378,6 +2382,152 @@ void GMainWindow::OnGameListNavigateToGamedbEntry(u64 program_id,      QDesktopServices::openUrl(QUrl(QStringLiteral("https://yuzu-emu.org/game/") + directory));  } +void GMainWindow::OnGameListCreateShortcut(u64 program_id, const std::string& game_path, +                                           GameListShortcutTarget target) { +    // Get path to yuzu executable +    const QStringList args = QApplication::arguments(); +    std::filesystem::path yuzu_command = args[0].toStdString(); + +#if defined(__linux__) || defined(__FreeBSD__) +    // If relative path, make it an absolute path +    if (yuzu_command.c_str()[0] == '.') { +        yuzu_command = Common::FS::GetCurrentDir() / yuzu_command; +    } + +#if defined(__linux__) +    // Warn once if we are making a shortcut to a volatile AppImage +    const std::string appimage_ending = +        std::string(Common::g_scm_rev).substr(0, 9).append(".AppImage"); +    if (yuzu_command.string().ends_with(appimage_ending) && +        !UISettings::values.shortcut_already_warned) { +        if (QMessageBox::warning(this, tr("Create Shortcut"), +                                 tr("This will create a shortcut to the current AppImage. This may " +                                    "not work well if you update. Continue?"), +                                 QMessageBox::StandardButton::Ok | +                                     QMessageBox::StandardButton::Cancel) == +            QMessageBox::StandardButton::Cancel) { +            return; +        } +        UISettings::values.shortcut_already_warned = true; +    } +#endif // __linux__ +#endif // __linux__ || __FreeBSD__ + +    std::filesystem::path target_directory{}; +    // Determine target directory for shortcut +#if defined(__linux__) || defined(__FreeBSD__) +    const char* home = std::getenv("HOME"); +    const std::filesystem::path home_path = (home == nullptr ? "~" : home); +    const char* xdg_data_home = std::getenv("XDG_DATA_HOME"); + +    if (target == GameListShortcutTarget::Desktop) { +        target_directory = home_path / "Desktop"; +        if (!Common::FS::IsDir(target_directory)) { +            QMessageBox::critical( +                this, tr("Create Shortcut"), +                tr("Cannot create shortcut on desktop. Path \"%1\" does not exist.") +                    .arg(QString::fromStdString(target_directory)), +                QMessageBox::StandardButton::Ok); +            return; +        } +    } else if (target == GameListShortcutTarget::Applications) { +        target_directory = (xdg_data_home == nullptr ? home_path / ".local/share" : xdg_data_home) / +                           "applications"; +        if (!Common::FS::CreateDirs(target_directory)) { +            QMessageBox::critical(this, tr("Create Shortcut"), +                                  tr("Cannot create shortcut in applications menu. Path \"%1\" " +                                     "does not exist and cannot be created.") +                                      .arg(QString::fromStdString(target_directory)), +                                  QMessageBox::StandardButton::Ok); +            return; +        } +    } +#endif + +    const std::string game_file_name = std::filesystem::path(game_path).filename().string(); +    // Determine full paths for icon and shortcut +#if defined(__linux__) || defined(__FreeBSD__) +    std::filesystem::path system_icons_path = +        (xdg_data_home == nullptr ? home_path / ".local/share/" : xdg_data_home) / +        "icons/hicolor/256x256"; +    if (!Common::FS::CreateDirs(system_icons_path)) { +        QMessageBox::critical( +            this, tr("Create Icon"), +            tr("Cannot create icon file. Path \"%1\" does not exist and cannot be created.") +                .arg(QString::fromStdString(system_icons_path)), +            QMessageBox::StandardButton::Ok); +        return; +    } +    std::filesystem::path icon_path = +        system_icons_path / (program_id == 0 ? fmt::format("yuzu-{}.png", game_file_name) +                                             : fmt::format("yuzu-{:016X}.png", program_id)); +    const std::filesystem::path shortcut_path = +        target_directory / (program_id == 0 ? fmt::format("yuzu-{}.desktop", game_file_name) +                                            : fmt::format("yuzu-{:016X}.desktop", program_id)); +#else +    const std::filesystem::path icon_path{}; +    const std::filesystem::path shortcut_path{}; +#endif + +    // Get title from game file +    const FileSys::PatchManager pm{program_id, system->GetFileSystemController(), +                                   system->GetContentProvider()}; +    const auto control = pm.GetControlMetadata(); +    const auto loader = Loader::GetLoader(*system, vfs->OpenFile(game_path, FileSys::Mode::Read)); + +    std::string title{fmt::format("{:016X}", program_id)}; + +    if (control.first != nullptr) { +        title = control.first->GetApplicationName(); +    } else { +        loader->ReadTitle(title); +    } + +    // Get icon from game file +    std::vector<u8> icon_image_file{}; +    if (control.second != nullptr) { +        icon_image_file = control.second->ReadAllBytes(); +    } else if (loader->ReadIcon(icon_image_file) != Loader::ResultStatus::Success) { +        LOG_WARNING(Frontend, "Could not read icon from {:s}", game_path); +    } + +    QImage icon_jpeg = +        QImage::fromData(icon_image_file.data(), static_cast<int>(icon_image_file.size())); +#if defined(__linux__) || defined(__FreeBSD__) +    // Convert and write the icon as a PNG +    if (!icon_jpeg.save(QString::fromStdString(icon_path.string()))) { +        LOG_ERROR(Frontend, "Could not write icon as PNG to file"); +    } else { +        LOG_INFO(Frontend, "Wrote an icon to {}", icon_path.string()); +    } +#endif // __linux__ + +#if defined(__linux__) || defined(__FreeBSD__) +    const std::string comment = +        tr("Start %1 with the yuzu Emulator").arg(QString::fromStdString(title)).toStdString(); +    const std::string arguments = fmt::format("-g \"{:s}\"", game_path); +    const std::string categories = "Game;Emulator;Qt;"; +    const std::string keywords = "Switch;Nintendo;"; +#else +    const std::string comment{}; +    const std::string arguments{}; +    const std::string categories{}; +    const std::string keywords{}; +#endif +    if (!CreateShortcut(shortcut_path.string(), title, comment, icon_path.string(), +                        yuzu_command.string(), arguments, categories, keywords)) { +        QMessageBox::critical(this, tr("Create Shortcut"), +                              tr("Failed to create a shortcut at %1") +                                  .arg(QString::fromStdString(shortcut_path.string()))); +        return; +    } + +    LOG_INFO(Frontend, "Wrote a shortcut to {}", shortcut_path.string()); +    QMessageBox::information( +        this, tr("Create Shortcut"), +        tr("Successfully created a shortcut to %1").arg(QString::fromStdString(title))); +} +  void GMainWindow::OnGameListOpenDirectory(const QString& directory) {      std::filesystem::path fs_path;      if (directory == QStringLiteral("SDMC")) { @@ -3301,6 +3451,38 @@ void GMainWindow::OpenPerGameConfiguration(u64 title_id, const std::string& file      }  } +bool GMainWindow::CreateShortcut(const std::string& shortcut_path, const std::string& title, +                                 const std::string& comment, const std::string& icon_path, +                                 const std::string& command, const std::string& arguments, +                                 const std::string& categories, const std::string& keywords) { +#if defined(__linux__) || defined(__FreeBSD__) +    // This desktop file template was writting referencing +    // https://specifications.freedesktop.org/desktop-entry-spec/desktop-entry-spec-1.0.html +    std::string shortcut_contents{}; +    shortcut_contents.append("[Desktop Entry]\n"); +    shortcut_contents.append("Type=Application\n"); +    shortcut_contents.append("Version=1.0\n"); +    shortcut_contents.append(fmt::format("Name={:s}\n", title)); +    shortcut_contents.append(fmt::format("Comment={:s}\n", comment)); +    shortcut_contents.append(fmt::format("Icon={:s}\n", icon_path)); +    shortcut_contents.append(fmt::format("TryExec={:s}\n", command)); +    shortcut_contents.append(fmt::format("Exec={:s} {:s}\n", command, arguments)); +    shortcut_contents.append(fmt::format("Categories={:s}\n", categories)); +    shortcut_contents.append(fmt::format("Keywords={:s}\n", keywords)); + +    std::ofstream shortcut_stream(shortcut_path); +    if (!shortcut_stream.is_open()) { +        LOG_WARNING(Common, "Failed to create file {:s}", shortcut_path); +        return false; +    } +    shortcut_stream << shortcut_contents; +    shortcut_stream.close(); + +    return true; +#endif +    return false; +} +  void GMainWindow::OnLoadAmiibo() {      if (emu_thread == nullptr || !emu_thread->IsRunning()) {          return; diff --git a/src/yuzu/main.h b/src/yuzu/main.h index 27644fae5..1047ba276 100644 --- a/src/yuzu/main.h +++ b/src/yuzu/main.h @@ -38,6 +38,7 @@ class QProgressDialog;  class WaitTreeWidget;  enum class GameListOpenTarget;  enum class GameListRemoveTarget; +enum class GameListShortcutTarget;  enum class DumpRomFSTarget;  enum class InstalledEntryType;  class GameListPlaceholder; @@ -293,6 +294,8 @@ private slots:      void OnGameListCopyTID(u64 program_id);      void OnGameListNavigateToGamedbEntry(u64 program_id,                                           const CompatibilityList& compatibility_list); +    void OnGameListCreateShortcut(u64 program_id, const std::string& game_path, +                                  GameListShortcutTarget target);      void OnGameListOpenDirectory(const QString& directory);      void OnGameListAddDirectory();      void OnGameListShowList(bool show); @@ -366,6 +369,10 @@ private:      bool CheckDarkMode();      QString GetTasStateDescription() const; +    bool CreateShortcut(const std::string& shortcut_path, const std::string& title, +                        const std::string& comment, const std::string& icon_path, +                        const std::string& command, const std::string& arguments, +                        const std::string& categories, const std::string& keywords);      std::unique_ptr<Ui::MainWindow> ui; diff --git a/src/yuzu/uisettings.h b/src/yuzu/uisettings.h index 452038cd9..2006b883e 100644 --- a/src/yuzu/uisettings.h +++ b/src/yuzu/uisettings.h @@ -138,6 +138,7 @@ struct Values {      bool configuration_applied;      bool reset_to_defaults; +    bool shortcut_already_warned{false};      Settings::Setting<bool> disable_web_applet{true, "disable_web_applet"};  }; | 
