Implemented Area mouse events on Mac OS X. Also fixed a few other things left behind in the previous commits. Also more TODOs.

This commit is contained in:
Pietro Gagliardi 2014-03-30 17:52:27 -04:00
parent 8c18adcfdb
commit 41a7e3dab8
5 changed files with 177 additions and 8 deletions

View File

@ -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.

View File

@ -13,7 +13,10 @@ import (
//// #include <HIToolbox/Events.h>
// #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 {

View File

@ -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;
}

View File

@ -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

View File

@ -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...