From ef9e4071e72463e6ff639b3d261de007225d55db Mon Sep 17 00:00:00 2001 From: Elias Naur Date: Wed, 9 Feb 2022 17:27:26 +0100 Subject: [PATCH] app: [macOS,iOS] don't use [NSString UTF8String] UTF8String is lossy in the presence of nul (\x00) runes. While here, don't CFRelease the input to nsstringToString; leave it up to the caller. Signed-off-by: Elias Naur --- app/os_darwin.go | 24 +++++++++++++++++++++--- app/os_ios.go | 8 +++++--- app/os_ios.m | 2 +- app/os_macos.go | 44 +++++++++++++++++++------------------------- app/os_macos.m | 12 ++++-------- 5 files changed, 50 insertions(+), 40 deletions(-) diff --git a/app/os_darwin.go b/app/os_darwin.go index c0554db3..1614cad6 100644 --- a/app/os_darwin.go +++ b/app/os_darwin.go @@ -28,6 +28,16 @@ static void nsstringGetCharacters(CFTypeRef cstr, unichar *chars, NSUInteger loc NSString *str = (__bridge NSString *)cstr; [str getCharacters:chars range:NSMakeRange(loc, length)]; } + +static CFTypeRef newNSString(unichar *chars, NSUInteger length) { + @autoreleasepool { + NSString *s = [NSString string]; + if (length > 0) { + s = [NSString stringWithCharacters:chars length:length]; + } + return CFBridgingRetain(s); + } +} */ import "C" import ( @@ -89,13 +99,11 @@ func gio_dispatchMainFuncs() { } } -// nsstringToString converts a NSString to a Go string, and -// releases the original string. +// nsstringToString converts a NSString to a Go string. func nsstringToString(str C.CFTypeRef) string { if str == 0 { return "" } - defer C.CFRelease(str) n := C.nsstringLength(str) if n == 0 { return "" @@ -106,6 +114,16 @@ func nsstringToString(str C.CFTypeRef) string { return string(utf8) } +// stringToNSString converts a Go string to a retained NSString. +func stringToNSString(str string) C.CFTypeRef { + u16 := utf16.Encode([]rune(str)) + var chars *C.unichar + if len(u16) > 0 { + chars = (*C.unichar)(unsafe.Pointer(&u16[0])) + } + return C.newNSString(chars, C.NSUInteger(len(u16))) +} + func NewDisplayLink(callback func()) (*displayLink, error) { d := &displayLink{ callback: callback, diff --git a/app/os_ios.go b/app/os_ios.go index ed79017f..f2583809 100644 --- a/app/os_ios.go +++ b/app/os_ios.go @@ -226,9 +226,9 @@ func onDeleteBackward(view C.CFTypeRef) { } //export onText -func onText(view C.CFTypeRef, str *C.char) { +func onText(view, str C.CFTypeRef) { w := views[view] - w.w.EditorInsert(C.GoString(str)) + w.w.EditorInsert(nsstringToString(str)) } //export onTouch @@ -259,7 +259,9 @@ func onTouch(last C.int, view, touchRef C.CFTypeRef, phase C.NSInteger, x, y C.C } func (w *window) ReadClipboard() { - content := nsstringToString(C.readClipboard()) + cstr := C.readClipboard() + defer C.CFRelease(cstr) + content := nsstringToString(cstr) w.w.Event(clipboard.Event{Text: content}) } diff --git a/app/os_ios.m b/app/os_ios.m index 94f254bc..0bb0b1a1 100644 --- a/app/os_ios.m +++ b/app/os_ios.m @@ -179,7 +179,7 @@ NSArray *_keyCommands; } - (void)insertText:(NSString *)text { - onText((__bridge CFTypeRef)self, (char *)text.UTF8String); + onText((__bridge CFTypeRef)self, (__bridge CFTypeRef)text); } - (BOOL)canBecomeFirstResponder { diff --git a/app/os_macos.go b/app/os_macos.go index e8b6ff23..43958204 100644 --- a/app/os_macos.go +++ b/app/os_macos.go @@ -11,8 +11,6 @@ import ( "runtime" "time" "unicode" - "unicode/utf16" - "unsafe" "gioui.org/f32" "gioui.org/io/clipboard" @@ -36,14 +34,11 @@ import ( __attribute__ ((visibility ("hidden"))) void gio_main(void); __attribute__ ((visibility ("hidden"))) CFTypeRef gio_createView(void); -__attribute__ ((visibility ("hidden"))) CFTypeRef gio_createWindow(CFTypeRef viewRef, const char *title, CGFloat width, CGFloat height, CGFloat minWidth, CGFloat minHeight, CGFloat maxWidth, CGFloat maxHeight); +__attribute__ ((visibility ("hidden"))) CFTypeRef gio_createWindow(CFTypeRef viewRef, CGFloat width, CGFloat height, CGFloat minWidth, CGFloat minHeight, CGFloat maxWidth, CGFloat maxHeight); -static void writeClipboard(unichar *chars, NSUInteger length) { +static void writeClipboard(CFTypeRef str) { @autoreleasepool { - NSString *s = [NSString string]; - if (length > 0) { - s = [NSString stringWithCharacters:chars length:length]; - } + NSString *s = (__bridge NSString *)str; NSPasteboard *p = NSPasteboard.generalPasteboard; [p declareTypes:@[NSPasteboardTypeString] owner:nil]; [p setString:s forType:NSPasteboardTypeString]; @@ -144,9 +139,9 @@ static NSRect getScreenFrame(CFTypeRef windowRef) { return [[window screen] frame]; } -static void setTitle(CFTypeRef windowRef, const char *title) { - NSWindow* window = (__bridge NSWindow *)windowRef; - window.title = [NSString stringWithUTF8String: title]; +static void setTitle(CFTypeRef windowRef, CFTypeRef titleRef) { + NSWindow *window = (__bridge NSWindow *)windowRef; + window.title = (__bridge NSString *)titleRef; } static CFTypeRef layerForView(CFTypeRef viewRef) { @@ -229,17 +224,16 @@ func (w *window) contextView() C.CFTypeRef { } func (w *window) ReadClipboard() { - content := nsstringToString(C.readClipboard()) + cstr := C.readClipboard() + defer C.CFRelease(cstr) + content := nsstringToString(cstr) w.w.Event(clipboard.Event{Text: content}) } func (w *window) WriteClipboard(s string) { - u16 := utf16.Encode([]rune(s)) - var chars *C.unichar - if len(u16) > 0 { - chars = (*C.unichar)(unsafe.Pointer(&u16[0])) - } - C.writeClipboard(chars, C.NSUInteger(len(u16))) + cstr := stringToNSString(s) + defer C.CFRelease(cstr) + C.writeClipboard(cstr) } func (w *window) updateWindowMode() { @@ -338,8 +332,8 @@ func (w *window) Configure(options []Option) { func (w *window) setTitle(prev, cnf Config) { if prev.Title != cnf.Title { w.config.Title = cnf.Title - title := C.CString(cnf.Title) - defer C.free(unsafe.Pointer(title)) + title := stringToNSString(cnf.Title) + defer C.CFRelease(title) C.setTitle(w.window, title) } } @@ -391,8 +385,8 @@ func (w *window) setStage(stage system.Stage) { } //export gio_onKeys -func gio_onKeys(view C.CFTypeRef, cstr *C.char, ti C.double, mods C.NSUInteger, keyDown C.bool) { - str := C.GoString(cstr) +func gio_onKeys(view, cstr C.CFTypeRef, ti C.double, mods C.NSUInteger, keyDown C.bool) { + str := nsstringToString(cstr) kmods := convertMods(mods) ks := key.Release if keyDown { @@ -411,8 +405,8 @@ func gio_onKeys(view C.CFTypeRef, cstr *C.char, ti C.double, mods C.NSUInteger, } //export gio_onText -func gio_onText(view C.CFTypeRef, cstr *C.char) { - str := C.GoString(cstr) +func gio_onText(view, cstr C.CFTypeRef) { + str := nsstringToString(cstr) w := mustView(view) w.w.EditorInsert(str) } @@ -579,7 +573,7 @@ func newWindow(win *callbacks, options []Option) error { } errch <- nil w.w = win - w.window = C.gio_createWindow(w.view, nil, 0, 0, 0, 0, 0, 0) + w.window = C.gio_createWindow(w.view, 0, 0, 0, 0, 0, 0) win.SetDriver(w) w.Configure(options) if nextTopLeft.x == 0 && nextTopLeft.y == 0 { diff --git a/app/os_macos.m b/app/os_macos.m index f65099b7..6b92abce 100644 --- a/app/os_macos.m +++ b/app/os_macos.m @@ -117,16 +117,15 @@ static void handleMouse(NSView *view, NSEvent *event, int typ, CGFloat dx, CGFlo } - (void)keyDown:(NSEvent *)event { NSString *keys = [event charactersIgnoringModifiers]; - gio_onKeys((__bridge CFTypeRef)self, (char *)[keys UTF8String], [event timestamp], [event modifierFlags], true); + gio_onKeys((__bridge CFTypeRef)self, (__bridge CFTypeRef)keys, [event timestamp], [event modifierFlags], true); [self interpretKeyEvents:[NSArray arrayWithObject:event]]; } - (void)keyUp:(NSEvent *)event { NSString *keys = [event charactersIgnoringModifiers]; - gio_onKeys((__bridge CFTypeRef)self, (char *)[keys UTF8String], [event timestamp], [event modifierFlags], false); + gio_onKeys((__bridge CFTypeRef)self, (__bridge CFTypeRef)keys, [event timestamp], [event modifierFlags], false); } - (void)insertText:(id)string { - const char *utf8 = [string UTF8String]; - gio_onText((__bridge CFTypeRef)self, (char *)utf8); + gio_onText((__bridge CFTypeRef)self, (__bridge CFTypeRef)string); } - (void)doCommandBySelector:(SEL)sel { // Don't pass commands up the responder chain. @@ -210,7 +209,7 @@ void gio_setCursor(NSUInteger curID) { } } -CFTypeRef gio_createWindow(CFTypeRef viewRef, const char *title, CGFloat width, CGFloat height, CGFloat minWidth, CGFloat minHeight, CGFloat maxWidth, CGFloat maxHeight) { +CFTypeRef gio_createWindow(CFTypeRef viewRef, CGFloat width, CGFloat height, CGFloat minWidth, CGFloat minHeight, CGFloat maxWidth, CGFloat maxHeight) { @autoreleasepool { NSRect rect = NSMakeRect(0, 0, width, height); NSUInteger styleMask = NSTitledWindowMask | @@ -229,9 +228,6 @@ CFTypeRef gio_createWindow(CFTypeRef viewRef, const char *title, CGFloat width, window.contentMaxSize = NSMakeSize(maxWidth, maxHeight); } [window setAcceptsMouseMovedEvents:YES]; - if (title != nil) { - window.title = [NSString stringWithUTF8String: title]; - } NSView *view = (__bridge NSView *)viewRef; [window setContentView:view]; [window makeFirstResponder:view];