app: [macOS] remove support for key bindings

The fix for #616 went to far by attempting to support macOS key bindings
through doCommandBySelector. Issue #625 is a consequence, but more
fundamentally, key bindings does not work with support for key.Release
events.

Remove key binding translation and fix #626, and add a check whether an
IME swallowed a key event, keeping #616 fixed.

While here, replace the KEY_ constants with ASCII codes.

Fixes: https://todo.sr.ht/~eliasnaur/gio/625
References: https://todo.sr.ht/~eliasnaur/gio/616
Signed-off-by: Elias Naur <mail@eliasnaur.com>
This commit is contained in:
Elias Naur
2025-01-13 20:41:53 +01:00
parent d2db4f6875
commit 8107ec2206
2 changed files with 53 additions and 102 deletions
+49 -49
View File
@@ -39,12 +39,6 @@ import (
#define MOUSE_DOWN 3
#define MOUSE_SCROLL 4
#define KEY_DELETE_BACKWARD 0x7f
#define KEY_RETURN 0xd
#define KEY_ENTER 0x3
#define KEY_ESCAPE 0x1b
#define KEY_TAB 0x09
__attribute__ ((visibility ("hidden"))) void gio_main(void);
__attribute__ ((visibility ("hidden"))) CFTypeRef gio_createView(int presentWithTrans);
__attribute__ ((visibility ("hidden"))) CFTypeRef gio_createWindow(CFTypeRef viewRef, CGFloat width, CGFloat height, CGFloat minWidth, CGFloat minHeight, CGFloat maxWidth, CGFloat maxHeight);
@@ -318,6 +312,14 @@ static void invalidateCharacterCoordinates(CFTypeRef viewRef) {
}
}
}
static void interpretKeyEvents(CFTypeRef viewRef, CFTypeRef eventRef) {
@autoreleasepool {
NSView *view = (__bridge NSView *)viewRef;
NSEvent *event = (__bridge NSEvent *)eventRef;
[view interpretKeyEvents:[NSArray arrayWithObject:event]];
}
}
*/
import "C"
@@ -351,7 +353,10 @@ type window struct {
scale float32
config Config
cmdKeysDown map[key.Name]struct{}
keysDown map[key.Name]struct{}
// doCommandBySelectorCalled tracks whether a call to
// interpretKeys resulted in a callback.
doCommandBySelectorCalled bool
}
// launched is closed when applicationDidFinishLaunching is called.
@@ -551,25 +556,36 @@ func (w *window) runOnMain(f func()) {
}
//export gio_onKeys
func gio_onKeys(h C.uintptr_t, cstr C.CFTypeRef, ti C.double, mods C.NSUInteger, keyDown C.bool) {
func gio_onKeys(h C.uintptr_t, event C.CFTypeRef, cstr C.CFTypeRef, ti C.double, mods C.NSUInteger, keyDown C.bool) {
w := windowFor(h)
str := nsstringToString(cstr)
kmods := convertMods(mods)
ks := key.Release
if keyDown {
ks = key.Press
w.doCommandBySelectorCalled = false
C.interpretKeyEvents(w.view, event)
}
w := windowFor(h)
for _, k := range str {
n, ok := convertKey(k)
if !ok && ks == key.Release {
// Key release events are not reported by onCommandBySelector.
n, ok = convertCommandKey(k)
if ok {
_, ok = w.cmdKeysDown[n]
delete(w.cmdKeysDown, n)
if n, ok := convertKey(k); ok {
if keyDown {
if !w.doCommandBySelectorCalled {
if _, isCmd := convertCommandKey(k); isCmd {
// doCommandBySelector was not called,
// so this event was swallowed by an IME.
return
}
}
if w.keysDown == nil {
w.keysDown = make(map[key.Name]struct{})
}
w.keysDown[n] = struct{}{}
} else {
if _, pressed := w.keysDown[n]; !pressed {
continue
}
delete(w.keysDown, n)
}
}
if ok {
w.ProcessEvent(key.Event{
Name: n,
Modifiers: kmods,
@@ -580,26 +596,9 @@ func gio_onKeys(h C.uintptr_t, cstr C.CFTypeRef, ti C.double, mods C.NSUInteger,
}
//export gio_onCommandBySelector
func gio_onCommandBySelector(h C.uintptr_t, k C.int, ti C.double, mods C.NSUInteger) {
func gio_onCommandBySelector(h C.uintptr_t) {
w := windowFor(h)
w.commandKey(rune(k), ti, mods)
}
func (w *window) commandKey(k rune, ti C.double, mods C.NSUInteger) bool {
n, ok := convertCommandKey(k)
if !ok {
return false
}
if w.cmdKeysDown == nil {
w.cmdKeysDown = make(map[key.Name]struct{})
}
w.cmdKeysDown[n] = struct{}{}
w.ProcessEvent(key.Event{
Modifiers: convertMods(mods),
Name: n,
State: key.Press,
})
return true
w.doCommandBySelectorCalled = true
}
//export gio_onFlagsChanged
@@ -816,18 +815,16 @@ func gio_substringForProposedRange(h C.uintptr_t, crng C.NSRange, actual C.NSRan
}
//export gio_insertText
func gio_insertText(h C.uintptr_t, cstr C.CFTypeRef, crng C.NSRange, ti C.double, mods C.NSUInteger) {
func gio_insertText(h C.uintptr_t, cstr C.CFTypeRef, crng C.NSRange) {
w := windowFor(h)
str := nsstringToString(cstr)
// macOS IME in some cases calls insertText for command keys such as backspace
// instead of doCommandBySelector.
hadCommands := false
for _, r := range str {
hadCommands = w.commandKey(r, ti, mods) || hadCommands
}
if hadCommands {
w.w.SetComposingRegion(key.Range{Start: -1, End: -1})
return
if _, ok := convertCommandKey(r); ok {
w.w.SetComposingRegion(key.Range{Start: -1, End: -1})
return
}
}
state := w.w.EditorState()
rng := state.compose
@@ -1037,7 +1034,7 @@ func osMain() {
func convertCommandKey(k rune) (key.Name, bool) {
var n key.Name
switch k {
case C.KEY_ESCAPE:
case '\x1b': // ASCII escape.
n = key.NameEscape
case C.NSLeftArrowFunctionKey:
n = key.NameLeftArrow
@@ -1047,19 +1044,19 @@ func convertCommandKey(k rune) (key.Name, bool) {
n = key.NameUpArrow
case C.NSDownArrowFunctionKey:
n = key.NameDownArrow
case C.KEY_RETURN:
case '\r':
n = key.NameReturn
case C.KEY_ENTER:
case '\x03':
n = key.NameEnter
case C.NSHomeFunctionKey:
n = key.NameHome
case C.NSEndFunctionKey:
n = key.NameEnd
case C.KEY_DELETE_BACKWARD, 0x08:
case '\x7f', '\b':
n = key.NameDeleteBackward
case C.NSDeleteFunctionKey:
n = key.NameDeleteForward
case C.KEY_TAB, 0x19:
case '\t', 0x19:
n = key.NameTab
case C.NSPageUpFunctionKey:
n = key.NamePageUp
@@ -1072,6 +1069,9 @@ func convertCommandKey(k rune) (key.Name, bool) {
}
func convertKey(k rune) (key.Name, bool) {
if n, ok := convertCommandKey(k); ok {
return n, true
}
var n key.Name
switch k {
case C.NSF1FunctionKey:
+4 -53
View File
@@ -15,7 +15,6 @@ __attribute__ ((visibility ("hidden"))) CALayer *gio_layerFactory(BOOL presentWi
@end
@interface GioView : NSView <CALayerDelegate,NSTextInputClient>
@property NSEvent *lastKeyDown;
@property uintptr_t handle;
@property BOOL presentWithTrans;
@end
@@ -133,12 +132,8 @@ static void handleMouse(GioView *view, NSEvent *event, int typ, CGFloat dx, CGFl
handleMouse(self, event, MOUSE_SCROLL, dx, dy);
}
- (void)keyDown:(NSEvent *)event {
// Stash the event for use by doCommandBySelector.
self.lastKeyDown = event;
[self interpretKeyEvents:[NSArray arrayWithObject:event]];
self.lastKeyDown = nil;
NSString *keys = [event charactersIgnoringModifiers];
gio_onKeys(self.handle, (__bridge CFTypeRef)keys, [event timestamp], [event modifierFlags], true);
gio_onKeys(self.handle, (__bridge CFTypeRef)event, (__bridge CFTypeRef)keys, [event timestamp], [event modifierFlags], true);
}
- (void)flagsChanged:(NSEvent *)event {
[self interpretKeyEvents:[NSArray arrayWithObject:event]];
@@ -146,54 +141,14 @@ static void handleMouse(GioView *view, NSEvent *event, int typ, CGFloat dx, CGFl
}
- (void)keyUp:(NSEvent *)event {
NSString *keys = [event charactersIgnoringModifiers];
gio_onKeys(self.handle, (__bridge CFTypeRef)keys, [event timestamp], [event modifierFlags], false);
gio_onKeys(self.handle, (__bridge CFTypeRef)event, (__bridge CFTypeRef)keys, [event timestamp], [event modifierFlags], false);
}
- (void)insertText:(id)string {
gio_onText(self.handle, (__bridge CFTypeRef)string);
}
- (void)doCommandBySelector:(SEL)action {
NSEvent *event = self.lastKeyDown;
if (event == nil) {
return;
}
int key = 0;
if (action == @selector(deleteBackward:)) {
key = KEY_DELETE_BACKWARD;
} else if (action == @selector(deleteForward:)) {
key = NSDeleteFunctionKey;
} else if (action == @selector(insertNewline:)) {
NSString *keys = [event charactersIgnoringModifiers];
if ([keys isEqualToString:@"\r"]) {
key = KEY_RETURN;
} else {
key = KEY_ENTER;
}
} else if (action == @selector(moveUp:)) {
key = NSUpArrowFunctionKey;
} else if (action == @selector(moveDown:)) {
key = NSDownArrowFunctionKey;
} else if (action == @selector(moveLeft:)) {
key = NSLeftArrowFunctionKey;
} else if (action == @selector(moveRight:)) {
key = NSRightArrowFunctionKey;
} else if (action == @selector(cancelOperation:)) {
key = KEY_ESCAPE;
} else if (action == @selector(insertTab:)) {
key = KEY_TAB;
} else if (action == @selector(scrollToBeginningOfDocument:)) {
key = NSHomeFunctionKey;
} else if (action == @selector(scrollToEndOfDocument:)) {
key = NSEndFunctionKey;
} else if (action == @selector(scrollPageUp:)) {
key = NSPageUpFunctionKey;
} else if (action == @selector(scrollPageDown:)) {
key = NSPageDownFunctionKey;
} else {
return;
}
gio_onCommandBySelector(self.handle, key, [event timestamp], [event modifierFlags]);
gio_onCommandBySelector(self.handle);
}
- (BOOL)hasMarkedText {
int res = gio_hasMarkedText(self.handle);
return res ? YES : NO;
@@ -229,10 +184,6 @@ static void handleMouse(GioView *view, NSEvent *event, int typ, CGFloat dx, CGFl
}
- (void)insertText:(id)string
replacementRange:(NSRange)replaceRange {
NSEvent *event = self.lastKeyDown;
if (event == nil) {
return;
}
NSString *str;
// string is either an NSAttributedString or an NSString.
if ([string isKindOfClass:[NSAttributedString class]]) {
@@ -240,7 +191,7 @@ static void handleMouse(GioView *view, NSEvent *event, int typ, CGFloat dx, CGFl
} else {
str = string;
}
gio_insertText(self.handle, (__bridge CFTypeRef)str, replaceRange, [event timestamp], [event modifierFlags]);
gio_insertText(self.handle, (__bridge CFTypeRef)str, replaceRange);
}
- (NSUInteger)characterIndexForPoint:(NSPoint)p {
return gio_characterIndexForPoint(self.handle, p);