Compare commits

...

21 commits

Author SHA1 Message Date
Angelo 64bd79a520
Merge 238dbaca71 into 202b8f32c8 2024-05-10 13:13:07 +02:00
Sour 202b8f32c8 Debugger: SMS - Added some missing values in register viewer 2024-05-10 18:37:45 +09:00
Sour bb745910d5 UI: Added shortcuts for next/previous track in audio player 2024-05-10 18:09:13 +09:00
angelo_wf 238dbaca71 Fix another wrong include 2024-04-21 12:04:52 +02:00
angelo_wf db6541ae76 Merge branch 'master' into mouse-handler-in-core 2024-04-13 12:29:36 +02:00
angelo_wf d5bf91e51e Remove unneeded include 2024-04-13 12:29:12 +02:00
angelo_wf 42b163153c Merge branch 'master' into mouse-handler-in-core 2024-04-01 21:00:57 +02:00
angelo_wf 33373d7873 Remove now unused GlobalMouse 2024-04-01 20:59:56 +02:00
edoornbos d808d3f074 Fix LinuxMouseManager 2024-04-01 19:32:24 +02:00
angelo_wf 5fb6025be1 Use result from CaptureMouse 2024-04-01 16:33:11 +02:00
Angelo 0a269f2426 Fix WindowsMouseManager.cpp 2024-04-01 15:34:08 +02:00
angelo_wf f4ee22bc5f Tentative Linux/X11 mouse support 2024-04-01 14:32:42 +02:00
angelo_wf c09eaaf17f Some updates/fixes, tentative Windows mouse impl. 2024-04-01 12:20:56 +02:00
angelo_wf 6a9cd6ea58 Prepare for adding Win/Linux mouse handling 2024-03-31 17:44:21 +02:00
angelo_wf 0366f1e7af Handle (macOS) mouse in core, use in UI 2024-03-31 16:27:26 +02:00
angelo_wf 83c85fee51 Merge branch 'master' into macos-mouse-support 2024-03-30 13:57:38 +01:00
angelo_wf 98ac589229 fix wrong enum value, remove unused headers 2024-03-30 13:44:37 +01:00
angelo_wf 04ce690523 Merge branch 'master' into macos-mouse-support 2024-01-28 21:50:24 +01:00
angelo_wf 116e71a484 Fixed some comments based on testing 2024-01-26 22:56:21 +01:00
angelo_wf 2b3eba4261 Make some comment clearer 2024-01-23 21:53:51 +01:00
angelo_wf a8aa4bf620 Add MacOS Mouse support 2024-01-21 15:56:55 +01:00
38 changed files with 646 additions and 680 deletions

View file

@ -742,6 +742,7 @@
<ClInclude Include="Shared\Video\VideoDecoder.h" />
<ClInclude Include="Shared\Video\VideoRenderer.h" />
<ClInclude Include="Shared\Audio\WaveRecorder.h" />
<ClInclude Include="Shared\Interfaces\IMouseManager.h" />
</ItemGroup>
<ItemGroup>
<ClCompile Include="Debugger\Base6502Assembler.cpp" />

View file

@ -1021,6 +1021,9 @@
<ClInclude Include="Shared\Interfaces\IMessageManager.h">
<Filter>Shared\Interfaces</Filter>
</ClInclude>
<ClInclude Include="Shared\Interfaces\IMouseManager.h">
<Filter>Shared\Interfaces</Filter>
</ClInclude>
<ClInclude Include="Shared\Interfaces\INotificationListener.h">
<Filter>Shared\Interfaces</Filter>
</ClInclude>

View file

@ -0,0 +1,33 @@
#pragma once
#include "pch.h"
enum class CursorImage
{
Hidden,
Arrow,
Cross
};
struct SystemMouseState
{
int32_t XPosition;
int32_t YPosition;
bool LeftButton;
bool RightButton;
bool MiddleButton;
bool Button4;
bool Button5;
};
class IMouseManager
{
public:
virtual ~IMouseManager() {}
virtual SystemMouseState GetSystemMouseState(void* rendererHandle) = 0;
virtual bool CaptureMouse(int32_t x, int32_t y, int32_t width, int32_t height, void* rendererHandle) = 0;
virtual void ReleaseMouse() = 0;
virtual void SetSystemMousePosition(int32_t x, int32_t y) = 0;
virtual void SetCursorImage(CursorImage cursor) = 0;
virtual double GetPixelScale() = 0;
};

View file

@ -857,6 +857,9 @@ enum class EmulatorShortcut
IncreaseVolume,
DecreaseVolume,
PreviousTrack,
NextTrack,
ToggleBgLayer1,
ToggleBgLayer2,
ToggleBgLayer3,

View file

@ -23,13 +23,16 @@
#include "Windows/Renderer.h"
#include "Windows/SoundManager.h"
#include "Windows/WindowsKeyManager.h"
#include "Windows/WindowsMouseManager.h"
#elif __APPLE__
#include "Linux/SdlSoundManager.h"
#include "Sdl/SdlSoundManager.h"
#include "MacOS/MacOSKeyManager.h"
#include "MacOS/MacOSMouseManager.h"
#else
#include "Linux/SdlRenderer.h"
#include "Linux/SdlSoundManager.h"
#include "Sdl/SdlRenderer.h"
#include "Sdl/SdlSoundManager.h"
#include "Linux/LinuxKeyManager.h"
#include "Linux/LinuxMouseManager.h"
#endif
#include "Shared/Video/SoftwareRenderer.h"
@ -37,6 +40,7 @@
unique_ptr<IRenderingDevice> _renderer;
unique_ptr<IAudioDevice> _soundManager;
unique_ptr<IKeyManager> _keyManager;
unique_ptr<IMouseManager> _mouseManager;
unique_ptr<Emulator> _emu(new Emulator());
bool _softwareRenderer = false;
@ -107,10 +111,13 @@ extern "C" {
if(!noInput) {
#ifdef _WIN32
_keyManager.reset(new WindowsKeyManager(_emu.get(), (HWND)_windowHandle));
_mouseManager.reset(new WindowsMouseManager());
#elif __APPLE__
_keyManager.reset(new MacOSKeyManager(_emu.get()));
_mouseManager.reset(new MacOSMouseManager());
#else
_keyManager.reset(new LinuxKeyManager(_emu.get()));
_mouseManager.reset(new LinuxMouseManager(_windowHandle));
#endif
KeyManager::RegisterKeyManager(_keyManager.get());

View file

@ -14,11 +14,11 @@
#ifdef _WIN32
#include "Windows/Renderer.h"
#include "Windows/SoundManager.h"
#include "Windows/WindowsKeyManager.h"
#elif __APPLE__
#include "Sdl/SdlSoundManager.h"
#else
#include "Linux/SdlRenderer.h"
#include "Linux/SdlSoundManager.h"
#include "Linux/LinuxKeyManager.h"
#include "Sdl/SdlRenderer.h"
#include "Sdl/SdlSoundManager.h"
#endif
extern unique_ptr<Emulator> _emu;

View file

@ -4,8 +4,10 @@
#include "Core/Shared/KeyManager.h"
#include "Core/Shared/ShortcutKeyHandler.h"
#include "Utilities/StringUtilities.h"
#include "Core/Shared/Interfaces/IMouseManager.h"
extern unique_ptr<IKeyManager> _keyManager;
extern unique_ptr<IMouseManager> _mouseManager;
extern unique_ptr<Emulator> _emu;
extern "C"
@ -81,4 +83,50 @@ extern "C"
{
_emu->ResetLagCounter();
}
}
DllExport SystemMouseState __stdcall GetSystemMouseState(void* rendererHandle)
{
if(_mouseManager) {
return _mouseManager->GetSystemMouseState(rendererHandle);
}
SystemMouseState state = {};
return state;
}
DllExport bool __stdcall CaptureMouse(int32_t x, int32_t y, int32_t width, int32_t height, void* rendererHandle)
{
if(_mouseManager) {
return _mouseManager->CaptureMouse(x, y, width, height, rendererHandle);
}
return false;
}
DllExport void __stdcall ReleaseMouse()
{
if(_mouseManager) {
_mouseManager->ReleaseMouse();
}
}
DllExport void __stdcall SetSystemMousePosition(int32_t x, int32_t y)
{
if(_mouseManager) {
_mouseManager->SetSystemMousePosition(x, y);
}
}
DllExport void __stdcall SetCursorImage(CursorImage image)
{
if(_mouseManager) {
_mouseManager->SetCursorImage(image);
}
}
DllExport double __stdcall GetPixelScale()
{
if(_mouseManager) {
return _mouseManager->GetPixelScale();
}
return 1.0;
}
}

View file

@ -3,12 +3,6 @@
#include "Core/Shared/EmuSettings.h"
#include "LinuxGameController.h"
#ifdef __APPLE__
bool LinuxGameController::IsButtonPressed(int buttonNumber) { return 0; }
int LinuxGameController::GetDeviceID() { return _deviceID; }
bool LinuxGameController::IsDisconnected() { return _disconnected; }
std::shared_ptr<LinuxGameController> LinuxGameController::GetController(Emulator* emu, int deviceID, bool logInformation) { return nullptr; }
#else
#include "libevdev/libevdev.h"
#include <unistd.h>
#include <stdio.h>
@ -236,4 +230,3 @@ static int print_event(struct input_event *ev)
return 0;
}
*/
#endif

115
Linux/LinuxMouseManager.cpp Normal file
View file

@ -0,0 +1,115 @@
#include <string>
#include <chrono>
#include <thread>
#include <stdint.h>
#include <X11/Xlib.h>
#include <X11/cursorfont.h>
#include "LinuxMouseManager.h"
#include "Core/Shared/MessageManager.h"
LinuxMouseManager::LinuxMouseManager(void* windowHandle)
{
_mainWindow = (Window) windowHandle;
_display = XOpenDisplay(nullptr);
_defaultScreen = XDefaultScreen(_display);
_rootWindow = XRootWindow(_display, _defaultScreen);
_defaultCursor = XCreateFontCursor(_display, XC_left_ptr);
_crossCursor = XCreateFontCursor(_display, XC_crosshair);
XColor color = {};
uint8_t nullCursorData = 0;
Pixmap pixmap = XCreateBitmapFromData(_display, _rootWindow, (const char*) &nullCursorData, 1, 1);
_hiddenCursor = XCreatePixmapCursor(_display, pixmap, pixmap, &color, &color, 0, 0);
}
LinuxMouseManager::~LinuxMouseManager() {}
SystemMouseState LinuxMouseManager::GetSystemMouseState(void* rendererHandle)
{
SystemMouseState state = {};
Window root = 0;
Window c = 0;
Window child = 0;
int rootX, rootY, childX, childY;
uint32_t mask;
XGrabServer(_display);
XQueryPointer(_display, _rootWindow, &root, &c, &rootX, &rootY, &childX, &childY, &mask);
if(root != _rootWindow) c = root;
while(c != 0) {
child = c;
XQueryPointer(_display, c, &root, &c, &rootX, &rootY, &childX, &childY, &mask);
}
XUngrabServer(_display);
XFlush(_display);
state.XPosition = rootX;
state.YPosition = rootY;
state.LeftButton = (mask & (1 << 8)) != 0;
state.RightButton = (mask & (1 << 10)) != 0;
state.MiddleButton = (mask & (1 << 9)) != 0;
//TODO back/forward are not supported by XQueryPointer?
//state.Button4 = (mask & (1 << 11)) != 0;
//state.Button5 = (mask & (1 << 12)) != 0;
return state;
}
bool LinuxMouseManager::CaptureMouse(int32_t x, int32_t y, int32_t width, int32_t height, void* rendererHandle)
{
if(rendererHandle == nullptr) {
//Due to the mouse position constantly being set to the center of the window and the cursor being hidden
//actually capturing the cursor when using the software renderer is not strictly needed
return true;
}
for(int i = 0; i < 10; i++) {
int result = XGrabPointer(_display, (Window) rendererHandle, true, NoEventMask, GrabModeAsync, GrabModeAsync, (Window) rendererHandle, _hiddenCursor, 0);
XFlush(_display);
if(result == 1 && i < 9) {
//XGrabPointer can fail with AlreadyGrabbed - this can be normal, retry a few times
std::this_thread::sleep_for(std::chrono::duration<int, std::milli>(100));
} else {
if(result != 0) {
std::string message = "XGrabPointer failed: ";
MessageManager::Log(message + std::to_string(result));
return false;
}
break;
}
}
return true;
}
void LinuxMouseManager::ReleaseMouse()
{
XUngrabPointer(_display, 0);
XFlush(_display);
}
void LinuxMouseManager::SetSystemMousePosition(int32_t x, int32_t y)
{
XWarpPointer(_display, 0, _rootWindow, 0, 0, 0, 0, x, y);
XFlush(_display);
}
void LinuxMouseManager::SetCursorImage(CursorImage cursor)
{
switch(cursor) {
case CursorImage::Hidden: XDefineCursor(_display, _mainWindow, _hiddenCursor); break;
case CursorImage::Arrow: XDefineCursor(_display, _mainWindow, _defaultCursor); break;
case CursorImage::Cross: XDefineCursor(_display, _mainWindow, _crossCursor); break;
}
XFlush(_display);
}
double LinuxMouseManager::GetPixelScale()
{
return 1.0;
}

35
Linux/LinuxMouseManager.h Normal file
View file

@ -0,0 +1,35 @@
#pragma once
#include <X11/Xlib.h>
#include <X11/cursorfont.h>
//X11 defines global macros 'Button4' and 'Button5', colliding with the fields of SystemMouseState
//Undefine them here, as they are not needed
#undef Button4
#undef Button5
#include "Shared/Interfaces/IMouseManager.h"
class LinuxMouseManager : public IMouseManager
{
private:
Window _mainWindow;
Display* _display;
int _defaultScreen;
Window _rootWindow;
Cursor _defaultCursor;
Cursor _crossCursor;
Cursor _hiddenCursor;
public:
LinuxMouseManager(void* windowHandle);
virtual ~LinuxMouseManager();
SystemMouseState GetSystemMouseState(void* rendererHandle);
bool CaptureMouse(int32_t x, int32_t y, int32_t width, int32_t height, void* rendererHandle);
void ReleaseMouse();
void SetSystemMousePosition(int32_t x, int32_t y);
void SetCursorImage(CursorImage cursor);
double GetPixelScale();
};

View file

@ -1,8 +1,6 @@
#pragma once
#include <unordered_map>
#include <vector>
#include <thread>
#include "Utilities/AutoResetEvent.h"
#include "Shared/Interfaces/IKeyManager.h"
#include "Shared/KeyDefinitions.h"

View file

@ -27,8 +27,9 @@ MacOSKeyManager::MacOSKeyManager(Emulator* emu)
_disableAllKeys = false;
_eventMonitor = [NSEvent addLocalMonitorForEventsMatchingMask:(NSEventMaskKeyDown | NSEventMaskKeyUp | NSEventMaskFlagsChanged)
handler:^ NSEvent* (NSEvent* event) {
NSEventMask eventMask = NSEventMaskKeyDown | NSEventMaskKeyUp | NSEventMaskFlagsChanged;
_eventMonitor = [NSEvent addLocalMonitorForEventsMatchingMask:eventMask handler:^ NSEvent* (NSEvent* event) {
if(_emu->GetSettings()->CheckFlag(EmulationFlags::InBackground)) {
//Allow UI to handle key-events when main window is not in focus
return event;

26
MacOS/MacOSMouseManager.h Normal file
View file

@ -0,0 +1,26 @@
#pragma once
#include "Shared/Interfaces/IMouseManager.h"
class MacOSMouseManager : public IMouseManager
{
private:
double _relativeX;
double _relativeY;
bool _mouseCaptured;
bool _cursorHidden;
void* _eventMonitor;
void SetRelativeMovement(double x, double y);
public:
MacOSMouseManager();
virtual ~MacOSMouseManager();
SystemMouseState GetSystemMouseState(void* rendererHandle);
bool CaptureMouse(int32_t x, int32_t y, int32_t width, int32_t height, void* rendererHandle);
void ReleaseMouse();
void SetSystemMousePosition(int32_t x, int32_t y);
void SetCursorImage(CursorImage cursor);
double GetPixelScale();
};

102
MacOS/MacOSMouseManager.mm Normal file
View file

@ -0,0 +1,102 @@
#import <Foundation/Foundation.h>
#import <Cocoa/Cocoa.h>
#include "MacOSMouseManager.h"
MacOSMouseManager::MacOSMouseManager()
{
_relativeX = 0.0;
_relativeY = 0.0;
_mouseCaptured = false;
_cursorHidden = false;
NSEventMask eventMask = NSEventMaskMouseMoved | NSEventMaskLeftMouseDragged | NSEventMaskRightMouseDragged | NSEventMaskOtherMouseDragged;
_eventMonitor = [NSEvent addLocalMonitorForEventsMatchingMask:eventMask handler:^ NSEvent* (NSEvent* event) {
//When mouse is captured on MacOS, absolute position is frozen and only deltaX/Y gives movement data
SetRelativeMovement([event deltaX], [event deltaY]);
return event;
}];
}
MacOSMouseManager::~MacOSMouseManager()
{
[NSEvent removeMonitor:(id) _eventMonitor];
}
SystemMouseState MacOSMouseManager::GetSystemMouseState(void* rendererHandle)
{
SystemMouseState state = {};
if(_mouseCaptured) {
state.XPosition = (int32_t) _relativeX;
state.YPosition = (int32_t) _relativeY;
} else {
NSPoint location = [NSEvent mouseLocation];
state.XPosition = (int32_t) location.x;
state.YPosition = (int32_t) (CGDisplayPixelsHigh(kCGDirectMainDisplay) - location.y);
}
NSUInteger buttons = [NSEvent pressedMouseButtons];
state.LeftButton = (buttons & 1) != 0;
state.RightButton = (buttons & 2) != 0;
state.MiddleButton = (buttons & 4) != 0;
state.Button4 = (buttons & 8) != 0;
state.Button5 = (buttons & 16) != 0;
return state;
}
bool MacOSMouseManager::CaptureMouse(int32_t x, int32_t y, int32_t width, int32_t height, void* rendererHandle)
{
NSPoint location = [NSEvent mouseLocation];
CGAssociateMouseAndMouseCursorPosition(NO);
_relativeX = location.x;
_relativeY = CGDisplayPixelsHigh(kCGDirectMainDisplay) - location.y;
_mouseCaptured = true;
return true;
}
void MacOSMouseManager::ReleaseMouse()
{
CGAssociateMouseAndMouseCursorPosition(YES);
_mouseCaptured = false;
}
void MacOSMouseManager::SetSystemMousePosition(int32_t x, int32_t y)
{
_relativeX = (double) x;
_relativeY = (double) y;
}
void MacOSMouseManager::SetRelativeMovement(double x, double y)
{
_relativeX += x;
_relativeY += y;
}
void MacOSMouseManager::SetCursorImage(CursorImage cursor)
{
if(cursor == CursorImage::Hidden && !_cursorHidden) {
[NSCursor hide];
_cursorHidden = true;
}
if(cursor != CursorImage::Hidden && _cursorHidden) {
[NSCursor unhide];
_cursorHidden = false;
}
switch(cursor) {
case CursorImage::Hidden: break; //Already handled above
case CursorImage::Arrow: [[NSCursor arrowCursor] set]; break;
case CursorImage::Cross: [[NSCursor crosshairCursor] set]; break;
}
}
double MacOSMouseManager::GetPixelScale()
{
//On MacOS, Avalonia seems to have a scaling-mismatch between getting/converting points and getting element sizes
//points seem to be given in DPI-aware pixels (as GetMouseState returns as well), but sizes in actual screen-pixels
//The result of this function is used in the UI to correct for this with mouse-related usage
NSScreen* screen = [NSScreen mainScreen];
if(screen == nil) {
return 1.0;
}
return [screen backingScaleFactor];
}

View file

@ -79,6 +79,9 @@ namespace Mesen.Config.Shortcuts
IncreaseVolume,
DecreaseVolume,
PreviousTrack,
NextTrack,
ToggleBgLayer1,
ToggleBgLayer2,
ToggleBgLayer3,

View file

@ -2020,7 +2020,7 @@ namespace Mesen.Debugger.ViewModels
new RegEntry("$00.7", "Vertical scroll lock", vdp.VerticalScrollLock),
new RegEntry("$01.0", "Zoom sprites (2x size)", vdp.EnableDoubleSpriteSize),
new RegEntry("$01.1", "8x16 sprites", vdp.UseLargeSprites),
new RegEntry("$01.1", "Large sprites (8x16 or 16x16)", vdp.UseLargeSprites),
new RegEntry("$01.3", "M3 - 240-line output", vdp.M3_Use240LineMode),
new RegEntry("$01.4", "M1 - 224-line output", vdp.M1_Use224LineMode),
new RegEntry("$01.5", "Vertical blank IRQ enabled", vdp.EnableVerticalBlankIrq),
@ -2028,6 +2028,8 @@ namespace Mesen.Debugger.ViewModels
new RegEntry("$01.7", "SG-1000 - 16K VRAM Mode", vdp.RenderingEnabled),
new RegEntry("$02", "Nametable address", vdp.NametableAddress),
new RegEntry("$03", "Pattern table address", vdp.BgPatternTableAddress),
new RegEntry("$04", "Color table address", vdp.ColorTableAddress),
new RegEntry("$05", "Sprite table address", vdp.SpriteTableAddress),
new RegEntry("$06", "Sprite tileset address", vdp.SpritePatternSelector),
new RegEntry("$07", "Background color index", vdp.BackgroundColorIndex),

View file

@ -24,6 +24,13 @@ namespace Mesen.Interop
[DllImport(DllPath)][return: MarshalAs(UnmanagedType.I1)] public static extern bool HasControlDevice(ControllerType type);
[DllImport(DllPath)] public static extern SystemMouseState GetSystemMouseState(IntPtr rendererHandle);
[DllImport(DllPath)][return: MarshalAs(UnmanagedType.I1)] public static extern bool CaptureMouse(Int32 x, Int32 y, Int32 width, Int32 height, IntPtr rendererHandle);
[DllImport(DllPath)] public static extern void ReleaseMouse();
[DllImport(DllPath)] public static extern void SetSystemMousePosition(Int32 x, Int32 y);
[DllImport(DllPath)] public static extern void SetCursorImage(CursorImage cursor);
[DllImport(DllPath)] public static extern double GetPixelScale();
[DllImport(DllPath, EntryPoint = "GetKeyName")] private static extern IntPtr GetKeyNameWrapper(UInt16 key, IntPtr outKeyName, Int32 maxLength);
public unsafe static string GetKeyName(UInt16 key)
{
@ -51,4 +58,23 @@ namespace Mesen.Interop
return keys;
}
}
public enum CursorImage
{
Hidden,
Arrow,
Cross
}
[StructLayout(LayoutKind.Sequential)]
public struct SystemMouseState
{
public Int32 XPosition;
public Int32 YPosition;
[MarshalAs(UnmanagedType.I1)] public bool LeftButton;
[MarshalAs(UnmanagedType.I1)] public bool RightButton;
[MarshalAs(UnmanagedType.I1)] public bool MiddleButton;
[MarshalAs(UnmanagedType.I1)] public bool Button4;
[MarshalAs(UnmanagedType.I1)] public bool Button5;
}
}

View file

@ -1721,6 +1721,10 @@ E
<Value ID="ToggleAudio">Enable/Disable Audio</Value>
<Value ID="IncreaseVolume">Increase Volume</Value>
<Value ID="DecreaseVolume">Decrease Volume</Value>
<Value ID="PreviousTrack">Audio Player - Previous Track</Value>
<Value ID="NextTrack">Audio Player - Next Track</Value>
<Value ID="RunSingleFrame">Run Single Frame</Value>
<Value ID="SetScale1x">Set Scale 1x</Value>

View file

@ -1,82 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Mesen.Utilities.GlobalMouseLib
{
public static class GlobalMouse
{
private static IGlobalMouseImpl _impl;
static GlobalMouse()
{
if(OperatingSystem.IsWindows()) {
_impl = new GlobalMouseWindowsImpl();
} else if(OperatingSystem.IsLinux()) {
_impl = new GlobalMouseX11Impl();
} else {
_impl = new GlobalMouseStubImpl();
}
}
public static bool IsMouseButtonPressed(MouseButtons button)
{
return _impl.IsMouseButtonPressed(button);
}
public static MousePosition GetMousePosition(IntPtr windowFilter)
{
return _impl.GetMousePosition(windowFilter);
}
public static void SetMousePosition(uint x, uint y)
{
_impl.SetMousePosition(x, y);
}
public static void SetCursorIcon(CursorIcon icon)
{
_impl.SetCursorIcon(icon);
}
public static void CaptureCursor(int x, int y, int width, int height, IntPtr rendererHandle)
{
_impl.CaptureCursor(x, y, width, height, rendererHandle);
}
public static void ReleaseCursor()
{
_impl.ReleaseCursor();
}
}
public enum MouseButtons
{
Left = 1,
Right = 2,
Middle = 3,
Button4 = 4,
Button5 = 5
}
public enum CursorIcon
{
Hidden,
Arrow,
Cross
}
public struct MousePosition
{
public int X;
public int Y;
public MousePosition(int x, int y)
{
X = x;
Y = y;
}
}
}

View file

@ -1,33 +0,0 @@
using System;
namespace Mesen.Utilities.GlobalMouseLib
{
public class GlobalMouseStubImpl : IGlobalMouseImpl
{
public MousePosition GetMousePosition(IntPtr windowFilter)
{
return new MousePosition(0, 0);
}
public bool IsMouseButtonPressed(MouseButtons button)
{
return false;
}
public void SetCursorIcon(CursorIcon icon)
{
}
public void SetMousePosition(uint x, uint y)
{
}
public void CaptureCursor(int x, int y, int width, int height, IntPtr rendererHandle)
{
}
public void ReleaseCursor()
{
}
}
}

View file

@ -1,126 +0,0 @@
using System;
using System.Runtime.InteropServices;
namespace Mesen.Utilities.GlobalMouseLib
{
public class GlobalMouseWindowsImpl : IGlobalMouseImpl
{
private IntPtr _arrowCursor = LoadCursor(IntPtr.Zero, new IntPtr((int)Cursor.IDC_ARROW));
private IntPtr _crossCursor = LoadCursor(IntPtr.Zero, new IntPtr((int)Cursor.IDC_CROSS));
public bool IsMouseButtonPressed(MouseButtons button)
{
switch(button) {
case MouseButtons.Left: return (GetAsyncKeyState((int)WindowsMouseButton.VK_LBUTTON) & 0x8000) != 0;
case MouseButtons.Right: return (GetAsyncKeyState((int)WindowsMouseButton.VK_RBUTTON) & 0x8000) != 0;
case MouseButtons.Middle: return (GetAsyncKeyState((int)WindowsMouseButton.VK_MBUTTON) & 0x8000) != 0;
case MouseButtons.Button4: return (GetAsyncKeyState((int)WindowsMouseButton.VK_XBUTTON1) & 0x8000) != 0;
case MouseButtons.Button5: return (GetAsyncKeyState((int)WindowsMouseButton.VK_XBUTTON2) & 0x8000) != 0;
}
return false;
}
public MousePosition GetMousePosition(IntPtr windowFilter)
{
GetCursorPos(out CursorPoint p);
if(windowFilter != IntPtr.Zero && WindowFromPoint(p) != windowFilter) {
//Mouse is over another window
return new MousePosition(-1, -1);
}
return new MousePosition(p.X, p.Y);
}
public void SetMousePosition(uint x, uint y)
{
SetCursorPos(x, y);
}
public void SetCursorIcon(CursorIcon icon)
{
switch(icon) {
case CursorIcon.Hidden: SetCursor(IntPtr.Zero); break;
case CursorIcon.Arrow: SetCursor(_arrowCursor); break;
case CursorIcon.Cross: SetCursor(_crossCursor); break;
}
}
public void CaptureCursor(int x, int y, int width, int height, IntPtr rendererHandle)
{
ClipCursor(new WinRect() { Left = x, Top = y, Right = x + width, Bottom = y + height });
}
public void ReleaseCursor()
{
ClipCursor(null);
}
[DllImport("User32.dll")]
private static extern short GetAsyncKeyState(int arrowKeys);
[DllImport("user32.dll")]
private static extern IntPtr LoadCursor(IntPtr hInstance, IntPtr lpCursorName);
[DllImport("user32.dll")]
private static extern IntPtr SetCursor(IntPtr hCursor);
[StructLayout(LayoutKind.Sequential)]
private struct CursorPoint
{
public int X;
public int Y;
}
[StructLayout(LayoutKind.Sequential)]
private class WinRect
{
public int Left;
public int Top;
public int Right;
public int Bottom;
}
[DllImport("user32.dll")]
private static extern bool GetCursorPos(out CursorPoint point);
[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool SetCursorPos(uint x, uint y);
[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool ClipCursor(WinRect? rect);
[DllImport("user32.dll")]
private static extern IntPtr WindowFromPoint(CursorPoint p);
private enum WindowsMouseButton
{
VK_LBUTTON = 1,
VK_RBUTTON = 2,
VK_MBUTTON = 4,
VK_XBUTTON1 = 5,
VK_XBUTTON2 = 6,
}
private enum Cursor
{
IDC_ARROW = 32512,
IDC_IBEAM = 32513,
IDC_WAIT = 32514,
IDC_CROSS = 32515,
IDC_UPARROW = 32516,
IDC_SIZE = 32640,
IDC_ICON = 32641,
IDC_SIZENWSE = 32642,
IDC_SIZENESW = 32643,
IDC_SIZEWE = 32644,
IDC_SIZENS = 32645,
IDC_SIZEALL = 32646,
IDC_NO = 32648,
IDC_HAND = 32649,
IDC_APPSTARTING = 32650,
IDC_HELP = 32651
}
}
}

View file

@ -1,334 +0,0 @@
using Mesen.Interop;
using System;
using System.Runtime.InteropServices;
namespace Mesen.Utilities.GlobalMouseLib;
public class GlobalMouseX11Impl : IGlobalMouseImpl
{
private X11Info _x11;
private IntPtr _mainWindow;
public GlobalMouseX11Impl()
{
_mainWindow = ApplicationHelper.GetMainWindow()?.TryGetPlatformHandle()?.Handle ?? IntPtr.Zero;
_x11 = new X11Info();
}
public MousePosition GetMousePosition(IntPtr windowFilter)
{
(int x, int y) = GetCursorPos(_x11, null, out _);
return new MousePosition(x, y);
}
public bool IsMouseButtonPressed(MouseButtons button)
{
GetCursorPos(_x11, null, out int keys);
switch(button) {
case MouseButtons.Left: return (keys & (1 << 8)) != 0;
case MouseButtons.Middle: return (keys & (1 << 9)) != 0;
case MouseButtons.Right: return (keys & (1 << 10)) != 0;
//TODO back/forward are not supported by XQueryPointer?
//case MouseButtons.Button4: return (keys & (1 << 11)) != 0;
//case MouseButtons.Button5: return (keys & (1 << 12)) != 0;
}
return false;
}
public void CaptureCursor(int x, int y, int width, int height, IntPtr rendererHandle)
{
for(int i = 0; i < 10; i++) {
int result = X11Api.XGrabPointer(_x11.Display, rendererHandle, true, X11Api.EventMask.NoEventMask, X11Api.GrabMode.GrabModeAsync, X11Api.GrabMode.GrabModeAsync, rendererHandle, _x11.HiddenCursor, IntPtr.Zero);
X11Api.XFlush(_x11.Display);
if(result == 1 && i < 9) {
//XGrabPointer can fail with AlreadyGrabbed - this can be normal, retry a few times
System.Threading.Thread.Sleep(100);
} else {
if(result != 0) {
EmuApi.WriteLogEntry("XGrabPointer failed: " + result.ToString());
}
return;
}
}
}
public void ReleaseCursor()
{
X11Api.XUngrabPointer(_x11.Display, IntPtr.Zero);
X11Api.XFlush(_x11.Display);
}
public void SetCursorIcon(CursorIcon icon)
{
X11Api.XDefineCursor(_x11.Display, _mainWindow, icon switch {
CursorIcon.Hidden => _x11.HiddenCursor,
CursorIcon.Cross => _x11.CrossCursor,
CursorIcon.Arrow or _ => _x11.DefaultCursor
});
X11Api.XFlush(_x11.Display);
}
public void SetMousePosition(uint x, uint y)
{
X11Api.XWarpPointer(_x11.Display, IntPtr.Zero, _x11.RootWindow, 0, 0, 0, 0, (int)x, (int)y);
X11Api.XFlush(_x11.Display);
}
public static void QueryPointer(IntPtr display, IntPtr w, out IntPtr root, out IntPtr child, out int root_x, out int root_y, out int child_x, out int child_y, out int mask)
{
IntPtr c;
X11Api.XGrabServer(display);
X11Api.XQueryPointer(display, w, out root, out c,
out root_x, out root_y, out child_x, out child_y,
out mask);
if(root != w)
c = root;
IntPtr child_last = IntPtr.Zero;
while(c != IntPtr.Zero) {
child_last = c;
X11Api.XQueryPointer(display, c, out root, out c,
out root_x, out root_y, out child_x, out child_y,
out mask);
}
X11Api.XUngrabServer(display);
X11Api.XFlush(display);
child = child_last;
}
public static (int x, int y) GetCursorPos(X11Info x11, IntPtr? handle, out int keys_buttons)
{
QueryPointer(x11.Display, handle ?? x11.RootWindow, out IntPtr root, out IntPtr child, out int root_x, out int root_y, out int win_x, out int win_y, out keys_buttons);
if(handle != null) {
return (win_x, win_y);
} else {
return (root_x, root_y);
}
}
}
public class X11Api
{
const string libX11 = "libX11.so.6";
/*const string libX11Randr = "libXrandr.so.2";
const string libX11Ext = "libXext.so.6";
const string libXInput = "libXi.so.6";
const string libXCursor = "libXcursor.so.1";*/
[DllImport(libX11)]
public static extern IntPtr XOpenDisplay(IntPtr display);
[DllImport(libX11)]
public static extern int XDefaultScreen(IntPtr display);
[DllImport(libX11)]
public static extern IntPtr XRootWindow(IntPtr display, int screen_number);
[DllImport(libX11)]
public static extern bool XQueryPointer(IntPtr display, IntPtr window, out IntPtr root, out IntPtr child,
out int root_x, out int root_y, out int win_x, out int win_y, out int keys_buttons);
[DllImport(libX11)]
public static extern void XGrabServer(IntPtr display);
[DllImport(libX11)]
public static extern void XUngrabServer(IntPtr display);
[DllImport(libX11)]
public static extern int XFlush(IntPtr display);
[DllImport(libX11)]
public static extern int XGrabPointer(IntPtr display, IntPtr window, bool owner_events, EventMask event_mask,
GrabMode pointer_mode, GrabMode keyboard_mode, IntPtr confine_to, IntPtr cursor, IntPtr timestamp);
[DllImport(libX11)]
public static extern int XUngrabPointer(IntPtr display, IntPtr timestamp);
[DllImport(libX11)]
public static extern IntPtr XCreateFontCursor(IntPtr display, CursorFontShape shape);
[DllImport(libX11)]
public static extern int XDefineCursor(IntPtr display, IntPtr window, IntPtr cursor);
[DllImport(libX11)]
public static extern uint XWarpPointer(IntPtr display, IntPtr src_w, IntPtr dest_w, int src_x, int src_y,
uint src_width, uint src_height, int dest_x, int dest_y);
[DllImport(libX11)]
public static extern IntPtr XCreatePixmapCursor(IntPtr display, IntPtr source, IntPtr mask,
ref XColor foreground_color, ref XColor background_color, int x_hot, int y_hot);
[DllImport(libX11)]
public static extern IntPtr XCreateBitmapFromData(IntPtr display, IntPtr drawable, byte[] data, int width, int height);
[StructLayout(LayoutKind.Sequential, Pack = 2)]
public struct XColor
{
internal IntPtr pixel;
internal ushort red;
internal ushort green;
internal ushort blue;
internal byte flags;
internal byte pad;
}
public enum GrabMode
{
GrabModeSync = 0,
GrabModeAsync = 1
}
[Flags]
public enum EventMask
{
NoEventMask = 0,
KeyPressMask = 1 << 0,
KeyReleaseMask = 1 << 1,
ButtonPressMask = 1 << 2,
ButtonReleaseMask = 1 << 3,
EnterWindowMask = 1 << 4,
LeaveWindowMask = 1 << 5,
PointerMotionMask = 1 << 6,
PointerMotionHintMask = 1 << 7,
Button1MotionMask = 1 << 8,
Button2MotionMask = 1 << 9,
Button3MotionMask = 1 << 10,
Button4MotionMask = 1 << 11,
Button5MotionMask = 1 << 12,
ButtonMotionMask = 1 << 13,
KeymapStateMask = 1 << 14,
ExposureMask = 1 << 15,
VisibilityChangeMask = 1 << 16,
StructureNotifyMask = 1 << 17,
ResizeRedirectMask = 1 << 18,
SubstructureNotifyMask = 1 << 19,
SubstructureRedirectMask = 1 << 20,
FocusChangeMask = 1 << 21,
PropertyChangeMask = 1 << 22,
ColormapChangeMask = 1 << 23,
OwnerGrabButtonMask = 1 << 24
}
public enum CursorFontShape
{
XC_X_cursor = 0,
XC_arrow = 2,
XC_based_arrow_down = 4,
XC_based_arrow_up = 6,
XC_boat = 8,
XC_bogosity = 10,
XC_bottom_left_corner = 12,
XC_bottom_right_corner = 14,
XC_bottom_side = 16,
XC_bottom_tee = 18,
XC_box_spiral = 20,
XC_center_ptr = 22,
XC_circle = 24,
XC_clock = 26,
XC_coffee_mug = 28,
XC_cross = 30,
XC_cross_reverse = 32,
XC_crosshair = 34,
XC_diamond_cross = 36,
XC_dot = 38,
XC_dotbox = 40,
XC_double_arrow = 42,
XC_draft_large = 44,
XC_draft_small = 46,
XC_draped_box = 48,
XC_exchange = 50,
XC_fleur = 52,
XC_gobbler = 54,
XC_gumby = 56,
XC_hand1 = 58,
XC_hand2 = 60,
XC_heart = 62,
XC_icon = 64,
XC_iron_cross = 66,
XC_left_ptr = 68,
XC_left_side = 70,
XC_left_tee = 72,
XC_left_button = 74,
XC_ll_angle = 76,
XC_lr_angle = 78,
XC_man = 80,
XC_middlebutton = 82,
XC_mouse = 84,
XC_pencil = 86,
XC_pirate = 88,
XC_plus = 90,
XC_question_arrow = 92,
XC_right_ptr = 94,
XC_right_side = 96,
XC_right_tee = 98,
XC_rightbutton = 100,
XC_rtl_logo = 102,
XC_sailboat = 104,
XC_sb_down_arrow = 106,
XC_sb_h_double_arrow = 108,
XC_sb_left_arrow = 110,
XC_sb_right_arrow = 112,
XC_sb_up_arrow = 114,
XC_sb_v_double_arrow = 116,
XC_sb_shuttle = 118,
XC_sizing = 120,
XC_spider = 122,
XC_spraycan = 124,
XC_star = 126,
XC_target = 128,
XC_tcross = 130,
XC_top_left_arrow = 132,
XC_top_left_corner = 134,
XC_top_right_corner = 136,
XC_top_side = 138,
XC_top_tee = 140,
XC_trek = 142,
XC_ul_angle = 144,
XC_umbrella = 146,
XC_ur_angle = 148,
XC_watch = 150,
XC_xterm = 152,
XC_num_glyphs = 154
}
}
public class X11Info
{
public IntPtr Display { get; }
public int DefaultScreen { get; }
public IntPtr RootWindow { get; }
public IntPtr DefaultCursor { get; }
public IntPtr CrossCursor { get; }
public IntPtr HiddenCursor { get; }
private static readonly byte[] NullCursorData = new byte[] { 0 };
public X11Info()
{
Display = X11Api.XOpenDisplay(IntPtr.Zero);
DefaultScreen = X11Api.XDefaultScreen(Display);
RootWindow = X11Api.XRootWindow(Display, DefaultScreen);
DefaultCursor = X11Api.XCreateFontCursor(Display, X11Api.CursorFontShape.XC_left_ptr);
CrossCursor = X11Api.XCreateFontCursor(Display, X11Api.CursorFontShape.XC_crosshair);
HiddenCursor = GetNullCursor(Display);
}
private IntPtr GetNullCursor(IntPtr display)
{
X11Api.XColor color = new X11Api.XColor();
IntPtr pixmap = X11Api.XCreateBitmapFromData(display, RootWindow, NullCursorData, 1, 1);
return X11Api.XCreatePixmapCursor(display, pixmap, pixmap, ref color, ref color, 0, 0);
}
}

View file

@ -1,14 +0,0 @@
using System;
namespace Mesen.Utilities.GlobalMouseLib
{
public interface IGlobalMouseImpl
{
MousePosition GetMousePosition(IntPtr windowFilter);
bool IsMouseButtonPressed(MouseButtons button);
void SetCursorIcon(CursorIcon icon);
void SetMousePosition(uint x, uint y);
void CaptureCursor(int x, int y, int width, int height, IntPtr rendererHandle);
void ReleaseCursor();
}
}

View file

@ -5,7 +5,6 @@ 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;
@ -27,19 +26,22 @@ namespace Mesen.Utilities
private DispatcherTimer _timer = new DispatcherTimer();
private NativeRenderer _renderer;
private Control _renderer;
private bool _usesSoftwareRenderer;
private MainMenuView _mainMenu;
private MainWindow _wnd;
private MousePosition _prevPosition;
private int _prevPositionX;
private int _prevPositionY;
private bool _mouseCaptured = false;
private DateTime _lastMouseMove = DateTime.Now;
public MouseManager(MainWindow wnd, NativeRenderer renderer, MainMenuView mainMenu)
public MouseManager(MainWindow wnd, Control renderer, MainMenuView mainMenu, bool usesSoftwareRenderer)
{
_wnd = wnd;
_renderer = renderer;
_mainMenu = mainMenu;
_usesSoftwareRenderer = usesSoftwareRenderer;
_timer.Interval = TimeSpan.FromMilliseconds(15);
_timer.Tick += tmrProcessMouse;
@ -58,9 +60,10 @@ namespace Mesen.Utilities
if(MainWindowViewModel.Instance.RecentGames.Visible) {
return;
}
SystemMouseState mouseState = InputApi.GetSystemMouseState(GetRendererHandle());
bool leftPressed = GlobalMouse.IsMouseButtonPressed(MouseButtons.Left);
if(_wnd.IsActive && leftPressed && !IsPointerInMenu() && (EmuApi.IsRunning() || !MainWindowViewModel.Instance.RecentGames.Visible)) {
if(_wnd.IsActive && mouseState.LeftButton && !IsPointerInMenu() && (EmuApi.IsRunning() || !MainWindowViewModel.Instance.RecentGames.Visible)) {
//Close menu when renderer is clicked
_mainMenu.MainMenu.Close();
if(MainWindowViewModel.Instance.AudioPlayer == null) {
@ -71,45 +74,41 @@ namespace Mesen.Utilities
}
PixelPoint rendererTopLeft = _renderer.PointToScreen(new Point());
PixelRect rendererScreenRect = new PixelRect(rendererTopLeft, PixelSize.FromSize(_renderer.Bounds.Size, LayoutHelper.GetLayoutScale(_wnd)));
PixelRect rendererScreenRect = new PixelRect(rendererTopLeft, PixelSize.FromSize(_renderer.Bounds.Size, LayoutHelper.GetLayoutScale(_wnd) / InputApi.GetPixelScale()));
MousePosition p = GlobalMouse.GetMousePosition(_renderer.Handle);
if(_prevPosition.X != p.X || _prevPosition.Y != p.Y) {
if(_prevPositionX != mouseState.XPosition || _prevPositionY != mouseState.YPosition) {
//Send mouse movement x/y values to core
if(_mouseCaptured) {
InputApi.SetMouseMovement((Int16)(p.X - _prevPosition.X), (Int16)(p.Y - _prevPosition.Y));
InputApi.SetMouseMovement((Int16)(mouseState.XPosition - _prevPositionX), (Int16)(mouseState.YPosition - _prevPositionY));
}
_prevPosition = p;
_prevPositionX = mouseState.XPosition;
_prevPositionY = mouseState.YPosition;
_lastMouseMove = DateTime.Now;
}
PixelPoint mousePos = new PixelPoint(p.X, p.Y);
PixelPoint mousePos = new PixelPoint(mouseState.XPosition, mouseState.YPosition);
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);
InputApi.SetCursorImage(CursorImage.Arrow);
}
return;
}
if(rendererScreenRect.Contains(mousePos)) {
//Send mouse state to emulation core
Point rendererPos = _renderer.PointToClient(mousePos) * LayoutHelper.GetLayoutScale(_wnd);
Point rendererPos = _renderer.PointToClient(mousePos) * LayoutHelper.GetLayoutScale(_wnd) / InputApi.GetPixelScale();
InputApi.SetMousePosition(rendererPos.X / rendererScreenRect.Width, rendererPos.Y / rendererScreenRect.Height);
bool rightPressed = GlobalMouse.IsMouseButtonPressed(MouseButtons.Right);
bool middlePressed = GlobalMouse.IsMouseButtonPressed(MouseButtons.Middle);
bool button4Pressed = GlobalMouse.IsMouseButtonPressed(MouseButtons.Button4);
bool button5Pressed = GlobalMouse.IsMouseButtonPressed(MouseButtons.Button5);
bool buttonPressed = (leftPressed || rightPressed || middlePressed || button4Pressed || button5Pressed);
bool buttonPressed = (mouseState.LeftButton || mouseState.RightButton || mouseState.MiddleButton || mouseState.Button4 || mouseState.Button5);
InputApi.SetKeyState(LeftMouseButtonKeyCode, leftPressed);
InputApi.SetKeyState(RightMouseButtonKeyCode, rightPressed);
InputApi.SetKeyState(MiddleMouseButtonKeyCode, middlePressed);
InputApi.SetKeyState(MouseButton4KeyCode, button4Pressed);
InputApi.SetKeyState(MouseButton5KeyCode, button5Pressed);
InputApi.SetKeyState(LeftMouseButtonKeyCode, mouseState.LeftButton);
InputApi.SetKeyState(RightMouseButtonKeyCode, mouseState.RightButton);
InputApi.SetKeyState(MiddleMouseButtonKeyCode, mouseState.MiddleButton);
InputApi.SetKeyState(MouseButton4KeyCode, mouseState.Button4);
InputApi.SetKeyState(MouseButton5KeyCode, mouseState.Button5);
if(!_mouseCaptured && AllowMouseCapture && buttonPressed) {
//If the mouse button is clicked and mouse isn't captured but can be, turn on mouse capture
@ -118,16 +117,18 @@ namespace Mesen.Utilities
if(_mouseCaptured) {
if(AllowMouseCapture) {
GlobalMouse.SetCursorIcon(CursorIcon.Hidden);
GlobalMouse.SetMousePosition((uint)(rendererTopLeft.X + rendererScreenRect.Width / 2), (uint)(rendererTopLeft.Y + rendererScreenRect.Height / 2));
_prevPosition = GlobalMouse.GetMousePosition(_renderer.Handle);
InputApi.SetCursorImage(CursorImage.Hidden);
InputApi.SetSystemMousePosition(rendererTopLeft.X + rendererScreenRect.Width / 2, rendererTopLeft.Y + rendererScreenRect.Height / 2);
SystemMouseState newState = InputApi.GetSystemMouseState(GetRendererHandle());
_prevPositionX = newState.XPosition;
_prevPositionY = newState.YPosition;
} else {
ReleaseMouse();
}
}
if(!_mouseCaptured) {
GlobalMouse.SetCursorIcon(MouseIcon);
InputApi.SetCursorImage(MouseIcon);
}
} else {
SetMouseOffScreen();
@ -137,8 +138,8 @@ namespace Mesen.Utilities
private void UpdateMainMenuVisibility()
{
//Get global mouse position without restrictions - need to know if mouse is over menu or not
MousePosition p = GlobalMouse.GetMousePosition(IntPtr.Zero);
PixelPoint mousePos = new PixelPoint(p.X, p.Y);
SystemMouseState mouseState = InputApi.GetSystemMouseState(IntPtr.Zero);
PixelPoint mousePos = new PixelPoint(mouseState.XPosition, mouseState.YPosition);
bool inExclusiveFullscreen = _wnd.WindowState == WindowState.FullScreen && ConfigManager.Config.Video.UseExclusiveFullscreen;
bool autoHideMenu = _wnd.WindowState == WindowState.FullScreen || ConfigManager.Config.Preferences.AutoHideMenu;
@ -196,12 +197,12 @@ namespace Mesen.Utilities
}
}
private CursorIcon MouseIcon
private CursorImage MouseIcon
{
get
{
if(!EmuApi.IsRunning() || EmuApi.IsPaused()) {
return CursorIcon.Arrow;
return CursorImage.Arrow;
}
bool hasLightGun = (
@ -214,18 +215,18 @@ namespace Mesen.Utilities
if(hasLightGun) {
if(ConfigManager.Config.Input.HidePointerForLightGuns) {
return CursorIcon.Hidden;
return CursorImage.Hidden;
} else {
return CursorIcon.Cross;
return CursorImage.Cross;
}
} else if(InputApi.HasControlDevice(ControllerType.OekaKidsTablet)) {
return CursorIcon.Cross;
return CursorImage.Cross;
}
if((DateTime.Now - _lastMouseMove).TotalSeconds > 1) {
return CursorIcon.Hidden;
return CursorImage.Hidden;
} else {
return CursorIcon.Arrow;
return CursorImage.Arrow;
}
}
}
@ -233,13 +234,13 @@ namespace Mesen.Utilities
private void CaptureMouse()
{
if(!_mouseCaptured && AllowMouseCapture) {
DisplayMessageHelper.DisplayMessage("Input", ResourceHelper.GetMessage("MouseModeEnabled"));
_mouseCaptured = true;
PixelPoint topLeft = _renderer.PointToScreen(new Point());
PixelRect rendererScreenRect = new PixelRect(topLeft, PixelSize.FromSize(_renderer.Bounds.Size, LayoutHelper.GetLayoutScale(_wnd)));
GlobalMouse.CaptureCursor(topLeft.X, topLeft.Y, rendererScreenRect.Width, rendererScreenRect.Height, _renderer.Handle);
if(InputApi.CaptureMouse(topLeft.X, topLeft.Y, rendererScreenRect.Width, rendererScreenRect.Height, GetRendererHandle())) {
DisplayMessageHelper.DisplayMessage("Input", ResourceHelper.GetMessage("MouseModeEnabled"));
_mouseCaptured = true;
}
}
}
@ -247,10 +248,15 @@ namespace Mesen.Utilities
{
if(_mouseCaptured) {
_mouseCaptured = false;
GlobalMouse.ReleaseCursor();
InputApi.ReleaseMouse();
}
}
private IntPtr GetRendererHandle()
{
return _usesSoftwareRenderer ? IntPtr.Zero : (_renderer as NativeRenderer)!.Handle;
}
public void Dispose()
{
if(_timer is DispatcherTimer timer) {

View file

@ -46,6 +46,9 @@ namespace Mesen.Utilities
case EmulatorShortcut.IncreaseVolume: IncreaseVolume(); break;
case EmulatorShortcut.DecreaseVolume: DecreaseVolume(); break;
case EmulatorShortcut.PreviousTrack: GoToPreviousTrack(); break;
case EmulatorShortcut.NextTrack: GoToNextTrack(); break;
case EmulatorShortcut.ToggleFps: ToggleFps(); break;
case EmulatorShortcut.ToggleGameTimer: ToggleGameTimer(); break;
case EmulatorShortcut.ToggleFrameCounter: ToggleFrameCounter(); break;
@ -436,14 +439,38 @@ namespace Mesen.Utilities
private void IncreaseVolume()
{
ConfigManager.Config.Audio.MasterVolume = (uint)Math.Min(100, (int)ConfigManager.Config.Audio.MasterVolume + 5);
ConfigManager.Config.Audio.ApplyConfig();
if(MainWindowModel.AudioPlayer == null) {
ConfigManager.Config.Audio.MasterVolume = (uint)Math.Min(100, (int)ConfigManager.Config.Audio.MasterVolume + 5);
ConfigManager.Config.Audio.ApplyConfig();
} else {
ConfigManager.Config.AudioPlayer.Volume = (uint)Math.Min(100, (int)ConfigManager.Config.AudioPlayer.Volume + 5);
ConfigManager.Config.AudioPlayer.ApplyConfig();
}
}
private void DecreaseVolume()
{
ConfigManager.Config.Audio.MasterVolume = (uint)Math.Max(0, (int)ConfigManager.Config.Audio.MasterVolume - 5);
ConfigManager.Config.Audio.ApplyConfig();
if(MainWindowModel.AudioPlayer == null) {
ConfigManager.Config.Audio.MasterVolume = (uint)Math.Max(0, (int)ConfigManager.Config.Audio.MasterVolume - 5);
ConfigManager.Config.Audio.ApplyConfig();
} else {
ConfigManager.Config.AudioPlayer.Volume = (uint)Math.Max(0, (int)ConfigManager.Config.AudioPlayer.Volume - 5);
ConfigManager.Config.AudioPlayer.ApplyConfig();
}
}
private void GoToPreviousTrack()
{
if(MainWindowModel.AudioPlayer != null) {
EmuApi.ProcessAudioPlayerAction(new AudioPlayerActionParams() { Action = AudioPlayerAction.PrevTrack });
}
}
private void GoToNextTrack()
{
if(MainWindowModel.AudioPlayer != null) {
EmuApi.ProcessAudioPlayerAction(new AudioPlayerActionParams() { Action = AudioPlayerAction.NextTrack });
}
}
private void ToggleFrameCounter()

View file

@ -83,6 +83,9 @@ namespace Mesen.ViewModels
EmulatorShortcut.IncreaseVolume,
EmulatorShortcut.DecreaseVolume,
EmulatorShortcut.PreviousTrack,
EmulatorShortcut.NextTrack,
EmulatorShortcut.MaxSpeed,
EmulatorShortcut.IncreaseSpeed,
EmulatorShortcut.DecreaseSpeed,

View file

@ -11,7 +11,6 @@ using Mesen.Interop;
using Avalonia.Threading;
using Avalonia;
using Mesen.Config;
using Mesen.Utilities.GlobalMouseLib;
using Mesen.Utilities;
using Mesen.Localization;
using Avalonia.Remote.Protocol.Input;
@ -116,15 +115,15 @@ namespace Mesen.Windows
private void UpdateKeyDisplay()
{
if(!_allowKeyboardOnly) {
MousePosition p = GlobalMouse.GetMousePosition(IntPtr.Zero);
PixelPoint mousePos = new PixelPoint(p.X, p.Y);
PixelRect clientBounds = new PixelRect(this.PointToScreen(new Point(0, 0)), PixelSize.FromSize(Bounds.Size, LayoutHelper.GetLayoutScale(this)));
SystemMouseState mouseState = InputApi.GetSystemMouseState(IntPtr.Zero);
PixelPoint mousePos = new PixelPoint(mouseState.XPosition, mouseState.YPosition);
PixelRect clientBounds = new PixelRect(this.PointToScreen(new Point(0, 0)), PixelSize.FromSize(Bounds.Size, LayoutHelper.GetLayoutScale(this) / InputApi.GetPixelScale()));
bool mouseInsideWindow = clientBounds.Contains(mousePos);
InputApi.SetKeyState(MouseManager.LeftMouseButtonKeyCode, mouseInsideWindow && GlobalMouse.IsMouseButtonPressed(MouseButtons.Left));
InputApi.SetKeyState(MouseManager.RightMouseButtonKeyCode, mouseInsideWindow && GlobalMouse.IsMouseButtonPressed(MouseButtons.Right));
InputApi.SetKeyState(MouseManager.MiddleMouseButtonKeyCode, mouseInsideWindow && GlobalMouse.IsMouseButtonPressed(MouseButtons.Middle));
InputApi.SetKeyState(MouseManager.MouseButton4KeyCode, mouseInsideWindow && GlobalMouse.IsMouseButtonPressed(MouseButtons.Button4));
InputApi.SetKeyState(MouseManager.MouseButton5KeyCode, mouseInsideWindow && GlobalMouse.IsMouseButtonPressed(MouseButtons.Button5));
InputApi.SetKeyState(MouseManager.LeftMouseButtonKeyCode, mouseInsideWindow && mouseState.LeftButton);
InputApi.SetKeyState(MouseManager.RightMouseButtonKeyCode, mouseInsideWindow && mouseState.RightButton);
InputApi.SetKeyState(MouseManager.MiddleMouseButtonKeyCode, mouseInsideWindow && mouseState.MiddleButton);
InputApi.SetKeyState(MouseManager.MouseButton4KeyCode, mouseInsideWindow && mouseState.Button4);
InputApi.SetKeyState(MouseManager.MouseButton5KeyCode, mouseInsideWindow && mouseState.Button5);
List<UInt16> scanCodes = InputApi.GetPressedKeys();

View file

@ -9,7 +9,6 @@ using Avalonia.Data;
using Mesen.Interop;
using Mesen.ViewModels;
using Avalonia.Layout;
using Mesen.Utilities.GlobalMouseLib;
using Mesen.Utilities;
using Mesen.Config;
using System.Runtime.InteropServices;
@ -72,15 +71,23 @@ namespace Mesen.Windows
return;
}
bool leftPressed = GlobalMouse.IsMouseButtonPressed(MouseButtons.Left);
MousePosition p = GlobalMouse.GetMousePosition(_renderer.Handle);
PixelPoint mousePos = new PixelPoint(p.X, p.Y);
PixelPoint rendererTopLeft = _renderer.PointToScreen(new Point());
PixelRect rendererScreenRect = new PixelRect(rendererTopLeft, PixelSize.FromSize(_renderer.Bounds.Size, LayoutHelper.GetLayoutScale(this)));
bool usesSoftwareRenderer = _model.IsSoftwareRendererVisible;
SystemMouseState mouseState = InputApi.GetSystemMouseState(usesSoftwareRenderer ? IntPtr.Zero : _renderer.Handle);
PixelPoint mousePos = new PixelPoint(mouseState.XPosition, mouseState.YPosition);
PixelPoint rendererTopLeft;
PixelRect rendererScreenRect;
if(usesSoftwareRenderer) {
rendererTopLeft = _softwareRenderer.PointToScreen(new Point());
rendererScreenRect = new PixelRect(rendererTopLeft, PixelSize.FromSize(_softwareRenderer.Bounds.Size, LayoutHelper.GetLayoutScale(this) / InputApi.GetPixelScale()));
} else {
rendererTopLeft = _renderer.PointToScreen(new Point());
rendererScreenRect = new PixelRect(rendererTopLeft, PixelSize.FromSize(_renderer.Bounds.Size, LayoutHelper.GetLayoutScale(this) / InputApi.GetPixelScale()));
}
if(rendererScreenRect.Contains(mousePos)) {
GlobalMouse.SetCursorIcon(CursorIcon.Arrow);
if(leftPressed && !_prevLeftPressed) {
if(mouseState.LeftButton && !_prevLeftPressed) {
if(_mainMenu.IsOpen) {
_mainMenu.Close();
} else {
@ -89,7 +96,7 @@ namespace Mesen.Windows
}
}
_prevLeftPressed = leftPressed;
_prevLeftPressed = mouseState.LeftButton;
}
private void InitializeComponent()

View file

@ -48,6 +48,7 @@ namespace Mesen.Windows
private NativeRenderer _renderer;
private SoftwareRendererView _softwareRenderer;
private Size _rendererSize;
private bool _usesSoftwareRenderer;
private Size _originalSize;
private PixelPoint _originalPos;
@ -67,6 +68,7 @@ namespace Mesen.Windows
{
_testModeEnabled = System.Diagnostics.Debugger.IsAttached;
_isLinux = OperatingSystem.IsLinux();
_usesSoftwareRenderer = ConfigManager.Config.Video.UseSoftwareRenderer || OperatingSystem.IsMacOS();
_model = new MainWindowViewModel();
DataContext = _model;
@ -94,7 +96,7 @@ namespace Mesen.Windows
_softwareRenderer = this.GetControl<SoftwareRendererView>("SoftwareRenderer");
_audioPlayer = this.GetControl<ContentControl>("AudioPlayer");
_mainMenu = this.GetControl<MainMenuView>("MainMenu");
_mouseManager = new MouseManager(this, _renderer, _mainMenu);
_mouseManager = new MouseManager(this, _usesSoftwareRenderer ? _softwareRenderer : _renderer, _mainMenu, _usesSoftwareRenderer);
ConfigManager.Config.MainWindow.LoadWindowSettings(this);
#if DEBUG
@ -214,7 +216,7 @@ namespace Mesen.Windows
ConfigManager.HomeFolder,
TryGetPlatformHandle()?.Handle ?? IntPtr.Zero,
_renderer.Handle,
ConfigManager.Config.Video.UseSoftwareRenderer || OperatingSystem.IsMacOS(),
_usesSoftwareRenderer,
cmdLine.NoAudio,
cmdLine.NoVideo,
cmdLine.NoInput

View file

@ -182,6 +182,7 @@
<ClInclude Include="WindowsKeyManager.h" />
<ClInclude Include="Renderer.h" />
<ClInclude Include="SoundManager.h" />
<ClInclude Include="WindowsMouseManager.h" />
<ClInclude Include="Common.h" />
</ItemGroup>
<ItemGroup>
@ -196,6 +197,7 @@
</ClCompile>
<ClCompile Include="Renderer.cpp" />
<ClCompile Include="SoundManager.cpp" />
<ClCompile Include="WindowsMouseManager.cpp" />
</ItemGroup>
<ItemGroup>
<None Include="DirectXTK\SpriteEffect_SpritePixelShader.inc" />

View file

@ -23,6 +23,9 @@
<ClInclude Include="WindowsKeyManager.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="WindowsMouseManager.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="XInputManager.h">
<Filter>Header Files</Filter>
</ClInclude>
@ -70,6 +73,9 @@
<ClCompile Include="WindowsKeyManager.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="WindowsMouseManager.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="XInputManager.cpp">
<Filter>Source Files</Filter>
</ClCompile>

View file

@ -0,0 +1,71 @@
#include "Common.h"
#include "WindowsMouseManager.h"
WindowsMouseManager::WindowsMouseManager()
{
_arrowCursor = LoadCursor(nullptr, IDC_ARROW);
_crossCursor = LoadCursor(nullptr, IDC_CROSS);
}
WindowsMouseManager::~WindowsMouseManager() {}
SystemMouseState WindowsMouseManager::GetSystemMouseState(void* rendererHandle)
{
SystemMouseState state = {};
POINT point;
GetCursorPos(&point);
if(rendererHandle != nullptr && WindowFromPoint(point) != (HWND) rendererHandle) {
//Mouse is over another window
state.XPosition = -1;
state.YPosition = -1;
} else {
state.XPosition = point.x;
state.YPosition = point.y;
}
state.LeftButton = (GetAsyncKeyState(VK_LBUTTON) & 0x8000) != 0;
state.RightButton = (GetAsyncKeyState(VK_RBUTTON) & 0x8000) != 0;
state.MiddleButton = (GetAsyncKeyState(VK_MBUTTON) & 0x8000) != 0;
state.Button4 = (GetAsyncKeyState(VK_XBUTTON1) & 0x8000) != 0;
state.Button5 = (GetAsyncKeyState(VK_XBUTTON2) & 0x8000) != 0;
return state;
}
bool WindowsMouseManager::CaptureMouse(int32_t x, int32_t y, int32_t width, int32_t height, void* rendererHandle)
{
if(rendererHandle == nullptr) {
//TODO Attempting to capture the cursor when using the sofware renderer behaves really erratically
//cursor is not actually locked but clicks outside the window do nothing, and movement is really odd (not usable)
return false;
}
RECT rect;
rect.left = x;
rect.top = y;
rect.right = x + width;
rect.bottom = y + height;
ClipCursor(&rect);
return true;
}
void WindowsMouseManager::ReleaseMouse()
{
ClipCursor(nullptr);
}
void WindowsMouseManager::SetSystemMousePosition(int32_t x, int32_t y)
{
SetCursorPos(x, y);
}
void WindowsMouseManager::SetCursorImage(CursorImage cursor)
{
switch(cursor) {
case CursorImage::Hidden: SetCursor(nullptr); break;
case CursorImage::Arrow: SetCursor((HCURSOR) _arrowCursor); break;
case CursorImage::Cross: SetCursor((HCURSOR) _crossCursor); break;
}
}
double WindowsMouseManager::GetPixelScale()
{
return 1.0;
}

View file

@ -0,0 +1,20 @@
#pragma once
#include "Shared/Interfaces/IMouseManager.h"
class WindowsMouseManager : public IMouseManager
{
private:
void* _arrowCursor;
void* _crossCursor;
public:
WindowsMouseManager();
virtual ~WindowsMouseManager();
SystemMouseState GetSystemMouseState(void* rendererHandle);
bool CaptureMouse(int32_t x, int32_t y, int32_t width, int32_t height, void* rendererHandle);
void ReleaseMouse();
void SetSystemMousePosition(int32_t x, int32_t y);
void SetCursorImage(CursorImage cursor);
double GetPixelScale();
};

View file

@ -94,11 +94,15 @@ ifneq ($(STATICLINK),false)
LINKOPTIONS += -static-libgcc -static-libstdc++
endif
ifeq ($(MESENOS),linux)
LINKOPTIONS += -lX11
endif
ifeq ($(MESENOS),osx)
LINKOPTIONS += -framework Foundation -framework Cocoa
endif
CXXFLAGS = -fPIC -Wall --std=c++17 $(MESENFLAGS) $(SDL2INC) -I $(realpath ./) -I $(realpath ./Core) -I $(realpath ./Utilities) -I $(realpath ./Linux) -I $(realpath ./MacOS)
CXXFLAGS = -fPIC -Wall --std=c++17 $(MESENFLAGS) $(SDL2INC) -I $(realpath ./) -I $(realpath ./Core) -I $(realpath ./Utilities) -I $(realpath ./Sdl) -I $(realpath ./Linux) -I $(realpath ./MacOS)
OBJCXXFLAGS = $(CXXFLAGS) -framework Foundation -framework Cocoa
CFLAGS = -fPIC -Wall $(MESENFLAGS)
@ -121,8 +125,8 @@ COREOBJ := $(CORESRC:.cpp=.o)
UTILSRC := $(shell find Utilities -name '*.cpp' -o -name '*.c')
UTILOBJ := $(addsuffix .o,$(basename $(UTILSRC)))
LINUXSRC := $(shell find Linux -name '*.cpp')
LINUXOBJ := $(LINUXSRC:.cpp=.o)
SDLSRC := $(shell find Sdl -name '*.cpp')
SDLOBJ := $(SDLSRC:.cpp=.o)
SEVENZIPSRC := $(shell find SevenZip -name '*.c')
SEVENZIPOBJ := $(SEVENZIPSRC:.c=.o)
@ -130,6 +134,13 @@ SEVENZIPOBJ := $(SEVENZIPSRC:.c=.o)
LUASRC := $(shell find Lua -name '*.c')
LUAOBJ := $(LUASRC:.c=.o)
ifeq ($(MESENOS),linux)
LINUXSRC := $(shell find Linux -name '*.cpp')
else
LINUXSRC :=
endif
LINUXOBJ := $(LINUXSRC:.cpp=.o)
ifeq ($(MESENOS),osx)
MACOSSRC := $(shell find MacOS -name '*.mm')
else
@ -183,10 +194,10 @@ pgohelper: InteropDLL/$(OBJFOLDER)/$(SHAREDLIB)
%.o: %.mm
$(CXX) $(OBJCXXFLAGS) -c $< -o $@
InteropDLL/$(OBJFOLDER)/$(SHAREDLIB): $(SEVENZIPOBJ) $(LUAOBJ) $(UTILOBJ) $(COREOBJ) $(LIBEVDEVOBJ) $(LINUXOBJ) $(DLLOBJ) $(MACOSOBJ)
InteropDLL/$(OBJFOLDER)/$(SHAREDLIB): $(SEVENZIPOBJ) $(LUAOBJ) $(UTILOBJ) $(COREOBJ) $(SDLOBJ) $(LIBEVDEVOBJ) $(LINUXOBJ) $(DLLOBJ) $(MACOSOBJ)
mkdir -p bin
mkdir -p InteropDLL/$(OBJFOLDER)
$(CXX) $(CXXFLAGS) $(LINKOPTIONS) $(LINKCHECKUNRESOLVED) -shared -o $(SHAREDLIB) $(DLLOBJ) $(SEVENZIPOBJ) $(LUAOBJ) $(LINUXOBJ) $(MACOSOBJ) $(LIBEVDEVOBJ) $(UTILOBJ) $(COREOBJ) $(SDL2INC) -pthread $(FSLIB) $(SDL2LIB) $(LIBEVDEVLIB)
$(CXX) $(CXXFLAGS) $(LINKOPTIONS) $(LINKCHECKUNRESOLVED) -shared -o $(SHAREDLIB) $(DLLOBJ) $(SEVENZIPOBJ) $(LUAOBJ) $(LINUXOBJ) $(MACOSOBJ) $(LIBEVDEVOBJ) $(UTILOBJ) $(SDLOBJ) $(COREOBJ) $(SDL2INC) -pthread $(FSLIB) $(SDL2LIB) $(LIBEVDEVLIB)
cp $(SHAREDLIB) bin/pgohelperlib.so
mv $(SHAREDLIB) InteropDLL/$(OBJFOLDER)
@ -200,6 +211,7 @@ clean:
rm -r -f $(COREOBJ)
rm -r -f $(UTILOBJ)
rm -r -f $(LINUXOBJ) $(LIBEVDEVOBJ)
rm -r -f $(SDLOBJ)
rm -r -f $(SEVENZIPOBJ)
rm -r -f $(LUAOBJ)
rm -r -f $(MACOSOBJ)