diff --git a/area.go b/area.go index 1fbaa28..7ee9070 100644 --- a/area.go +++ b/area.go @@ -85,8 +85,8 @@ type MouseEvent struct { Up uint // If Down is nonzero, Count indicates the number of clicks: 1 for single-click, 2 for double-click. - // If Count == 2, AT LEAST one event with Count == 1 will have been sent prior. - // (This is a platform-specific issue: some platforms send one, some send two.) + // If Count == 2, AT LEAST zero events with Count == 1 will have been sent prior. + // (This is a platform-specific issue: some platforms send none, some send one, and some send two.) Count uint // Modifiers is a bit mask indicating the modifier keys being held during the event. diff --git a/area_darwin.go b/area_darwin.go index 50f43a3..c5c3845 100644 --- a/area_darwin.go +++ b/area_darwin.go @@ -13,7 +13,10 @@ import ( //// #include // #include "objc_darwin.h" // extern void areaView_drawRect(id, struct xrect); -// extern BOOL areaView_isFlipped(id, SEL); +// extern BOOL areaView_isFlipped_acceptsFirstResponder(id, SEL); +// extern void areaView_mouseMoved(id, SEL, id); +// extern void areaView_mouseDown_mouseDragged(id, SEL, id); +// extern void areaView_mouseUp(id, SEL, id); import "C" const ( @@ -25,8 +28,27 @@ var ( _drawRect = sel_getUid("drawRect:") _isFlipped = sel_getUid("isFlipped") + _acceptsFirstResponder = sel_getUid("acceptsFirstResponder") ) +// uintptr due to a bug; see https://code.google.com/p/go/issues/detail?id=7665 +type eventMethod struct { + sel string + m uintptr +} +var eventMethods = []eventMethod{ + eventMethod{"mouseMoved:", uintptr(C.areaView_mouseMoved)}, + eventMethod{"mouseDown:", uintptr(C.areaView_mouseDown_mouseDragged)}, + eventMethod{"mouseDragged:", uintptr(C.areaView_mouseDown_mouseDragged)}, + eventMethod{"mouseUp:", uintptr(C.areaView_mouseUp)}, + eventMethod{"rightMouseDown:", uintptr(C.areaView_mouseDown_mouseDragged)}, + eventMethod{"rightMouseDragged:", uintptr(C.areaView_mouseDown_mouseDragged)}, + eventMethod{"rightMouseUp:", uintptr(C.areaView_mouseUp)}, + eventMethod{"otherMouseDown:", uintptr(C.areaView_mouseDown_mouseDragged)}, + eventMethod{"otherMouseDragged:", uintptr(C.areaView_mouseDown_mouseDragged)}, + eventMethod{"otherMouseUp:", uintptr(C.areaView_mouseUp)}, +} + func mkAreaClass() error { areaclass, err := makeAreaClass(__goArea) if err != nil { @@ -39,10 +61,22 @@ func mkAreaClass() error { } // TODO rename this function (it overrides anyway) err = addDelegateMethod(areaclass, _isFlipped, - C.areaView_isFlipped, area_boolret) + C.areaView_isFlipped_acceptsFirstResponder, area_boolret) if err != nil { return fmt.Errorf("error overriding Area isFlipped method: %v", err) } + err = addDelegateMethod(areaclass, _acceptsFirstResponder, + C.areaView_isFlipped_acceptsFirstResponder, area_boolret) + if err != nil { + return fmt.Errorf("error overriding Area acceptsFirstResponder method: %v", err) + } + for _, m := range eventMethods { + err = addDelegateMethod(areaclass, sel_getUid(m.sel), + unsafe.Pointer(m.m), delegate_void) + if err != nil { + return fmt.Errorf("error overriding Area %s method: %v", m.sel, err) + } + } _goArea = objc_getClass(__goArea) return nil } @@ -69,11 +103,105 @@ func areaView_drawRect(self C.id, rect C.struct_xrect) { C.int64_t(cliprect.Min.X), C.int64_t(cliprect.Min.Y)) } -//export areaView_isFlipped -func areaView_isFlipped(self C.id, sel C.SEL) C.BOOL { +//export areaView_isFlipped_acceptsFirstResponder +func areaView_isFlipped_acceptsFirstResponder(self C.id, sel C.SEL) C.BOOL { + // yes use the same function for both methods since they're just going to return YES anyway + // isFlipped gives us a coordinate system with (0,0) at the top-left + // acceptsFirstResponder lets us respond to events return C.BOOL(C.YES) } +var ( + _modifierFlags = sel_getUid("modifierFlags") + _buttonNumber = sel_getUid("buttonNumber") + _clickCount = sel_getUid("clickCount") +) + +func parseModifiers(e C.id) (m Modifiers) { + const ( + _NSShiftKeyMask = 1 << 17 + _NSControlKeyMask = 1 << 18 + _NSAlternateKeyMask = 1 << 19 + _NSCommandKeyMask = 1 << 20 + ) + + mods := uintptr(C.objc_msgSend_uintret_noargs(e, _modifierFlags)) + if (mods & _NSShiftKeyMask) != 0 { + m |= Shift + } + if (mods & _NSControlKeyMask) != 0 { + // TODO + } + if (mods & _NSAlternateKeyMask) != 0 { + m |= Alt + } + if (mods & _NSCommandKeyMask) != 0 { + m |= Ctrl + } + return m +} + +func areaMouseEvent(self C.id, e C.id, click bool, up bool) { + var me MouseEvent + + s := getSysData(self) + xp := C.getTranslatedEventPoint(self, e) + me.Pos = image.Pt(int(xp.x), int(xp.y)) + me.Modifiers = parseModifiers(e) + which := uint(C.objc_msgSend_intret_noargs(e, _buttonNumber)) + 1 + if which == 3 { // swap middle and right button numbers + which = 2 + } else if which == 2 { + which = 3 + } + if click && up { + me.Up = which + } else if click { + me.Down = which + me.Count = uint(C.objc_msgSend_intret_noargs(e, _clickCount)) + } else { + which = 0 // reset for Held processing below + } + held := C.objc_msgSend_uintret_noargs(e, _clickCount) + if which != 1 && (held & 1) != 0 { // button 1 + me.Held = append(me.Held, 1) + } + if which != 2 && (held & 4) != 0 { // button 2; mind the swap + me.Held = append(me.Held, 2) + } + if which != 3 && (held & 2) != 0 { // button 3 + me.Held = append(me.Held, 3) + } + // TODO remove this part? + held >>= 3 + for i := uint(4); held != 0; i++ { + if which != i && (held & 1) != 0 { + me.Held = append(me.Held, i) + } + held >>= 1 + } + repaint := s.handler.Mouse(me) + if repaint { + C.objc_msgSend_noargs(self, _display) + } +} + +//export areaView_mouseMoved +func areaView_mouseMoved(self C.id, sel C.SEL, e C.id) { + // TODO not triggered? + areaMouseEvent(self, e, false, false) +} + +//export areaView_mouseDown_mouseDragged +func areaView_mouseDown_mouseDragged(self C.id, sel C.SEL, e C.id) { + areaMouseEvent(self, e, true, false) +} + +//export areaView_mouseUp +func areaView_mouseUp(self C.id, sel C.SEL, e C.id) { + areaMouseEvent(self, e, true, true) +} + // TODO combine these with the listbox functions? func newAreaScrollView(area C.id) C.id { diff --git a/bleh_darwin.m b/bleh_darwin.m index e373be8..48f0ae7 100644 --- a/bleh_darwin.m +++ b/bleh_darwin.m @@ -241,7 +241,7 @@ static SEL s_drawInRect; static SEL s_release; static BOOL drawImage_init = NO; -id drawImage(void *pixels, int64_t width, int64_t height, int64_t stride, int64_t xdest, int64_t ydest) +void drawImage(void *pixels, int64_t width, int64_t height, int64_t stride, int64_t xdest, int64_t ydest) { unsigned char *planes[1]; /* NSBitmapImageRep wants an array of planes; we have one plane */ id bitmap; @@ -278,3 +278,33 @@ id drawImage(void *pixels, int64_t width, int64_t height, int64_t stride, int64_ nil); /* hints: */ objc_msgSend(bitmap, s_release); } + +/* +more NSPoint fumbling +*/ + +static SEL s_locationInWindow; +static SEL s_convertPointFromView; +static BOOL getTranslatedEventPoint_init = NO; + +static NSPoint (*objc_msgSend_stret_point)(id, SEL, ...) = + (NSPoint (*)(id, SEL, ...)) objc_msgSend; + +struct xpoint getTranslatedEventPoint(id self, id event) +{ + NSPoint p; + struct xpoint ret; + + if (getTranslatedEventPoint_init == NO) { + s_locationInWindow = sel_getUid("locationInWindow"); + s_convertPointFromView = sel_getUid("convertPoint:fromView:"); + getTranslatedEventPoint_init = YES; + } + p = objc_msgSend_stret_point(event, s_locationInWindow); + p = objc_msgSend_stret_point(self, s_convertPointFromView, + p, /* convertPoint: */ + nil); /* fromView: */ + ret.x = (int64_t) p.x; + ret.y = (int64_t) p.y; + return ret; +} diff --git a/objc_darwin.h b/objc_darwin.h index 8f65f1c..2807d93 100644 --- a/objc_darwin.h +++ b/objc_darwin.h @@ -47,6 +47,11 @@ struct xsize { extern struct xsize objc_msgSend_stret_size_noargs(id obj, SEL sel); +struct xpoint { + int64_t x; + int64_t y; +}; + extern uintptr_t objc_msgSend_uintret_noargs(id objc, SEL sel); extern intptr_t objc_msgSend_intret_noargs(id obj, SEL sel); @@ -107,6 +112,7 @@ extern id makeDummyEvent(); /* for area_darwin.go */ extern BOOL addAreaViewDrawMethod(Class); -extern id drawImage(void *, int64_t, int64_t, int64_t, int64_t, int64_t); +extern void drawImage(void *, int64_t, int64_t, int64_t, int64_t, int64_t); +extern struct xpoint getTranslatedEventPoint(id, id); #endif diff --git a/todo.md b/todo.md index 25d3e55..081e3c1 100644 --- a/todo.md +++ b/todo.md @@ -51,6 +51,11 @@ important things: - make sure mouse events don't trigger if the control size is larger than the Area size and the mouse event happens outside the Area range on all platforms super ultra important things: +- formalize dragging + - implement dragging on windows + - may need to drop Held depending on weirdness I see in OS X +- cap click count to 2 on all platforms + - cap mouse button count to 3? or should a function be used instead? - the windows build appears to be unstable: - 64-bit crashes in malloc in wine with heap corruption warnings aplenty during DLL loading; in windows 7 it works fine - 32-bit: it works, but if I save the class name converted to UTF-16 beforehand, wine indicates that the class name is replaced with the window title, so something there is wrong...