Miria: The Death of OpenTK 3 (#2194)

* openal: Update to OpenTK 4

* Ryujinx.Graphics.OpenGL: Update to OpenTK 4

* Entirely removed OpenTK 3, still wip

* Use SPB for context creation and handling

Still need to test on GLX and readd input support

* Start implementing a new input system

So far only gamepad are supported, no configuration possible via UI but detected via hotplug/removal

Button mapping backend is implemented

TODO: front end, configuration handling and configuration migration
TODO: keyboard support

* Enforce RGB only framebuffer on the GLWidget

Fix possible transparent window

* Implement UI gamepad frontend

Also fix bad mapping of minus button and ensure gamepad config is updated in real time

* Handle controller being disconnected and reconnected again

* Revert "Enforce RGB only framebuffer on the GLWidget"

This reverts commit 0949715d1a03ec793e35e37f7b610cbff2d63965.

* Fix first color clear

* Filter SDL2 events a bit

* Start working on the keyboard detail

- Rework configuration classes a bit to be more clean.
- Integrate fully the keyboard configuration to the front end (TODO: assigner)
- Start skeleton for the GTK3 keyboard driver

* Add KeyboardStateSnapshot and its integration

* Implement keyboard assigner and GTK3 key mapping

TODO: controller configuration mapping and IGamepad implementation for keyboard

* Add missing SR and SL definitions

* Fix copy pasta mistake on config for previous commit

* Implement IGamepad interface for GTK3 keyboard

* Fix some implementation still being commented in the controller ui for keyboard

* Port screen handle code

* Remove all configuration management code and move HidNew to Hid

* Rename InputConfigNew to InputConfig

* Add a version field to the input config

* Prepare serialization and deserialization of new input config and migrate profile loading and saving

* Support input configuration saving to config and bump config version to 23.

* Clean up in ConfigurationState

* Reference SPB via a nuget package

* Move new input system to Ryujinx.Input project and SDL2 detail to Ryujinx.Input.SDL2

* move GTK3 input to the right directory

* Fix triggers on SDL2

* Update to SDL2 2.0.14 via our own fork

* Update buttons definition for SDL2 2.0.14 and report gamepad features

* Implement motion support again with SDL2

TODO: cemu hooks integration

* Switch to latest of nightly SDL2

* SDL2: Fix bugs in gamepad id matching allowing different gamepad to match on the same device index

* Ensure values are set in UI when the gamepad get hot plugged

* Avoid trying to add controllers in the Update method and don't open SDL2 gamepad instance before checking ids

This fixes permanent rumble of pro controller in some hotplug scenario

* Fix more UI bugs

* Move legcay motion code around before reintegration

* gamecontroller UI tweaks here and there

* Hide Motion on non motion configurations

* Update the TODO grave

Some TODO were fixed long time ago or are quite oudated...

* Integrate cemu hooks motion configuration

* Integrate cemu hooks configuration options to the UI again

* cemuhooks => cemuhooks

* Add cemu hook support again

* Fix regression on normal motion and fix some very nasty bugs around

* Fix for XCB multithreads issue on Linux

* Enable motion by default

* Block inputs in the main view when in the controller configuration window

* Some fixes for the controller ui again

* Add joycon support and fixes other hints

* Bug fixes and clean up

- Invert default mapping if not a Nintendo controller
- Keep alive the controller being selected on the controller window (allow to avoid big delay for controller needing time to init when doing button assignment)
- Clean up hints in use
- Remove debug logs around
- Fixes potential double free with SDL2Gamepad

* Move the button assigner and motion logic to the Ryujinx.Input project

* Reimplement raw keyboard hle input

Also move out the logic of the hotkeys

* Move all remaining Input manager stuffs to the Ryujinx.Input project

* Increment configuration version yet again because of master changes

* Ensure input config isn't null when not present

* Fixes for VS not being nice

* Fix broken gamepad caching logic causing crashes on ui

* Ensure the background context is destroyed

* Update dependencies

* Readd retrocompat with old format of the config to avoid parsing and crashes on those versions

Also updated the debug Config.json

* Document new input APIs

* Isolate SDL2Driver to the project and remove external export of it

* Add support for external gamepad db mappings on SDL2

* Last clean up before PR

* Addresses first part of comments

* Address gdkchan's comments

* Do not use JsonException

* Last comment fixes
This commit is contained in:
Mary 2021-04-14 12:28:43 +02:00 committed by GitHub
parent e13b15da75
commit 3b7c07c3c9
91 changed files with 4516 additions and 2048 deletions

View file

@ -1,39 +1,37 @@
using ARMeilleure.Translation;
using ARMeilleure.Translation.PTC;
using Gdk;
using OpenTK;
using OpenTK.Graphics;
using OpenTK.Graphics.OpenGL;
using OpenTK.Input;
using Ryujinx.Common;
using Ryujinx.Common.Configuration;
using Ryujinx.Common.Configuration.Hid;
using Ryujinx.Common.Logging;
using Ryujinx.Configuration;
using Ryujinx.Graphics.OpenGL;
using Ryujinx.HLE.HOS.Services.Hid;
using Ryujinx.Modules.Motion;
using Ryujinx.Input;
using Ryujinx.Input.HLE;
using Ryujinx.Ui.Widgets;
using SPB.Graphics;
using SPB.Graphics.OpenGL;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Threading;
using Key = Ryujinx.Input.Key;
namespace Ryujinx.Ui
{
using Switch = HLE.Switch;
public class GlRenderer : GLWidget
{
static GlRenderer()
{
OpenTK.Graphics.GraphicsContext.ShareContexts = true;
}
private const int SwitchPanelWidth = 1280;
private const int SwitchPanelHeight = 720;
private const int TargetFps = 60;
public ManualResetEvent WaitEvent { get; set; }
public NpadManager NpadManager { get; }
public static event EventHandler<StatusUpdatedEventArgs> StatusUpdatedEvent;
@ -58,9 +56,7 @@ namespace Ryujinx.Ui
private Renderer _renderer;
private HotkeyButtons _prevHotkeyButtons;
private Client _dsuClient;
private KeyboardHotkeyState _prevHotkeyState;
private GraphicsDebugLevel _glLogLevel;
@ -71,14 +67,22 @@ namespace Ryujinx.Ui
private static readonly Cursor _invisibleCursor = new Cursor(Display.Default, CursorType.BlankCursor);
private long _lastCursorMoveTime;
private bool _hideCursorOnIdle;
private InputManager _inputManager;
private IKeyboard _keyboardInterface;
public GlRenderer(Switch device, GraphicsDebugLevel glLogLevel)
public GlRenderer(Switch device, InputManager inputManager, GraphicsDebugLevel glLogLevel)
: base (GetGraphicsMode(),
3, 3,
glLogLevel == GraphicsDebugLevel.None
? GraphicsContextFlags.ForwardCompatible
: GraphicsContextFlags.ForwardCompatible | GraphicsContextFlags.Debug)
? OpenGLContextFlags.Compat
: OpenGLContextFlags.Compat | OpenGLContextFlags.Debug)
{
_inputManager = inputManager;
NpadManager = _inputManager.CreateNpadManager();
_keyboardInterface = (IKeyboard)_inputManager.KeyboardDriver.GetGamepad("0");
NpadManager.ReloadConfiguration(ConfigurationState.Instance.Hid.InputConfig.Value.ToList());
WaitEvent = new ManualResetEvent(false);
_device = device;
@ -101,8 +105,6 @@ namespace Ryujinx.Ui
Shown += Renderer_Shown;
_dsuClient = new Client();
_glLogLevel = glLogLevel;
_exitEvent = new ManualResetEvent(false);
@ -130,15 +132,15 @@ namespace Ryujinx.Ui
});
}
private static GraphicsMode GetGraphicsMode()
private static FramebufferFormat GetGraphicsMode()
{
return Environment.OSVersion.Platform == PlatformID.Unix ? new GraphicsMode(new ColorFormat(24)) : new GraphicsMode(new ColorFormat());
return Environment.OSVersion.Platform == PlatformID.Unix ? new FramebufferFormat(new ColorFormat(8, 8, 8, 0), 16, 0, ColorFormat.Zero, 0, 2, false) : FramebufferFormat.Default;
}
private void GLRenderer_ShuttingDown(object sender, EventArgs args)
{
_device.DisposeGpu();
_dsuClient?.Dispose();
NpadManager.Dispose();
}
private void Parent_FocusOutEvent(object o, Gtk.FocusOutEventArgs args)
@ -155,7 +157,7 @@ namespace Ryujinx.Ui
{
ConfigurationState.Instance.HideCursorOnIdle.Event -= HideCursorStateChanged;
_dsuClient?.Dispose();
NpadManager.Dispose();
Dispose();
}
@ -164,13 +166,13 @@ namespace Ryujinx.Ui
_isFocused = this.ParentWindow.State.HasFlag(Gdk.WindowState.Focused);
}
public void HandleScreenState(KeyboardState keyboard)
public void HandleScreenState(KeyboardStateSnapshot 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))
|| keyboard.IsKeyDown(OpenTK.Input.Key.Escape);
bool toggleFullscreen = keyboard.IsPressed(Key.F11)
|| ((keyboard.IsPressed(Key.AltLeft)
|| keyboard.IsPressed(Key.AltRight))
&& keyboard.IsPressed(Key.Enter))
|| keyboard.IsPressed(Key.Escape);
bool fullScreenToggled = ParentWindow.State.HasFlag(Gdk.WindowState.Fullscreen);
@ -185,7 +187,7 @@ namespace Ryujinx.Ui
}
else
{
if (keyboard.IsKeyDown(OpenTK.Input.Key.Escape))
if (keyboard.IsPressed(Key.Escape))
{
if (!ConfigurationState.Instance.ShowConfirmExit || GtkDialog.CreateExitDialog())
{
@ -203,7 +205,7 @@ namespace Ryujinx.Ui
_toggleFullscreen = toggleFullscreen;
bool toggleDockedMode = keyboard.IsKeyDown(OpenTK.Input.Key.F9);
bool toggleDockedMode = keyboard.IsPressed(Key.F9);
if (toggleDockedMode != _toggleDockedMode)
{
@ -225,8 +227,8 @@ namespace Ryujinx.Ui
private void GLRenderer_Initialized(object sender, EventArgs e)
{
// Release the GL exclusivity that OpenTK gave us as we aren't going to use it in GTK Thread.
GraphicsContext.MakeCurrent(null);
// Release the GL exclusivity that SPB gave us as we aren't going to use it in GTK Thread.
OpenGLContext.MakeCurrent(null);
WaitEvent.Set();
}
@ -244,8 +246,6 @@ namespace Ryujinx.Ui
public void Start()
{
IsRenderHandler = true;
_chrono.Restart();
_isActive = true;
@ -389,7 +389,7 @@ namespace Ryujinx.Ui
public void Exit()
{
_dsuClient?.Dispose();
NpadManager?.Dispose();
if (_isStopped)
{
@ -416,15 +416,17 @@ namespace Ryujinx.Ui
public void Render()
{
// First take exclusivity on the OpenGL context.
_renderer.InitializeBackgroundContext(GraphicsContext);
_renderer.InitializeBackgroundContext(SPBOpenGLContext.CreateBackgroundContext(OpenGLContext));
Gtk.Window parent = Toplevel as Gtk.Window;
parent.Present();
GraphicsContext.MakeCurrent(WindowInfo);
OpenGLContext.MakeCurrent(NativeWindow);
_device.Gpu.Renderer.Initialize(_glLogLevel);
// Make sure the first frame is not transparent.
GL.ClearColor(OpenTK.Color.Black);
GL.ClearColor(0, 0, 0, 1.0f);
GL.Clear(ClearBufferMask.ColorBufferBit);
SwapBuffers();
@ -478,7 +480,7 @@ namespace Ryujinx.Ui
public void SwapBuffers()
{
OpenTK.Graphics.GraphicsContext.CurrentContext.SwapBuffers();
NativeWindow.SwapBuffers();
}
public void MainLoop()
@ -510,13 +512,13 @@ namespace Ryujinx.Ui
{
Gtk.Application.Invoke(delegate
{
KeyboardState keyboard = OpenTK.Input.Keyboard.GetState();
KeyboardStateSnapshot keyboard = _keyboardInterface.GetKeyboardStateSnapshot();
HandleScreenState(keyboard);
if (keyboard.IsKeyDown(OpenTK.Input.Key.Delete))
if (keyboard.IsPressed(Key.Delete))
{
if (!ParentWindow.State.HasFlag(Gdk.WindowState.Fullscreen))
if (!ParentWindow.State.HasFlag(WindowState.Fullscreen))
{
Ptc.Continue();
}
@ -524,154 +526,19 @@ namespace Ryujinx.Ui
});
}
List<GamepadInput> gamepadInputs = new List<GamepadInput>(NpadDevices.MaxControllers);
List<SixAxisInput> motionInputs = new List<SixAxisInput>(NpadDevices.MaxControllers);
MotionDevice motionDevice = new MotionDevice(_dsuClient);
foreach (InputConfig inputConfig in ConfigurationState.Instance.Hid.InputConfig.Value)
{
ControllerKeys currentButton = 0;
JoystickPosition leftJoystick = new JoystickPosition();
JoystickPosition rightJoystick = new JoystickPosition();
KeyboardInput? hidKeyboard = null;
int leftJoystickDx = 0;
int leftJoystickDy = 0;
int rightJoystickDx = 0;
int rightJoystickDy = 0;
if (inputConfig.EnableMotion)
{
motionDevice.RegisterController(inputConfig.PlayerIndex);
}
if (inputConfig is KeyboardConfig keyboardConfig)
{
if (_isFocused)
{
// Keyboard Input
KeyboardController keyboardController = new KeyboardController(keyboardConfig);
currentButton = keyboardController.GetButtons();
(leftJoystickDx, leftJoystickDy) = keyboardController.GetLeftStick();
(rightJoystickDx, rightJoystickDy) = keyboardController.GetRightStick();
leftJoystick = new JoystickPosition
{
Dx = leftJoystickDx,
Dy = leftJoystickDy
};
rightJoystick = new JoystickPosition
{
Dx = rightJoystickDx,
Dy = rightJoystickDy
};
if (ConfigurationState.Instance.Hid.EnableKeyboard)
{
hidKeyboard = keyboardController.GetKeysDown();
}
if (!hidKeyboard.HasValue)
{
hidKeyboard = new KeyboardInput
{
Modifier = 0,
Keys = new int[0x8]
};
}
if (ConfigurationState.Instance.Hid.EnableKeyboard)
{
_device.Hid.Keyboard.Update(hidKeyboard.Value);
}
}
}
else if (inputConfig is Common.Configuration.Hid.ControllerConfig controllerConfig)
{
// Controller Input
JoystickController joystickController = new JoystickController(controllerConfig);
currentButton |= joystickController.GetButtons();
(leftJoystickDx, leftJoystickDy) = joystickController.GetLeftStick();
(rightJoystickDx, rightJoystickDy) = joystickController.GetRightStick();
leftJoystick = new JoystickPosition
{
Dx = controllerConfig.LeftJoycon.InvertStickX ? -leftJoystickDx : leftJoystickDx,
Dy = controllerConfig.LeftJoycon.InvertStickY ? -leftJoystickDy : leftJoystickDy
};
rightJoystick = new JoystickPosition
{
Dx = controllerConfig.RightJoycon.InvertStickX ? -rightJoystickDx : rightJoystickDx,
Dy = controllerConfig.RightJoycon.InvertStickY ? -rightJoystickDy : rightJoystickDy
};
}
currentButton |= _device.Hid.UpdateStickButtons(leftJoystick, rightJoystick);
motionDevice.Poll(inputConfig, inputConfig.Slot);
SixAxisInput sixAxisInput = new SixAxisInput()
{
PlayerId = (HLE.HOS.Services.Hid.PlayerIndex)inputConfig.PlayerIndex,
Accelerometer = motionDevice.Accelerometer,
Gyroscope = motionDevice.Gyroscope,
Rotation = motionDevice.Rotation,
Orientation = motionDevice.Orientation
};
motionInputs.Add(sixAxisInput);
gamepadInputs.Add(new GamepadInput
{
PlayerId = (HLE.HOS.Services.Hid.PlayerIndex)inputConfig.PlayerIndex,
Buttons = currentButton,
LStick = leftJoystick,
RStick = rightJoystick
});
if (inputConfig.ControllerType == Common.Configuration.Hid.ControllerType.JoyconPair)
{
if (!inputConfig.MirrorInput)
{
motionDevice.Poll(inputConfig, inputConfig.AltSlot);
sixAxisInput = new SixAxisInput()
{
PlayerId = (HLE.HOS.Services.Hid.PlayerIndex)inputConfig.PlayerIndex,
Accelerometer = motionDevice.Accelerometer,
Gyroscope = motionDevice.Gyroscope,
Rotation = motionDevice.Rotation,
Orientation = motionDevice.Orientation
};
}
motionInputs.Add(sixAxisInput);
}
}
_device.Hid.Npads.Update(gamepadInputs);
_device.Hid.Npads.UpdateSixAxis(motionInputs);
_device.TamperMachine.UpdateInput(gamepadInputs);
NpadManager.Update(_device.Hid, _device.TamperMachine);
if(_isFocused)
{
// Hotkeys
HotkeyButtons currentHotkeyButtons = KeyboardController.GetHotkeyButtons(OpenTK.Input.Keyboard.GetState());
KeyboardHotkeyState currentHotkeyState = GetHotkeyState();
if (currentHotkeyButtons.HasFlag(HotkeyButtons.ToggleVSync) &&
!_prevHotkeyButtons.HasFlag(HotkeyButtons.ToggleVSync))
if (currentHotkeyState.HasFlag(KeyboardHotkeyState.ToggleVSync) &&
!_prevHotkeyState.HasFlag(KeyboardHotkeyState.ToggleVSync))
{
_device.EnableDeviceVsync = !_device.EnableDeviceVsync;
}
_prevHotkeyButtons = currentHotkeyButtons;
_prevHotkeyState = currentHotkeyState;
}
//Touchscreen
@ -739,5 +606,24 @@ namespace Ryujinx.Ui
return true;
}
[Flags]
private enum KeyboardHotkeyState
{
None,
ToggleVSync
}
private KeyboardHotkeyState GetHotkeyState()
{
KeyboardHotkeyState state = KeyboardHotkeyState.None;
if (_keyboardInterface.IsPressed((Key)ConfigurationState.Instance.Hid.Hotkeys.Value.ToggleVsync))
{
state |= KeyboardHotkeyState.ToggleVSync;
}
return state;
}
}
}

118
Ryujinx/Ui/GLWidget.cs Normal file
View file

@ -0,0 +1,118 @@
using Gtk;
using SPB.Graphics;
using SPB.Graphics.OpenGL;
using SPB.Platform;
using SPB.Platform.GLX;
using SPB.Platform.WGL;
using SPB.Windowing;
using System;
using System.ComponentModel;
using System.Runtime.InteropServices;
namespace Ryujinx.Ui
{
[ToolboxItem(true)]
public class GLWidget : DrawingArea
{
private bool _initialized;
public event EventHandler Initialized;
public event EventHandler ShuttingDown;
public OpenGLContextBase OpenGLContext { get; private set; }
public NativeWindowBase NativeWindow { get; private set; }
public FramebufferFormat FramebufferFormat { get; }
public int GLVersionMajor { get; }
public int GLVersionMinor { get; }
public OpenGLContextFlags ContextFlags { get; }
public bool DirectRendering { get; }
public OpenGLContextBase SharedContext { get; }
public GLWidget(FramebufferFormat framebufferFormat, int major, int minor, OpenGLContextFlags flags = OpenGLContextFlags.Default, bool directRendering = true, OpenGLContextBase sharedContext = null)
{
FramebufferFormat = framebufferFormat;
GLVersionMajor = major;
GLVersionMinor = minor;
ContextFlags = flags;
DirectRendering = directRendering;
SharedContext = sharedContext;
}
protected override bool OnDrawn(Cairo.Context cr)
{
if (!_initialized)
{
Intialize();
}
return true;
}
private NativeWindowBase RetrieveNativeWindow()
{
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
{
IntPtr windowHandle = gdk_win32_window_get_handle(Window.Handle);
return new WGLWindow(new NativeHandle(windowHandle));
}
else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
{
IntPtr displayHandle = gdk_x11_display_get_xdisplay(Display.Handle);
IntPtr windowHandle = gdk_x11_window_get_xid(Window.Handle);
return new GLXWindow(new NativeHandle(displayHandle), new NativeHandle(windowHandle));
}
throw new NotImplementedException();
}
[DllImport("libgdk-3-0.dll")]
private static extern IntPtr gdk_win32_window_get_handle(IntPtr d);
[DllImport("libgdk-3.so.0")]
private static extern IntPtr gdk_x11_display_get_xdisplay(IntPtr gdkDisplay);
[DllImport("libgdk-3.so.0")]
private static extern IntPtr gdk_x11_window_get_xid(IntPtr gdkWindow);
private void Intialize()
{
NativeWindow = RetrieveNativeWindow();
Window.EnsureNative();
OpenGLContext = PlatformHelper.CreateOpenGLContext(FramebufferFormat, GLVersionMajor, GLVersionMinor, ContextFlags, DirectRendering, SharedContext);
OpenGLContext.Initialize(NativeWindow);
OpenGLContext.MakeCurrent(NativeWindow);
_initialized = true;
Initialized?.Invoke(this, EventArgs.Empty);
}
protected override void Dispose(bool disposing)
{
// Try to bind the OpenGL context before calling the shutdown event
try
{
OpenGLContext?.MakeCurrent(NativeWindow);
}
catch (Exception) { }
ShuttingDown?.Invoke(this, EventArgs.Empty);
// Unbind context and destroy everything
try
{
OpenGLContext?.MakeCurrent(null);
}
catch (Exception) { }
OpenGLContext.Dispose();
}
}
}

View file

@ -1,17 +0,0 @@
using Ryujinx.Common.Configuration.Hid;
namespace Ryujinx.Ui.Input
{
interface ButtonAssigner
{
void Init();
void ReadInput();
bool HasAnyButtonPressed();
bool ShouldCancel();
string GetPressedButton();
}
}

View file

@ -1,227 +0,0 @@
using OpenTK.Input;
using Ryujinx.Common.Configuration.Hid;
using System.Collections.Generic;
using System;
using System.IO;
namespace Ryujinx.Ui.Input
{
class JoystickButtonAssigner : ButtonAssigner
{
private int _index;
private double _triggerThreshold;
private JoystickState _currState;
private JoystickState _prevState;
private JoystickButtonDetector _detector;
public JoystickButtonAssigner(int index, double triggerThreshold)
{
_index = index;
_triggerThreshold = triggerThreshold;
_detector = new JoystickButtonDetector();
}
public void Init()
{
_currState = Joystick.GetState(_index);
_prevState = _currState;
}
public void ReadInput()
{
_prevState = _currState;
_currState = Joystick.GetState(_index);
CollectButtonStats();
}
public bool HasAnyButtonPressed()
{
return _detector.HasAnyButtonPressed();
}
public bool ShouldCancel()
{
return Mouse.GetState().IsAnyButtonDown || Keyboard.GetState().IsAnyKeyDown;
}
public string GetPressedButton()
{
List<ControllerInputId> pressedButtons = _detector.GetPressedButtons();
// Reverse list so axis button take precedence when more than one button is recognized.
pressedButtons.Reverse();
return pressedButtons.Count > 0 ? pressedButtons[0].ToString() : "";
}
private void CollectButtonStats()
{
JoystickCapabilities capabilities = Joystick.GetCapabilities(_index);
ControllerInputId pressedButton;
// Buttons
for (int i = 0; i != capabilities.ButtonCount; i++)
{
if (_currState.IsButtonDown(i) && _prevState.IsButtonUp(i))
{
Enum.TryParse($"Button{i}", out pressedButton);
_detector.AddInput(pressedButton, 1);
}
if (_currState.IsButtonUp(i) && _prevState.IsButtonDown(i))
{
Enum.TryParse($"Button{i}", out pressedButton);
_detector.AddInput(pressedButton, -1);
}
}
// Axis
for (int i = 0; i != capabilities.AxisCount; i++)
{
float axisValue = _currState.GetAxis(i);
Enum.TryParse($"Axis{i}", out pressedButton);
_detector.AddInput(pressedButton, axisValue);
}
// Hats
for (int i = 0; i != capabilities.HatCount; i++)
{
string currPos = GetHatPosition(_currState.GetHat((JoystickHat)i));
string prevPos = GetHatPosition(_prevState.GetHat((JoystickHat)i));
if (currPos == prevPos)
{
continue;
}
if (currPos != "")
{
Enum.TryParse($"Hat{i}{currPos}", out pressedButton);
_detector.AddInput(pressedButton, 1);
}
if (prevPos != "")
{
Enum.TryParse($"Hat{i}{prevPos}", out pressedButton);
_detector.AddInput(pressedButton, -1);
}
}
}
private string GetHatPosition(JoystickHatState hatState)
{
if (hatState.IsUp) return "Up";
if (hatState.IsDown) return "Down";
if (hatState.IsLeft) return "Left";
if (hatState.IsRight) return "Right";
return "";
}
private class JoystickButtonDetector
{
private Dictionary<ControllerInputId, InputSummary> _stats;
public JoystickButtonDetector()
{
_stats = new Dictionary<ControllerInputId, InputSummary>();
}
public bool HasAnyButtonPressed()
{
foreach (var inputSummary in _stats.Values)
{
if (checkButtonPressed(inputSummary))
{
return true;
}
}
return false;
}
public List<ControllerInputId> GetPressedButtons()
{
List<ControllerInputId> pressedButtons = new List<ControllerInputId>();
foreach (var kvp in _stats)
{
if (!checkButtonPressed(kvp.Value))
{
continue;
}
pressedButtons.Add(kvp.Key);
}
return pressedButtons;
}
public void AddInput(ControllerInputId button, float value)
{
InputSummary inputSummary;
if (!_stats.TryGetValue(button, out inputSummary))
{
inputSummary = new InputSummary();
_stats.Add(button, inputSummary);
}
inputSummary.AddInput(value);
}
public override string ToString()
{
TextWriter writer = new StringWriter();
foreach (var kvp in _stats)
{
writer.WriteLine($"Button {kvp.Key} -> {kvp.Value}");
}
return writer.ToString();
}
private bool checkButtonPressed(InputSummary sequence)
{
float distance = Math.Abs(sequence.Min - sequence.Avg) + Math.Abs(sequence.Max - sequence.Avg);
return distance > 1.5; // distance range [0, 2]
}
}
private class InputSummary
{
public float Min, Max, Sum, Avg;
public int NumSamples;
public InputSummary()
{
Min = float.MaxValue;
Max = float.MinValue;
Sum = 0;
NumSamples = 0;
Avg = 0;
}
public void AddInput(float value)
{
Min = Math.Min(Min, value);
Max = Math.Max(Max, value);
Sum += value;
NumSamples += 1;
Avg = Sum / NumSamples;
}
public override string ToString()
{
return $"Avg: {Avg} Min: {Min} Max: {Max} Sum: {Sum} NumSamples: {NumSamples}";
}
}
}
}

View file

@ -1,51 +0,0 @@
using OpenTK.Input;
using System;
using Key = Ryujinx.Configuration.Hid.Key;
namespace Ryujinx.Ui.Input
{
class KeyboardKeyAssigner : ButtonAssigner
{
private int _index;
private KeyboardState _keyboardState;
public KeyboardKeyAssigner(int index)
{
_index = index;
}
public void Init() { }
public void ReadInput()
{
_keyboardState = KeyboardController.GetKeyboardState(_index);
}
public bool HasAnyButtonPressed()
{
return _keyboardState.IsAnyKeyDown;
}
public bool ShouldCancel()
{
return Mouse.GetState().IsAnyButtonDown || Keyboard.GetState().IsKeyDown(OpenTK.Input.Key.Escape);
}
public string GetPressedButton()
{
string keyPressed = "";
foreach (Key key in Enum.GetValues(typeof(Key)))
{
if (_keyboardState.IsKeyDown((OpenTK.Input.Key)key))
{
keyPressed = key.ToString();
break;
}
}
return !ShouldCancel() ? keyPressed : "";
}
}
}

View file

@ -1,149 +0,0 @@
using OpenTK;
using OpenTK.Input;
using Ryujinx.Common.Configuration.Hid;
using Ryujinx.HLE.HOS.Services.Hid;
using System;
using ControllerConfig = Ryujinx.Common.Configuration.Hid.ControllerConfig;
namespace Ryujinx.Ui
{
public class JoystickController
{
private readonly ControllerConfig _config;
public JoystickController(ControllerConfig config)
{
_config = config;
}
private bool IsEnabled()
{
return Joystick.GetState(_config.Index).IsConnected;
}
public ControllerKeys GetButtons()
{
// NOTE: This should be initialized AFTER GTK for compat reasons with OpenTK SDL2 backend and GTK on Linux.
// BODY: Usage of Joystick.GetState must be defer to after GTK full initialization. Otherwise, GTK will segfault because SDL2 was already init *sighs*
if (!IsEnabled())
{
return 0;
}
JoystickState joystickState = Joystick.GetState(_config.Index);
ControllerKeys buttons = 0;
if (IsActivated(joystickState, _config.LeftJoycon.DPadUp)) buttons |= ControllerKeys.DpadUp;
if (IsActivated(joystickState, _config.LeftJoycon.DPadDown)) buttons |= ControllerKeys.DpadDown;
if (IsActivated(joystickState, _config.LeftJoycon.DPadLeft)) buttons |= ControllerKeys.DpadLeft;
if (IsActivated(joystickState, _config.LeftJoycon.DPadRight)) buttons |= ControllerKeys.DpadRight;
if (IsActivated(joystickState, _config.LeftJoycon.StickButton)) buttons |= ControllerKeys.LStick;
if (IsActivated(joystickState, _config.LeftJoycon.ButtonMinus)) buttons |= ControllerKeys.Minus;
if (IsActivated(joystickState, _config.LeftJoycon.ButtonL)) buttons |= ControllerKeys.L;
if (IsActivated(joystickState, _config.LeftJoycon.ButtonZl)) buttons |= ControllerKeys.Zl;
if (IsActivated(joystickState, _config.LeftJoycon.ButtonSl)) buttons |= ControllerKeys.SlLeft;
if (IsActivated(joystickState, _config.LeftJoycon.ButtonSr)) buttons |= ControllerKeys.SrLeft;
if (IsActivated(joystickState, _config.RightJoycon.ButtonA)) buttons |= ControllerKeys.A;
if (IsActivated(joystickState, _config.RightJoycon.ButtonB)) buttons |= ControllerKeys.B;
if (IsActivated(joystickState, _config.RightJoycon.ButtonX)) buttons |= ControllerKeys.X;
if (IsActivated(joystickState, _config.RightJoycon.ButtonY)) buttons |= ControllerKeys.Y;
if (IsActivated(joystickState, _config.RightJoycon.StickButton)) buttons |= ControllerKeys.RStick;
if (IsActivated(joystickState, _config.RightJoycon.ButtonPlus)) buttons |= ControllerKeys.Plus;
if (IsActivated(joystickState, _config.RightJoycon.ButtonR)) buttons |= ControllerKeys.R;
if (IsActivated(joystickState, _config.RightJoycon.ButtonZr)) buttons |= ControllerKeys.Zr;
if (IsActivated(joystickState, _config.RightJoycon.ButtonSl)) buttons |= ControllerKeys.SlRight;
if (IsActivated(joystickState, _config.RightJoycon.ButtonSr)) buttons |= ControllerKeys.SrRight;
return buttons;
}
private bool IsActivated(JoystickState joystickState, ControllerInputId controllerInputId)
{
if (controllerInputId <= ControllerInputId.Button20)
{
return joystickState.IsButtonDown((int)controllerInputId);
}
else if (controllerInputId <= ControllerInputId.Axis5)
{
int axis = controllerInputId - ControllerInputId.Axis0;
return joystickState.GetAxis(axis) > _config.TriggerThreshold;
}
else if (controllerInputId <= ControllerInputId.Hat2Right)
{
int hat = (controllerInputId - ControllerInputId.Hat0Up) / 4;
int baseHatId = (int)ControllerInputId.Hat0Up + (hat * 4);
JoystickHatState hatState = joystickState.GetHat((JoystickHat)hat);
if (hatState.IsUp && ((int)controllerInputId % baseHatId == 0)) return true;
if (hatState.IsDown && ((int)controllerInputId % baseHatId == 1)) return true;
if (hatState.IsLeft && ((int)controllerInputId % baseHatId == 2)) return true;
if (hatState.IsRight && ((int)controllerInputId % baseHatId == 3)) return true;
}
return false;
}
public (short, short) GetLeftStick()
{
if (!IsEnabled())
{
return (0, 0);
}
return GetStick(_config.LeftJoycon.StickX, _config.LeftJoycon.StickY, _config.DeadzoneLeft);
}
public (short, short) GetRightStick()
{
if (!IsEnabled())
{
return (0, 0);
}
return GetStick(_config.RightJoycon.StickX, _config.RightJoycon.StickY, _config.DeadzoneRight);
}
private (short, short) GetStick(ControllerInputId stickXInputId, ControllerInputId stickYInputId, float deadzone)
{
if (stickXInputId < ControllerInputId.Axis0 || stickXInputId > ControllerInputId.Axis5 ||
stickYInputId < ControllerInputId.Axis0 || stickYInputId > ControllerInputId.Axis5)
{
return (0, 0);
}
JoystickState jsState = Joystick.GetState(_config.Index);
int xAxis = stickXInputId - ControllerInputId.Axis0;
int yAxis = stickYInputId - ControllerInputId.Axis0;
float xValue = jsState.GetAxis(xAxis);
float yValue = -jsState.GetAxis(yAxis); // Invert Y-axis
return ApplyDeadzone(new Vector2(xValue, yValue), deadzone);
}
private (short, short) ApplyDeadzone(Vector2 axis, float deadzone)
{
return (ClampAxis(MathF.Abs(axis.X) > deadzone ? axis.X : 0f),
ClampAxis(MathF.Abs(axis.Y) > deadzone ? axis.Y : 0f));
}
private static short ClampAxis(float value)
{
if (value <= -short.MaxValue)
{
return -short.MaxValue;
}
else
{
return (short)(value * short.MaxValue);
}
}
}
}

View file

@ -1,291 +0,0 @@
using System;
using OpenTK;
using OpenTK.Input;
using Ryujinx.Common.Configuration.Hid;
using Ryujinx.Configuration;
using Ryujinx.HLE.HOS.Services.Hid;
namespace Ryujinx.Ui
{
[Flags]
public enum HotkeyButtons
{
ToggleVSync = 1 << 0,
}
public class KeyboardController
{
private readonly KeyboardConfig _config;
public KeyboardController(KeyboardConfig config)
{
_config = config;
}
public static KeyboardState GetKeyboardState(int index)
{
if (index == KeyboardConfig.AllKeyboardsIndex || index < 0)
{
return Keyboard.GetState();
}
return Keyboard.GetState(index - 1);
}
public ControllerKeys GetButtons()
{
KeyboardState keyboard = GetKeyboardState(_config.Index);
ControllerKeys buttons = 0;
if (keyboard[(Key)_config.LeftJoycon.StickButton]) buttons |= ControllerKeys.LStick;
if (keyboard[(Key)_config.LeftJoycon.DPadUp]) buttons |= ControllerKeys.DpadUp;
if (keyboard[(Key)_config.LeftJoycon.DPadDown]) buttons |= ControllerKeys.DpadDown;
if (keyboard[(Key)_config.LeftJoycon.DPadLeft]) buttons |= ControllerKeys.DpadLeft;
if (keyboard[(Key)_config.LeftJoycon.DPadRight]) buttons |= ControllerKeys.DpadRight;
if (keyboard[(Key)_config.LeftJoycon.ButtonMinus]) buttons |= ControllerKeys.Minus;
if (keyboard[(Key)_config.LeftJoycon.ButtonL]) buttons |= ControllerKeys.L;
if (keyboard[(Key)_config.LeftJoycon.ButtonZl]) buttons |= ControllerKeys.Zl;
if (keyboard[(Key)_config.LeftJoycon.ButtonSl]) buttons |= ControllerKeys.SlLeft;
if (keyboard[(Key)_config.LeftJoycon.ButtonSr]) buttons |= ControllerKeys.SrLeft;
if (keyboard[(Key)_config.RightJoycon.StickButton]) buttons |= ControllerKeys.RStick;
if (keyboard[(Key)_config.RightJoycon.ButtonA]) buttons |= ControllerKeys.A;
if (keyboard[(Key)_config.RightJoycon.ButtonB]) buttons |= ControllerKeys.B;
if (keyboard[(Key)_config.RightJoycon.ButtonX]) buttons |= ControllerKeys.X;
if (keyboard[(Key)_config.RightJoycon.ButtonY]) buttons |= ControllerKeys.Y;
if (keyboard[(Key)_config.RightJoycon.ButtonPlus]) buttons |= ControllerKeys.Plus;
if (keyboard[(Key)_config.RightJoycon.ButtonR]) buttons |= ControllerKeys.R;
if (keyboard[(Key)_config.RightJoycon.ButtonZr]) buttons |= ControllerKeys.Zr;
if (keyboard[(Key)_config.RightJoycon.ButtonSl]) buttons |= ControllerKeys.SlRight;
if (keyboard[(Key)_config.RightJoycon.ButtonSr]) buttons |= ControllerKeys.SrRight;
return buttons;
}
public (short, short) GetLeftStick()
{
KeyboardState keyboard = GetKeyboardState(_config.Index);
short dx = 0;
short dy = 0;
if (keyboard[(Key)_config.LeftJoycon.StickUp]) dy += 1;
if (keyboard[(Key)_config.LeftJoycon.StickDown]) dy += -1;
if (keyboard[(Key)_config.LeftJoycon.StickLeft]) dx += -1;
if (keyboard[(Key)_config.LeftJoycon.StickRight]) dx += 1;
Vector2 stick = new Vector2(dx, dy);
stick.NormalizeFast();
return ((short)(stick.X * short.MaxValue), (short)(stick.Y * short.MaxValue));
}
public (short, short) GetRightStick()
{
KeyboardState keyboard = GetKeyboardState(_config.Index);
short dx = 0;
short dy = 0;
if (keyboard[(Key)_config.RightJoycon.StickUp]) dy += 1;
if (keyboard[(Key)_config.RightJoycon.StickDown]) dy += -1;
if (keyboard[(Key)_config.RightJoycon.StickLeft]) dx += -1;
if (keyboard[(Key)_config.RightJoycon.StickRight]) dx += 1;
Vector2 stick = new Vector2(dx, dy);
stick.NormalizeFast();
return ((short)(stick.X * short.MaxValue), (short)(stick.Y * short.MaxValue));
}
public static HotkeyButtons GetHotkeyButtons(KeyboardState keyboard)
{
HotkeyButtons buttons = 0;
if (keyboard[(Key)ConfigurationState.Instance.Hid.Hotkeys.Value.ToggleVsync])
{
buttons |= HotkeyButtons.ToggleVSync;
}
return buttons;
}
class KeyMappingEntry
{
public Key TargetKey;
public byte Target;
}
private static readonly KeyMappingEntry[] KeyMapping = new KeyMappingEntry[]
{
new KeyMappingEntry { TargetKey = Key.A, Target = 0x4 },
new KeyMappingEntry { TargetKey = Key.B, Target = 0x5 },
new KeyMappingEntry { TargetKey = Key.C, Target = 0x6 },
new KeyMappingEntry { TargetKey = Key.D, Target = 0x7 },
new KeyMappingEntry { TargetKey = Key.E, Target = 0x8 },
new KeyMappingEntry { TargetKey = Key.F, Target = 0x9 },
new KeyMappingEntry { TargetKey = Key.G, Target = 0xA },
new KeyMappingEntry { TargetKey = Key.H, Target = 0xB },
new KeyMappingEntry { TargetKey = Key.I, Target = 0xC },
new KeyMappingEntry { TargetKey = Key.J, Target = 0xD },
new KeyMappingEntry { TargetKey = Key.K, Target = 0xE },
new KeyMappingEntry { TargetKey = Key.L, Target = 0xF },
new KeyMappingEntry { TargetKey = Key.M, Target = 0x10 },
new KeyMappingEntry { TargetKey = Key.N, Target = 0x11 },
new KeyMappingEntry { TargetKey = Key.O, Target = 0x12 },
new KeyMappingEntry { TargetKey = Key.P, Target = 0x13 },
new KeyMappingEntry { TargetKey = Key.Q, Target = 0x14 },
new KeyMappingEntry { TargetKey = Key.R, Target = 0x15 },
new KeyMappingEntry { TargetKey = Key.S, Target = 0x16 },
new KeyMappingEntry { TargetKey = Key.T, Target = 0x17 },
new KeyMappingEntry { TargetKey = Key.U, Target = 0x18 },
new KeyMappingEntry { TargetKey = Key.V, Target = 0x19 },
new KeyMappingEntry { TargetKey = Key.W, Target = 0x1A },
new KeyMappingEntry { TargetKey = Key.X, Target = 0x1B },
new KeyMappingEntry { TargetKey = Key.Y, Target = 0x1C },
new KeyMappingEntry { TargetKey = Key.Z, Target = 0x1D },
new KeyMappingEntry { TargetKey = Key.Number1, Target = 0x1E },
new KeyMappingEntry { TargetKey = Key.Number2, Target = 0x1F },
new KeyMappingEntry { TargetKey = Key.Number3, Target = 0x20 },
new KeyMappingEntry { TargetKey = Key.Number4, Target = 0x21 },
new KeyMappingEntry { TargetKey = Key.Number5, Target = 0x22 },
new KeyMappingEntry { TargetKey = Key.Number6, Target = 0x23 },
new KeyMappingEntry { TargetKey = Key.Number7, Target = 0x24 },
new KeyMappingEntry { TargetKey = Key.Number8, Target = 0x25 },
new KeyMappingEntry { TargetKey = Key.Number9, Target = 0x26 },
new KeyMappingEntry { TargetKey = Key.Number0, Target = 0x27 },
new KeyMappingEntry { TargetKey = Key.Enter, Target = 0x28 },
new KeyMappingEntry { TargetKey = Key.Escape, Target = 0x29 },
new KeyMappingEntry { TargetKey = Key.BackSpace, Target = 0x2A },
new KeyMappingEntry { TargetKey = Key.Tab, Target = 0x2B },
new KeyMappingEntry { TargetKey = Key.Space, Target = 0x2C },
new KeyMappingEntry { TargetKey = Key.Minus, Target = 0x2D },
new KeyMappingEntry { TargetKey = Key.Plus, Target = 0x2E },
new KeyMappingEntry { TargetKey = Key.BracketLeft, Target = 0x2F },
new KeyMappingEntry { TargetKey = Key.BracketRight, Target = 0x30 },
new KeyMappingEntry { TargetKey = Key.BackSlash, Target = 0x31 },
new KeyMappingEntry { TargetKey = Key.Tilde, Target = 0x32 },
new KeyMappingEntry { TargetKey = Key.Semicolon, Target = 0x33 },
new KeyMappingEntry { TargetKey = Key.Quote, Target = 0x34 },
new KeyMappingEntry { TargetKey = Key.Grave, Target = 0x35 },
new KeyMappingEntry { TargetKey = Key.Comma, Target = 0x36 },
new KeyMappingEntry { TargetKey = Key.Period, Target = 0x37 },
new KeyMappingEntry { TargetKey = Key.Slash, Target = 0x38 },
new KeyMappingEntry { TargetKey = Key.CapsLock, Target = 0x39 },
new KeyMappingEntry { TargetKey = Key.F1, Target = 0x3a },
new KeyMappingEntry { TargetKey = Key.F2, Target = 0x3b },
new KeyMappingEntry { TargetKey = Key.F3, Target = 0x3c },
new KeyMappingEntry { TargetKey = Key.F4, Target = 0x3d },
new KeyMappingEntry { TargetKey = Key.F5, Target = 0x3e },
new KeyMappingEntry { TargetKey = Key.F6, Target = 0x3f },
new KeyMappingEntry { TargetKey = Key.F7, Target = 0x40 },
new KeyMappingEntry { TargetKey = Key.F8, Target = 0x41 },
new KeyMappingEntry { TargetKey = Key.F9, Target = 0x42 },
new KeyMappingEntry { TargetKey = Key.F10, Target = 0x43 },
new KeyMappingEntry { TargetKey = Key.F11, Target = 0x44 },
new KeyMappingEntry { TargetKey = Key.F12, Target = 0x45 },
new KeyMappingEntry { TargetKey = Key.PrintScreen, Target = 0x46 },
new KeyMappingEntry { TargetKey = Key.ScrollLock, Target = 0x47 },
new KeyMappingEntry { TargetKey = Key.Pause, Target = 0x48 },
new KeyMappingEntry { TargetKey = Key.Insert, Target = 0x49 },
new KeyMappingEntry { TargetKey = Key.Home, Target = 0x4A },
new KeyMappingEntry { TargetKey = Key.PageUp, Target = 0x4B },
new KeyMappingEntry { TargetKey = Key.Delete, Target = 0x4C },
new KeyMappingEntry { TargetKey = Key.End, Target = 0x4D },
new KeyMappingEntry { TargetKey = Key.PageDown, Target = 0x4E },
new KeyMappingEntry { TargetKey = Key.Right, Target = 0x4F },
new KeyMappingEntry { TargetKey = Key.Left, Target = 0x50 },
new KeyMappingEntry { TargetKey = Key.Down, Target = 0x51 },
new KeyMappingEntry { TargetKey = Key.Up, Target = 0x52 },
new KeyMappingEntry { TargetKey = Key.NumLock, Target = 0x53 },
new KeyMappingEntry { TargetKey = Key.KeypadDivide, Target = 0x54 },
new KeyMappingEntry { TargetKey = Key.KeypadMultiply, Target = 0x55 },
new KeyMappingEntry { TargetKey = Key.KeypadMinus, Target = 0x56 },
new KeyMappingEntry { TargetKey = Key.KeypadPlus, Target = 0x57 },
new KeyMappingEntry { TargetKey = Key.KeypadEnter, Target = 0x58 },
new KeyMappingEntry { TargetKey = Key.Keypad1, Target = 0x59 },
new KeyMappingEntry { TargetKey = Key.Keypad2, Target = 0x5A },
new KeyMappingEntry { TargetKey = Key.Keypad3, Target = 0x5B },
new KeyMappingEntry { TargetKey = Key.Keypad4, Target = 0x5C },
new KeyMappingEntry { TargetKey = Key.Keypad5, Target = 0x5D },
new KeyMappingEntry { TargetKey = Key.Keypad6, Target = 0x5E },
new KeyMappingEntry { TargetKey = Key.Keypad7, Target = 0x5F },
new KeyMappingEntry { TargetKey = Key.Keypad8, Target = 0x60 },
new KeyMappingEntry { TargetKey = Key.Keypad9, Target = 0x61 },
new KeyMappingEntry { TargetKey = Key.Keypad0, Target = 0x62 },
new KeyMappingEntry { TargetKey = Key.KeypadPeriod, Target = 0x63 },
new KeyMappingEntry { TargetKey = Key.NonUSBackSlash, Target = 0x64 },
new KeyMappingEntry { TargetKey = Key.F13, Target = 0x68 },
new KeyMappingEntry { TargetKey = Key.F14, Target = 0x69 },
new KeyMappingEntry { TargetKey = Key.F15, Target = 0x6A },
new KeyMappingEntry { TargetKey = Key.F16, Target = 0x6B },
new KeyMappingEntry { TargetKey = Key.F17, Target = 0x6C },
new KeyMappingEntry { TargetKey = Key.F18, Target = 0x6D },
new KeyMappingEntry { TargetKey = Key.F19, Target = 0x6E },
new KeyMappingEntry { TargetKey = Key.F20, Target = 0x6F },
new KeyMappingEntry { TargetKey = Key.F21, Target = 0x70 },
new KeyMappingEntry { TargetKey = Key.F22, Target = 0x71 },
new KeyMappingEntry { TargetKey = Key.F23, Target = 0x72 },
new KeyMappingEntry { TargetKey = Key.F24, Target = 0x73 },
new KeyMappingEntry { TargetKey = Key.ControlLeft, Target = 0xE0 },
new KeyMappingEntry { TargetKey = Key.ShiftLeft, Target = 0xE1 },
new KeyMappingEntry { TargetKey = Key.AltLeft, Target = 0xE2 },
new KeyMappingEntry { TargetKey = Key.WinLeft, Target = 0xE3 },
new KeyMappingEntry { TargetKey = Key.ControlRight, Target = 0xE4 },
new KeyMappingEntry { TargetKey = Key.ShiftRight, Target = 0xE5 },
new KeyMappingEntry { TargetKey = Key.AltRight, Target = 0xE6 },
new KeyMappingEntry { TargetKey = Key.WinRight, Target = 0xE7 },
};
private static readonly KeyMappingEntry[] KeyModifierMapping = new KeyMappingEntry[]
{
new KeyMappingEntry { TargetKey = Key.ControlLeft, Target = 0 },
new KeyMappingEntry { TargetKey = Key.ShiftLeft, Target = 1 },
new KeyMappingEntry { TargetKey = Key.AltLeft, Target = 2 },
new KeyMappingEntry { TargetKey = Key.WinLeft, Target = 3 },
new KeyMappingEntry { TargetKey = Key.ControlRight, Target = 4 },
new KeyMappingEntry { TargetKey = Key.ShiftRight, Target = 5 },
new KeyMappingEntry { TargetKey = Key.AltRight, Target = 6 },
new KeyMappingEntry { TargetKey = Key.WinRight, Target = 7 },
new KeyMappingEntry { TargetKey = Key.CapsLock, Target = 8 },
new KeyMappingEntry { TargetKey = Key.ScrollLock, Target = 9 },
new KeyMappingEntry { TargetKey = Key.NumLock, Target = 10 },
};
public KeyboardInput GetKeysDown()
{
KeyboardState keyboard = GetKeyboardState(_config.Index);
KeyboardInput hidKeyboard = new KeyboardInput
{
Modifier = 0,
Keys = new int[0x8]
};
foreach (KeyMappingEntry entry in KeyMapping)
{
int value = keyboard[entry.TargetKey] ? 1 : 0;
hidKeyboard.Keys[entry.Target / 0x20] |= (value << (entry.Target % 0x20));
}
foreach (KeyMappingEntry entry in KeyModifierMapping)
{
int value = keyboard[entry.TargetKey] ? 1 : 0;
hidKeyboard.Modifier |= value << entry.Target;
}
return hidKeyboard;
}
}
}

View file

@ -17,6 +17,9 @@ using Ryujinx.HLE.FileSystem;
using Ryujinx.HLE.FileSystem.Content;
using Ryujinx.HLE.HOS;
using Ryujinx.HLE.HOS.Services.Account.Acc;
using Ryujinx.Input.GTK3;
using Ryujinx.Input.HLE;
using Ryujinx.Input.SDL2;
using Ryujinx.Modules;
using Ryujinx.Ui.App;
using Ryujinx.Ui.Applet;
@ -65,6 +68,7 @@ namespace Ryujinx.Ui
private bool _lastScannedAmiiboShowAll = false;
public GlRenderer GlRendererWidget;
public InputManager InputManager;
#pragma warning disable CS0169, CS0649, IDE0044
@ -223,6 +227,8 @@ namespace Ryujinx.Ui
};
Task.Run(RefreshFirmwareLabel);
InputManager = new InputManager(new GTK3KeyboardDriver(this), new SDL2GamepadDriver());
}
private void WindowStateEvent_Changed(object o, WindowStateEventArgs args)
@ -295,6 +301,11 @@ namespace Ryujinx.Ui
}
}
protected override void OnDestroyed()
{
InputManager.Dispose();
}
private void InitializeSwitchInstance()
{
_virtualFileSystem.Reload();
@ -636,7 +647,7 @@ namespace Ryujinx.Ui
DisplaySleep.Prevent();
GlRendererWidget = new GlRenderer(_emulationContext, ConfigurationState.Instance.Logger.GraphicsDebugLevel);
GlRendererWidget = new GlRenderer(_emulationContext, InputManager, ConfigurationState.Instance.Logger.GraphicsDebugLevel);
Application.Invoke(delegate
{

View file

@ -0,0 +1,20 @@
using SPB.Graphics;
using System;
namespace Ryujinx.Ui
{
public class OpenToolkitBindingsContext : OpenTK.IBindingsContext
{
private IBindingsContext _bindingContext;
public OpenToolkitBindingsContext(IBindingsContext bindingsContext)
{
_bindingContext = bindingsContext;
}
public IntPtr GetProcAddress(string procName)
{
return _bindingContext.GetProcAddress(procName);
}
}
}

View file

@ -0,0 +1,49 @@
using OpenTK;
using OpenTK.Graphics;
using OpenTK.Graphics.OpenGL;
using Ryujinx.Graphics.OpenGL;
using SPB.Graphics;
using SPB.Graphics.OpenGL;
using SPB.Platform;
using SPB.Windowing;
namespace Ryujinx.Ui
{
class SPBOpenGLContext : IOpenGLContext
{
private OpenGLContextBase _context;
private NativeWindowBase _window;
private SPBOpenGLContext(OpenGLContextBase context, NativeWindowBase window)
{
_context = context;
_window = window;
}
public void Dispose()
{
_context.Dispose();
_window.Dispose();
}
public void MakeCurrent()
{
_context.MakeCurrent(_window);
}
public static SPBOpenGLContext CreateBackgroundContext(OpenGLContextBase sharedContext)
{
OpenGLContextBase context = PlatformHelper.CreateOpenGLContext(FramebufferFormat.Default, 3, 3, OpenGLContextFlags.Compat, true, sharedContext);
NativeWindowBase window = PlatformHelper.CreateWindow(FramebufferFormat.Default, 0, 0, 100, 100);
context.Initialize(window);
context.MakeCurrent(window);
GL.LoadBindings(new OpenToolkitBindingsContext(context));
context.MakeCurrent(null);
return new SPBOpenGLContext(context, window);
}
}
}

File diff suppressed because it is too large Load diff

View file

@ -48,8 +48,8 @@
<property name="title" translatable="yes">Ryujinx - Controller Settings</property>
<property name="modal">True</property>
<property name="window_position">center</property>
<property name="default_width">1150</property>
<property name="default_height">690</property>
<property name="default_width">1200</property>
<property name="default_height">720</property>
<child type="titlebar">
<placeholder/>
</child>
@ -113,21 +113,6 @@
<property name="position">1</property>
</packing>
</child>
<child>
<object class="GtkToggleButton" id="_refreshInputDevicesButton">
<property name="label" translatable="yes">Refresh</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<property name="margin_left">5</property>
<signal name="toggled" handler="RefreshInputDevicesButton_Pressed" swapped="no"/>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">2</property>
</packing>
</child>
</object>
<packing>
<property name="expand">False</property>
@ -682,7 +667,7 @@
<property name="width_request">80</property>
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="label" translatable="yes">LStick Lt/Rt</property>
<property name="label" translatable="yes">LStick</property>
<property name="xalign">0</property>
</object>
<packing>
@ -691,20 +676,7 @@
</packing>
</child>
<child>
<object class="GtkLabel">
<property name="width_request">80</property>
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="label" translatable="yes">LStick Up/Dn</property>
<property name="xalign">0</property>
</object>
<packing>
<property name="left_attach">0</property>
<property name="top_attach">1</property>
</packing>
</child>
<child>
<object class="GtkToggleButton" id="_lStickX">
<object class="GtkToggleButton" id="_lStick">
<property name="label" translatable="yes"> </property>
<property name="width_request">65</property>
<property name="visible">True</property>
@ -716,22 +688,9 @@
<property name="top_attach">0</property>
</packing>
</child>
<child>
<object class="GtkToggleButton" id="_lStickY">
<property name="label" translatable="yes"> </property>
<property name="width_request">65</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
</object>
<packing>
<property name="left_attach">1</property>
<property name="top_attach">1</property>
</packing>
</child>
<child>
<object class="GtkCheckButton" id="_invertLStickX">
<property name="label" translatable="yes">Invert</property>
<property name="label" translatable="yes">Invert Stick X</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">False</property>
@ -744,7 +703,7 @@
</child>
<child>
<object class="GtkCheckButton" id="_invertLStickY">
<property name="label" translatable="yes">Invert</property>
<property name="label" translatable="yes">Invert Stick Y</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">False</property>
@ -1501,7 +1460,7 @@
<property name="width_request">80</property>
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="label" translatable="yes">RStick Lt/Rt</property>
<property name="label" translatable="yes">RStick</property>
<property name="xalign">0</property>
</object>
<packing>
@ -1510,20 +1469,7 @@
</packing>
</child>
<child>
<object class="GtkLabel">
<property name="width_request">80</property>
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="label" translatable="yes">RStick Up/Dn</property>
<property name="xalign">0</property>
</object>
<packing>
<property name="left_attach">0</property>
<property name="top_attach">1</property>
</packing>
</child>
<child>
<object class="GtkToggleButton" id="_rStickX">
<object class="GtkToggleButton" id="_rStick">
<property name="label" translatable="yes"> </property>
<property name="width_request">65</property>
<property name="visible">True</property>
@ -1535,22 +1481,9 @@
<property name="top_attach">0</property>
</packing>
</child>
<child>
<object class="GtkToggleButton" id="_rStickY">
<property name="label" translatable="yes"> </property>
<property name="width_request">65</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
</object>
<packing>
<property name="left_attach">1</property>
<property name="top_attach">1</property>
</packing>
</child>
<child>
<object class="GtkCheckButton" id="_invertRStickX">
<property name="label" translatable="yes">Invert</property>
<property name="label" translatable="yes">Invert Stick X</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">False</property>
@ -1563,7 +1496,7 @@
</child>
<child>
<object class="GtkCheckButton" id="_invertRStickY">
<property name="label" translatable="yes">Invert</property>
<property name="label" translatable="yes">Invert Stick Y</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">False</property>
@ -1640,7 +1573,7 @@
</packing>
</child>
<child>
<object class="GtkBox" id="MotionBox">
<object class="GtkBox" id="_motionBox">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="margin_left">10</property>
@ -1679,7 +1612,21 @@
</packing>
</child>
<child>
<object class="GtkBox">
<object class="GtkCheckButton" id="_enableCemuHook">
<property name="label" translatable="yes">Use CemuHook compatible motion</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">False</property>
<property name="draw_indicator">True</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">2</property>
</packing>
</child>
<child>
<object class="GtkBox" id="_motionControllerSlot">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="spacing">10</property>
@ -1718,7 +1665,7 @@
<property name="expand">False</property>
<property name="fill">True</property>
<property name="padding">5</property>
<property name="position">2</property>
<property name="position">3</property>
</packing>
</child>
<child>
@ -1761,11 +1708,11 @@
<property name="expand">False</property>
<property name="fill">True</property>
<property name="padding">5</property>
<property name="position">3</property>
<property name="position">4</property>
</packing>
</child>
<child>
<object class="GtkBox" id="_altBox">
<object class="GtkBox" id="_motionAltBox">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="orientation">vertical</property>
@ -1829,11 +1776,11 @@
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">4</property>
<property name="position">5</property>
</packing>
</child>
<child>
<object class="GtkBox">
<object class="GtkBox" id="_dsuServerHostBox">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="spacing">30</property>
@ -1866,11 +1813,11 @@
<property name="expand">False</property>
<property name="fill">True</property>
<property name="padding">5</property>
<property name="position">5</property>
<property name="position">6</property>
</packing>
</child>
<child>
<object class="GtkBox">
<object class="GtkBox" id="_dsuServerPortBox">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="spacing">30</property>
@ -1903,7 +1850,7 @@
<property name="expand">False</property>
<property name="fill">True</property>
<property name="padding">5</property>
<property name="position">6</property>
<property name="position">7</property>
</packing>
</child>
<child>
@ -1916,7 +1863,7 @@
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">7</property>
<property name="position">8</property>
</packing>
</child>
<child>
@ -1930,7 +1877,7 @@
<packing>
<property name="expand">True</property>
<property name="fill">True</property>
<property name="position">8</property>
<property name="position">9</property>
</packing>
</child>
</object>

View file

@ -578,7 +578,7 @@ namespace Ryujinx.Ui.Windows
{
((ToggleButton)sender).SetStateFlags(StateFlags.Normal, true);
ControllerWindow controllerWindow = new ControllerWindow(playerIndex);
ControllerWindow controllerWindow = new ControllerWindow(_parent, playerIndex);
controllerWindow.SetSizeRequest((int)(controllerWindow.DefaultWidth * Program.WindowScaleFactor), (int)(controllerWindow.DefaultHeight * Program.WindowScaleFactor));
controllerWindow.Show();