Files
UTM/Platform/iOS/Display/VMDisplayMetalViewController+Private.h
T
osy a070e93140 display(iOS): add touch mode that scrolls instead of dragging
In the touch modes the left button goes down the moment a finger lands,
because the touch itself is the drag. Every other gesture starts with a
finger landing too, so a two finger tap, scroll or pinch first clicked
and dragged whatever was under the first finger until the gesture was
recognized, and a long press held the left button for half a second
before its right click.

Add "Touch mode (drag to scroll)" as a third touch mode that behaves
like a touch screen and never presses a button on touch down. A tap is
a left click, a long press runs the long press action, and dragging one
finger scrolls the mouse wheel so that the content follows the finger.
Since a touch no longer drags, a long press that moves turns into a left
button drag so windows and selections can still be moved with one
finger. A ring is shown when the long press is recognized because
nothing in the guest reacts until the finger lifts.

A finger never lands on the same spot twice, and a guest only counts two
clicks on the same spot as a double click, so a second tap that follows
quickly and close by clicks where the first one did.

The existing touch modes are unchanged.

Reworked from #7697.

Assisted-by: Claude:claude-fable-5-1
2026-09-20 15:02:56 -05:00

99 lines
3.2 KiB
Objective-C

//
// Copyright © 2023 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 "VMDisplayMetalViewController.h"
#import <TargetConditionals.h>
#if !defined(WITH_USB)
@import CocoaSpiceNoUsb;
#else
@import CocoaSpice;
#endif
@class VMCursor;
@class VMScroll;
@class GCController;
NS_ASSUME_NONNULL_BEGIN
@interface VMDisplayMetalViewController ()
@property (nonatomic, nullable) CSMetalRenderer *renderer;
// cursor handling
@property (nonatomic) CGPoint lastTwoPanOrigin;
@property (nonatomic) CGPoint lastLongPressOrigin;
@property (nonatomic) CGPoint lastTapLocation;
@property (nonatomic) NSTimeInterval lastTapTime;
@property (nonatomic) BOOL isTouchScrolling;
@property (nonatomic) BOOL mouseLeftDown;
@property (nonatomic) BOOL mouseRightDown;
@property (nonatomic) BOOL mouseMiddleDown;
@property (nonatomic) BOOL mouseSideDown;
@property (nonatomic) BOOL mouseExtraDown;
@property (nonatomic) BOOL pencilForceRightClickOnce;
@property (nonatomic, nullable) VMCursor *cursor;
@property (nonatomic, nullable) VMScroll *scroll;
// Gestures
@property (nonatomic, nullable) UISwipeGestureRecognizer *swipeUp;
@property (nonatomic, nullable) UISwipeGestureRecognizer *swipeDown;
@property (nonatomic, nullable) UISwipeGestureRecognizer *swipeScrollUp;
@property (nonatomic, nullable) UISwipeGestureRecognizer *swipeScrollDown;
@property (nonatomic, nullable) UIPanGestureRecognizer *pan;
@property (nonatomic, nullable) UIPanGestureRecognizer *twoPan;
@property (nonatomic, nullable) UIPanGestureRecognizer *threePan;
@property (nonatomic, nullable) UITapGestureRecognizer *tap;
@property (nonatomic, nullable) UITapGestureRecognizer *tapPencil;
@property (nonatomic, nullable) UITapGestureRecognizer *twoTap;
@property (nonatomic, nullable) UILongPressGestureRecognizer *longPress;
@property (nonatomic, nullable) UIPinchGestureRecognizer *pinch;
//Gamepad
@property (nonatomic, nullable) GCController *controller;
#if !defined(TARGET_OS_VISION) || !TARGET_OS_VISION
// Feedback generators
@property (nonatomic, nullable) UISelectionFeedbackGenerator *clickFeedbackGenerator;
#endif
@end
NS_ASSUME_NONNULL_END
static inline CGFloat CGPointToPixel(UIView * _Nonnull view, CGFloat point) {
#if defined(TARGET_OS_VISION) && TARGET_OS_VISION
return point * 2.0;
#else
UIScreen *screen = view.window.screen;
if (!screen) {
screen = [UIScreen mainScreen];
}
return point * screen.nativeScale;
#endif
}
static inline CGFloat CGPixelToPoint(UIView * _Nonnull view, CGFloat pixel) {
#if defined(TARGET_OS_VISION) && TARGET_OS_VISION
return pixel / 2.0;
#else
UIScreen *screen = view.window.screen;
if (!screen) {
screen = [UIScreen mainScreen];
}
return pixel / screen.nativeScale;
#endif
}