diff options
| -rw-r--r-- | redo/events_darwin.go | 132 | ||||
| -rw-r--r-- | redo/events_notdarwin.go | 154 |
2 files changed, 286 insertions, 0 deletions
diff --git a/redo/events_darwin.go b/redo/events_darwin.go new file mode 100644 index 0000000..d9cbe0c --- /dev/null +++ b/redo/events_darwin.go @@ -0,0 +1,132 @@ +// 30 march 2014 + +package ui + +/* +Mac OS X uses its own set of hardware key codes that are different from PC keyboard scancodes, but are positional (like PC keyboard scancodes). These are defined in <HIToolbox/Events.h>, a Carbon header. As far as I can tell, there's no way to include this header without either using an absolute path or linking Carbon into the program, so the constant values are used here instead. + +The Cocoa docs do guarantee that -[NSEvent keyCode] results in key codes that are the same as those returned by Carbon; that is, these codes. +*/ + +// use uintptr to be safe +var keycodeKeys = map[uintptr]byte{ + 0x00: 'a', + 0x01: 's', + 0x02: 'd', + 0x03: 'f', + 0x04: 'h', + 0x05: 'g', + 0x06: 'z', + 0x07: 'x', + 0x08: 'c', + 0x09: 'v', + 0x0B: 'b', + 0x0C: 'q', + 0x0D: 'w', + 0x0E: 'e', + 0x0F: 'r', + 0x10: 'y', + 0x11: 't', + 0x12: '1', + 0x13: '2', + 0x14: '3', + 0x15: '4', + 0x16: '6', + 0x17: '5', + 0x18: '=', + 0x19: '9', + 0x1A: '7', + 0x1B: '-', + 0x1C: '8', + 0x1D: '0', + 0x1E: ']', + 0x1F: 'o', + 0x20: 'u', + 0x21: '[', + 0x22: 'i', + 0x23: 'p', + 0x25: 'l', + 0x26: 'j', + 0x27: '\'', + 0x28: 'k', + 0x29: ';', + 0x2A: '\\', + 0x2B: ',', + 0x2C: '/', + 0x2D: 'n', + 0x2E: 'm', + 0x2F: '.', + 0x32: '`', + 0x24: '\n', + 0x30: '\t', + 0x31: ' ', + 0x33: '\b', +} + +var keycodeExtKeys = map[uintptr]ExtKey{ + 0x41: NDot, + 0x43: NMultiply, + 0x45: NAdd, + 0x4B: NDivide, + 0x4C: NEnter, + 0x4E: NSubtract, + 0x52: N0, + 0x53: N1, + 0x54: N2, + 0x55: N3, + 0x56: N4, + 0x57: N5, + 0x58: N6, + 0x59: N7, + 0x5B: N8, + 0x5C: N9, + 0x35: Escape, + 0x60: F5, + 0x61: F6, + 0x62: F7, + 0x63: F3, + 0x64: F8, + 0x65: F9, + 0x67: F11, + 0x6D: F10, + 0x6F: F12, + 0x72: Insert, // listed as the Help key but it's in the same position on an Apple keyboard as the Insert key on a Windows keyboard; thanks to SeanieB from irc.badnik.net and Psy in irc.freenode.net/#macdev for confirming they have the same code + 0x73: Home, + 0x74: PageUp, + 0x75: Delete, + 0x76: F4, + 0x77: End, + 0x78: F2, + 0x79: PageDown, + 0x7A: F1, + 0x7B: Left, + 0x7C: Right, + 0x7D: Down, + 0x7E: Up, +} + +var keycodeModifiers = map[uintptr]Modifiers{ + 0x37: Super, // left command + 0x38: Shift, // left shift + 0x3A: Alt, // left option + 0x3B: Ctrl, // left control + 0x3C: Shift, // right shift + 0x3D: Alt, // right alt + 0x3E: Ctrl, // right control + + // the following is not in Events.h for some reason + // thanks to Nicole and jedivulcan from irc.badnik.net + 0x36: Super, // right command +} + +func fromKeycode(keycode uintptr) (ke KeyEvent, ok bool) { + if key, ok := keycodeKeys[keycode]; ok { + ke.Key = key + return ke, true + } + if extkey, ok := keycodeExtKeys[keycode]; ok { + ke.ExtKey = extkey + return ke, true + } + return ke, false +} diff --git a/redo/events_notdarwin.go b/redo/events_notdarwin.go new file mode 100644 index 0000000..be9515a --- /dev/null +++ b/redo/events_notdarwin.go @@ -0,0 +1,154 @@ +// +build !darwin +// Mac OS X uses its own set of position-independent key codes + +// 29 march 2014 + +package ui + +import ( + "image" +) + +/* +Windows and GTK+ have a limit of 2 and 3 clicks, respectively, natively supported. Fortunately, we can simulate the double/triple-click behavior to build higher-order clicks. We can use the same algorithm Windows uses on both: + http://blogs.msdn.com/b/oldnewthing/archive/2004/10/18/243925.aspx +For GTK+, we pull the double-click time and double-click distance, which work the same as the equivalents on Windows (so the distance is in all directions), from the GtkSettings system. + +On GTK+ this will also allow us to discard the GDK_BUTTON_2PRESS and GDK_BUTTON_3PRESS events, so the button press stream will be just like on other platforms. + +Thanks to mclasen, garnacho_, halfline, and tristan in irc.gimp.net/#gtk+. + +TODO - technically a GDK_BUTTON_3PRESS is detected in half the time as a GDK_BUTTON_2PRESS... handle? +*/ + +// the zero value is a reset clickCounter ready for use +// it doesn't matter that all the non-count fields are zero: the first click will fail the curButton test straightaway, so it'll return 1 and set the rest of the structure accordingly +type clickCounter struct { + curButton uint + rect image.Rectangle + prevTime uintptr + count uint +} + +// x, y, xdist, ydist, and c.rect must have the same units +// so must time, maxTime, and c.prevTime +func (c *clickCounter) click(button uint, x int, y int, time uintptr, maxTime uintptr, xdist int, ydist int) uint { + if button != c.curButton { // different button; start over + c.count = 0 + } + if !image.Pt(x, y).In(c.rect) { // not in the allowed region for a double-click; don't count + c.count = 0 + } + if (time - c.prevTime) > maxTime { // too slow; don't count + // note the above expression; time > (c.prevTime + maxTime) can overflow! + c.count = 0 + } + c.count++ // if either of the above ifs happened, this will make the click count 1; otherwise it will make the click count 2, 3, 4, 5, ... + + // now we need to update the internal structures for the next test + c.curButton = button + c.prevTime = time + c.rect = image.Rect(x-xdist, y-ydist, + x+xdist, y+ydist) + + return c.count +} + +// call this when losing focus, etc. +func (c *clickCounter) reset() { + c.count = 0 +} + +/* +For position independence across international keyboard layouts, typewriter keys are read using scancodes (which are always set 1). +Windows provides the scancodes directly in the LPARAM. +GTK+ provides the scancodes directly from the underlying window system via GdkEventKey.hardware_keycode. +On X11, this is scancode + 8 (because X11 keyboard codes have a range of [8,255]). +Wayland is guaranteed to give the same result (thanks ebassi in irc.gimp.net/#gtk+). +On Linux, where evdev is used instead of polling scancodes directly from the keyboard, evdev's typewriter section key code constants are the same as scancodes anyway, so the rules above apply. +Typewriter section scancodes are the same across international keyboards with some exceptions that have been accounted for (see KeyEvent's documentation); see http://www.quadibloc.com/comp/scan.htm for details. +Non-typewriter keys can be handled safely using constants provided by the respective backend API. + +Because GTK+ keysyms may or may not obey Num Lock, we also handle the 0-9 and . keys on the numeric keypad with scancodes (they match too). +*/ + +// use uintptr to be safe; the size of the scancode/hardware key code field on each platform is different +var scancodeKeys = map[uintptr]byte{ + 0x02: '1', + 0x03: '2', + 0x04: '3', + 0x05: '4', + 0x06: '5', + 0x07: '6', + 0x08: '7', + 0x09: '8', + 0x0A: '9', + 0x0B: '0', + 0x0C: '-', + 0x0D: '=', + 0x0E: '\b', // seems to be safe on GTK+; TODO safe on windows? + 0x0F: '\t', // seems to be safe on GTK+; TODO safe on windows? + 0x10: 'q', + 0x11: 'w', + 0x12: 'e', + 0x13: 'r', + 0x14: 't', + 0x15: 'y', + 0x16: 'u', + 0x17: 'i', + 0x18: 'o', + 0x19: 'p', + 0x1A: '[', + 0x1B: ']', + 0x1C: '\n', // seems to be safe on GTK+; TODO safe on windows? + 0x1E: 'a', + 0x1F: 's', + 0x20: 'd', + 0x21: 'f', + 0x22: 'g', + 0x23: 'h', + 0x24: 'j', + 0x25: 'k', + 0x26: 'l', + 0x27: ';', + 0x28: '\'', + 0x29: '`', + 0x2B: '\\', + 0x2C: 'z', + 0x2D: 'x', + 0x2E: 'c', + 0x2F: 'v', + 0x30: 'b', + 0x31: 'n', + 0x32: 'm', + 0x33: ',', + 0x34: '.', + 0x35: '/', + 0x39: ' ', +} + +var scancodeExtKeys = map[uintptr]ExtKey{ + 0x47: N7, + 0x48: N8, + 0x49: N9, + 0x4B: N4, + 0x4C: N5, + 0x4D: N6, + 0x4F: N1, + 0x50: N2, + 0x51: N3, + 0x52: N0, + 0x53: NDot, +} + +func fromScancode(scancode uintptr) (ke KeyEvent, ok bool) { + if key, ok := scancodeKeys[scancode]; ok { + ke.Key = key + return ke, true + } + if extkey, ok := scancodeExtKeys[scancode]; ok { + ke.ExtKey = extkey + return ke, true + } + return ke, false +} |
