Mesen2/UI/Utilities/MouseManager.cs
2023-01-25 16:03:56 -05:00

243 lines
7.4 KiB
C#

using Avalonia;
using Avalonia.Controls;
using Avalonia.Threading;
using Mesen.Config;
using Mesen.Interop;
using Mesen.Localization;
using Mesen.Utilities.GlobalMouseLib;
using Mesen.ViewModels;
using Mesen.Views;
using Mesen.Windows;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Mesen.Utilities
{
public class MouseManager : IDisposable
{
public const int LeftMouseButtonKeyCode = 0x200;
public const int RightMouseButtonKeyCode = 0x201;
public const int MiddleMouseButtonKeyCode = 0x202;
public const int MouseButton4KeyCode = 0x203;
public const int MouseButton5KeyCode = 0x204;
private DispatcherTimer _timer = new DispatcherTimer();
private NativeRenderer _renderer;
private MainMenuView _mainMenu;
private MainWindow _wnd;
private MousePosition _prevPosition;
private bool _mouseCaptured = false;
private DateTime _lastMouseMove = DateTime.Now;
public MouseManager(MainWindow wnd, NativeRenderer renderer, MainMenuView mainMenu)
{
_wnd = wnd;
_renderer = renderer;
_mainMenu = mainMenu;
_timer.Interval = TimeSpan.FromMilliseconds(15);
_timer.Tick += tmrProcessMouse;
_timer.Start();
}
private bool IsPointerInMenu()
{
return _mainMenu.IsPointerOver || MenuHelper.IsPointerInMenu(_mainMenu.MainMenu);
}
private void tmrProcessMouse(object? sender, EventArgs e)
{
if(MainWindowViewModel.Instance.RecentGames.Visible) {
return;
}
bool leftPressed = GlobalMouse.IsMouseButtonPressed(MouseButtons.Left);
if(_wnd.IsActive && leftPressed && !IsPointerInMenu() && EmuApi.IsRunning()) {
//Close menu when renderer is clicked
_mainMenu.MainMenu.Close();
if(MainWindowViewModel.Instance.AudioPlayer == null) {
//Only give renderer focus when the audio player isn't active
//Otherwise clicking on the audio player's buttons does nothing
_renderer.Focus();
}
}
PixelPoint rendererTopLeft = _renderer.PointToScreen(new Point());
PixelRect rendererScreenRect = new PixelRect(rendererTopLeft, PixelSize.FromSize(_renderer.Bounds.Size, 1.0));
MousePosition p = GlobalMouse.GetMousePosition();
if(_prevPosition.X != p.X || _prevPosition.Y != p.Y) {
//Send mouse movement x/y values to core
InputApi.SetMouseMovement((Int16)(p.X - _prevPosition.X), (Int16)(p.Y - _prevPosition.Y));
_prevPosition = p;
_lastMouseMove = DateTime.Now;
}
PixelPoint mousePos = new PixelPoint(p.X, p.Y);
UpdateMainMenuVisibility(mousePos);
if(_wnd.IsActive && (_mainMenu.IsPointerOver || _mainMenu.IsKeyboardFocusWithin || _mainMenu.MainMenu.IsOpen)) {
//When mouse or keyboard focus is in menu, release mouse and keep arrow cursor
SetMouseOffScreen();
ReleaseMouse();
if(rendererScreenRect.Contains(mousePos)) {
GlobalMouse.SetCursorIcon(CursorIcon.Arrow);
}
return;
}
if(_wnd.IsActive && rendererScreenRect.Contains(mousePos)) {
//Send mouse state to emulation core
Point rendererPos = _renderer.PointToClient(mousePos);
InputApi.SetMousePosition(rendererPos.X / rendererScreenRect.Width, rendererPos.Y / rendererScreenRect.Height);
InputApi.SetKeyState(LeftMouseButtonKeyCode, leftPressed);
InputApi.SetKeyState(RightMouseButtonKeyCode, GlobalMouse.IsMouseButtonPressed(MouseButtons.Right));
InputApi.SetKeyState(MiddleMouseButtonKeyCode, GlobalMouse.IsMouseButtonPressed(MouseButtons.Middle));
InputApi.SetKeyState(MouseButton4KeyCode, GlobalMouse.IsMouseButtonPressed(MouseButtons.Button4));
InputApi.SetKeyState(MouseButton5KeyCode, GlobalMouse.IsMouseButtonPressed(MouseButtons.Button5));
if(!_mouseCaptured && AllowMouseCapture && leftPressed) {
//If the mouse button is clicked and mouse isn't captured but can be, turn on mouse capture
CaptureMouse();
}
if(_mouseCaptured) {
if(AllowMouseCapture) {
GlobalMouse.SetCursorIcon(CursorIcon.Hidden);
PixelPoint rendererCenter = _renderer.PointToScreen(_renderer.Bounds.Center);
GlobalMouse.SetMousePosition((uint)rendererCenter.X, (uint)rendererCenter.Y);
_prevPosition = new MousePosition(rendererCenter.X, rendererCenter.Y);
} else {
ReleaseMouse();
}
}
if(!_mouseCaptured) {
GlobalMouse.SetCursorIcon(MouseIcon);
}
} else {
SetMouseOffScreen();
}
}
private void UpdateMainMenuVisibility(PixelPoint mousePos)
{
bool autoHideMenu = _wnd.WindowState == WindowState.FullScreen || ConfigManager.Config.Preferences.AutoHideMenu;
if(autoHideMenu) {
if(_mainMenu.MainMenu.IsOpen) {
MainWindowViewModel.Instance.IsMenuVisible = true;
} else {
PixelPoint wndTopLeft = _wnd.PointToScreen(new Point(0, 0));
bool showMenu = (
mousePos.Y >= wndTopLeft.Y - 15 && mousePos.Y <= wndTopLeft.Y + Math.Max(_mainMenu.Bounds.Height + 10, 35) &&
mousePos.X >= wndTopLeft.X && mousePos.X <= wndTopLeft.X + _wnd.Bounds.Width
);
MainWindowViewModel.Instance.IsMenuVisible = showMenu;
}
} else {
MainWindowViewModel.Instance.IsMenuVisible = true;
}
}
private static void SetMouseOffScreen()
{
//Send mouse state to emulation core (mouse is set as off the screen)
InputApi.SetMousePosition(-1, -1);
InputApi.SetKeyState(LeftMouseButtonKeyCode, false);
InputApi.SetKeyState(RightMouseButtonKeyCode, false);
InputApi.SetKeyState(MiddleMouseButtonKeyCode, false);
}
private bool AllowMouseCapture
{
get
{
if(!EmuApi.IsRunning() || EmuApi.IsPaused()) {
return false;
}
bool hasMouseDevice = (
InputApi.HasControlDevice(ControllerType.SnesMouse) ||
InputApi.HasControlDevice(ControllerType.SuborMouse) ||
InputApi.HasControlDevice(ControllerType.FamicomArkanoidController) ||
InputApi.HasControlDevice(ControllerType.NesArkanoidController) ||
InputApi.HasControlDevice(ControllerType.HoriTrack)
);
if(hasMouseDevice) {
return true;
}
return false;
}
}
private CursorIcon MouseIcon
{
get
{
if(!EmuApi.IsRunning() || EmuApi.IsPaused()) {
return CursorIcon.Arrow;
}
bool hasLightGun = (
InputApi.HasControlDevice(ControllerType.FamicomZapper) ||
InputApi.HasControlDevice(ControllerType.NesZapper) ||
InputApi.HasControlDevice(ControllerType.SuperScope) ||
InputApi.HasControlDevice(ControllerType.BandaiHyperShot)
);
if(hasLightGun) {
if(ConfigManager.Config.Input.HidePointerForLightGuns) {
return CursorIcon.Hidden;
} else {
return CursorIcon.Cross;
}
} else if(InputApi.HasControlDevice(ControllerType.OekaKidsTablet)) {
return CursorIcon.Cross;
}
if((DateTime.Now - _lastMouseMove).TotalSeconds > 1) {
return CursorIcon.Hidden;
} else {
return CursorIcon.Arrow;
}
}
}
private void CaptureMouse()
{
if(!_mouseCaptured && AllowMouseCapture) {
EmuApi.DisplayMessage("Input", ResourceHelper.GetMessage("MouseModeEnabled"));
_mouseCaptured = true;
PixelPoint topLeft = _renderer.PointToScreen(new Point(0, 0));
GlobalMouse.CaptureCursor(topLeft.X, topLeft.Y, (int)_renderer.Bounds.Width, (int)_renderer.Bounds.Height, _renderer.Handle);
}
}
private void ReleaseMouse()
{
if(_mouseCaptured) {
_mouseCaptured = false;
GlobalMouse.ReleaseCursor();
}
}
public void Dispose()
{
if(_timer is DispatcherTimer timer) {
timer.Tick -= tmrProcessMouse;
timer.Stop();
}
}
}
}