From 496ad98216196e6386e5ce81585205345803d54f Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Mon, 12 May 2014 02:06:05 -0400 Subject: [PATCH] Replaced the horribly memory-inefficient NSIndexSet operation to get the list of selected indices in the Mac OS X Listbox with a far simpler option that avoids deferring some of the work (apart from objc_msgSend() calls) to cgo and the use of reflect.SliceHeader at all! It just grabs the indices from the NSIndexSet one at a time using the previous index as an anchor. --- bleh_darwin.m | 31 +++++-------------------------- listbox_darwin.go | 22 +++++++++------------- objc_darwin.h | 3 ++- 3 files changed, 16 insertions(+), 40 deletions(-) diff --git a/bleh_darwin.m b/bleh_darwin.m index 201750f..330bc20 100644 --- a/bleh_darwin.m +++ b/bleh_darwin.m @@ -30,7 +30,6 @@ id *_NSObservedObjectKey = (id *) (&NSObservedObjectKey); These are all the selectors and class IDs used by the functions below. */ -static SEL s_getIndexes; /* NSIndexSetEntries() */ static id c_NSEvent; /* makeDummyEvent() */ static SEL s_newEvent; static id c_NSBitmapImageRep; /* drawImage() */ @@ -50,7 +49,6 @@ static SEL s_initTrackingArea; void initBleh() { - s_getIndexes = sel_getUid("getIndexes:maxCount:inIndexRange:"); c_NSEvent = objc_getClass("NSEvent"); s_newEvent = sel_getUid("otherEventWithType:location:modifierFlags:timestamp:windowNumber:context:subtype:data1:data2:"); c_NSBitmapImageRep = objc_getClass("NSBitmapImageRep"); @@ -78,6 +76,11 @@ uintptr_t objc_msgSend_uintret_noargs(id obj, SEL sel) return (uintptr_t) ((NSUInteger) objc_msgSend(obj, sel)); } +uintptr_t objc_msgSend_uintret_uint(id obj, SEL sel, uintptr_t a) +{ + return (uintptr_t) ((NSUInteger) objc_msgSend(obj, sel, (NSUInteger) a)); +} + id objc_msgSend_uint(id obj, SEL sel, uintptr_t a) { return objc_msgSend(obj, sel, (NSUInteger) a); @@ -193,30 +196,6 @@ id objc_msgSend_point(id obj, SEL sel, int64_t x, int64_t y) return objc_msgSend(obj, sel, NSMakePoint((CGFloat) x, (CGFloat) y)); } -/* -This is a doozy: it deals with a NSUInteger array needed for this one selector, and converts them all into a uintptr_t array so we can use it from Go. The two arrays are created at runtime with malloc(); only the NSUInteger one is freed here, while Go frees the returned one. It's not optimal. -*/ - -uintptr_t *NSIndexSetEntries(id indexset, uintptr_t count) -{ - NSUInteger *nsuints; - uintptr_t *ret; - uintptr_t i; - size_t countsize; - - countsize = (size_t) count; - nsuints = (NSUInteger *) malloc(countsize * sizeof (NSUInteger)); - /* TODO check return value */ - objc_msgSend(indexset, s_getIndexes, - nsuints, (NSUInteger) count, nil); - ret = (uintptr_t *) malloc(countsize * sizeof (uintptr_t)); - for (i = 0; i < count; i++) { - ret[i] = (uintptr_t) nsuints[i]; - } - free(nsuints); - return ret; -} - /* See uitask_darwin.go: we need to synthesize a NSEvent so -[NSApplication stop:] will work. We cannot simply init the default NSEvent though (it throws an exception) so we must do it "the right way". This involves a very convoluted initializer; we'll just do it here to keep things clean on the Go side (this will only be run once anyway, on program exit). */ diff --git a/listbox_darwin.go b/listbox_darwin.go index 1001a46..9e2323d 100644 --- a/listbox_darwin.go +++ b/listbox_darwin.go @@ -3,8 +3,7 @@ package ui import ( - "reflect" - "unsafe" + // ... ) /* @@ -230,6 +229,8 @@ var ( _setHeaderView = sel_getUid("setHeaderView:") _selectedRowIndexes = sel_getUid("selectedRowIndexes") _count = sel_getUid("count") + _firstIndex = sel_getUid("firstIndex") + _indexGreaterThanIndex = sel_getUid("indexGreaterThanIndex:") _numberOfRows = sel_getUid("numberOfRows") _deselectAll = sel_getUid("deselectAll:") ) @@ -261,24 +262,19 @@ func insertListboxBefore(listbox C.id, what string, before int, alternate bool) insertListboxArrayBefore(array, what, before) } -// TODO change to use [indices firstIndex] and [indices indexGreaterThanIndex:], as suggested http://stackoverflow.com/questions/3773180/how-to-get-indexes-from-nsindexset-into-an-nsarray-in-cocoa +// technique from http://stackoverflow.com/questions/3773180/how-to-get-indexes-from-nsindexset-into-an-nsarray-in-cocoa +// we don't care that the indices were originally NSUInteger since by this point we have a problem anyway; Go programs generally use int indices anyway +// we also don't care about NSNotFound because we check the count first AND because NSIndexSet is always sorted (and NSNotFound can be a valid index if the list is large enough... since it's NSIntegerMax, not NSUIntegerMax) func selectedListboxIndices(listbox C.id) (list []int) { - var cindices []C.uintptr_t - indices := C.objc_msgSend_noargs(listboxInScrollView(listbox), _selectedRowIndexes) count := int(C.objc_msgSend_uintret_noargs(indices, _count)) if count == 0 { return nil } list = make([]int, count) - cidx := C.NSIndexSetEntries(indices, C.uintptr_t(count)) - defer C.free(unsafe.Pointer(cidx)) - pcindices := (*reflect.SliceHeader)(unsafe.Pointer(&cindices)) - pcindices.Cap = count - pcindices.Len = count - pcindices.Data = uintptr(unsafe.Pointer(cidx)) - for i := 0; i < count; i++ { - list[i] = int(cindices[i]) + list[0] = int(C.objc_msgSend_uintret_noargs(indices, _firstIndex)) + for i := 1; i < count; i++ { + list[i] = int(C.objc_msgSend_uintret_uint(indices, _indexGreaterThanIndex, C.uintptr_t(list[i - 1]))) } return list } diff --git a/objc_darwin.h b/objc_darwin.h index c7a7157..d86131c 100644 --- a/objc_darwin.h +++ b/objc_darwin.h @@ -49,7 +49,8 @@ struct xpoint { int64_t y; }; -extern uintptr_t objc_msgSend_uintret_noargs(id objc, SEL sel); +extern uintptr_t objc_msgSend_uintret_noargs(id obj, SEL sel); +extern uintptr_t objc_msgSend_uintret_uint(id obj, SEL sel, uintptr_t a); extern intptr_t objc_msgSend_intret_noargs(id obj, SEL sel);