mirror of
https://git.sr.ht/~eliasnaur/gio
synced 2026-07-01 15:45:38 +00:00
2e524200ab
Commit c0c25b777 replaced the synchronizing of the display link callback
from a sync.Map to a cgo.Handle. However, the change didn't take into
account the lifecycle issues: a callback may happen just as the cgo.Handle
is freed, leading to a misuse crash.
This change restores the sync.Map synchronization, which avoids the
lifecycle issue.
Fixes: https://todo.sr.ht/~eliasnaur/gio/526
Signed-off-by: Elias Naur <mail@eliasnaur.com>
274 lines
7.6 KiB
Objective-C
274 lines
7.6 KiB
Objective-C
// SPDX-License-Identifier: Unlicense OR MIT
|
|
|
|
// +build darwin,ios
|
|
|
|
@import UIKit;
|
|
|
|
#include <stdint.h>
|
|
#include "_cgo_export.h"
|
|
#include "framework_ios.h"
|
|
|
|
__attribute__ ((visibility ("hidden"))) Class gio_layerClass(void);
|
|
|
|
@interface GioView: UIView <UIKeyInput>
|
|
@end
|
|
|
|
@implementation GioViewController
|
|
|
|
CGFloat _keyboardHeight;
|
|
|
|
- (void)loadView {
|
|
gio_runMain();
|
|
|
|
CGRect zeroFrame = CGRectMake(0, 0, 0, 0);
|
|
self.view = [[UIView alloc] initWithFrame:zeroFrame];
|
|
self.view.layoutMargins = UIEdgeInsetsMake(0, 0, 0, 0);
|
|
UIView *drawView = [[GioView alloc] initWithFrame:zeroFrame];
|
|
[self.view addSubview: drawView];
|
|
#ifndef TARGET_OS_TV
|
|
drawView.multipleTouchEnabled = YES;
|
|
#endif
|
|
drawView.preservesSuperviewLayoutMargins = YES;
|
|
drawView.layoutMargins = UIEdgeInsetsMake(0, 0, 0, 0);
|
|
onCreate((__bridge CFTypeRef)drawView, (__bridge CFTypeRef)self);
|
|
[[NSNotificationCenter defaultCenter] addObserver:self
|
|
selector:@selector(keyboardWillChange:)
|
|
name:UIKeyboardWillShowNotification
|
|
object:nil];
|
|
[[NSNotificationCenter defaultCenter] addObserver:self
|
|
selector:@selector(keyboardWillChange:)
|
|
name:UIKeyboardWillChangeFrameNotification
|
|
object:nil];
|
|
[[NSNotificationCenter defaultCenter] addObserver:self
|
|
selector:@selector(keyboardWillHide:)
|
|
name:UIKeyboardWillHideNotification
|
|
object:nil];
|
|
[[NSNotificationCenter defaultCenter] addObserver: self
|
|
selector: @selector(applicationDidEnterBackground:)
|
|
name: UIApplicationDidEnterBackgroundNotification
|
|
object: nil];
|
|
[[NSNotificationCenter defaultCenter] addObserver: self
|
|
selector: @selector(applicationWillEnterForeground:)
|
|
name: UIApplicationWillEnterForegroundNotification
|
|
object: nil];
|
|
}
|
|
|
|
- (void)applicationWillEnterForeground:(UIApplication *)application {
|
|
UIView *drawView = self.view.subviews[0];
|
|
if (drawView != nil) {
|
|
gio_onDraw((__bridge CFTypeRef)drawView);
|
|
}
|
|
}
|
|
|
|
- (void)applicationDidEnterBackground:(UIApplication *)application {
|
|
UIView *drawView = self.view.subviews[0];
|
|
if (drawView != nil) {
|
|
onStop((__bridge CFTypeRef)drawView);
|
|
}
|
|
}
|
|
|
|
- (void)viewDidDisappear:(BOOL)animated {
|
|
[super viewDidDisappear:animated];
|
|
CFTypeRef viewRef = (__bridge CFTypeRef)self.view.subviews[0];
|
|
onDestroy(viewRef);
|
|
}
|
|
|
|
- (void)viewDidLayoutSubviews {
|
|
[super viewDidLayoutSubviews];
|
|
UIView *view = self.view.subviews[0];
|
|
CGRect frame = self.view.bounds;
|
|
// Adjust view bounds to make room for the keyboard.
|
|
frame.size.height -= _keyboardHeight;
|
|
view.frame = frame;
|
|
gio_onDraw((__bridge CFTypeRef)view);
|
|
}
|
|
|
|
- (void)didReceiveMemoryWarning {
|
|
onLowMemory();
|
|
[super didReceiveMemoryWarning];
|
|
}
|
|
|
|
- (void)keyboardWillChange:(NSNotification *)note {
|
|
NSDictionary *userInfo = note.userInfo;
|
|
CGRect f = [userInfo[UIKeyboardFrameEndUserInfoKey] CGRectValue];
|
|
_keyboardHeight = f.size.height;
|
|
[self.view setNeedsLayout];
|
|
}
|
|
|
|
- (void)keyboardWillHide:(NSNotification *)note {
|
|
_keyboardHeight = 0.0;
|
|
[self.view setNeedsLayout];
|
|
}
|
|
@end
|
|
|
|
static void handleTouches(int last, UIView *view, NSSet<UITouch *> *touches, UIEvent *event) {
|
|
CGFloat scale = view.contentScaleFactor;
|
|
NSUInteger i = 0;
|
|
NSUInteger n = [touches count];
|
|
CFTypeRef viewRef = (__bridge CFTypeRef)view;
|
|
for (UITouch *touch in touches) {
|
|
CFTypeRef touchRef = (__bridge CFTypeRef)touch;
|
|
i++;
|
|
NSArray<UITouch *> *coalescedTouches = [event coalescedTouchesForTouch:touch];
|
|
NSUInteger j = 0;
|
|
NSUInteger m = [coalescedTouches count];
|
|
for (UITouch *coalescedTouch in [event coalescedTouchesForTouch:touch]) {
|
|
CGPoint loc = [coalescedTouch locationInView:view];
|
|
j++;
|
|
int lastTouch = last && i == n && j == m;
|
|
onTouch(lastTouch, viewRef, touchRef, touch.phase, loc.x*scale, loc.y*scale, [coalescedTouch timestamp]);
|
|
}
|
|
}
|
|
}
|
|
|
|
@implementation GioView
|
|
NSArray<UIKeyCommand *> *_keyCommands;
|
|
+ (void)onFrameCallback:(CADisplayLink *)link {
|
|
gio_onFrameCallback((__bridge CFTypeRef)link);
|
|
}
|
|
+ (Class)layerClass {
|
|
return gio_layerClass();
|
|
}
|
|
- (void)willMoveToWindow:(UIWindow *)newWindow {
|
|
if (self.window != nil) {
|
|
[[NSNotificationCenter defaultCenter] removeObserver:self
|
|
name:UIWindowDidBecomeKeyNotification
|
|
object:self.window];
|
|
[[NSNotificationCenter defaultCenter] removeObserver:self
|
|
name:UIWindowDidResignKeyNotification
|
|
object:self.window];
|
|
}
|
|
self.contentScaleFactor = newWindow.screen.nativeScale;
|
|
[[NSNotificationCenter defaultCenter] addObserver:self
|
|
selector:@selector(onWindowDidBecomeKey:)
|
|
name:UIWindowDidBecomeKeyNotification
|
|
object:newWindow];
|
|
[[NSNotificationCenter defaultCenter] addObserver:self
|
|
selector:@selector(onWindowDidResignKey:)
|
|
name:UIWindowDidResignKeyNotification
|
|
object:newWindow];
|
|
}
|
|
|
|
- (void)onWindowDidBecomeKey:(NSNotification *)note {
|
|
if (self.isFirstResponder) {
|
|
onFocus((__bridge CFTypeRef)self, YES);
|
|
}
|
|
}
|
|
|
|
- (void)onWindowDidResignKey:(NSNotification *)note {
|
|
if (self.isFirstResponder) {
|
|
onFocus((__bridge CFTypeRef)self, NO);
|
|
}
|
|
}
|
|
|
|
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
|
|
handleTouches(0, self, touches, event);
|
|
}
|
|
|
|
- (void)touchesMoved:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
|
|
handleTouches(0, self, touches, event);
|
|
}
|
|
|
|
- (void)touchesEnded:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
|
|
handleTouches(1, self, touches, event);
|
|
}
|
|
|
|
- (void)touchesCancelled:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
|
|
handleTouches(1, self, touches, event);
|
|
}
|
|
|
|
- (void)insertText:(NSString *)text {
|
|
onText((__bridge CFTypeRef)self, (__bridge CFTypeRef)text);
|
|
}
|
|
|
|
- (BOOL)canBecomeFirstResponder {
|
|
return YES;
|
|
}
|
|
|
|
- (BOOL)hasText {
|
|
return YES;
|
|
}
|
|
|
|
- (void)deleteBackward {
|
|
onDeleteBackward((__bridge CFTypeRef)self);
|
|
}
|
|
|
|
- (void)onUpArrow {
|
|
onUpArrow((__bridge CFTypeRef)self);
|
|
}
|
|
|
|
- (void)onDownArrow {
|
|
onDownArrow((__bridge CFTypeRef)self);
|
|
}
|
|
|
|
- (void)onLeftArrow {
|
|
onLeftArrow((__bridge CFTypeRef)self);
|
|
}
|
|
|
|
- (void)onRightArrow {
|
|
onRightArrow((__bridge CFTypeRef)self);
|
|
}
|
|
|
|
- (NSArray<UIKeyCommand *> *)keyCommands {
|
|
if (_keyCommands == nil) {
|
|
_keyCommands = @[
|
|
[UIKeyCommand keyCommandWithInput:UIKeyInputUpArrow
|
|
modifierFlags:0
|
|
action:@selector(onUpArrow)],
|
|
[UIKeyCommand keyCommandWithInput:UIKeyInputDownArrow
|
|
modifierFlags:0
|
|
action:@selector(onDownArrow)],
|
|
[UIKeyCommand keyCommandWithInput:UIKeyInputLeftArrow
|
|
modifierFlags:0
|
|
action:@selector(onLeftArrow)],
|
|
[UIKeyCommand keyCommandWithInput:UIKeyInputRightArrow
|
|
modifierFlags:0
|
|
action:@selector(onRightArrow)]
|
|
];
|
|
}
|
|
return _keyCommands;
|
|
}
|
|
@end
|
|
|
|
CFTypeRef gio_createDisplayLink(void) {
|
|
CADisplayLink *dl = [CADisplayLink displayLinkWithTarget:[GioView class] selector:@selector(onFrameCallback:)];
|
|
dl.paused = YES;
|
|
NSRunLoop *runLoop = [NSRunLoop mainRunLoop];
|
|
[dl addToRunLoop:runLoop forMode:[runLoop currentMode]];
|
|
return (__bridge_retained CFTypeRef)dl;
|
|
}
|
|
|
|
int gio_startDisplayLink(CFTypeRef dlref) {
|
|
CADisplayLink *dl = (__bridge CADisplayLink *)dlref;
|
|
dl.paused = NO;
|
|
return 0;
|
|
}
|
|
|
|
int gio_stopDisplayLink(CFTypeRef dlref) {
|
|
CADisplayLink *dl = (__bridge CADisplayLink *)dlref;
|
|
dl.paused = YES;
|
|
return 0;
|
|
}
|
|
|
|
void gio_releaseDisplayLink(CFTypeRef dlref) {
|
|
CADisplayLink *dl = (__bridge CADisplayLink *)dlref;
|
|
[dl invalidate];
|
|
CFRelease(dlref);
|
|
}
|
|
|
|
void gio_setDisplayLinkDisplay(CFTypeRef dl, uint64_t did) {
|
|
// Nothing to do on iOS.
|
|
}
|
|
|
|
void gio_hideCursor() {
|
|
// Not supported.
|
|
}
|
|
|
|
void gio_showCursor() {
|
|
// Not supported.
|
|
}
|
|
|
|
void gio_setCursor(NSUInteger curID) {
|
|
// Not supported.
|
|
}
|