diff --git a/Ryujinx.Audio/Ryujinx.Audio.csproj b/Ryujinx.Audio/Ryujinx.Audio.csproj
index b541043c75..80940c770e 100644
--- a/Ryujinx.Audio/Ryujinx.Audio.csproj
+++ b/Ryujinx.Audio/Ryujinx.Audio.csproj
@@ -27,7 +27,7 @@
-
+
diff --git a/Ryujinx.Common/Ryujinx.Common.csproj b/Ryujinx.Common/Ryujinx.Common.csproj
index 43a853a409..1b7cf8afbf 100644
--- a/Ryujinx.Common/Ryujinx.Common.csproj
+++ b/Ryujinx.Common/Ryujinx.Common.csproj
@@ -28,7 +28,8 @@
-
+
+
diff --git a/Ryujinx.Graphics.OpenGL/Ryujinx.Graphics.OpenGL.csproj b/Ryujinx.Graphics.OpenGL/Ryujinx.Graphics.OpenGL.csproj
index f2a9377737..2754005552 100644
--- a/Ryujinx.Graphics.OpenGL/Ryujinx.Graphics.OpenGL.csproj
+++ b/Ryujinx.Graphics.OpenGL/Ryujinx.Graphics.OpenGL.csproj
@@ -7,7 +7,7 @@
-
+
diff --git a/Ryujinx.sln b/Ryujinx.sln
index f023368ba8..40a086e5f2 100644
--- a/Ryujinx.sln
+++ b/Ryujinx.sln
@@ -32,7 +32,8 @@ EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Graphics.Shader", "Ryujinx.Graphics.Shader\Ryujinx.Graphics.Shader.csproj", "{03B955CD-AD84-4B93-AAA7-BF17923BBAA5}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Ryujinx.Graphics.Nvdec", "Ryujinx.Graphics.Nvdec\Ryujinx.Graphics.Nvdec.csproj", "{85A0FA56-DC01-4A42-8808-70DAC76BD66D}"
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Debugger", "Ryujinx.Debugger\Ryujinx.Debugger.csproj", "{2E02B7F3-245E-43B1-AE5B-44167A0FDA20}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Ryujinx.Debugger", "Ryujinx.Debugger\Ryujinx.Debugger.csproj", "{79E4EE34-9C5F-4BE6-8529-A49D32B5B0CC}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
@@ -170,6 +171,14 @@ Global
{2E02B7F3-245E-43B1-AE5B-44167A0FDA20}.Profile Release|Any CPU.Build.0 = Profile Release|Any CPU
{2E02B7F3-245E-43B1-AE5B-44167A0FDA20}.Release|Any CPU.ActiveCfg = Release|Any CPU
{2E02B7F3-245E-43B1-AE5B-44167A0FDA20}.Release|Any CPU.Build.0 = Release|Any CPU
+ {79E4EE34-9C5F-4BE6-8529-A49D32B5B0CC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {79E4EE34-9C5F-4BE6-8529-A49D32B5B0CC}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {79E4EE34-9C5F-4BE6-8529-A49D32B5B0CC}.Profile Debug|Any CPU.ActiveCfg = Profile Debug|Any CPU
+ {79E4EE34-9C5F-4BE6-8529-A49D32B5B0CC}.Profile Debug|Any CPU.Build.0 = Profile Debug|Any CPU
+ {79E4EE34-9C5F-4BE6-8529-A49D32B5B0CC}.Profile Release|Any CPU.ActiveCfg = Profile Release|Any CPU
+ {79E4EE34-9C5F-4BE6-8529-A49D32B5B0CC}.Profile Release|Any CPU.Build.0 = Profile Release|Any CPU
+ {79E4EE34-9C5F-4BE6-8529-A49D32B5B0CC}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {79E4EE34-9C5F-4BE6-8529-A49D32B5B0CC}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
diff --git a/Ryujinx/Program.cs b/Ryujinx/Program.cs
index 24fbb9b8b5..4aaa5e9f2c 100644
--- a/Ryujinx/Program.cs
+++ b/Ryujinx/Program.cs
@@ -3,6 +3,7 @@ using Ryujinx.Common.Logging;
using Ryujinx.Configuration;
using Ryujinx.Debugger.Profiler;
using Ryujinx.Ui;
+using OpenTK;
using System;
using System.IO;
@@ -12,6 +13,12 @@ namespace Ryujinx
{
static void Main(string[] args)
{
+ Toolkit.Init(new ToolkitOptions
+ {
+ Backend = PlatformBackend.PreferNative,
+ EnableHighResolution = true
+ });
+
Console.Title = "Ryujinx Console";
string systemPath = Environment.GetEnvironmentVariable("Path", EnvironmentVariableTarget.Machine);
diff --git a/Ryujinx/Ryujinx.csproj b/Ryujinx/Ryujinx.csproj
index cc8e6d5450..bde01b243e 100644
--- a/Ryujinx/Ryujinx.csproj
+++ b/Ryujinx/Ryujinx.csproj
@@ -72,9 +72,10 @@
+
-
+
diff --git a/Ryujinx/Ui/GLRenderer.cs b/Ryujinx/Ui/GLRenderer.cs
new file mode 100644
index 0000000000..dff72b3644
--- /dev/null
+++ b/Ryujinx/Ui/GLRenderer.cs
@@ -0,0 +1,499 @@
+using Gdk;
+using OpenTK;
+using OpenTK.Graphics;
+using OpenTK.Graphics.OpenGL;
+using OpenTK.Input;
+using OpenTK.Platform;
+using Ryujinx.Configuration;
+using Ryujinx.Graphics.OpenGL;
+using Ryujinx.HLE;
+using Ryujinx.HLE.Input;
+using Ryujinx.Ui;
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.Threading;
+
+namespace Ryujinx.Ui
+{
+ public class GLRenderer : GLWidget
+ {
+ private const int TouchScreenWidth = 1280;
+ private const int TouchScreenHeight = 720;
+ private const int TargetFps = 60;
+
+ public ManualResetEvent WaitEvent { get; set; }
+
+ public bool IsActive { get; set; }
+ public bool IsStopped { get; set; }
+ public bool IsFocused { get; set; }
+
+ private double _mouseX;
+ private double _mouseY;
+ private bool _mousePressed;
+
+ private bool _titleEvent;
+
+ private bool _toggleFullscreen;
+
+ private string _newTitle;
+
+ private readonly long _ticksPerFrame;
+
+ private long _ticks = 0;
+
+ private System.Diagnostics.Stopwatch _chrono;
+
+ private Switch _device;
+
+ private Renderer _renderer;
+
+ private HotkeyButtons _prevHotkeyButtons = 0;
+
+ private Input.NpadController _primaryController;
+
+ public GLRenderer(Switch device)
+ : base (new GraphicsMode(new ColorFormat(24)),
+ 3, 3,
+ GraphicsContextFlags.ForwardCompatible)
+ {
+ WaitEvent = new ManualResetEvent(false);
+
+ _device = device;
+
+ this.Initialized += GLRenderer_Initialized;
+ this.Destroyed += GLRenderer_Destroyed;
+
+ Initialize();
+
+ _chrono = new System.Diagnostics.Stopwatch();
+
+ _ticksPerFrame = System.Diagnostics.Stopwatch.Frequency / TargetFps;
+
+ _primaryController = new Input.NpadController(ConfigurationState.Instance.Hid.JoystickControls);
+
+ AddEvents((int)(Gdk.EventMask.ButtonPressMask
+ | Gdk.EventMask.ButtonReleaseMask
+ | Gdk.EventMask.PointerMotionMask
+ | Gdk.EventMask.KeyPressMask
+ | Gdk.EventMask.KeyReleaseMask));
+
+ this.Shown += Renderer_Shown;
+ }
+
+ private void Parent_FocusOutEvent(object o, Gtk.FocusOutEventArgs args)
+ {
+ IsFocused = false;
+ }
+
+ private void Parent_FocusInEvent(object o, Gtk.FocusInEventArgs args)
+ {
+ IsFocused = true;
+ }
+
+ private void GLRenderer_Destroyed(object sender, EventArgs e)
+ {
+ Exit();
+
+ this.Dispose();
+ }
+
+ protected void Renderer_Shown(object sender, EventArgs e)
+ {
+ IsFocused = this.ParentWindow.State.HasFlag(Gdk.WindowState.Focused);
+ }
+
+ public void HandleScreenState(KeyboardState keyboard)
+ {
+ bool toggleFullscreen = keyboard.IsKeyDown(OpenTK.Input.Key.F11)
+ || ((keyboard.IsKeyDown(OpenTK.Input.Key.AltLeft)
+ || keyboard.IsKeyDown(OpenTK.Input.Key.AltRight))
+ && keyboard.IsKeyDown(OpenTK.Input.Key.Enter));
+
+ if (toggleFullscreen == _toggleFullscreen)
+ {
+ return;
+ }
+
+ _toggleFullscreen = toggleFullscreen;
+
+ Gtk.Application.Invoke(delegate
+ {
+ if (this.ParentWindow.State.HasFlag(Gdk.WindowState.Fullscreen))
+ {
+ if (keyboard.IsKeyDown(OpenTK.Input.Key.Escape) || _toggleFullscreen)
+ {
+ this.ParentWindow.Unfullscreen();
+ (this.Toplevel as MainWindow)?.ToggleExtraWidgets(true);
+ }
+ }
+ else
+ {
+ if (keyboard.IsKeyDown(OpenTK.Input.Key.Escape))
+ {
+ Exit();
+ }
+
+ if (_toggleFullscreen)
+ {
+ this.ParentWindow.Fullscreen();
+ (this.Toplevel as MainWindow)?.ToggleExtraWidgets(false);
+ }
+ }
+ });
+ }
+
+ private void GLRenderer_Initialized(object sender, EventArgs e)
+ {
+ // Release the GL exclusivity that OpenTK gave us.
+ GraphicsContext.MakeCurrent(null);
+
+ WaitEvent.Set();
+ }
+
+ protected override bool OnConfigureEvent(EventConfigure evnt)
+ {
+ var result = base.OnConfigureEvent(evnt);
+
+ _renderer.Window.SetSize(AllocatedWidth, AllocatedHeight);
+
+ return result;
+ }
+
+ public void Start()
+ {
+ IsRenderHandler = true;
+
+ _chrono.Restart();
+
+ IsActive = true;
+
+ Gtk.Window parent = this.Toplevel as Gtk.Window;
+
+ parent.FocusInEvent += Parent_FocusInEvent;
+ parent.FocusOutEvent += Parent_FocusOutEvent;
+
+ Gtk.Application.Invoke(delegate
+ {
+ parent.Present();
+ });
+
+ Thread renderLoopThread = new Thread(Render)
+ {
+ Name = "GUI.RenderLoop"
+ };
+ renderLoopThread.Start();
+
+ MainLoop();
+
+ renderLoopThread.Join();
+
+ Exit();
+ }
+
+ protected override bool OnButtonPressEvent(EventButton evnt)
+ {
+ _mouseX = evnt.X;
+ _mouseY = evnt.Y;
+
+ if (evnt.Button == 1)
+ {
+ _mousePressed = true;
+ }
+
+ return false;
+ }
+
+ protected override bool OnButtonReleaseEvent(EventButton evnt)
+ {
+ if (evnt.Button == 1)
+ {
+ _mousePressed = false;
+ }
+
+ return false;
+ }
+
+ protected override bool OnMotionNotifyEvent(EventMotion evnt)
+ {
+ if (evnt.Device.InputSource == InputSource.Mouse)
+ {
+ _mouseX = evnt.X;
+ _mouseY = evnt.Y;
+ }
+
+ return false;
+ }
+
+ public void Exit()
+ {
+ if (IsStopped)
+ {
+ return;
+ }
+
+ IsStopped = true;
+ IsActive = false;
+
+ using (ScopedGlContext scopedGLContext = new ScopedGlContext(WindowInfo, GraphicsContext))
+ {
+ _device.DisposeGpu();
+ }
+
+ WaitEvent.Set();
+ }
+
+ public void Initialize()
+ {
+ if (!(_device.Gpu.Renderer is Renderer))
+ {
+ throw new NotSupportedException($"GPU renderer must be an OpenGL renderer when using GLRenderer!");
+ }
+
+ _renderer = (Renderer)_device.Gpu.Renderer;
+ }
+
+ public void Render()
+ {
+ using (ScopedGlContext scopedGLContext = new ScopedGlContext(WindowInfo, GraphicsContext))
+ {
+ _renderer.Initialize();
+
+ SwapBuffers();
+ }
+
+ while (IsActive)
+ {
+ if (IsStopped)
+ {
+ return;
+ }
+
+ using (ScopedGlContext scopedGLContext = new ScopedGlContext(WindowInfo, GraphicsContext))
+ {
+ _ticks += _chrono.ElapsedTicks;
+
+ _chrono.Restart();
+
+ if (_device.WaitFifo())
+ {
+ _device.ProcessFrame();
+ }
+
+ if (_ticks >= _ticksPerFrame)
+ {
+ _device.PresentFrame(SwapBuffers);
+
+ _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.TitleIdText) ? string.Empty
+ : " | " + _device.System.TitleIdText.ToUpper();
+
+ _newTitle = $"Ryujinx{titleNameSection}{titleIdSection} | Host FPS: {hostFps:0.0} | Game FPS: {gameFps:0.0} | " +
+ $"Game Vsync: {(_device.EnableDeviceVsync ? "On" : "Off")}";
+
+ _titleEvent = true;
+
+ _device.System.SignalVsync();
+
+ _device.VsyncEvent.Set();
+
+ _ticks = Math.Min(_ticks - _ticksPerFrame, _ticksPerFrame);
+ }
+ }
+ }
+ }
+
+ public void SwapBuffers()
+ {
+ OpenTK.Graphics.GraphicsContext.CurrentContext.SwapBuffers();
+ }
+
+ public void MainLoop()
+ {
+ while (IsActive)
+ {
+ if (_titleEvent)
+ {
+ _titleEvent = false;
+
+ Gtk.Application.Invoke(delegate
+ {
+ this.ParentWindow.Title = _newTitle;
+ });
+ }
+
+ if (IsFocused)
+ {
+ UpdateFrame();
+ }
+
+ // Polling becomes expensive if it's not slept
+ Thread.Sleep(1);
+ }
+ }
+
+ private bool UpdateFrame()
+ {
+ if (!IsActive)
+ {
+ return true;
+ }
+
+ if (IsStopped)
+ {
+ return false;
+ }
+
+ HotkeyButtons currentHotkeyButtons = 0;
+ ControllerButtons currentButton = 0;
+ JoystickPosition leftJoystick;
+ JoystickPosition rightJoystick;
+ HLE.Input.Keyboard? hidKeyboard = null;
+
+ KeyboardState keyboard = OpenTK.Input.Keyboard.GetState();
+
+ Gtk.Application.Invoke(delegate
+ {
+ HandleScreenState(keyboard);
+ });
+
+ int leftJoystickDx = 0;
+ int leftJoystickDy = 0;
+ int rightJoystickDx = 0;
+ int rightJoystickDy = 0;
+
+ // 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 (IsFocused && _mousePressed)
+ {
+ int screenWidth = AllocatedWidth;
+ int screenHeight = AllocatedHeight;
+
+ if (AllocatedWidth > (AllocatedHeight * TouchScreenWidth) / TouchScreenHeight)
+ {
+ screenWidth = (AllocatedHeight * TouchScreenWidth) / TouchScreenHeight;
+ }
+ else
+ {
+ screenHeight = (AllocatedWidth * TouchScreenHeight) / TouchScreenWidth;
+ }
+
+ int startX = (AllocatedWidth - screenWidth) >> 1;
+ int startY = (AllocatedHeight - screenHeight) >> 1;
+
+ int endX = startX + screenWidth;
+ int endY = startY + screenHeight;
+
+
+ if (_mouseX >= startX &&
+ _mouseY >= startY &&
+ _mouseX < endX &&
+ _mouseY < endY)
+ {
+ int screenMouseX = (int)_mouseX - startX;
+ int screenMouseY = (int)_mouseY - startY;
+
+ int mX = (screenMouseX * TouchScreenWidth) / screenWidth;
+ int mY = (screenMouseY * TouchScreenHeight) / screenHeight;
+
+ 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;
+
+ return true;
+ }
+ }
+}
diff --git a/Ryujinx/Ui/GLScreen.cs b/Ryujinx/Ui/GLScreen.cs
deleted file mode 100644
index 5e83458aef..0000000000
--- a/Ryujinx/Ui/GLScreen.cs
+++ /dev/null
@@ -1,375 +0,0 @@
-using OpenTK;
-using OpenTK.Graphics;
-using OpenTK.Input;
-using Ryujinx.Configuration;
-using Ryujinx.Graphics.OpenGL;
-using Ryujinx.HLE;
-using Ryujinx.HLE.Input;
-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;
-
- public GlScreen(Switch device)
- : base(1280, 720,
- new GraphicsMode(), "Ryujinx", 0,
- DisplayDevice.Default, 3, 3,
- GraphicsContextFlags.ForwardCompatible)
- {
- _device = device;
-
- if (!(device.Gpu.Renderer is Renderer))
- {
- throw new NotSupportedException($"GPU renderer must be an OpenGL renderer when using GlScreen!");
- }
-
- _renderer = (Renderer)device.Gpu.Renderer;
-
- _primaryController = new Input.NpadController(ConfigurationState.Instance.Hid.JoystickControls);
-
- Location = new Point(
- (DisplayDevice.Default.Width / 2) - (Width / 2),
- (DisplayDevice.Default.Height / 2) - (Height / 2));
- }
-
- private void RenderLoop()
- {
- MakeCurrent();
-
- _renderer.Initialize();
-
- 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;
-
- _renderer.Window.SetSize(Width, Height);
- }
-
- ticks += chrono.ElapsedTicks;
-
- chrono.Restart();
-
- if (ticks >= ticksPerFrame)
- {
- RenderFrame();
-
- // Queue max. 1 vsync
- ticks = Math.Min(ticks - ticksPerFrame, ticksPerFrame);
- }
- }
-
- _device.DisposeGpu();
- }
-
- 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)
- {
- Name = "GUI.RenderThread"
- };
-
- _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;
-
- // 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()
- {
- _device.PresentFrame(SwapBuffers);
-
- _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.TitleIdText) ? string.Empty
- : " | " + _device.System.TitleIdText.ToUpper();
-
- _newTitle = $"Ryujinx{titleNameSection}{titleIdSection} | Host FPS: {hostFps:0.0} | Game FPS: {gameFps:0.0} | " +
- $"Game Vsync: {(_device.EnableDeviceVsync ? "On" : "Off")}";
-
- _titleEvent = true;
-
- _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;
- }
- }
-}
diff --git a/Ryujinx/Ui/MainWindow.cs b/Ryujinx/Ui/MainWindow.cs
index 734103fed2..6c771bb96f 100644
--- a/Ryujinx/Ui/MainWindow.cs
+++ b/Ryujinx/Ui/MainWindow.cs
@@ -8,7 +8,6 @@ using Ryujinx.Graphics.GAL;
using Ryujinx.Graphics.OpenGL;
using Ryujinx.HLE.FileSystem;
using Ryujinx.HLE.FileSystem.Content;
-using Ryujinx.HLE.FileSystem;
using System;
using System.Diagnostics;
using System.IO;
@@ -30,7 +29,7 @@ namespace Ryujinx.Ui
private static HLE.Switch _emulationContext;
- private static GlScreen _screen;
+ private static GLRenderer _gLWidget;
private static AutoResetEvent _screenExitStatus = new AutoResetEvent(false);
@@ -43,32 +42,38 @@ namespace Ryujinx.Ui
private static TreeView _treeView;
- private static Debugger.Debugger _debugger;
+ private static Ryujinx.Debugger.Debugger _debugger;
#pragma warning disable CS0649
#pragma warning disable IDE0044
- [GUI] Window _mainWin;
- [GUI] CheckMenuItem _fullScreen;
- [GUI] MenuItem _stopEmulation;
- [GUI] CheckMenuItem _favToggle;
- [GUI] MenuItem _firmwareInstallFile;
- [GUI] MenuItem _firmwareInstallDirectory;
- [GUI] CheckMenuItem _iconToggle;
- [GUI] CheckMenuItem _appToggle;
- [GUI] CheckMenuItem _developerToggle;
- [GUI] CheckMenuItem _versionToggle;
- [GUI] CheckMenuItem _timePlayedToggle;
- [GUI] CheckMenuItem _lastPlayedToggle;
- [GUI] CheckMenuItem _fileExtToggle;
- [GUI] CheckMenuItem _fileSizeToggle;
- [GUI] CheckMenuItem _pathToggle;
- [GUI] TreeView _gameTable;
- [GUI] TreeSelection _gameTableSelection;
- [GUI] Label _progressLabel;
- [GUI] Label _firmwareVersionLabel;
- [GUI] LevelBar _progressBar;
- [GUI] MenuItem _openDebugger;
- [GUI] MenuItem _toolsMenu;
+
+ [GUI] Window _mainWin;
+ [GUI] MenuBar _menuBar;
+ [GUI] Box _footerBox;
+ [GUI] MenuItem _fullScreen;
+ [GUI] MenuItem _stopEmulation;
+ [GUI] CheckMenuItem _favToggle;
+ [GUI] MenuItem _firmwareInstallFile;
+ [GUI] MenuItem _firmwareInstallDirectory;
+ [GUI] MenuItem _openDebugger;
+ [GUI] CheckMenuItem _iconToggle;
+ [GUI] CheckMenuItem _appToggle;
+ [GUI] CheckMenuItem _developerToggle;
+ [GUI] CheckMenuItem _versionToggle;
+ [GUI] CheckMenuItem _timePlayedToggle;
+ [GUI] CheckMenuItem _lastPlayedToggle;
+ [GUI] CheckMenuItem _fileExtToggle;
+ [GUI] CheckMenuItem _fileSizeToggle;
+ [GUI] CheckMenuItem _pathToggle;
+ [GUI] TreeView _gameTable;
+ [GUI] ScrolledWindow _gameTableWindow;
+ [GUI] TreeSelection _gameTableSelection;
+ [GUI] Label _progressLabel;
+ [GUI] Label _firmwareVersionLabel;
+ [GUI] LevelBar _progressBar;
+ [GUI] Box _viewBox;
+ [GUI] Box _listStatusBox;
+
#pragma warning restore CS0649
#pragma warning restore IDE0044
@@ -130,7 +135,7 @@ namespace Ryujinx.Ui
_debugger = new Debugger.Debugger();
_openDebugger.Activated += _openDebugger_Opened;
#else
- _openDebugger.Visible = false;
+ _openDebugger.Hide();
#endif
_gameTable.Model = _tableStore = new ListStore(
@@ -154,6 +159,8 @@ namespace Ryujinx.Ui
UpdateGameTable();
Task.Run(RefreshFirmwareLabel);
+
+ _fullScreen.Activated += FullScreen_Toggled;
}
#if USE_DEBUGGING
@@ -384,27 +391,85 @@ namespace Ryujinx.Ui
{
device.Hid.InitializePrimaryController(ConfigurationState.Instance.Hid.ControllerType);
- using (_screen = new GlScreen(device))
+ _gLWidget?.Exit();
+ _gLWidget?.Dispose();
+ _gLWidget = new GLRenderer(_emulationContext);
+
+ Application.Invoke(delegate
{
- _screen.MainLoop();
- }
+ _viewBox.Remove(_gameTableWindow);
+ _gLWidget.Expand = true;
+ _viewBox.Child = _gLWidget;
+
+ _gLWidget.ShowAll();
+ _listStatusBox.Hide();
+ });
+
+ _gLWidget.WaitEvent.WaitOne();
+
+ _gLWidget.Start();
+
+ Application.Invoke(delegate
+ {
+ _viewBox.Remove(_gLWidget);
+ _gLWidget.Exit();
+
+ if(_gLWidget.Window != this.Window && _gLWidget.Window != null)
+ {
+ _gLWidget.Window.Dispose();
+ }
+
+ _viewBox.Add(_gameTableWindow);
+
+ _gameTableWindow.Expand = true;
+
+ this.Window.Title = "Ryujinx";
+
+ _listStatusBox.ShowAll();
+
+ UpdateColumns();
+ UpdateGameTable();
+
+ Task.Run(RefreshFirmwareLabel);
+ });
device.Dispose();
_emulationContext = null;
- _screen = null;
_gameLoaded = false;
+ _gLWidget = null;
DiscordIntegrationModule.SwitchToMainMenu();
- _screenExitStatus.Set();
-
Application.Invoke(delegate
{
_stopEmulation.Sensitive = false;
_firmwareInstallFile.Sensitive = true;
_firmwareInstallDirectory.Sensitive = true;
});
+
+ _screenExitStatus.Set();
+ }
+
+ public void ToggleExtraWidgets(bool show)
+ {
+ if (_gLWidget != null)
+ {
+ if (show)
+ {
+ _menuBar.ShowAll();
+ _footerBox.ShowAll();
+ }
+ else
+ {
+ _menuBar.Hide();
+ _footerBox.Hide();
+ }
+ }
+
+ bool fullScreenToggled = this.Window.State.HasFlag(Gdk.WindowState.Fullscreen);
+
+ _fullScreen.Label = !fullScreenToggled ? "Exit Fullscreen" : "Enter Fullscreen";
}
private static void UpdateGameMetadata(string titleId)
@@ -439,9 +504,9 @@ namespace Ryujinx.Ui
{
UpdateGameMetadata(device.System.TitleIdText);
- if (_screen != null)
+ if (_gLWidget != null)
{
- _screen.Exit();
+ _gLWidget.Exit();
_screenExitStatus.WaitOne();
}
}
@@ -605,7 +670,7 @@ namespace Ryujinx.Ui
private void StopEmulation_Pressed(object sender, EventArgs args)
{
- _screen?.Exit();
+ _gLWidget?.Exit();
}
private void Installer_File_Pressed(object o, EventArgs args)
@@ -803,13 +868,23 @@ namespace Ryujinx.Ui
private void FullScreen_Toggled(object o, EventArgs args)
{
- if (_fullScreen.Active)
+ bool fullScreenToggled = this.Window.State.HasFlag(Gdk.WindowState.Fullscreen);
+
+ if (!fullScreenToggled)
{
Fullscreen();
+
+ _fullScreen.Label = "Exit Fullscreen";
+
+ ToggleExtraWidgets(false);
}
else
{
Unfullscreen();
+
+ _fullScreen.Label = "Enter Fullscreen";
+
+ ToggleExtraWidgets(true);
}
}
diff --git a/Ryujinx/Ui/MainWindow.glade b/Ryujinx/Ui/MainWindow.glade
index d3cdc59346..8477f392a4 100644
--- a/Ryujinx/Ui/MainWindow.glade
+++ b/Ryujinx/Ui/MainWindow.glade
@@ -17,7 +17,7 @@
False
vertical
-