From 1627154e3d0ff1c27b7fb525ec830c26de283a8d Mon Sep 17 00:00:00 2001 From: osy <50960678+osy@users.noreply.github.com> Date: Sat, 10 Sep 2022 10:03:58 +0300 Subject: [PATCH] display(iOS): hide home indicator and mouse cursor Fixes #4390 --- Platform/Main.swift | 2 + .../VMDisplayMetalViewController+Pointer.m | 9 +-- .../Display/VMDisplayMetalViewController.h | 1 - .../Display/VMDisplayMetalViewController.m | 2 +- .../iOS/Display/VMDisplayViewController.h | 3 +- .../iOS/Display/VMDisplayViewController.m | 20 ++++--- .../iOS/Display/VMDisplayViewController.swift | 20 ++++--- Platform/iOS/UIViewControllerPatch.swift | 55 +++++++++++++++++++ UTM.xcodeproj/project.pbxproj | 6 ++ 9 files changed, 93 insertions(+), 25 deletions(-) create mode 100644 Platform/iOS/UIViewControllerPatch.swift diff --git a/Platform/Main.swift b/Platform/Main.swift index c8f2395e9..1c6a75815 100644 --- a/Platform/Main.swift +++ b/Platform/Main.swift @@ -53,6 +53,8 @@ class Main { if jb_increase_memlimit() { logger.info("MEM: successfully removed memory limits") } + // UIViewController patches + UIViewController.patch() #endif UTMApp.main() } diff --git a/Platform/iOS/Display/VMDisplayMetalViewController+Pointer.m b/Platform/iOS/Display/VMDisplayMetalViewController+Pointer.m index d9f0b5e8d..f56b71385 100644 --- a/Platform/iOS/Display/VMDisplayMetalViewController+Pointer.m +++ b/Platform/iOS/Display/VMDisplayMetalViewController+Pointer.m @@ -48,9 +48,6 @@ - (void)initGCMouse { } } -- (BOOL)prefersPointerLocked { - return _mouseCaptured; -} - (void)mouseDidBecomeCurrent:(NSNotification *)notification API_AVAILABLE(ios(14)) { GCMouse *mouse = notification.object; @@ -88,9 +85,8 @@ - (void)mouseDidBecomeCurrent:(NSNotification *)notification API_AVAILABLE(ios(1 }; } // no handler to the gcmouse scroll event, gestureScroll works fine. - _mouseCaptured = YES; dispatch_async(dispatch_get_main_queue(), ^{ - [self setNeedsUpdateOfPrefersPointerLocked]; + self.prefersPointerLocked = YES; }); } @@ -104,9 +100,8 @@ - (void)mouseDidStopBeingCurrent:(NSNotification *)notification API_AVAILABLE(io for (int i = 0; i < MIN(4, mouse.mouseInput.auxiliaryButtons.count); i++) { mouse.mouseInput.auxiliaryButtons[i].pressedChangedHandler = nil; } - _mouseCaptured = NO; dispatch_async(dispatch_get_main_queue(), ^{ - [self setNeedsUpdateOfPrefersPointerLocked]; + self.prefersPointerLocked = NO; }); } diff --git a/Platform/iOS/Display/VMDisplayMetalViewController.h b/Platform/iOS/Display/VMDisplayMetalViewController.h index 5122ec58a..7c24163cc 100644 --- a/Platform/iOS/Display/VMDisplayMetalViewController.h +++ b/Platform/iOS/Display/VMDisplayMetalViewController.h @@ -35,7 +35,6 @@ NS_ASSUME_NONNULL_BEGIN BOOL _pencilForceRightClickOnce; VMCursor *_cursor; VMScroll *_scroll; - BOOL _mouseCaptured; // Gestures UISwipeGestureRecognizer *_swipeUp; diff --git a/Platform/iOS/Display/VMDisplayMetalViewController.m b/Platform/iOS/Display/VMDisplayMetalViewController.m index d9444de5e..dc4a692ac 100644 --- a/Platform/iOS/Display/VMDisplayMetalViewController.m +++ b/Platform/iOS/Display/VMDisplayMetalViewController.m @@ -114,7 +114,7 @@ - (void)viewDidLoad { - (void)viewWillAppear:(BOOL)animated { [super viewWillAppear:animated]; - self.prefersStatusBarHidden = YES; + self.prefersHomeIndicatorAutoHidden = YES; } - (void)viewDidAppear:(BOOL)animated { diff --git a/Platform/iOS/Display/VMDisplayViewController.h b/Platform/iOS/Display/VMDisplayViewController.h index b11fa330a..be0a8b54e 100644 --- a/Platform/iOS/Display/VMDisplayViewController.h +++ b/Platform/iOS/Display/VMDisplayViewController.h @@ -25,7 +25,8 @@ @property (weak, nonatomic) id delegate; @property (nonatomic) BOOL hasAutoSave; -@property (nonatomic, readwrite) BOOL prefersStatusBarHidden; +@property (nonatomic, readwrite) BOOL prefersHomeIndicatorAutoHidden; +@property (nonatomic, readwrite) BOOL prefersPointerLocked; - (void)showKeyboard; - (void)hideKeyboard; diff --git a/Platform/iOS/Display/VMDisplayViewController.m b/Platform/iOS/Display/VMDisplayViewController.m index b290f0ade..2c96602df 100644 --- a/Platform/iOS/Display/VMDisplayViewController.m +++ b/Platform/iOS/Display/VMDisplayViewController.m @@ -21,19 +21,25 @@ @implementation VMDisplayViewController #pragma mark - Properties -@synthesize prefersStatusBarHidden = _prefersStatusBarHidden; +@synthesize prefersHomeIndicatorAutoHidden = _prefersHomeIndicatorAutoHidden; +@synthesize prefersPointerLocked = _prefersPointerLocked; - (BOOL)prefersHomeIndicatorAutoHidden { - return YES; // always hide home indicator + return _prefersHomeIndicatorAutoHidden; } -- (UIStatusBarStyle)preferredStatusBarStyle { - return UIStatusBarStyleLightContent; +- (void)setPrefersHomeIndicatorAutoHidden:(BOOL)prefersHomeIndicatorAutoHidden { + _prefersHomeIndicatorAutoHidden = prefersHomeIndicatorAutoHidden; + [self setNeedsUpdateOfHomeIndicatorAutoHidden]; } -- (void)setPrefersStatusBarHidden:(BOOL)prefersStatusBarHidden { - _prefersStatusBarHidden = prefersStatusBarHidden; - [self setNeedsStatusBarAppearanceUpdate]; +- (BOOL)prefersPointerLocked { + return _prefersPointerLocked; +} + +- (void)setPrefersPointerLocked:(BOOL)prefersPointerLocked { + _prefersPointerLocked = prefersPointerLocked; + [self setNeedsUpdateOfPrefersPointerLocked]; } - (void)showKeyboard { diff --git a/Platform/iOS/Display/VMDisplayViewController.swift b/Platform/iOS/Display/VMDisplayViewController.swift index f61684fbe..fa6948d8a 100644 --- a/Platform/iOS/Display/VMDisplayViewController.swift +++ b/Platform/iOS/Display/VMDisplayViewController.swift @@ -19,10 +19,6 @@ import SwiftUI private var memoryAlertOnce = false @objc public extension VMDisplayViewController { - var largeScreen: Bool { - traitCollection.horizontalSizeClass == .regular && traitCollection.verticalSizeClass == .regular - } - var runInBackground: Bool { boolForSetting("RunInBackground") } @@ -36,10 +32,6 @@ private var memoryAlertOnce = false public extension VMDisplayViewController { override func viewDidLoad() { super.viewDidLoad() - - if largeScreen { - prefersStatusBarHidden = true - } } override func viewWillAppear(_ animated: Bool) { @@ -47,8 +39,20 @@ public extension VMDisplayViewController { navigationController?.setNavigationBarHidden(true, animated: animated) } + override func viewWillDisappear(_ animated: Bool) { + super.viewWillDisappear(animated) + if let root = view.window?.rootViewController { + root.setChildForHomeIndicatorAutoHidden(nil) + root.setChildViewControllerForPointerLock(nil) + } + } + override func viewDidAppear(_ animated: Bool) { super.viewDidAppear(animated) + if let root = view.window?.rootViewController { + root.setChildForHomeIndicatorAutoHidden(self) + root.setChildViewControllerForPointerLock(self) + } if runInBackground { logger.info("Start location tracking to enable running in background") UTMLocationManager.sharedInstance().startUpdatingLocation() diff --git a/Platform/iOS/UIViewControllerPatch.swift b/Platform/iOS/UIViewControllerPatch.swift new file mode 100644 index 000000000..bb68064ef --- /dev/null +++ b/Platform/iOS/UIViewControllerPatch.swift @@ -0,0 +1,55 @@ +// +// Copyright © 2022 osy. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +import UIKit + +/// We need to set these when the VM starts running since there is no way to do it from SwiftUI right now +extension UIViewController { + private static var _childForHomeIndicatorAutoHiddenStorage: [UIViewController: UIViewController?] = [:] + + @objc private dynamic var _childForHomeIndicatorAutoHidden: UIViewController? { + Self._childForHomeIndicatorAutoHiddenStorage[self] ?? nil + } + + @objc dynamic func setChildForHomeIndicatorAutoHidden(_ value: UIViewController?) { + Self._childForHomeIndicatorAutoHiddenStorage[self] = value + setNeedsUpdateOfHomeIndicatorAutoHidden() + } + + private static var _childViewControllerForPointerLockStorage: [UIViewController: UIViewController?] = [:] + + @objc private dynamic var _childViewControllerForPointerLock: UIViewController? { + Self._childViewControllerForPointerLockStorage[self] ?? nil + } + + @objc dynamic func setChildViewControllerForPointerLock(_ value: UIViewController?) { + Self._childViewControllerForPointerLockStorage[self] = value + setNeedsUpdateOfPrefersPointerLocked() + } + + static func patch() { + let originalChildForHomeIndicatorAutoHidden = #selector(getter: Self.childForHomeIndicatorAutoHidden) + let swizzleChildForHomeIndicatorAutoHidden = #selector(getter: Self._childForHomeIndicatorAutoHidden) + let originalChildForHomeIndicatorAutoHiddenMethod = class_getInstanceMethod(Self.self, originalChildForHomeIndicatorAutoHidden)! + let swizzleChildForHomeIndicatorAutoHiddenMethod = class_getInstanceMethod(Self.self, swizzleChildForHomeIndicatorAutoHidden)! + method_exchangeImplementations(originalChildForHomeIndicatorAutoHiddenMethod, swizzleChildForHomeIndicatorAutoHiddenMethod) + let originalChildViewControllerForPointerLock = #selector(getter: Self.childViewControllerForPointerLock) + let swizzleChildViewControllerForPointerLock = #selector(getter: Self._childViewControllerForPointerLock) + let originalChildViewControllerForPointerLockMethod = class_getInstanceMethod(Self.self, originalChildViewControllerForPointerLock)! + let swizzleChildViewControllerForPointerLockMethod = class_getInstanceMethod(Self.self, swizzleChildViewControllerForPointerLock)! + method_exchangeImplementations(originalChildViewControllerForPointerLockMethod, swizzleChildViewControllerForPointerLockMethod) + } +} diff --git a/UTM.xcodeproj/project.pbxproj b/UTM.xcodeproj/project.pbxproj index 0d74d0cfb..75213398d 100644 --- a/UTM.xcodeproj/project.pbxproj +++ b/UTM.xcodeproj/project.pbxproj @@ -100,6 +100,8 @@ 841E999928AC817D003C6CB6 /* UTMQemuVirtualMachine.swift in Sources */ = {isa = PBXBuildFile; fileRef = 841E999728AC817D003C6CB6 /* UTMQemuVirtualMachine.swift */; }; 841E999A28AC817D003C6CB6 /* UTMQemuVirtualMachine.swift in Sources */ = {isa = PBXBuildFile; fileRef = 841E999728AC817D003C6CB6 /* UTMQemuVirtualMachine.swift */; }; 84258C42288F806400C66366 /* VMToolbarUSBMenuView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84258C41288F806400C66366 /* VMToolbarUSBMenuView.swift */; }; + 842B9F8D28CC58B700031EE7 /* UIViewControllerPatch.swift in Sources */ = {isa = PBXBuildFile; fileRef = 842B9F8C28CC58B700031EE7 /* UIViewControllerPatch.swift */; }; + 842B9F8E28CC58B700031EE7 /* UIViewControllerPatch.swift in Sources */ = {isa = PBXBuildFile; fileRef = 842B9F8C28CC58B700031EE7 /* UIViewControllerPatch.swift */; }; 8432329028C2CDAD00CFBC97 /* VMNavigationListView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8432328F28C2CDAD00CFBC97 /* VMNavigationListView.swift */; }; 8432329128C2CDAD00CFBC97 /* VMNavigationListView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8432328F28C2CDAD00CFBC97 /* VMNavigationListView.swift */; }; 8432329228C2CDAD00CFBC97 /* VMNavigationListView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8432328F28C2CDAD00CFBC97 /* VMNavigationListView.swift */; }; @@ -1628,6 +1630,7 @@ 841E999628AC80CA003C6CB6 /* UTMQemuVirtualMachine-Protected.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "UTMQemuVirtualMachine-Protected.h"; sourceTree = ""; }; 841E999728AC817D003C6CB6 /* UTMQemuVirtualMachine.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UTMQemuVirtualMachine.swift; sourceTree = ""; }; 84258C41288F806400C66366 /* VMToolbarUSBMenuView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VMToolbarUSBMenuView.swift; sourceTree = ""; }; + 842B9F8C28CC58B700031EE7 /* UIViewControllerPatch.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIViewControllerPatch.swift; sourceTree = ""; }; 8432328F28C2CDAD00CFBC97 /* VMNavigationListView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VMNavigationListView.swift; sourceTree = ""; }; 8432329328C2ED9000CFBC97 /* FileBrowseField.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FileBrowseField.swift; sourceTree = ""; }; 8432329728C3017F00CFBC97 /* GlobalFileImporter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GlobalFileImporter.swift; sourceTree = ""; }; @@ -2709,6 +2712,7 @@ CE7BED4D22600F5000A1E1B6 /* Display */, CE8813D224CD230300532628 /* ActivityView.swift */, CED814EE24C7EB760042F0F1 /* ImagePicker.swift */, + 842B9F8C28CC58B700031EE7 /* UIViewControllerPatch.swift */, 841E58D02893AF5400137A20 /* UTMApp.swift */, CEBBF1A424B56A2900C15049 /* UTMDataExtension.swift */, 841E58CA28937EE200137A20 /* UTMExternalSceneDelegate.swift */, @@ -3682,6 +3686,7 @@ CE2D92B324AD46670059923A /* qapi-events-net.c in Sources */, CE2D92B424AD46670059923A /* qapi-visit-run-state.c in Sources */, CED814E924C79F070042F0F1 /* VMConfigDriveCreateView.swift in Sources */, + 842B9F8D28CC58B700031EE7 /* UIViewControllerPatch.swift in Sources */, CE2D92B524AD46670059923A /* qapi-commands-tpm.c in Sources */, CE19392626DCB094005CEC17 /* RAMSlider.swift in Sources */, CE2D92B724AD46670059923A /* qapi-visit-common.c in Sources */, @@ -4248,6 +4253,7 @@ CEA45E6B263519B5002FA97D /* UTMLegacyQemuConfiguration+Display.m in Sources */, CEA45E6C263519B5002FA97D /* UTMVirtualMachine.swift in Sources */, 84B36D2A27B790BE00C22685 /* DestructiveButton.swift in Sources */, + 842B9F8E28CC58B700031EE7 /* UIViewControllerPatch.swift in Sources */, CEA45E6D263519B5002FA97D /* qapi-visit-block-core.c in Sources */, CEE7E937287CFDB100282049 /* UTMLegacyQemuConfiguration+Constants.m in Sources */, 841619AB284315F9000034B2 /* UTMConfigurationInfo.swift in Sources */,