dc02ac08ca
* Make controllers modular, support changing controller type * return readable events * signal hid events * fix style
325 lines
8.9 KiB
C#
325 lines
8.9 KiB
C#
using OpenTK;
|
|
using OpenTK.Graphics;
|
|
using OpenTK.Input;
|
|
using Ryujinx.Graphics.Gal;
|
|
using Ryujinx.HLE;
|
|
using Ryujinx.HLE.Input;
|
|
using System;
|
|
using System.Threading;
|
|
|
|
using Stopwatch = System.Diagnostics.Stopwatch;
|
|
|
|
namespace Ryujinx
|
|
{
|
|
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 KeyboardState? _keyboard = null;
|
|
|
|
private MouseState? _mouse = null;
|
|
|
|
private Thread _renderThread;
|
|
|
|
private bool _resizeEvent;
|
|
|
|
private bool _titleEvent;
|
|
|
|
private string _newTitle;
|
|
|
|
public GlScreen(Switch device, IGalRenderer renderer)
|
|
: base(1280, 720,
|
|
new GraphicsMode(), "Ryujinx", 0,
|
|
DisplayDevice.Default, 3, 3,
|
|
GraphicsContextFlags.ForwardCompatible)
|
|
{
|
|
_device = device;
|
|
_renderer = renderer;
|
|
|
|
Location = new Point(
|
|
(DisplayDevice.Default.Width / 2) - (Width / 2),
|
|
(DisplayDevice.Default.Height / 2) - (Height / 2));
|
|
}
|
|
|
|
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()
|
|
{
|
|
HidControllerButtons currentButton = 0;
|
|
HidJoystickPosition leftJoystick;
|
|
HidJoystickPosition rightJoystick;
|
|
|
|
int leftJoystickDx = 0;
|
|
int leftJoystickDy = 0;
|
|
int rightJoystickDx = 0;
|
|
int rightJoystickDy = 0;
|
|
|
|
//Keyboard Input
|
|
if (_keyboard.HasValue)
|
|
{
|
|
KeyboardState keyboard = _keyboard.Value;
|
|
|
|
currentButton = Config.NpadKeyboard.GetButtons(keyboard);
|
|
|
|
(leftJoystickDx, leftJoystickDy) = Config.NpadKeyboard.GetLeftStick(keyboard);
|
|
|
|
(rightJoystickDx, rightJoystickDy) = Config.NpadKeyboard.GetRightStick(keyboard);
|
|
}
|
|
|
|
currentButton |= Config.NpadController.GetButtons();
|
|
|
|
//Keyboard has priority stick-wise
|
|
if (leftJoystickDx == 0 && leftJoystickDy == 0)
|
|
{
|
|
(leftJoystickDx, leftJoystickDy) = Config.NpadController.GetLeftStick();
|
|
}
|
|
|
|
if (rightJoystickDx == 0 && rightJoystickDy == 0)
|
|
{
|
|
(rightJoystickDx, rightJoystickDy) = Config.NpadController.GetRightStick();
|
|
}
|
|
|
|
leftJoystick = new HidJoystickPosition
|
|
{
|
|
DX = leftJoystickDx,
|
|
DY = leftJoystickDy
|
|
};
|
|
|
|
rightJoystick = new HidJoystickPosition
|
|
{
|
|
DX = rightJoystickDx,
|
|
DY = rightJoystickDy
|
|
};
|
|
|
|
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;
|
|
|
|
HidTouchPoint currentPoint = new HidTouchPoint
|
|
{
|
|
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();
|
|
}
|
|
|
|
HidControllerBase controller = _device.Hid.PrimaryController;
|
|
|
|
controller.SendInput(currentButton, leftJoystick, rightJoystick);
|
|
}
|
|
|
|
private new void RenderFrame()
|
|
{
|
|
_renderer.RenderTarget.Render();
|
|
|
|
_device.Statistics.RecordSystemFrameTime();
|
|
|
|
double hostFps = _device.Statistics.GetSystemFrameRate();
|
|
double gameFps = _device.Statistics.GetGameFrameRate();
|
|
|
|
string titleSection = string.IsNullOrWhiteSpace(_device.System.CurrentTitle) ? string.Empty
|
|
: " | " + _device.System.CurrentTitle;
|
|
|
|
_newTitle = $"Ryujinx{titleSection} | 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)
|
|
{
|
|
_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;
|
|
}
|
|
}
|
|
} |