Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 9 additions & 3 deletions osu.Android/Input/AndroidKeyboardHandler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -32,9 +32,15 @@ public bool HandleKeyEvent(KeyEvent e)
e.KeyCode == Keycode.AppSwitch)
return false;

// In DeX, source might include other flags, use HasFlag
if (!e.Source.HasFlag(InputSourceType.Keyboard))
return false;
// In DeX, source might include other flags (like Mouse or Stylus).
// We should allow anything that is clearly a keyboard or has a valid keycode.
if (!e.Source.HasFlag(InputSourceType.Keyboard) && e.Source != InputSourceType.Unknown)
{
// If it's not a keyboard source, only allow if it's from a device that HAS a keyboard
var device = e.Device;
if (device == null || device.KeyboardType == global::Android.Views.InputKeyboardType.None)
return false;
}

var key = mapKey(e.KeyCode);
if (key == Key.Unknown) return false;
Expand Down
2 changes: 2 additions & 0 deletions osu.Android/Input/AndroidStylusHandler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,8 @@ private void handlePointer(MotionEvent e, int historyIndex)
float x = historyIndex < 0 ? e.GetX(pointer_index) : e.GetHistoricalX(pointer_index, historyIndex);
float y = historyIndex < 0 ? e.GetY(pointer_index) : e.GetHistoricalY(pointer_index, historyIndex);
float pressure = historyIndex < 0 ? e.GetPressure(pointer_index) : e.GetHistoricalPressure(pointer_index, historyIndex);
float tiltX = e.GetAxisValue(Axis.Tilt, pointer_index);
float tiltY = e.GetAxisValue(Axis.Orientation, pointer_index);

// DeX windowed mode offset correction
if (View != null)
Expand Down
25 changes: 24 additions & 1 deletion osu.Android/OsuGameActivity.cs
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,20 @@ protected override void OnCreate(Bundle? savedInstanceState)

public override bool DispatchKeyEvent(KeyEvent? e)
{
if (e != null && KeyboardHandler != null && KeyboardHandler.HandleKeyEvent(e))
if (e == null) return false;

// Intercept mouse back button which often triggers Keycode.Back
if (e.KeyCode == Keycode.Back && (e.Source.HasFlag(InputSourceType.Mouse) || e.Source.HasFlag(InputSourceType.Stylus)))
{
if (e.Action == KeyEventActions.Down)
KeyboardHandler?.HandleKeyEvent(new KeyEvent(KeyEventActions.Down, Keycode.Escape));
else if (e.Action == KeyEventActions.Up)
KeyboardHandler?.HandleKeyEvent(new KeyEvent(KeyEventActions.Up, Keycode.Escape));

return true;
}

if (KeyboardHandler != null && KeyboardHandler.HandleKeyEvent(e))
return true;

return base.DispatchKeyEvent(e);
Expand All @@ -157,6 +170,11 @@ public override bool DispatchTouchEvent(MotionEvent? e)
handled = MouseHandler?.HandleMotionEvent(e) ?? false;
}

// Stylus events should NEVER be passed to base.DispatchTouchEvent, as it triggers
// Android's touch-mode which hides the cursor and shows touch effects.
if (isStylusEvent(e))
return handled;

// In DeX mode, we MUST call base even if "handled" to ensure window focus and system gestures work.
// However, if we fully consumed it (e.g. gameplay), we return true to prevent UI double-clicks.
return base.DispatchTouchEvent(e) || handled;
Expand All @@ -183,6 +201,11 @@ public override bool DispatchGenericMotionEvent(MotionEvent? e)
handled = MouseHandler?.HandleMotionEvent(e) ?? false;
}

// Stylus hover events should not be passed to base to avoid system-level hover effects
// and touch-mode triggers.
if (isStylusEvent(e))
return handled;

return base.DispatchGenericMotionEvent(e) || handled;
}

Expand Down
36 changes: 33 additions & 3 deletions osu.Android/OsuGameAndroid.cs
Original file line number Diff line number Diff line change
Expand Up @@ -368,9 +368,39 @@ public void SelectHighestRefreshRate()
if (window == null || windowManager == null)
return;

var display = OperatingSystem.IsAndroidVersionAtLeast(30)
? gameActivity.Display
: windowManager.DefaultDisplay;
global::Android.Views.Display? display = null;

if (OperatingSystem.IsAndroidVersionAtLeast(30))
{
// Prefer the display associated with the activity (which would be the external monitor in DeX)
display = gameActivity.Display;
}

if (display == null)
{
// Fallback to DisplayManager to find an external display
if (gameActivity.GetSystemService(global::Android.Content.Context.DisplayService) is global::Android.Hardware.Display.DisplayManager dm)
{
var displays = dm.GetDisplays();

if (gameActivity.IsDeX)
{
// Find the largest external display (most likely the monitor)
var displayList = displays?.ToList();
if (displayList != null)
{
display = displayList.Where(d => d.DisplayId != 0)
.OrderByDescending(d => d.GetSupportedModes()?.FirstOrDefault()?.RefreshRate ?? 0)
.ThenByDescending(d => d.GetSupportedModes()?.FirstOrDefault()?.PhysicalWidth ?? 0)
.FirstOrDefault() ?? displayList.FirstOrDefault(d => d.DisplayId == 0);
}
}
else
{
display = displays?.FirstOrDefault(d => d.DisplayId == 0);
}
}
}

if (display == null)
return;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -266,6 +266,7 @@ protected override void LoadComplete()
var fullscreenResolutions = display.NewValue.DisplayModes
.Where(m => m.Size.Width >= 800 && m.Size.Height >= 600)
.OrderByDescending(m => Math.Max(m.Size.Height, m.Size.Width))
.ThenByDescending(m => m.RefreshRate)
.Select(m => m.Size)
.Distinct()
.ToList();
Expand Down Expand Up @@ -361,10 +362,10 @@ private void onDisplaysChanged(IEnumerable<Display> displays)

private void updateDisplaySettingsVisibility()
{
resolutionFullscreenCanBeShown.Value = windowModeDropdown.Current.Value == WindowMode.Fullscreen && resolutionsFullscreen.Count > 1;
resolutionFullscreenCanBeShown.Value = (windowModeDropdown.Current.Value == WindowMode.Fullscreen && resolutionsFullscreen.Count > 1) || RuntimeInfo.OS == RuntimeInfo.Platform.Android;
resolutionWindowedCanBeShown.Value = windowModeDropdown.Current.Value == WindowMode.Windowed && resolutionsWindowed.Count > 1;

displayDropdownCanBeShown.Value = displayDropdown.Items.Count() > 1;
displayDropdownCanBeShown.Value = displayDropdown.Items.Count() > 1 || RuntimeInfo.OS == RuntimeInfo.Platform.Android;
minimiseOnFocusLossCanBeShown.Value = RuntimeInfo.IsDesktop && windowModeDropdown.Current.Value == WindowMode.Fullscreen;
safeAreaConsiderationsCanBeShown.Value = host.Window?.SafeAreaPadding.Value.Total != Vector2.Zero;
}
Expand Down
2 changes: 1 addition & 1 deletion osu.Game/Tests/Visual/OnlinePlay/OnlinePlayTestScene.cs
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ public abstract partial class OnlinePlayTestScene : ScreenTestScene, IOnlinePlay
private readonly Container content;
private readonly Container drawableDependenciesContainer;
private DelegatedDependencyContainer dependencies = null!;
private int currentRoomId;
private int currentRoomId = 1;

protected OnlinePlayTestScene()
{
Expand Down
Loading