mirror of
https://github.com/TwilitRealm/dusklight
synced 2026-08-03 00:16:36 -04:00
Merge branch 'main' into 26-04-01-quick-transform
This commit is contained in:
@@ -72,6 +72,15 @@ static PCCondData& GetCondData(OSCond* cond) {
|
||||
return *it->second;
|
||||
}
|
||||
|
||||
void ClearCondMap() {
|
||||
std::lock_guard<std::mutex> lock(GetCondMapMutex());
|
||||
auto& map = GetCondMap();
|
||||
for (auto& pair : map) {
|
||||
pair.second->cv.notify_all();
|
||||
}
|
||||
map.clear();
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// C API functions
|
||||
// ============================================================================
|
||||
|
||||
+24
-64
@@ -17,6 +17,7 @@
|
||||
#include <memory>
|
||||
|
||||
#include "JSystem/JKernel/JKRHeap.h"
|
||||
#include "dusk/main.h"
|
||||
#include "dusk/os.h"
|
||||
|
||||
#if _WIN32
|
||||
@@ -38,6 +39,13 @@ struct PCThreadData {
|
||||
void* param;
|
||||
bool started = false;
|
||||
bool suspended = false;
|
||||
|
||||
~PCThreadData() {
|
||||
if (dusk::IsShuttingDown) {
|
||||
// Don't care about threads if we're shutting down.
|
||||
nativeThread.detach();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Lazy-initialized to avoid DLL static init crashes (used before DllMain completes)
|
||||
@@ -50,6 +58,16 @@ static std::unordered_map<OSThread*, std::unique_ptr<PCThreadData>>& GetThreadDa
|
||||
return map;
|
||||
}
|
||||
|
||||
static PCThreadData* GetThreadData(OSThread* thread) {
|
||||
std::lock_guard mapLock(GetThreadDataMutex());
|
||||
auto it = GetThreadDataMap().find(thread);
|
||||
if (it != GetThreadDataMap().end()) {
|
||||
return it->second.get();
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Side-table for OSThreadQueue -> condition_variable (for OSSleepThread/OSWakeupThread)
|
||||
static std::mutex& GetQueueCvMutex() {
|
||||
static std::mutex mtx;
|
||||
@@ -85,8 +103,6 @@ static OSThread sDefaultThread;
|
||||
static u8 sDefaultStack[64 * 1024];
|
||||
static u32 sDefaultStackEnd = OS_THREAD_STACK_MAGIC;
|
||||
|
||||
OSThreadQueue __OSActiveThreadQueue;
|
||||
|
||||
// Global interrupt mutex (coarse-grained lock replacing interrupt disable)
|
||||
// Lazy-initialized to avoid DLL static init crashes
|
||||
static std::recursive_mutex& GetInterruptMutex() {
|
||||
@@ -108,36 +124,6 @@ static OSSwitchThreadCallback sSwitchThreadCallback = nullptr;
|
||||
// Internal helpers
|
||||
// ============================================================================
|
||||
|
||||
// Linked list macros for the active thread queue
|
||||
static void EnqueueActive(OSThread* thread) {
|
||||
OSThread* prev = __OSActiveThreadQueue.tail;
|
||||
if (prev == nullptr) {
|
||||
__OSActiveThreadQueue.head = thread;
|
||||
} else {
|
||||
prev->linkActive.next = thread;
|
||||
}
|
||||
thread->linkActive.prev = prev;
|
||||
thread->linkActive.next = nullptr;
|
||||
__OSActiveThreadQueue.tail = thread;
|
||||
}
|
||||
|
||||
static void DequeueActive(OSThread* thread) {
|
||||
OSThread* next = thread->linkActive.next;
|
||||
OSThread* prev = thread->linkActive.prev;
|
||||
if (next == nullptr) {
|
||||
__OSActiveThreadQueue.tail = prev;
|
||||
} else {
|
||||
next->linkActive.prev = prev;
|
||||
}
|
||||
if (prev == nullptr) {
|
||||
__OSActiveThreadQueue.head = next;
|
||||
} else {
|
||||
prev->linkActive.next = next;
|
||||
}
|
||||
thread->linkActive.next = nullptr;
|
||||
thread->linkActive.prev = nullptr;
|
||||
}
|
||||
|
||||
// Thread entry wrapper - runs on the new std::thread
|
||||
static void ThreadEntryWrapper(OSThread* thread, PCThreadData* data) {
|
||||
// Set thread-local pointer
|
||||
@@ -195,8 +181,6 @@ void __OSThreadInit(void) {
|
||||
tls_currentThread = &sDefaultThread;
|
||||
|
||||
// Active queue
|
||||
OSInitThreadQueue(&__OSActiveThreadQueue);
|
||||
EnqueueActive(&sDefaultThread);
|
||||
sActiveThreadCount = 1;
|
||||
|
||||
OSReport("[PC-OSThread] Thread system initialized (multi-threaded mode)\n");
|
||||
@@ -273,7 +257,6 @@ int OSCreateThread(OSThread* thread, void* (*func)(void*), void* param,
|
||||
}
|
||||
|
||||
// Add to active queue
|
||||
EnqueueActive(thread);
|
||||
sActiveThreadCount++;
|
||||
|
||||
OSReport("[PC-OSThread] Created thread %p (priority=%d, stackSize=%u)\n",
|
||||
@@ -353,16 +336,7 @@ s32 OSResumeThread(OSThread* thread) {
|
||||
|
||||
// Only wake up if suspend count drops to 0
|
||||
if (thread->suspend == 0) {
|
||||
PCThreadData* data = nullptr;
|
||||
|
||||
// Lock the global map to safely retrieve our thread data pointer
|
||||
{
|
||||
std::lock_guard<std::mutex> mapLock(GetThreadDataMutex());
|
||||
auto it = GetThreadDataMap().find(thread);
|
||||
if (it != GetThreadDataMap().end()) {
|
||||
data = it->second.get();
|
||||
}
|
||||
}
|
||||
PCThreadData* data = GetThreadData(thread);
|
||||
|
||||
if (data) {
|
||||
// Lock the specific thread mutex to safely modify state and notify
|
||||
@@ -377,7 +351,6 @@ s32 OSResumeThread(OSThread* thread) {
|
||||
threadLock.unlock();
|
||||
|
||||
data->nativeThread = std::thread(ThreadEntryWrapper, thread, data);
|
||||
data->nativeThread.detach();
|
||||
OSReport("[PC-OSThread] Started thread %p\n", thread);
|
||||
} else {
|
||||
// Resume from suspension: signal the condition variable
|
||||
@@ -400,16 +373,7 @@ s32 OSSuspendThread(OSThread* thread) {
|
||||
|
||||
// If transitioning from running (0) to suspended (1)
|
||||
if (prevSuspend == 0) {
|
||||
PCThreadData* data = nullptr;
|
||||
|
||||
// Lock the global map to find our thread data
|
||||
{
|
||||
std::lock_guard<std::mutex> mapLock(GetThreadDataMutex());
|
||||
auto it = GetThreadDataMap().find(thread);
|
||||
if (it != GetThreadDataMap().end()) {
|
||||
data = it->second.get();
|
||||
}
|
||||
}
|
||||
PCThreadData* data = GetThreadData(thread);
|
||||
|
||||
if (data && data->started) {
|
||||
std::unique_lock<std::mutex> threadLock(data->mtx);
|
||||
@@ -497,7 +461,6 @@ void OSExitThread(void* val) {
|
||||
currentThread->val = val;
|
||||
|
||||
if (currentThread->attr & OS_THREAD_ATTR_DETACH) {
|
||||
DequeueActive(currentThread);
|
||||
currentThread->state = 0;
|
||||
} else {
|
||||
currentThread->state = OS_THREAD_STATE_MORIBUND;
|
||||
@@ -509,10 +472,10 @@ void OSExitThread(void* val) {
|
||||
}
|
||||
|
||||
void OSCancelThread(OSThread* thread) {
|
||||
CRASH("OSCancelThread not implemented");
|
||||
if (!thread) return;
|
||||
|
||||
if (thread->attr & OS_THREAD_ATTR_DETACH) {
|
||||
DequeueActive(thread);
|
||||
thread->state = 0;
|
||||
} else {
|
||||
thread->state = OS_THREAD_STATE_MORIBUND;
|
||||
@@ -523,11 +486,11 @@ void OSCancelThread(OSThread* thread) {
|
||||
}
|
||||
|
||||
void OSDetachThread(OSThread* thread) {
|
||||
CRASH("OSDetachThread not implemented");
|
||||
if (!thread) return;
|
||||
thread->attr |= OS_THREAD_ATTR_DETACH;
|
||||
|
||||
if (thread->state == OS_THREAD_STATE_MORIBUND) {
|
||||
DequeueActive(thread);
|
||||
thread->state = 0;
|
||||
}
|
||||
OSWakeupThread(&thread->queueJoin);
|
||||
@@ -536,17 +499,14 @@ void OSDetachThread(OSThread* thread) {
|
||||
int OSJoinThread(OSThread* thread, void* val) {
|
||||
if (!thread) return 0;
|
||||
|
||||
if (!(thread->attr & OS_THREAD_ATTR_DETACH) &&
|
||||
thread->state != OS_THREAD_STATE_MORIBUND &&
|
||||
thread->queueJoin.head == nullptr) {
|
||||
OSSleepThread(&thread->queueJoin);
|
||||
if (!(thread->attr & OS_THREAD_ATTR_DETACH)) {
|
||||
GetThreadData(thread)->nativeThread.join();
|
||||
}
|
||||
|
||||
if (thread->state == OS_THREAD_STATE_MORIBUND) {
|
||||
if (val) {
|
||||
*(s32*)val = (s32)(intptr_t)thread->val;
|
||||
}
|
||||
DequeueActive(thread);
|
||||
thread->state = 0;
|
||||
return 1;
|
||||
}
|
||||
|
||||
@@ -133,6 +133,20 @@ void RenderAudioSubframe() {
|
||||
|
||||
InterleaveOutputData(OutBuffer, OutInterleaveBuffer);
|
||||
|
||||
if (JASDriver::extMixCallback != nullptr && JASDriver::sMixMode == MIX_MODE_INTERLEAVE) {
|
||||
static_assert(OutputSubframe::NUM_CHANNELS == 2); // This code only works with Stereo so far.
|
||||
// NOTE: In the real game, this gets called on the entire audio frame, rather than the subframe.
|
||||
// That's probably more efficient, but I didn't wanna change the code to calculate the
|
||||
// entire audio buffers at once.
|
||||
// This is only used for the movie player, and it seems to work fine with the smaller calls.
|
||||
const auto mixData = JASDriver::extMixCallback(DSP_SUBFRAME_SIZE);
|
||||
if (mixData) {
|
||||
for (int i = 0; i < OutInterleaveBuffer.size(); i++) {
|
||||
OutInterleaveBuffer[i] += static_cast<f32>(mixData[i]) / static_cast<f32>(0x7FFF);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#if defined(DUSK_DUMP_AUDIO)
|
||||
outRaw.write((const char*)OutInterleaveBuffer.data(), sizeof(OutInterleaveBuffer));
|
||||
#endif
|
||||
|
||||
@@ -181,6 +181,7 @@ namespace dusk {
|
||||
if (ImGui::BeginMainMenuBar()) {
|
||||
m_menuGame.draw();
|
||||
m_menuTools.draw();
|
||||
m_menuEnhancements.draw();
|
||||
|
||||
ImGui::SetCursorPosX(ImGui::GetWindowWidth() - 80.0f);
|
||||
ImGuiIO& io = ImGui::GetIO();
|
||||
@@ -223,104 +224,3 @@ namespace dusk {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class Limiter
|
||||
{
|
||||
using delta_clock = std::chrono::high_resolution_clock;
|
||||
using duration_t = std::chrono::nanoseconds;
|
||||
|
||||
public:
|
||||
void Reset()
|
||||
{
|
||||
m_oldTime = delta_clock::now();
|
||||
}
|
||||
|
||||
void Sleep(duration_t targetFrameTime)
|
||||
{
|
||||
if (targetFrameTime.count() == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
auto start = delta_clock::now();
|
||||
duration_t adjustedSleepTime = SleepTime(targetFrameTime);
|
||||
if (adjustedSleepTime.count() > 0)
|
||||
{
|
||||
NanoSleep(adjustedSleepTime);
|
||||
duration_t overslept = TimeSince(start) - adjustedSleepTime;
|
||||
if (overslept < duration_t{ targetFrameTime })
|
||||
{
|
||||
m_overheadTimes[m_overheadTimeIdx] = overslept;
|
||||
m_overheadTimeIdx = (m_overheadTimeIdx + 1) % m_overheadTimes.size();
|
||||
}
|
||||
}
|
||||
Reset();
|
||||
}
|
||||
|
||||
duration_t SleepTime(duration_t targetFrameTime)
|
||||
{
|
||||
const auto sleepTime = duration_t{ targetFrameTime } - TimeSince(m_oldTime);
|
||||
m_overhead = std::accumulate(m_overheadTimes.begin(), m_overheadTimes.end(), duration_t{}) /
|
||||
m_overheadTimes.size();
|
||||
if (sleepTime > m_overhead)
|
||||
{
|
||||
return sleepTime - m_overhead;
|
||||
}
|
||||
return duration_t{ 0 };
|
||||
}
|
||||
|
||||
private:
|
||||
delta_clock::time_point m_oldTime;
|
||||
std::array<duration_t, 4> m_overheadTimes{};
|
||||
size_t m_overheadTimeIdx = 0;
|
||||
duration_t m_overhead = duration_t{ 0 };
|
||||
|
||||
duration_t TimeSince(delta_clock::time_point start)
|
||||
{
|
||||
return std::chrono::duration_cast<duration_t>(delta_clock::now() - start);
|
||||
}
|
||||
|
||||
#if _WIN32
|
||||
bool m_initialized;
|
||||
double m_countPerNs;
|
||||
|
||||
void NanoSleep(const duration_t duration)
|
||||
{
|
||||
if (!m_initialized)
|
||||
{
|
||||
LARGE_INTEGER freq;
|
||||
QueryPerformanceFrequency(&freq);
|
||||
m_countPerNs = static_cast<double>(freq.QuadPart) / 1000000000.0;
|
||||
m_initialized = true;
|
||||
}
|
||||
|
||||
DWORD ms = std::chrono::duration_cast<std::chrono::milliseconds>(duration).count();
|
||||
auto tickCount =
|
||||
static_cast<LONGLONG>(static_cast<double>(duration.count()) * m_countPerNs);
|
||||
LARGE_INTEGER count;
|
||||
QueryPerformanceCounter(&count);
|
||||
if (ms > 10)
|
||||
{
|
||||
// Adjust for Sleep overhead
|
||||
::Sleep(ms - 10);
|
||||
}
|
||||
auto end = count.QuadPart + tickCount;
|
||||
do
|
||||
{
|
||||
QueryPerformanceCounter(&count);
|
||||
} while (count.QuadPart < end);
|
||||
}
|
||||
#else
|
||||
void NanoSleep(const duration_t duration)
|
||||
{
|
||||
std::this_thread::sleep_for(duration);
|
||||
}
|
||||
#endif
|
||||
};
|
||||
|
||||
static Limiter g_frameLimiter;
|
||||
void frame_limiter()
|
||||
{
|
||||
g_frameLimiter.Sleep(
|
||||
std::chrono::duration_cast<std::chrono::nanoseconds>(std::chrono::seconds{ 1 }) / 60);
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
#include "imgui.h"
|
||||
#include "ImGuiMenuGame.hpp"
|
||||
#include "ImGuiMenuTools.hpp"
|
||||
#include "ImGuiMenuEnhancements.hpp"
|
||||
|
||||
namespace dusk {
|
||||
class ImGuiConsole {
|
||||
@@ -14,8 +15,6 @@ namespace dusk {
|
||||
ImGuiConsole();
|
||||
void draw();
|
||||
|
||||
bool isBloomEnabled() { return m_menuGame.isBloomEnabled(); }
|
||||
bool isWaterProjectionOffsetEnabled() { return m_menuGame.isWaterProjectionOffsetEnabled(); }
|
||||
ImGuiMenuTools::CollisionViewSettings& getCollisionViewSettings() { return m_menuTools.getCollisionViewSettings(); }
|
||||
|
||||
static bool CheckMenuViewToggle(ImGuiKey key, bool& active);
|
||||
@@ -25,6 +24,7 @@ namespace dusk {
|
||||
|
||||
ImGuiMenuGame m_menuGame;
|
||||
ImGuiMenuTools m_menuTools;
|
||||
ImGuiMenuEnhancements m_menuEnhancements;
|
||||
};
|
||||
|
||||
extern ImGuiConsole g_imguiConsole;
|
||||
|
||||
@@ -0,0 +1,56 @@
|
||||
#include "fmt/format.h"
|
||||
#include "imgui.h"
|
||||
#include "aurora/gfx.h"
|
||||
|
||||
#include "ImGuiConsole.hpp"
|
||||
#include "ImGuiMenuEnhancements.hpp"
|
||||
#include <imgui_internal.h>
|
||||
|
||||
namespace dusk {
|
||||
EnhancementsSettings ImGuiMenuEnhancements::m_enhancements = {
|
||||
.fastIronBoots = false,
|
||||
.invertCameraXAxis = false,
|
||||
.restoreWiiGlitches = false,
|
||||
.enableBloom = true,
|
||||
.useWaterProjectionOffset = false,
|
||||
};
|
||||
|
||||
ImGuiMenuEnhancements::ImGuiMenuEnhancements() {}
|
||||
|
||||
void ImGuiMenuEnhancements::draw() {
|
||||
if (ImGui::BeginMenu("Enhancements")) {
|
||||
if (ImGui::BeginMenu("Quality of Life")) {
|
||||
ImGui::Checkbox("Fast Iron Boots", &m_enhancements.fastIronBoots);
|
||||
ImGui::Checkbox("Invert Camera X Axis", &m_enhancements.invertCameraXAxis);
|
||||
ImGui::Checkbox("Quick Transform (R+Y)", &m_enhancements.quickTransform);
|
||||
ImGui::EndMenu();
|
||||
}
|
||||
|
||||
if (ImGui::BeginMenu("Graphics")) {
|
||||
ImGui::Checkbox("Native Bloom", &m_enhancements.enableBloom);
|
||||
ImGui::Checkbox("Water Projection Offset", &m_enhancements.useWaterProjectionOffset);
|
||||
if (ImGui::IsItemHovered()) {
|
||||
ImGui::SetTooltip("Adds GC-specific -0.01 transS offset\n"
|
||||
"that causes ~6px ghost artifacts in water reflections");
|
||||
}
|
||||
|
||||
ImGui::EndMenu();
|
||||
}
|
||||
|
||||
if (ImGui::BeginMenu("Restorations")) {
|
||||
ImGui::Checkbox("Restore Wii 1.0 Glitches", &m_enhancements.restoreWiiGlitches);
|
||||
if (ImGui::IsItemHovered()) {
|
||||
ImGui::SetTooltip("Restores patched glitches from Wii USA 1.0, the first released version");
|
||||
}
|
||||
|
||||
ImGui::EndMenu();
|
||||
}
|
||||
|
||||
if (ImGui::BeginMenu("Cheats")) {
|
||||
ImGui::EndMenu();
|
||||
}
|
||||
|
||||
ImGui::EndMenu();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
#ifndef DUSK_IMGUI_MENUENHANCEMENTS_HPP
|
||||
#define DUSK_IMGUI_MENUENHANCEMENTS_HPP
|
||||
|
||||
#include <aurora/aurora.h>
|
||||
#include <pad.h>
|
||||
#include <string>
|
||||
|
||||
#include "imgui.h"
|
||||
|
||||
namespace dusk {
|
||||
struct EnhancementsSettings {
|
||||
bool fastIronBoots;
|
||||
bool invertCameraXAxis;
|
||||
bool quickTransform;
|
||||
bool restoreWiiGlitches;
|
||||
bool enableBloom;
|
||||
bool useWaterProjectionOffset;
|
||||
};
|
||||
|
||||
class ImGuiMenuEnhancements {
|
||||
public:
|
||||
ImGuiMenuEnhancements();
|
||||
void draw();
|
||||
|
||||
static EnhancementsSettings m_enhancements;
|
||||
};
|
||||
}
|
||||
|
||||
#endif // DUSK_IMGUI_MENUENHANCEMENTS_HPP
|
||||
@@ -25,17 +25,8 @@ namespace dusk {
|
||||
|
||||
if (ImGui::BeginMenu("Graphics")) {
|
||||
if (ImGui::MenuItem("Toggle Fullscreen", "F11")) {
|
||||
m_graphicsSettings.m_fullscreen = !m_graphicsSettings.m_fullscreen;
|
||||
VISetWindowFullscreen(m_graphicsSettings.m_fullscreen);
|
||||
}
|
||||
|
||||
ImGui::Separator();
|
||||
|
||||
ImGui::Checkbox("Native Bloom", &m_graphicsSettings.m_enableBloom);
|
||||
ImGui::Checkbox("Water Projection Offset", &m_graphicsSettings.m_waterProjectionOffset);
|
||||
if (ImGui::IsItemHovered()) {
|
||||
ImGui::SetTooltip("Adds GC-specific -0.01 transS offset\n"
|
||||
"that causes ~6px ghost artifacts in water reflections");
|
||||
m_fullscreen = !m_fullscreen;
|
||||
VISetWindowFullscreen(m_fullscreen);
|
||||
}
|
||||
|
||||
ImGui::EndMenu();
|
||||
@@ -76,12 +67,6 @@ namespace dusk {
|
||||
ImGui::EndMenu();
|
||||
}
|
||||
|
||||
if (ImGui::BeginMenu("Tweaks")) {
|
||||
ImGui::MenuItem("Fast iron boots", nullptr, &tweaks::FastIronBoots);
|
||||
ImGui::MenuItem("Quick Transform (R+Y)", nullptr, &tweaks::QuickTransform);
|
||||
ImGui::EndMenu();
|
||||
}
|
||||
|
||||
ImGui::EndMenu();
|
||||
}
|
||||
|
||||
@@ -93,8 +78,8 @@ namespace dusk {
|
||||
}
|
||||
|
||||
if (ImGui::IsKeyPressed(ImGuiKey_F11)) {
|
||||
m_graphicsSettings.m_fullscreen = !m_graphicsSettings.m_fullscreen;
|
||||
VISetWindowFullscreen(m_graphicsSettings.m_fullscreen);
|
||||
m_fullscreen = !m_fullscreen;
|
||||
VISetWindowFullscreen(m_fullscreen);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -12,8 +12,6 @@ namespace dusk {
|
||||
public:
|
||||
ImGuiMenuGame();
|
||||
void draw();
|
||||
bool isBloomEnabled() { return m_graphicsSettings.m_enableBloom; }
|
||||
bool isWaterProjectionOffsetEnabled() { return m_graphicsSettings.m_waterProjectionOffset; }
|
||||
|
||||
void windowInputViewer();
|
||||
void windowControllerConfig();
|
||||
@@ -35,11 +33,7 @@ namespace dusk {
|
||||
int m_pendingPort = -1;
|
||||
} m_controllerConfig;
|
||||
|
||||
struct {
|
||||
bool m_enableBloom = 1;
|
||||
bool m_waterProjectionOffset = false;
|
||||
bool m_fullscreen = false;
|
||||
} m_graphicsSettings;
|
||||
bool m_fullscreen = false;
|
||||
|
||||
bool m_showControllerConfig = false;
|
||||
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
#include "dusk/layout.hpp"
|
||||
|
||||
using namespace dusk;
|
||||
|
||||
LayoutRect LayoutRect::FitRectInRect(
|
||||
const f32 widthOuter,
|
||||
const f32 heightOuter,
|
||||
const f32 widthInner,
|
||||
const f32 heightInner) {
|
||||
|
||||
// Try as if constrained vertically first.
|
||||
auto width = widthInner * (heightOuter / heightInner);
|
||||
auto height = heightOuter;
|
||||
if (width > widthOuter) {
|
||||
// Otherwise, constrained horizontally.
|
||||
width = widthOuter;
|
||||
height = heightOuter * (widthOuter / widthInner);
|
||||
}
|
||||
|
||||
// Center it
|
||||
const auto posX = (widthOuter - width) / 2;
|
||||
const auto posY = (heightOuter - height) / 2;
|
||||
|
||||
return {posX, posY, posX + width, posY + height};
|
||||
}
|
||||
+28
-5
@@ -11,7 +11,7 @@
|
||||
#include <unordered_map>
|
||||
#include <memory>
|
||||
#include <dusk/logging.h>
|
||||
|
||||
#include <dusk/main.h>
|
||||
|
||||
#ifndef _WIN32
|
||||
#include <sys/time.h>
|
||||
@@ -84,6 +84,16 @@ static PCMessageQueueData& GetMsgQueueData(OSMessageQueue* mq) {
|
||||
return *it->second;
|
||||
}
|
||||
|
||||
static void ClearMsgQueueMap() {
|
||||
std::lock_guard<std::mutex> lock(GetMsgQueueMapMutex());
|
||||
auto& map = GetMsgQueueMap();
|
||||
for (auto & [_, value] : map) {
|
||||
value->cvReceive.notify_all();
|
||||
value->cvSend.notify_all();
|
||||
}
|
||||
map.clear();
|
||||
}
|
||||
|
||||
void OSInitMessageQueue(OSMessageQueue* mq, void* msgArray, s32 msgCount) {
|
||||
if (!mq) return;
|
||||
mq->queueSend.head = mq->queueSend.tail = nullptr;
|
||||
@@ -104,7 +114,10 @@ int OSSendMessage(OSMessageQueue* mq, void* msg, s32 flags) {
|
||||
if (mq->usedCount >= mq->msgCount) {
|
||||
if (flags == OS_MESSAGE_NOBLOCK) return 0;
|
||||
// BLOCK: wait until space is available
|
||||
data.cvSend.wait(lock, [mq]() { return mq->usedCount < mq->msgCount; });
|
||||
data.cvSend.wait(lock, [mq] { return mq->usedCount < mq->msgCount || dusk::IsShuttingDown; });
|
||||
}
|
||||
if (dusk::IsShuttingDown) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
s32 idx = (mq->firstIndex + mq->usedCount) % mq->msgCount;
|
||||
@@ -124,7 +137,10 @@ int OSReceiveMessage(OSMessageQueue* mq, void* msg, s32 flags) {
|
||||
if (mq->usedCount == 0) {
|
||||
if (flags == OS_MESSAGE_NOBLOCK) return 0;
|
||||
// BLOCK: wait until a message arrives
|
||||
data.cvReceive.wait(lock, [mq]() { return mq->usedCount > 0; });
|
||||
data.cvReceive.wait(lock, [mq] { return mq->usedCount > 0 || dusk::IsShuttingDown; });
|
||||
}
|
||||
if (dusk::IsShuttingDown) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (msg) {
|
||||
@@ -146,7 +162,10 @@ int OSJamMessage(OSMessageQueue* mq, void* msg, s32 flags) {
|
||||
if (mq->usedCount >= mq->msgCount) {
|
||||
if (flags == OS_MESSAGE_NOBLOCK) return 0;
|
||||
// BLOCK: wait until space is available
|
||||
data.cvSend.wait(lock, [mq]() { return mq->usedCount < mq->msgCount; });
|
||||
data.cvSend.wait(lock, [mq] { return mq->usedCount < mq->msgCount || dusk::IsShuttingDown; });
|
||||
}
|
||||
if (dusk::IsShuttingDown) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Jam inserts at the front of the queue
|
||||
@@ -185,8 +204,12 @@ BOOL OSGetResetButtonState() { return FALSE; }
|
||||
BOOL OSInitFont(OSFontHeader* fontData) { return FALSE; }
|
||||
BOOL OSLink(OSModuleInfo* newModule, void* bss) { return TRUE; }
|
||||
|
||||
void ClearCondMap();
|
||||
void OSResetSystem(int reset, u32 resetCode, BOOL forceMenu) {
|
||||
OSReport("[PC] OSResetSystem called (reset=%d, code=%u)\n", reset, resetCode);
|
||||
dusk::IsShuttingDown = true;
|
||||
ClearMsgQueueMap();
|
||||
ClearCondMap();
|
||||
}
|
||||
|
||||
void OSSetStringTable(void* stringTable) {}
|
||||
@@ -998,7 +1021,7 @@ f32 GXGetYScaleFactor(u16 efbHeight, u16 xfbHeight) {
|
||||
void GXInitTexCacheRegion(GXTexRegion* region, GXBool is_32b_mipmap, u32 tmem_even,
|
||||
GXTexCacheSize size_even, u32 tmem_odd, GXTexCacheSize size_odd) {
|
||||
STUB_LOG();
|
||||
}
|
||||
}
|
||||
// XXX, this should be some struct?
|
||||
// GXRenderModeObj GXNtsc480IntDf;
|
||||
//GXRenderModeObj GXNtsc480Int;
|
||||
|
||||
Reference in New Issue
Block a user