386 lines
11 KiB
C#
386 lines
11 KiB
C#
using OpenTK;
|
|
using OpenTK.Graphics;
|
|
using OpenTK.Input;
|
|
using Ryujinx.Configuration;
|
|
using Ryujinx.Graphics.OpenGL;
|
|
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 Renderer _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, Renderer 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();
|
|
|
|
_renderer.InitializeCounters();
|
|
|
|
Stopwatch chrono = new Stopwatch();
|
|
|
|
chrono.Start();
|
|
|
|
long ticksPerFrame = Stopwatch.Frequency / TargetFps;
|
|
|
|
long ticks = 0;
|
|
|
|
while (Exists && !IsExiting)
|
|
{
|
|
if (_device.WaitFifo())
|
|
{
|
|
_device.ProcessFrame();
|
|
}
|
|
|
|
if (_resizeEvent)
|
|
{
|
|
_resizeEvent = false;
|
|
|
|
// TODO: Resize
|
|
}
|
|
|
|
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;
|
|
|
|
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.Window.Present();
|
|
|
|
_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;
|
|
}
|
|
}
|
|
}
|