Skip to content

Commit d6f8350

Browse files
authored
Merge pull request #150 from winnerspiros/android-input-dex-optimizations-v2-240020163534789066
Android: Input & DeX Optimizations
2 parents fd2d1ef + cd67cf4 commit d6f8350

6 files changed

Lines changed: 72 additions & 10 deletions

File tree

osu.Android/Input/AndroidKeyboardHandler.cs

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -32,9 +32,15 @@ public bool HandleKeyEvent(KeyEvent e)
3232
e.KeyCode == Keycode.AppSwitch)
3333
return false;
3434

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

3945
var key = mapKey(e.KeyCode);
4046
if (key == Key.Unknown) return false;

osu.Android/Input/AndroidStylusHandler.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,8 @@ private void handlePointer(MotionEvent e, int historyIndex)
8080
float x = historyIndex < 0 ? e.GetX(pointer_index) : e.GetHistoricalX(pointer_index, historyIndex);
8181
float y = historyIndex < 0 ? e.GetY(pointer_index) : e.GetHistoricalY(pointer_index, historyIndex);
8282
float pressure = historyIndex < 0 ? e.GetPressure(pointer_index) : e.GetHistoricalPressure(pointer_index, historyIndex);
83+
float tiltX = e.GetAxisValue(Axis.Tilt, pointer_index);
84+
float tiltY = e.GetAxisValue(Axis.Orientation, pointer_index);
8385

8486
// DeX windowed mode offset correction
8587
if (View != null)

osu.Android/OsuGameActivity.cs

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -130,7 +130,20 @@ protected override void OnCreate(Bundle? savedInstanceState)
130130

131131
public override bool DispatchKeyEvent(KeyEvent? e)
132132
{
133-
if (e != null && KeyboardHandler != null && KeyboardHandler.HandleKeyEvent(e))
133+
if (e == null) return false;
134+
135+
// Intercept mouse back button which often triggers Keycode.Back
136+
if (e.KeyCode == Keycode.Back && (e.Source.HasFlag(InputSourceType.Mouse) || e.Source.HasFlag(InputSourceType.Stylus)))
137+
{
138+
if (e.Action == KeyEventActions.Down)
139+
KeyboardHandler?.HandleKeyEvent(new KeyEvent(KeyEventActions.Down, Keycode.Escape));
140+
else if (e.Action == KeyEventActions.Up)
141+
KeyboardHandler?.HandleKeyEvent(new KeyEvent(KeyEventActions.Up, Keycode.Escape));
142+
143+
return true;
144+
}
145+
146+
if (KeyboardHandler != null && KeyboardHandler.HandleKeyEvent(e))
134147
return true;
135148

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

173+
// Stylus events should NEVER be passed to base.DispatchTouchEvent, as it triggers
174+
// Android's touch-mode which hides the cursor and shows touch effects.
175+
if (isStylusEvent(e))
176+
return handled;
177+
160178
// In DeX mode, we MUST call base even if "handled" to ensure window focus and system gestures work.
161179
// However, if we fully consumed it (e.g. gameplay), we return true to prevent UI double-clicks.
162180
return base.DispatchTouchEvent(e) || handled;
@@ -183,6 +201,11 @@ public override bool DispatchGenericMotionEvent(MotionEvent? e)
183201
handled = MouseHandler?.HandleMotionEvent(e) ?? false;
184202
}
185203

204+
// Stylus hover events should not be passed to base to avoid system-level hover effects
205+
// and touch-mode triggers.
206+
if (isStylusEvent(e))
207+
return handled;
208+
186209
return base.DispatchGenericMotionEvent(e) || handled;
187210
}
188211

osu.Android/OsuGameAndroid.cs

Lines changed: 33 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -368,9 +368,39 @@ public void SelectHighestRefreshRate()
368368
if (window == null || windowManager == null)
369369
return;
370370

371-
var display = OperatingSystem.IsAndroidVersionAtLeast(30)
372-
? gameActivity.Display
373-
: windowManager.DefaultDisplay;
371+
global::Android.Views.Display? display = null;
372+
373+
if (OperatingSystem.IsAndroidVersionAtLeast(30))
374+
{
375+
// Prefer the display associated with the activity (which would be the external monitor in DeX)
376+
display = gameActivity.Display;
377+
}
378+
379+
if (display == null)
380+
{
381+
// Fallback to DisplayManager to find an external display
382+
if (gameActivity.GetSystemService(global::Android.Content.Context.DisplayService) is global::Android.Hardware.Display.DisplayManager dm)
383+
{
384+
var displays = dm.GetDisplays();
385+
386+
if (gameActivity.IsDeX)
387+
{
388+
// Find the largest external display (most likely the monitor)
389+
var displayList = displays?.ToList();
390+
if (displayList != null)
391+
{
392+
display = displayList.Where(d => d.DisplayId != 0)
393+
.OrderByDescending(d => d.GetSupportedModes()?.FirstOrDefault()?.RefreshRate ?? 0)
394+
.ThenByDescending(d => d.GetSupportedModes()?.FirstOrDefault()?.PhysicalWidth ?? 0)
395+
.FirstOrDefault() ?? displayList.FirstOrDefault(d => d.DisplayId == 0);
396+
}
397+
}
398+
else
399+
{
400+
display = displays?.FirstOrDefault(d => d.DisplayId == 0);
401+
}
402+
}
403+
}
374404

375405
if (display == null)
376406
return;

osu.Game/Overlays/Settings/Sections/Graphics/LayoutSettings.cs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -266,6 +266,7 @@ protected override void LoadComplete()
266266
var fullscreenResolutions = display.NewValue.DisplayModes
267267
.Where(m => m.Size.Width >= 800 && m.Size.Height >= 600)
268268
.OrderByDescending(m => Math.Max(m.Size.Height, m.Size.Width))
269+
.ThenByDescending(m => m.RefreshRate)
269270
.Select(m => m.Size)
270271
.Distinct()
271272
.ToList();
@@ -361,10 +362,10 @@ private void onDisplaysChanged(IEnumerable<Display> displays)
361362

362363
private void updateDisplaySettingsVisibility()
363364
{
364-
resolutionFullscreenCanBeShown.Value = windowModeDropdown.Current.Value == WindowMode.Fullscreen && resolutionsFullscreen.Count > 1;
365+
resolutionFullscreenCanBeShown.Value = (windowModeDropdown.Current.Value == WindowMode.Fullscreen && resolutionsFullscreen.Count > 1) || RuntimeInfo.OS == RuntimeInfo.Platform.Android;
365366
resolutionWindowedCanBeShown.Value = windowModeDropdown.Current.Value == WindowMode.Windowed && resolutionsWindowed.Count > 1;
366367

367-
displayDropdownCanBeShown.Value = displayDropdown.Items.Count() > 1;
368+
displayDropdownCanBeShown.Value = displayDropdown.Items.Count() > 1 || RuntimeInfo.OS == RuntimeInfo.Platform.Android;
368369
minimiseOnFocusLossCanBeShown.Value = RuntimeInfo.IsDesktop && windowModeDropdown.Current.Value == WindowMode.Fullscreen;
369370
safeAreaConsiderationsCanBeShown.Value = host.Window?.SafeAreaPadding.Value.Total != Vector2.Zero;
370371
}

osu.Game/Tests/Visual/OnlinePlay/OnlinePlayTestScene.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ public abstract partial class OnlinePlayTestScene : ScreenTestScene, IOnlinePlay
3535
private readonly Container content;
3636
private readonly Container drawableDependenciesContainer;
3737
private DelegatedDependencyContainer dependencies = null!;
38-
private int currentRoomId;
38+
private int currentRoomId = 1;
3939

4040
protected OnlinePlayTestScene()
4141
{

0 commit comments

Comments
 (0)