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 <mail@eliasnaur.com>
This commit is contained in:
Elias Naur
2022-02-09 17:27:26 +01:00
parent b28307baeb
commit ef9e4071e7
5 changed files with 50 additions and 40 deletions
+21 -3
View File
@@ -28,6 +28,16 @@ static void nsstringGetCharacters(CFTypeRef cstr, unichar *chars, NSUInteger loc
NSString *str = (__bridge NSString *)cstr; NSString *str = (__bridge NSString *)cstr;
[str getCharacters:chars range:NSMakeRange(loc, length)]; [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 "C"
import ( import (
@@ -89,13 +99,11 @@ func gio_dispatchMainFuncs() {
} }
} }
// nsstringToString converts a NSString to a Go string, and // nsstringToString converts a NSString to a Go string.
// releases the original string.
func nsstringToString(str C.CFTypeRef) string { func nsstringToString(str C.CFTypeRef) string {
if str == 0 { if str == 0 {
return "" return ""
} }
defer C.CFRelease(str)
n := C.nsstringLength(str) n := C.nsstringLength(str)
if n == 0 { if n == 0 {
return "" return ""
@@ -106,6 +114,16 @@ func nsstringToString(str C.CFTypeRef) string {
return string(utf8) 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) { func NewDisplayLink(callback func()) (*displayLink, error) {
d := &displayLink{ d := &displayLink{
callback: callback, callback: callback,
+5 -3
View File
@@ -226,9 +226,9 @@ func onDeleteBackward(view C.CFTypeRef) {
} }
//export onText //export onText
func onText(view C.CFTypeRef, str *C.char) { func onText(view, str C.CFTypeRef) {
w := views[view] w := views[view]
w.w.EditorInsert(C.GoString(str)) w.w.EditorInsert(nsstringToString(str))
} }
//export onTouch //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() { 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}) w.w.Event(clipboard.Event{Text: content})
} }
+1 -1
View File
@@ -179,7 +179,7 @@ NSArray<UIKeyCommand *> *_keyCommands;
} }
- (void)insertText:(NSString *)text { - (void)insertText:(NSString *)text {
onText((__bridge CFTypeRef)self, (char *)text.UTF8String); onText((__bridge CFTypeRef)self, (__bridge CFTypeRef)text);
} }
- (BOOL)canBecomeFirstResponder { - (BOOL)canBecomeFirstResponder {
+19 -25
View File
@@ -11,8 +11,6 @@ import (
"runtime" "runtime"
"time" "time"
"unicode" "unicode"
"unicode/utf16"
"unsafe"
"gioui.org/f32" "gioui.org/f32"
"gioui.org/io/clipboard" "gioui.org/io/clipboard"
@@ -36,14 +34,11 @@ import (
__attribute__ ((visibility ("hidden"))) void gio_main(void); __attribute__ ((visibility ("hidden"))) void gio_main(void);
__attribute__ ((visibility ("hidden"))) CFTypeRef gio_createView(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 { @autoreleasepool {
NSString *s = [NSString string]; NSString *s = (__bridge NSString *)str;
if (length > 0) {
s = [NSString stringWithCharacters:chars length:length];
}
NSPasteboard *p = NSPasteboard.generalPasteboard; NSPasteboard *p = NSPasteboard.generalPasteboard;
[p declareTypes:@[NSPasteboardTypeString] owner:nil]; [p declareTypes:@[NSPasteboardTypeString] owner:nil];
[p setString:s forType:NSPasteboardTypeString]; [p setString:s forType:NSPasteboardTypeString];
@@ -144,9 +139,9 @@ static NSRect getScreenFrame(CFTypeRef windowRef) {
return [[window screen] frame]; return [[window screen] frame];
} }
static void setTitle(CFTypeRef windowRef, const char *title) { static void setTitle(CFTypeRef windowRef, CFTypeRef titleRef) {
NSWindow* window = (__bridge NSWindow *)windowRef; NSWindow *window = (__bridge NSWindow *)windowRef;
window.title = [NSString stringWithUTF8String: title]; window.title = (__bridge NSString *)titleRef;
} }
static CFTypeRef layerForView(CFTypeRef viewRef) { static CFTypeRef layerForView(CFTypeRef viewRef) {
@@ -229,17 +224,16 @@ func (w *window) contextView() C.CFTypeRef {
} }
func (w *window) ReadClipboard() { 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}) w.w.Event(clipboard.Event{Text: content})
} }
func (w *window) WriteClipboard(s string) { func (w *window) WriteClipboard(s string) {
u16 := utf16.Encode([]rune(s)) cstr := stringToNSString(s)
var chars *C.unichar defer C.CFRelease(cstr)
if len(u16) > 0 { C.writeClipboard(cstr)
chars = (*C.unichar)(unsafe.Pointer(&u16[0]))
}
C.writeClipboard(chars, C.NSUInteger(len(u16)))
} }
func (w *window) updateWindowMode() { func (w *window) updateWindowMode() {
@@ -338,8 +332,8 @@ func (w *window) Configure(options []Option) {
func (w *window) setTitle(prev, cnf Config) { func (w *window) setTitle(prev, cnf Config) {
if prev.Title != cnf.Title { if prev.Title != cnf.Title {
w.config.Title = cnf.Title w.config.Title = cnf.Title
title := C.CString(cnf.Title) title := stringToNSString(cnf.Title)
defer C.free(unsafe.Pointer(title)) defer C.CFRelease(title)
C.setTitle(w.window, title) C.setTitle(w.window, title)
} }
} }
@@ -391,8 +385,8 @@ func (w *window) setStage(stage system.Stage) {
} }
//export gio_onKeys //export gio_onKeys
func gio_onKeys(view C.CFTypeRef, cstr *C.char, ti C.double, mods C.NSUInteger, keyDown C.bool) { func gio_onKeys(view, cstr C.CFTypeRef, ti C.double, mods C.NSUInteger, keyDown C.bool) {
str := C.GoString(cstr) str := nsstringToString(cstr)
kmods := convertMods(mods) kmods := convertMods(mods)
ks := key.Release ks := key.Release
if keyDown { if keyDown {
@@ -411,8 +405,8 @@ func gio_onKeys(view C.CFTypeRef, cstr *C.char, ti C.double, mods C.NSUInteger,
} }
//export gio_onText //export gio_onText
func gio_onText(view C.CFTypeRef, cstr *C.char) { func gio_onText(view, cstr C.CFTypeRef) {
str := C.GoString(cstr) str := nsstringToString(cstr)
w := mustView(view) w := mustView(view)
w.w.EditorInsert(str) w.w.EditorInsert(str)
} }
@@ -579,7 +573,7 @@ func newWindow(win *callbacks, options []Option) error {
} }
errch <- nil errch <- nil
w.w = win 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) win.SetDriver(w)
w.Configure(options) w.Configure(options)
if nextTopLeft.x == 0 && nextTopLeft.y == 0 { if nextTopLeft.x == 0 && nextTopLeft.y == 0 {
+4 -8
View File
@@ -117,16 +117,15 @@ static void handleMouse(NSView *view, NSEvent *event, int typ, CGFloat dx, CGFlo
} }
- (void)keyDown:(NSEvent *)event { - (void)keyDown:(NSEvent *)event {
NSString *keys = [event charactersIgnoringModifiers]; 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]]; [self interpretKeyEvents:[NSArray arrayWithObject:event]];
} }
- (void)keyUp:(NSEvent *)event { - (void)keyUp:(NSEvent *)event {
NSString *keys = [event charactersIgnoringModifiers]; 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 { - (void)insertText:(id)string {
const char *utf8 = [string UTF8String]; gio_onText((__bridge CFTypeRef)self, (__bridge CFTypeRef)string);
gio_onText((__bridge CFTypeRef)self, (char *)utf8);
} }
- (void)doCommandBySelector:(SEL)sel { - (void)doCommandBySelector:(SEL)sel {
// Don't pass commands up the responder chain. // 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 { @autoreleasepool {
NSRect rect = NSMakeRect(0, 0, width, height); NSRect rect = NSMakeRect(0, 0, width, height);
NSUInteger styleMask = NSTitledWindowMask | NSUInteger styleMask = NSTitledWindowMask |
@@ -229,9 +228,6 @@ CFTypeRef gio_createWindow(CFTypeRef viewRef, const char *title, CGFloat width,
window.contentMaxSize = NSMakeSize(maxWidth, maxHeight); window.contentMaxSize = NSMakeSize(maxWidth, maxHeight);
} }
[window setAcceptsMouseMovedEvents:YES]; [window setAcceptsMouseMovedEvents:YES];
if (title != nil) {
window.title = [NSString stringWithUTF8String: title];
}
NSView *view = (__bridge NSView *)viewRef; NSView *view = (__bridge NSView *)viewRef;
[window setContentView:view]; [window setContentView:view];
[window makeFirstResponder:view]; [window makeFirstResponder:view];