Skip to content

Commit

Permalink
[iOS] Implement raw mouse input support and captured mouse mode.
Browse files Browse the repository at this point in the history
  • Loading branch information
bruvzg committed May 23, 2024
1 parent b947c53 commit d4cc9b7
Show file tree
Hide file tree
Showing 4 changed files with 183 additions and 0 deletions.
5 changes: 5 additions & 0 deletions platform/ios/display_server_ios.h
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,8 @@ class DisplayServerIOS : public DisplayServer {

int virtual_keyboard_height = 0;

MouseMode mouse_mode = MOUSE_MODE_VISIBLE;

void perform_event(const Ref<InputEvent> &p_event);

DisplayServerIOS(const String &p_rendering_driver, DisplayServer::WindowMode p_mode, DisplayServer::VSyncMode p_vsync_mode, uint32_t p_flags, const Vector2i *p_position, const Vector2i &p_resolution, int p_screen, Context p_context, Error &r_error);
Expand Down Expand Up @@ -222,6 +224,9 @@ class DisplayServerIOS : public DisplayServer {
virtual void virtual_keyboard_show(const String &p_existing_text, const Rect2 &p_screen_rect, VirtualKeyboardType p_type, int p_max_length, int p_cursor_start, int p_cursor_end) override;
virtual void virtual_keyboard_hide() override;

virtual void mouse_set_mode(MouseMode p_mode) override;
virtual MouseMode mouse_get_mode() const override;

void virtual_keyboard_set_height(int height);
virtual int virtual_keyboard_get_height() const override;

Expand Down
16 changes: 16 additions & 0 deletions platform/ios/display_server_ios.mm
Original file line number Diff line number Diff line change
Expand Up @@ -712,6 +712,22 @@ _FORCE_INLINE_ int _convert_utf32_offset_to_utf16(const String &p_existing_text,
return virtual_keyboard_height;
}

void DisplayServerIOS::mouse_set_mode(DisplayServer::MouseMode p_mode) {
if (mouse_mode == p_mode) {
return;
}
ERR_FAIL_COND_MSG(p_mode == DisplayServer::MouseMode::MOUSE_MODE_HIDDEN || p_mode == DisplayServer::MouseMode::MOUSE_MODE_CONFINED || p_mode == DisplayServer::MouseMode::MOUSE_MODE_CONFINED_HIDDEN, "Confined and hidden mouse modes not supported.");

mouse_mode = p_mode;
if (@available(iOS 14, *)) {
[AppDelegate.viewController setNeedsUpdateOfPrefersPointerLocked];
}
}

DisplayServer::MouseMode DisplayServerIOS::mouse_get_mode() const {
return mouse_mode;
}

void DisplayServerIOS::clipboard_set(const String &p_text) {
[UIPasteboard generalPasteboard].string = [NSString stringWithUTF8String:p_text.utf8()];
}
Expand Down
12 changes: 12 additions & 0 deletions platform/ios/godot_view.mm
Original file line number Diff line number Diff line change
Expand Up @@ -355,6 +355,9 @@ - (void)clearTouches {

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
for (UITouch *touch in touches) {
if (touch.type != UITouchTypeDirect && touch.type != UITouchTypePencil && DisplayServerIOS::get_singleton()->mouse_get_mode() == DisplayServer::MouseMode::MOUSE_MODE_CAPTURED) {
continue;
}
int tid = [self getTouchIDForTouch:touch];
ERR_FAIL_COND(tid == -1);
CGPoint touchPoint = [touch locationInView:self];
Expand All @@ -364,6 +367,9 @@ - (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {

- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
for (UITouch *touch in touches) {
if (touch.type != UITouchTypeDirect && touch.type != UITouchTypePencil && DisplayServerIOS::get_singleton()->mouse_get_mode() == DisplayServer::MouseMode::MOUSE_MODE_CAPTURED) {
continue;
}
int tid = [self getTouchIDForTouch:touch];
ERR_FAIL_COND(tid == -1);
CGPoint touchPoint = [touch locationInView:self];
Expand All @@ -376,6 +382,9 @@ - (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {

- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
for (UITouch *touch in touches) {
if (touch.type != UITouchTypeDirect && touch.type != UITouchTypePencil && DisplayServerIOS::get_singleton()->mouse_get_mode() == DisplayServer::MouseMode::MOUSE_MODE_CAPTURED) {
continue;
}
int tid = [self getTouchIDForTouch:touch];
ERR_FAIL_COND(tid == -1);
[self removeTouch:touch];
Expand All @@ -386,6 +395,9 @@ - (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {

- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event {
for (UITouch *touch in touches) {
if (touch.type != UITouchTypeDirect && touch.type != UITouchTypePencil && DisplayServerIOS::get_singleton()->mouse_get_mode() == DisplayServer::MouseMode::MOUSE_MODE_CAPTURED) {
continue;
}
int tid = [self getTouchIDForTouch:touch];
ERR_FAIL_COND(tid == -1);
DisplayServerIOS::get_singleton()->touches_canceled(tid);
Expand Down
150 changes: 150 additions & 0 deletions platform/ios/view_controller.mm
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,10 @@ @interface ViewController () <GodotViewDelegate>

@property(strong, nonatomic) UIView *godotLoadingOverlay;

@property(strong, nonatomic) NSMutableArray *connectedMice;

@property(nonatomic) BitField<MouseButtonMask> last_button_state;

@end

@implementation ViewController
Expand Down Expand Up @@ -152,6 +156,139 @@ - (instancetype)initWithCoder:(NSCoder *)coder {

- (void)godot_commonInit {
// Initialize view controller values.

self.connectedMice = [NSMutableArray array];
if (@available(iOS 14, *)) {
[[NSNotificationCenter defaultCenter]
addObserver:self
selector:@selector(mouseWasConnected:)
name:GCMouseDidConnectNotification
object:nil];

[[NSNotificationCenter defaultCenter]
addObserver:self
selector:@selector(mouseWasDisconnected:)
name:GCMouseDidDisconnectNotification
object:nil];
}
}

- (void)mouseWasConnected:(NSNotification *)notification {
if (@available(iOS 14, *)) {
GCMouse *mouse = (GCMouse *)notification.object;

if (mouse) {
print_verbose(vformat("Mouse connected: %s", String::utf8([mouse.vendorName UTF8String])));
[self.connectedMice addObject:mouse];
[self setMouseInputHandler:mouse];
}
}
}

- (void)mouseWasDisconnected:(NSNotification *)notification {
if (@available(iOS 14, *)) {
GCMouse *mouse = (GCMouse *)notification.object;

if (mouse) {
print_verbose(vformat("Mouse disconnected: %s", String::utf8([mouse.vendorName UTF8String])));
[self.connectedMice removeObject:mouse];
}
}
}

- (void)sendMouseButton:(MouseButton)button pressed:(bool)pressed {
Ref<InputEventMouseButton> mb;
mb.instantiate();
mb->set_button_index(button);
mb->set_pressed(pressed);
if (mb->is_pressed()) {
self.last_button_state.set_flag(mouse_button_to_mask(mb->get_button_index()));
} else {
self.last_button_state.clear_flag(mouse_button_to_mask(mb->get_button_index()));
}
mb->set_button_mask(self.last_button_state);
mb->set_position(DisplayServerIOS::get_singleton()->window_get_size() / 2);
mb->set_global_position(DisplayServerIOS::get_singleton()->window_get_size() / 2);
Input::get_singleton()->parse_input_event(mb);
}

- (void)sendScroll:(Vector2)factor {
if (factor.x != 0) { // Note: X is primary wheel, not horizontal.
Ref<InputEventMouseButton> sc;
sc.instantiate();
sc->set_button_index(factor.x < 0 ? MouseButton::WHEEL_DOWN : MouseButton::WHEEL_UP);
sc->set_factor(Math::abs(factor.x));
sc->set_pressed(true);
sc->set_button_mask(self.last_button_state);
sc->set_position(DisplayServerIOS::get_singleton()->window_get_size() / 2);
sc->set_global_position(DisplayServerIOS::get_singleton()->window_get_size() / 2);
Input::get_singleton()->parse_input_event(sc);
sc = sc->duplicate();
sc->set_pressed(false);
Input::get_singleton()->parse_input_event(sc);
}
if (factor.y != 0) { // Note: Y is secondary wheel, not vertical.
Ref<InputEventMouseButton> sc;
sc.instantiate();
sc->set_button_index(factor.y < 0 ? MouseButton::WHEEL_LEFT : MouseButton::WHEEL_RIGHT);
sc->set_factor(Math::abs(factor.y));
sc->set_pressed(true);
sc->set_button_mask(self.last_button_state);
sc->set_position(DisplayServerIOS::get_singleton()->window_get_size() / 2);
sc->set_global_position(DisplayServerIOS::get_singleton()->window_get_size() / 2);
Input::get_singleton()->parse_input_event(sc);
sc = sc->duplicate();
sc->set_pressed(false);
Input::get_singleton()->parse_input_event(sc);
}
}

- (void)setMouseInputHandler:(GCMouse *)mouse API_AVAILABLE(ios(14.0)) {
if (mouse.mouseInput != nil) {
mouse.mouseInput.mouseMovedHandler = ^(GCMouseInput *mouse_input, float deltaX, float deltaY) {
if (DisplayServerIOS::get_singleton() && DisplayServerIOS::get_singleton()->mouse_get_mode() == DisplayServer::MouseMode::MOUSE_MODE_CAPTURED) {
Ref<InputEventMouseMotion> mm;
mm.instantiate();

mm->set_button_mask(self.last_button_state);
mm->set_position(DisplayServerIOS::get_singleton()->window_get_size() / 2);
mm->set_global_position(DisplayServerIOS::get_singleton()->window_get_size() / 2);
mm->set_velocity(Vector2(0, 0));
mm->set_screen_velocity(Vector2(0, 0));
mm->set_relative(Vector2(deltaX, deltaY));
mm->set_relative_screen_position(mm->get_relative());
Input::get_singleton()->parse_input_event(mm);
}
};
mouse.mouseInput.leftButton.pressedChangedHandler = ^(GCControllerButtonInput *_Nonnull button, float value, BOOL pressed) {
if (DisplayServerIOS::get_singleton() && DisplayServerIOS::get_singleton()->mouse_get_mode() == DisplayServer::MouseMode::MOUSE_MODE_CAPTURED) {
[self sendMouseButton:MouseButton::LEFT pressed:button.isPressed];
}
};
mouse.mouseInput.rightButton.pressedChangedHandler = ^(GCControllerButtonInput *_Nonnull button, float value, BOOL pressed) {
if (DisplayServerIOS::get_singleton() && DisplayServerIOS::get_singleton()->mouse_get_mode() == DisplayServer::MouseMode::MOUSE_MODE_CAPTURED) {
[self sendMouseButton:MouseButton::RIGHT pressed:button.isPressed];
}
};
mouse.mouseInput.middleButton.pressedChangedHandler = ^(GCControllerButtonInput *_Nonnull button, float value, BOOL pressed) {
if (DisplayServerIOS::get_singleton() && DisplayServerIOS::get_singleton()->mouse_get_mode() == DisplayServer::MouseMode::MOUSE_MODE_CAPTURED) {
[self sendMouseButton:MouseButton::MIDDLE pressed:button.isPressed];
}
};
for (NSUInteger i = 0; i < [mouse.mouseInput.auxiliaryButtons count]; i++) {
GCControllerButtonInput *button_element = [mouse.mouseInput.auxiliaryButtons objectAtIndex:i];
button_element.pressedChangedHandler = ^(GCControllerButtonInput *_Nonnull button, float value, BOOL pressed) {
if (DisplayServerIOS::get_singleton() && DisplayServerIOS::get_singleton()->mouse_get_mode() == DisplayServer::MouseMode::MOUSE_MODE_CAPTURED) {
[self sendMouseButton:(MouseButton)((NSUInteger)MouseButton::MB_XBUTTON1 + i) pressed:button.isPressed];
}
};
}
mouse.mouseInput.scroll.valueChangedHandler = ^(GCControllerDirectionPad *dpad, float xValue, float yValue) {
if (DisplayServerIOS::get_singleton() && DisplayServerIOS::get_singleton()->mouse_get_mode() == DisplayServer::MouseMode::MOUSE_MODE_CAPTURED) {
[self sendScroll:Vector2(xValue, yValue)];
}
};
}
}

- (void)didReceiveMemoryWarning {
Expand Down Expand Up @@ -212,6 +349,7 @@ - (BOOL)godotViewFinishedSetup:(GodotView *)view {
}

- (void)dealloc {
self.connectedMice = nil;
self.keyboardView = nil;

self.renderer = nil;
Expand Down Expand Up @@ -288,6 +426,18 @@ - (BOOL)prefersHomeIndicatorAutoHidden {
}
}

- (BOOL)prefersPointerLocked API_AVAILABLE(ios(14.0)) {
if (DisplayServerIOS::get_singleton()) {
DisplayServer::MouseMode mm = DisplayServerIOS::get_singleton()->mouse_get_mode();
if (mm == DisplayServer::MouseMode::MOUSE_MODE_VISIBLE) {
return NO;
} else {
return YES;
}
}
return NO;
}

// MARK: Keyboard

- (void)keyboardOnScreen:(NSNotification *)notification {
Expand Down

0 comments on commit d4cc9b7

Please sign in to comment.