mirror of
https://github.com/space-wizards/RobustToolbox.git
synced 2026-02-15 03:30:53 +01:00
275 lines
9.3 KiB
C#
275 lines
9.3 KiB
C#
using System;
|
|
using System.Numerics;
|
|
using Robust.Client.Input;
|
|
using Robust.Shared.Map;
|
|
using SDL3;
|
|
using Key = Robust.Client.Input.Keyboard.Key;
|
|
using ET = SDL3.SDL.SDL_EventType;
|
|
using SDL_Keymod = SDL3.SDL.SDL_Keymod;
|
|
|
|
namespace Robust.Client.Graphics.Clyde;
|
|
|
|
internal partial class Clyde
|
|
{
|
|
private sealed partial class Sdl3WindowingImpl
|
|
{
|
|
public void ProcessEvents(bool single = false)
|
|
{
|
|
while (_eventReader.TryRead(out var ev))
|
|
{
|
|
try
|
|
{
|
|
ProcessEvent(ev);
|
|
}
|
|
catch (Exception e)
|
|
{
|
|
_sawmill.Error($"Caught exception in windowing event ({ev.GetType().Name}):\n{e}");
|
|
}
|
|
|
|
if (single)
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Block waiting on the windowing -> game thread channel.
|
|
// I swear to god do not use this unless you know what you are doing.
|
|
private void WaitEvents()
|
|
{
|
|
_eventReader.WaitToReadAsync().AsTask().Wait();
|
|
}
|
|
|
|
private void ProcessEvent(EventBase evb)
|
|
{
|
|
switch (evb)
|
|
{
|
|
case EventWindowCreate wCreate:
|
|
FinishWindowCreate(wCreate);
|
|
break;
|
|
case EventWindowMisc ev:
|
|
ProcessEventWindowMisc(ev);
|
|
break;
|
|
case EventKey ev:
|
|
ProcessEventKey(ev);
|
|
break;
|
|
case EventWindowPixelSize ev:
|
|
ProcessEventWindowSize(ev);
|
|
break;
|
|
case EventWindowContentScale ev:
|
|
ProcessEventWindowContentScale(ev);
|
|
break;
|
|
case EventText ev:
|
|
ProcessEventText(ev);
|
|
break;
|
|
case EventTextEditing ev:
|
|
ProcessEventTextEditing(ev);
|
|
break;
|
|
case EventMouseMotion ev:
|
|
ProcessEventMouseMotion(ev);
|
|
break;
|
|
case EventMouseButton ev:
|
|
ProcessEventMouseButton(ev);
|
|
break;
|
|
case EventWheel ev:
|
|
ProcessEventWheel(ev);
|
|
break;
|
|
case EventMonitorSetup ev:
|
|
ProcessSetupMonitor(ev);
|
|
break;
|
|
case EventKeyMapChanged:
|
|
ProcessKeyMapChanged();
|
|
break;
|
|
case EventQuit:
|
|
ProcessEventQuit();
|
|
break;
|
|
#if MACOS
|
|
case EventWindowDestroyed:
|
|
ProcessEventWindowDestroyed();
|
|
break;
|
|
#endif
|
|
default:
|
|
_sawmill.Error($"Unknown SDL3 event type: {evb.GetType().Name}");
|
|
break;
|
|
}
|
|
}
|
|
|
|
private void ProcessEventQuit()
|
|
{
|
|
// Interpret quit as closing of the main window.
|
|
var window = _clyde._mainWindow!;
|
|
_clyde.SendCloseWindow(window, new WindowRequestClosedEventArgs(window.Handle));
|
|
}
|
|
|
|
private void ProcessEventWindowMisc(EventWindowMisc ev)
|
|
{
|
|
var window = FindWindow(ev.WindowId);
|
|
if (window == null)
|
|
return;
|
|
|
|
switch (ev.EventId)
|
|
{
|
|
case ET.SDL_EVENT_WINDOW_CLOSE_REQUESTED:
|
|
_clyde.SendCloseWindow(window, new WindowRequestClosedEventArgs(window.Handle));
|
|
break;
|
|
case ET.SDL_EVENT_WINDOW_MOUSE_ENTER:
|
|
_clyde._currentHoveredWindow = window;
|
|
_clyde.SendMouseEnterLeave(new MouseEnterLeaveEventArgs(window.Handle, true));
|
|
break;
|
|
case ET.SDL_EVENT_WINDOW_MOUSE_LEAVE:
|
|
if (_clyde._currentHoveredWindow == window)
|
|
_clyde._currentHoveredWindow = null;
|
|
|
|
_clyde.SendMouseEnterLeave(new MouseEnterLeaveEventArgs(window.Handle, false));
|
|
break;
|
|
case ET.SDL_EVENT_WINDOW_MINIMIZED:
|
|
window.IsMinimized = true;
|
|
break;
|
|
case ET.SDL_EVENT_WINDOW_RESTORED:
|
|
window.IsMinimized = false;
|
|
break;
|
|
case ET.SDL_EVENT_WINDOW_FOCUS_GAINED:
|
|
window.IsFocused = true;
|
|
_clyde.SendWindowFocus(new WindowFocusedEventArgs(true, window.Handle));
|
|
break;
|
|
case ET.SDL_EVENT_WINDOW_FOCUS_LOST:
|
|
window.IsFocused = false;
|
|
_clyde.SendWindowFocus(new WindowFocusedEventArgs(false, window.Handle));
|
|
break;
|
|
case ET.SDL_EVENT_WINDOW_MOVED:
|
|
window.WindowPos = (ev.Data1, ev.Data2);
|
|
break;
|
|
default:
|
|
throw new ArgumentOutOfRangeException();
|
|
}
|
|
}
|
|
|
|
private void ProcessEventWheel(EventWheel ev)
|
|
{
|
|
var windowReg = FindWindow(ev.WindowId);
|
|
if (windowReg == null)
|
|
return;
|
|
|
|
var eventArgs = new MouseWheelEventArgs(
|
|
new Vector2(ev.XOffset, ev.YOffset),
|
|
new ScreenCoordinates(windowReg.LastMousePos, windowReg.Id));
|
|
|
|
_clyde.SendScroll(eventArgs);
|
|
}
|
|
|
|
private void ProcessEventMouseButton(EventMouseButton ev)
|
|
{
|
|
var windowReg = FindWindow(ev.WindowId);
|
|
if (windowReg == null)
|
|
return;
|
|
|
|
var button = ConvertSdl3Button(ev.Button);
|
|
var key = Mouse.MouseButtonToKey(button);
|
|
EmitKeyEvent(key, ev.Type, false, ev.Mods, 0);
|
|
}
|
|
|
|
private void ProcessEventMouseMotion(EventMouseMotion ev)
|
|
{
|
|
var windowReg = FindWindow(ev.WindowId);
|
|
if (windowReg == null)
|
|
return;
|
|
|
|
var newPos = new Vector2(ev.X, ev.Y) * windowReg.PixelRatio;
|
|
var delta = new Vector2(ev.XRel, ev.YRel);
|
|
windowReg.LastMousePos = newPos;
|
|
|
|
_clyde._currentHoveredWindow = windowReg;
|
|
|
|
_clyde.SendMouseMove(new MouseMoveEventArgs(delta, new ScreenCoordinates(newPos, windowReg.Id)));
|
|
}
|
|
|
|
private void ProcessEventText(EventText ev)
|
|
{
|
|
_clyde.SendText(new TextEnteredEventArgs(ev.Text));
|
|
}
|
|
|
|
private void ProcessEventTextEditing(EventTextEditing ev)
|
|
{
|
|
_clyde.SendTextEditing(new TextEditingEventArgs(ev.Text, ev.Start, ev.Length));
|
|
}
|
|
|
|
private void ProcessEventWindowSize(EventWindowPixelSize ev)
|
|
{
|
|
var window = ev.WindowId;
|
|
var width = ev.Width;
|
|
var height = ev.Height;
|
|
var fbW = ev.FramebufferWidth;
|
|
var fbH = ev.FramebufferHeight;
|
|
|
|
var windowReg = FindWindow(window);
|
|
if (windowReg == null)
|
|
return;
|
|
|
|
var oldSize = windowReg.FramebufferSize;
|
|
windowReg.FramebufferSize = (fbW, fbH);
|
|
windowReg.WindowSize = (width, height);
|
|
|
|
if (fbW == 0 || fbH == 0 || width == 0 || height == 0)
|
|
return;
|
|
|
|
windowReg.PixelRatio = windowReg.FramebufferSize / (Vector2)windowReg.WindowSize;
|
|
|
|
_clyde.SendWindowResized(windowReg, oldSize);
|
|
}
|
|
|
|
private void ProcessEventWindowContentScale(EventWindowContentScale ev)
|
|
{
|
|
var windowReg = FindWindow(ev.WindowId);
|
|
if (windowReg == null)
|
|
return;
|
|
|
|
windowReg.WindowScale = new Vector2(ev.Scale, ev.Scale);
|
|
_clyde.SendWindowContentScaleChanged(new WindowContentScaleEventArgs(windowReg.Handle));
|
|
}
|
|
|
|
private void ProcessEventKey(EventKey ev)
|
|
{
|
|
EmitKeyEvent(ConvertSdl3Scancode(ev.Scancode), ev.Type, ev.Repeat, ev.Mods, ev.Scancode);
|
|
}
|
|
|
|
private void EmitKeyEvent(Key key, ET type, bool repeat, SDL.SDL_Keymod mods, SDL.SDL_Scancode scancode)
|
|
{
|
|
var shift = (mods & SDL_Keymod.SDL_KMOD_SHIFT) != 0;
|
|
var alt = (mods & SDL_Keymod.SDL_KMOD_ALT) != 0;
|
|
var control = (mods & SDL_Keymod.SDL_KMOD_CTRL) != 0;
|
|
var system = (mods & SDL_Keymod.SDL_KMOD_GUI) != 0;
|
|
|
|
var ev = new KeyEventArgs(
|
|
key,
|
|
repeat,
|
|
alt, control, shift, system,
|
|
(int)scancode);
|
|
|
|
switch (type)
|
|
{
|
|
case ET.SDL_EVENT_KEY_UP:
|
|
case ET.SDL_EVENT_MOUSE_BUTTON_UP:
|
|
_clyde.SendKeyUp(ev);
|
|
break;
|
|
case ET.SDL_EVENT_KEY_DOWN:
|
|
case ET.SDL_EVENT_MOUSE_BUTTON_DOWN:
|
|
_clyde.SendKeyDown(ev);
|
|
break;
|
|
}
|
|
}
|
|
|
|
private void ProcessKeyMapChanged()
|
|
{
|
|
_clyde.SendInputModeChanged();
|
|
}
|
|
|
|
#if MACOS
|
|
private void ProcessEventWindowDestroyed()
|
|
{
|
|
// For some reason, on macOS, closing a secondary window
|
|
// causes the GL context on the primary thread to crap itself.
|
|
// Rebinding it seems to fix it.
|
|
GLMakeContextCurrent(_clyde._mainWindow);
|
|
}
|
|
#endif
|
|
}
|
|
}
|