mirror of
https://github.com/ryujinx-mirror/ryujinx.git
synced 2024-12-24 00:25:50 +00:00
01a4c80ed5
The configuration system was quite fragile and too dependent on everything, this fix #812 . The changes: The file configuration is now entirely independent from the internal configuration state. The file configuration is versioned (current version is 1). Every configuration elements are now reactive properties that the emulator can register on to handle initialization and configuration changes. The configuration system is now in Ryujinx.Common to be accessible on every projects. Discord integration is now independent from the UI and can be reloaded. The primary controller is now configurable at runtime (NOTE: the UI currently doesn't have any options to configure real controller). The logger is entirely reloadable. You can now hotplug your controller when the emulator is running. The logger now takes name for every LogTarget to make them removable at runtime. The logger now always add the default "console" target to avoid loosing early init logs. The configuration system now generates a default file configuration if it's missing or too new. General system stability improvements to enhance the user's experience
388 lines
11 KiB
C#
388 lines
11 KiB
C#
using OpenTK;
|
|
using OpenTK.Graphics;
|
|
using OpenTK.Input;
|
|
using Ryujinx.Configuration;
|
|
using Ryujinx.Graphics.Gal;
|
|
using Ryujinx.HLE;
|
|
using Ryujinx.HLE.Input;
|
|
using Ryujinx.Profiler.UI;
|
|
using Ryujinx.Ui;
|
|
using System;
|
|
using System.Threading;
|
|
|
|
using Stopwatch = System.Diagnostics.Stopwatch;
|
|
|
|
namespace Ryujinx.Ui
|
|
{
|
|
public class GlScreen : GameWindow
|
|
{
|
|
private const int TouchScreenWidth = 1280;
|
|
private const int TouchScreenHeight = 720;
|
|
|
|
private const int TargetFps = 60;
|
|
|
|
private Switch _device;
|
|
|
|
private IGalRenderer _renderer;
|
|
|
|
private HotkeyButtons _prevHotkeyButtons = 0;
|
|
|
|
private KeyboardState? _keyboard = null;
|
|
|
|
private MouseState? _mouse = null;
|
|
|
|
private Input.NpadController _primaryController;
|
|
|
|
private Thread _renderThread;
|
|
|
|
private bool _resizeEvent;
|
|
|
|
private bool _titleEvent;
|
|
|
|
private string _newTitle;
|
|
|
|
#if USE_PROFILING
|
|
private ProfileWindowManager _profileWindow;
|
|
#endif
|
|
|
|
public GlScreen(Switch device, IGalRenderer renderer)
|
|
: base(1280, 720,
|
|
new GraphicsMode(), "Ryujinx", 0,
|
|
DisplayDevice.Default, 3, 3,
|
|
GraphicsContextFlags.ForwardCompatible)
|
|
{
|
|
_device = device;
|
|
_renderer = renderer;
|
|
|
|
_primaryController = new Input.NpadController(ConfigurationState.Instance.Hid.JoystickControls);
|
|
|
|
Location = new Point(
|
|
(DisplayDevice.Default.Width / 2) - (Width / 2),
|
|
(DisplayDevice.Default.Height / 2) - (Height / 2));
|
|
|
|
#if USE_PROFILING
|
|
// Start profile window, it will handle itself from there
|
|
_profileWindow = new ProfileWindowManager();
|
|
#endif
|
|
}
|
|
|
|
private void RenderLoop()
|
|
{
|
|
MakeCurrent();
|
|
|
|
Stopwatch chrono = new Stopwatch();
|
|
|
|
chrono.Start();
|
|
|
|
long ticksPerFrame = Stopwatch.Frequency / TargetFps;
|
|
|
|
long ticks = 0;
|
|
|
|
while (Exists && !IsExiting)
|
|
{
|
|
if (_device.WaitFifo())
|
|
{
|
|
_device.ProcessFrame();
|
|
}
|
|
|
|
_renderer.RunActions();
|
|
|
|
if (_resizeEvent)
|
|
{
|
|
_resizeEvent = false;
|
|
|
|
_renderer.RenderTarget.SetWindowSize(Width, Height);
|
|
}
|
|
|
|
ticks += chrono.ElapsedTicks;
|
|
|
|
chrono.Restart();
|
|
|
|
if (ticks >= ticksPerFrame)
|
|
{
|
|
RenderFrame();
|
|
|
|
// Queue max. 1 vsync
|
|
ticks = Math.Min(ticks - ticksPerFrame, ticksPerFrame);
|
|
}
|
|
}
|
|
}
|
|
|
|
public void MainLoop()
|
|
{
|
|
VSync = VSyncMode.Off;
|
|
|
|
Visible = true;
|
|
|
|
_renderer.RenderTarget.SetWindowSize(Width, Height);
|
|
|
|
Context.MakeCurrent(null);
|
|
|
|
// OpenTK doesn't like sleeps in its thread, to avoid this a renderer thread is created
|
|
_renderThread = new Thread(RenderLoop);
|
|
|
|
_renderThread.Start();
|
|
|
|
while (Exists && !IsExiting)
|
|
{
|
|
ProcessEvents();
|
|
|
|
if (!IsExiting)
|
|
{
|
|
UpdateFrame();
|
|
|
|
if (_titleEvent)
|
|
{
|
|
_titleEvent = false;
|
|
|
|
Title = _newTitle;
|
|
}
|
|
}
|
|
|
|
// Polling becomes expensive if it's not slept
|
|
Thread.Sleep(1);
|
|
}
|
|
}
|
|
|
|
private new void UpdateFrame()
|
|
{
|
|
HotkeyButtons currentHotkeyButtons = 0;
|
|
ControllerButtons currentButton = 0;
|
|
JoystickPosition leftJoystick;
|
|
JoystickPosition rightJoystick;
|
|
HLE.Input.Keyboard? hidKeyboard = null;
|
|
|
|
int leftJoystickDx = 0;
|
|
int leftJoystickDy = 0;
|
|
int rightJoystickDx = 0;
|
|
int rightJoystickDy = 0;
|
|
|
|
// Keyboard Input
|
|
if (_keyboard.HasValue)
|
|
{
|
|
KeyboardState keyboard = _keyboard.Value;
|
|
|
|
#if USE_PROFILING
|
|
// Profiler input, lets the profiler get access to the main windows keyboard state
|
|
_profileWindow.UpdateKeyInput(keyboard);
|
|
#endif
|
|
|
|
// Normal Input
|
|
currentHotkeyButtons = KeyboardControls.GetHotkeyButtons(ConfigurationState.Instance.Hid.KeyboardControls, keyboard);
|
|
currentButton = KeyboardControls.GetButtons(ConfigurationState.Instance.Hid.KeyboardControls, keyboard);
|
|
|
|
if (ConfigurationState.Instance.Hid.EnableKeyboard)
|
|
{
|
|
hidKeyboard = KeyboardControls.GetKeysDown(ConfigurationState.Instance.Hid.KeyboardControls, keyboard);
|
|
}
|
|
|
|
(leftJoystickDx, leftJoystickDy) = KeyboardControls.GetLeftStick(ConfigurationState.Instance.Hid.KeyboardControls, keyboard);
|
|
(rightJoystickDx, rightJoystickDy) = KeyboardControls.GetRightStick(ConfigurationState.Instance.Hid.KeyboardControls, keyboard);
|
|
}
|
|
|
|
if (!hidKeyboard.HasValue)
|
|
{
|
|
hidKeyboard = new HLE.Input.Keyboard
|
|
{
|
|
Modifier = 0,
|
|
Keys = new int[0x8]
|
|
};
|
|
}
|
|
|
|
currentButton |= _primaryController.GetButtons();
|
|
|
|
// Keyboard has priority stick-wise
|
|
if (leftJoystickDx == 0 && leftJoystickDy == 0)
|
|
{
|
|
(leftJoystickDx, leftJoystickDy) = _primaryController.GetLeftStick();
|
|
}
|
|
|
|
if (rightJoystickDx == 0 && rightJoystickDy == 0)
|
|
{
|
|
(rightJoystickDx, rightJoystickDy) = _primaryController.GetRightStick();
|
|
}
|
|
|
|
leftJoystick = new JoystickPosition
|
|
{
|
|
Dx = leftJoystickDx,
|
|
Dy = leftJoystickDy
|
|
};
|
|
|
|
rightJoystick = new JoystickPosition
|
|
{
|
|
Dx = rightJoystickDx,
|
|
Dy = rightJoystickDy
|
|
};
|
|
|
|
currentButton |= _device.Hid.UpdateStickButtons(leftJoystick, rightJoystick);
|
|
|
|
bool hasTouch = false;
|
|
|
|
// Get screen touch position from left mouse click
|
|
// OpenTK always captures mouse events, even if out of focus, so check if window is focused.
|
|
if (Focused && _mouse?.LeftButton == ButtonState.Pressed)
|
|
{
|
|
MouseState mouse = _mouse.Value;
|
|
|
|
int scrnWidth = Width;
|
|
int scrnHeight = Height;
|
|
|
|
if (Width > (Height * TouchScreenWidth) / TouchScreenHeight)
|
|
{
|
|
scrnWidth = (Height * TouchScreenWidth) / TouchScreenHeight;
|
|
}
|
|
else
|
|
{
|
|
scrnHeight = (Width * TouchScreenHeight) / TouchScreenWidth;
|
|
}
|
|
|
|
int startX = (Width - scrnWidth) >> 1;
|
|
int startY = (Height - scrnHeight) >> 1;
|
|
|
|
int endX = startX + scrnWidth;
|
|
int endY = startY + scrnHeight;
|
|
|
|
if (mouse.X >= startX &&
|
|
mouse.Y >= startY &&
|
|
mouse.X < endX &&
|
|
mouse.Y < endY)
|
|
{
|
|
int scrnMouseX = mouse.X - startX;
|
|
int scrnMouseY = mouse.Y - startY;
|
|
|
|
int mX = (scrnMouseX * TouchScreenWidth) / scrnWidth;
|
|
int mY = (scrnMouseY * TouchScreenHeight) / scrnHeight;
|
|
|
|
TouchPoint currentPoint = new TouchPoint
|
|
{
|
|
X = mX,
|
|
Y = mY,
|
|
|
|
// Placeholder values till more data is acquired
|
|
DiameterX = 10,
|
|
DiameterY = 10,
|
|
Angle = 90
|
|
};
|
|
|
|
hasTouch = true;
|
|
|
|
_device.Hid.SetTouchPoints(currentPoint);
|
|
}
|
|
}
|
|
|
|
if (!hasTouch)
|
|
{
|
|
_device.Hid.SetTouchPoints();
|
|
}
|
|
|
|
if (ConfigurationState.Instance.Hid.EnableKeyboard && hidKeyboard.HasValue)
|
|
{
|
|
_device.Hid.WriteKeyboard(hidKeyboard.Value);
|
|
}
|
|
|
|
BaseController controller = _device.Hid.PrimaryController;
|
|
|
|
controller.SendInput(currentButton, leftJoystick, rightJoystick);
|
|
|
|
// Toggle vsync
|
|
if (currentHotkeyButtons.HasFlag(HotkeyButtons.ToggleVSync) &&
|
|
!_prevHotkeyButtons.HasFlag(HotkeyButtons.ToggleVSync))
|
|
{
|
|
_device.EnableDeviceVsync = !_device.EnableDeviceVsync;
|
|
}
|
|
|
|
_prevHotkeyButtons = currentHotkeyButtons;
|
|
}
|
|
|
|
private new void RenderFrame()
|
|
{
|
|
_renderer.RenderTarget.Render();
|
|
|
|
_device.Statistics.RecordSystemFrameTime();
|
|
|
|
double hostFps = _device.Statistics.GetSystemFrameRate();
|
|
double gameFps = _device.Statistics.GetGameFrameRate();
|
|
|
|
string titleNameSection = string.IsNullOrWhiteSpace(_device.System.TitleName) ? string.Empty
|
|
: " | " + _device.System.TitleName;
|
|
|
|
string titleIDSection = string.IsNullOrWhiteSpace(_device.System.TitleId) ? string.Empty
|
|
: " | " + _device.System.TitleId.ToUpper();
|
|
|
|
_newTitle = $"Ryujinx{titleNameSection}{titleIDSection} | Host FPS: {hostFps:0.0} | Game FPS: {gameFps:0.0} | " +
|
|
$"Game Vsync: {(_device.EnableDeviceVsync ? "On" : "Off")}";
|
|
|
|
_titleEvent = true;
|
|
|
|
SwapBuffers();
|
|
|
|
_device.System.SignalVsync();
|
|
|
|
_device.VsyncEvent.Set();
|
|
}
|
|
|
|
protected override void OnUnload(EventArgs e)
|
|
{
|
|
#if USE_PROFILING
|
|
_profileWindow.Close();
|
|
#endif
|
|
|
|
_renderThread.Join();
|
|
|
|
base.OnUnload(e);
|
|
}
|
|
|
|
protected override void OnResize(EventArgs e)
|
|
{
|
|
_resizeEvent = true;
|
|
}
|
|
|
|
protected override void OnKeyDown(KeyboardKeyEventArgs e)
|
|
{
|
|
bool toggleFullscreen = e.Key == Key.F11 ||
|
|
(e.Modifiers.HasFlag(KeyModifiers.Alt) && e.Key == Key.Enter);
|
|
|
|
if (WindowState == WindowState.Fullscreen)
|
|
{
|
|
if (e.Key == Key.Escape || toggleFullscreen)
|
|
{
|
|
WindowState = WindowState.Normal;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (e.Key == Key.Escape)
|
|
{
|
|
Exit();
|
|
}
|
|
|
|
if (toggleFullscreen)
|
|
{
|
|
WindowState = WindowState.Fullscreen;
|
|
}
|
|
}
|
|
|
|
_keyboard = e.Keyboard;
|
|
}
|
|
|
|
protected override void OnKeyUp(KeyboardKeyEventArgs e)
|
|
{
|
|
_keyboard = e.Keyboard;
|
|
}
|
|
|
|
protected override void OnMouseDown(MouseButtonEventArgs e)
|
|
{
|
|
_mouse = e.Mouse;
|
|
}
|
|
|
|
protected override void OnMouseUp(MouseButtonEventArgs e)
|
|
{
|
|
_mouse = e.Mouse;
|
|
}
|
|
|
|
protected override void OnMouseMove(MouseMoveEventArgs e)
|
|
{
|
|
_mouse = e.Mouse;
|
|
}
|
|
}
|
|
}
|