All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH 0/2] cocoa.m: keyboard quality of life reborn
@ 2021-07-30 17:09 John Arbuckle
  2021-07-30 17:09 ` [PATCH 1/2] ui/cocoa.m: Add full keyboard grab support John Arbuckle
  2021-07-30 17:09 ` [PATCH 2/2] ui/cocoa.m: Add ability to swap command/meta and options keys John Arbuckle
  0 siblings, 2 replies; 3+ messages in thread
From: John Arbuckle @ 2021-07-30 17:09 UTC (permalink / raw)
  To: Peter Maydell, Gerd Hoffmann, Gustavo Noronha Silva, QEMU devel list
  Cc: John Arbuckle

These patches can really help improve keyboard usage with a guest.
Based on patches by Gustavo Noronha Silva <gustavo@noronha.dev.br>. 

John Arbuckle (2):
  Add full keyboard grab support
  Add ability to swap command/meta and options keys

 ui/cocoa.m | 178 ++++++++++++++++++++++++++++++++++++++++++++++++++---
 1 file changed, 170 insertions(+), 8 deletions(-)

-- 
2.24.3 (Apple Git-128)



^ permalink raw reply	[flat|nested] 3+ messages in thread

* [PATCH 1/2] ui/cocoa.m: Add full keyboard grab support
  2021-07-30 17:09 [PATCH 0/2] cocoa.m: keyboard quality of life reborn John Arbuckle
@ 2021-07-30 17:09 ` John Arbuckle
  2021-07-30 17:09 ` [PATCH 2/2] ui/cocoa.m: Add ability to swap command/meta and options keys John Arbuckle
  1 sibling, 0 replies; 3+ messages in thread
From: John Arbuckle @ 2021-07-30 17:09 UTC (permalink / raw)
  To: Peter Maydell, Gerd Hoffmann, Gustavo Noronha Silva, QEMU devel list
  Cc: John Arbuckle

There are keyboard shortcuts that are vital for use in a guest that runs Mac OS.
These shortcuts are reserved for Mac OS use only which makes having the guest
see them impossible on a Mac OS host - until now. This patch will enable the
user to decide if the guest should see all keyboard shortcuts using a menu item.
This patch adds a new menu called Options and a new menu item called
"Full Keyboard Grab". Simply selecting this menu item will turn the feature on
or off at any time. Mac OS requires the user to enable access to assistive
devices to use this feature. How to do this varies with each Mac OS version.
Based on patch by Gustavo Noronha Silva <gustavo@noronha.dev.br>. 

Signed-off-by: John Arbuckle <programmingkidx@gmail.com>
---
 ui/cocoa.m | 112 +++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 112 insertions(+)

diff --git a/ui/cocoa.m b/ui/cocoa.m
index 9f72844b07..fdef9e9901 100644
--- a/ui/cocoa.m
+++ b/ui/cocoa.m
@@ -114,6 +114,9 @@ static void cocoa_switch(DisplayChangeListener *dcl,
 typedef void (^CodeBlock)(void);
 typedef bool (^BoolCodeBlock)(void);
 
+static CFMachPortRef eventsTap = NULL;
+static CFRunLoopSourceRef eventsTapSource = NULL;
+
 static void with_iothread_lock(CodeBlock block)
 {
     bool locked = qemu_mutex_iothread_locked();
@@ -332,10 +335,27 @@ - (float) cdx;
 - (float) cdy;
 - (QEMUScreen) gscreen;
 - (void) raiseAllKeys;
+- (void) setFullGrab;
 @end
 
 QemuCocoaView *cocoaView;
 
+// Part of the full keyboard grab system
+static CGEventRef handleTapEvent(CGEventTapProxy proxy, CGEventType type,
+CGEventRef cgEvent, void *userInfo)
+{
+    QemuCocoaView *cocoaView = (QemuCocoaView*) userInfo;
+    NSEvent* event = [NSEvent eventWithCGEvent:cgEvent];
+    if ([cocoaView isMouseGrabbed] && [cocoaView handleEvent:event]) {
+        COCOA_DEBUG("Global events tap: qemu handled the event, capturing!\n");
+        return NULL;
+    }
+    COCOA_DEBUG("Global events tap: qemu did not handle the event, letting it"
+                " through...\n");
+
+    return cgEvent;
+}
+
 @implementation QemuCocoaView
 - (id)initWithFrame:(NSRect)frameRect
 {
@@ -361,6 +381,12 @@ - (void) dealloc
     }
 
     qkbd_state_free(kbd);
+    if (eventsTap) {
+        CFRelease(eventsTap);
+    }
+    if (eventsTapSource) {
+        CFRelease(eventsTapSource);
+    }
     [super dealloc];
 }
 
@@ -1086,6 +1112,50 @@ - (void) raiseAllKeys
         qkbd_state_lift_all_keys(kbd);
     });
 }
+
+// Inserts the event tap.
+// This enables us to receive keyboard events that Mac OS would
+// otherwise not let us see - like Command-Option-Esc.
+- (void) setFullGrab
+{
+    COCOA_DEBUG("QemuCocoaView: setFullGrab\n");
+    NSString *advice = @"Try enabling access to assistive devices";
+    CGEventMask mask = CGEventMaskBit(kCGEventKeyDown) |
+    CGEventMaskBit(kCGEventKeyUp) | CGEventMaskBit(kCGEventFlagsChanged);
+    eventsTap = CGEventTapCreate(kCGHIDEventTap, kCGHeadInsertEventTap,
+                                 kCGEventTapOptionDefault, mask, handleTapEvent,
+                                 self);
+    if (!eventsTap) {
+        @throw [NSException
+                 exceptionWithName:@"Tap failure"
+                reason:[NSString stringWithFormat:@"%@\n%@", @"Could not "
+                        "create event tap.", advice]
+                userInfo:nil];
+    } else {
+        COCOA_DEBUG("Global events tap created! Will capture system key"
+                    " combos.\n");
+    }
+
+    eventsTapSource =
+    CFMachPortCreateRunLoopSource(kCFAllocatorDefault, eventsTap, 0);
+    if (!eventsTapSource ) {
+        @throw [NSException
+                 exceptionWithName:@"Tap failure"
+                 reason:@"Could not obtain current CFRunLoop source."
+                userInfo:nil];
+    }
+    CFRunLoopRef runLoop = CFRunLoopGetCurrent();
+    if (!runLoop) {
+           @throw [NSException
+                 exceptionWithName:@"Tap failure"
+                 reason:@"Could not obtain current CFRunLoop."
+                userInfo:nil];
+    }
+
+    CFRunLoopAddSource(runLoop, eventsTapSource, kCFRunLoopDefaultMode);
+    CFRelease(eventsTapSource);
+}
+
 @end
 
 
@@ -1117,6 +1187,7 @@ - (void)openDocumentation:(NSString *)filename;
 - (IBAction) do_about_menu_item: (id) sender;
 - (void)make_about_window;
 - (void)adjustSpeed:(id)sender;
+- (IBAction)doFullGrab:(id)sender;
 @end
 
 @implementation QemuCocoaAppController
@@ -1569,6 +1640,35 @@ - (void)adjustSpeed:(id)sender
     COCOA_DEBUG("cpu throttling at %d%c\n", cpu_throttle_get_percentage(), '%');
 }
 
+// The action method to the 'Options->Full Keyboard Grab' menu item
+- (IBAction)doFullGrab:(id) sender
+{
+    @try
+    {
+        // Set the state of the menu item
+        // if already checked
+        if ([sender state] == NSControlStateValueOn) {
+            // remove runloop source
+            CFRunLoopSourceInvalidate(eventsTapSource);
+            if (!eventsTap) {
+                CFRelease(eventsTap);
+            }
+            [sender setState: NSControlStateValueOff];
+        }
+
+        // if not already checked
+        else {
+            [cocoaView setFullGrab];
+            [sender setState: NSControlStateValueOn];
+        }
+    }
+    @catch(NSException *e) {
+        NSBeep();
+        NSLog(@"Exception in doFullGrab: %@", [e reason]);
+        QEMU_Alert([e reason]);
+    }
+}
+
 @end
 
 @interface QemuApplication : NSApplication
@@ -1655,6 +1755,18 @@ static void create_initial_menus(void)
     [menuItem setSubmenu:menu];
     [[NSApp mainMenu] addItem:menuItem];
 
+    // Options menu
+    menu = [[NSMenu alloc] initWithTitle:@"Options"];
+
+    [menu addItem: [[[NSMenuItem alloc] initWithTitle:
+                         @"Full Keyboard Grab" action:@selector(doFullGrab:)
+                                        keyEquivalent:@""] autorelease]];
+
+    menuItem = [[[NSMenuItem alloc] initWithTitle:@"Options" action:nil
+                                    keyEquivalent:@""] autorelease];
+    [menuItem setSubmenu:menu];
+    [[NSApp mainMenu] addItem:menuItem];
+
     // Window menu
     menu = [[NSMenu alloc] initWithTitle:@"Window"];
     [menu addItem: [[[NSMenuItem alloc] initWithTitle:@"Minimize" action:@selector(performMiniaturize:) keyEquivalent:@"m"] autorelease]]; // Miniaturize
-- 
2.24.3 (Apple Git-128)



^ permalink raw reply related	[flat|nested] 3+ messages in thread

* [PATCH 2/2] ui/cocoa.m: Add ability to swap command/meta and options keys
  2021-07-30 17:09 [PATCH 0/2] cocoa.m: keyboard quality of life reborn John Arbuckle
  2021-07-30 17:09 ` [PATCH 1/2] ui/cocoa.m: Add full keyboard grab support John Arbuckle
@ 2021-07-30 17:09 ` John Arbuckle
  1 sibling, 0 replies; 3+ messages in thread
From: John Arbuckle @ 2021-07-30 17:09 UTC (permalink / raw)
  To: Peter Maydell, Gerd Hoffmann, Gustavo Noronha Silva, QEMU devel list
  Cc: John Arbuckle

For users who are use to using a Macintosh keyboard having to use a PC keyboard
can be a pain because the Command/meta and Option/Alt keys are switched. To
make life easier this option is added to allow the user to switch how the guest
reacts to each of these keys being pushed. It can make a Macintosh keyboard user
feel almost at home with a PC keyboard. The same could be said for PC keyboard
users who have to use a Macinosh keyboard.
Based on patch by Gustavo Noronha Silva <gustavo@noronha.dev.br>.

Signed-off-by: John Arbuckle <programmingkidx@gmail.com>
---
 ui/cocoa.m | 66 +++++++++++++++++++++++++++++++++++++++++++++++-------
 1 file changed, 58 insertions(+), 8 deletions(-)

diff --git a/ui/cocoa.m b/ui/cocoa.m
index fdef9e9901..98596d5f38 100644
--- a/ui/cocoa.m
+++ b/ui/cocoa.m
@@ -116,6 +116,7 @@ static void cocoa_switch(DisplayChangeListener *dcl,
 
 static CFMachPortRef eventsTap = NULL;
 static CFRunLoopSourceRef eventsTapSource = NULL;
+static bool swap_command_option_keys = false;
 
 static void with_iothread_lock(CodeBlock block)
 {
@@ -810,12 +811,22 @@ - (bool) handleEventLocked:(NSEvent *)event
         qkbd_state_key_event(kbd, Q_KEY_CODE_CTRL_R, false);
     }
     if (!(modifiers & NSEventModifierFlagOption)) {
-        qkbd_state_key_event(kbd, Q_KEY_CODE_ALT, false);
-        qkbd_state_key_event(kbd, Q_KEY_CODE_ALT_R, false);
+        if (swap_command_option_keys) {
+            qkbd_state_key_event(kbd, Q_KEY_CODE_META_L, false);
+            qkbd_state_key_event(kbd, Q_KEY_CODE_META_R, false);
+        } else {
+            qkbd_state_key_event(kbd, Q_KEY_CODE_ALT, false);
+            qkbd_state_key_event(kbd, Q_KEY_CODE_ALT_R, false);
+        }
     }
     if (!(modifiers & NSEventModifierFlagCommand)) {
-        qkbd_state_key_event(kbd, Q_KEY_CODE_META_L, false);
-        qkbd_state_key_event(kbd, Q_KEY_CODE_META_R, false);
+        if (swap_command_option_keys) {
+            qkbd_state_key_event(kbd, Q_KEY_CODE_ALT, false);
+            qkbd_state_key_event(kbd, Q_KEY_CODE_ALT_R, false);
+        } else {
+            qkbd_state_key_event(kbd, Q_KEY_CODE_META_L, false);
+            qkbd_state_key_event(kbd, Q_KEY_CODE_META_R, false);
+        }
     }
 
     switch ([event type]) {
@@ -847,13 +858,22 @@ - (bool) handleEventLocked:(NSEvent *)event
 
                 case kVK_Option:
                     if (!!(modifiers & NSEventModifierFlagOption)) {
-                        [self toggleKey:Q_KEY_CODE_ALT];
+                        if (swap_command_option_keys) {
+                            [self toggleKey:Q_KEY_CODE_META_L];
+                        } else {
+                            [self toggleKey:Q_KEY_CODE_ALT];
+                        }
+
                     }
                     break;
 
                 case kVK_RightOption:
                     if (!!(modifiers & NSEventModifierFlagOption)) {
-                        [self toggleKey:Q_KEY_CODE_ALT_R];
+                        if (swap_command_option_keys) {
+                            [self toggleKey:Q_KEY_CODE_META_R];
+                        } else {
+                            [self toggleKey:Q_KEY_CODE_ALT_R];
+                        }
                     }
                     break;
 
@@ -861,14 +881,22 @@ - (bool) handleEventLocked:(NSEvent *)event
                 case kVK_Command:
                     if (isMouseGrabbed &&
                         !!(modifiers & NSEventModifierFlagCommand)) {
-                        [self toggleKey:Q_KEY_CODE_META_L];
+                        if (swap_command_option_keys) {
+                            [self toggleKey:Q_KEY_CODE_ALT];
+                        } else {
+                            [self toggleKey:Q_KEY_CODE_META_L];
+                        }
                     }
                     break;
 
                 case kVK_RightCommand:
                     if (isMouseGrabbed &&
                         !!(modifiers & NSEventModifierFlagCommand)) {
-                        [self toggleKey:Q_KEY_CODE_META_R];
+                        if (swap_command_option_keys) {
+                            [self toggleKey:Q_KEY_CODE_ALT_R];
+                        } else {
+                            [self toggleKey:Q_KEY_CODE_META_R];
+                        }
                     }
                     break;
             }
@@ -1188,6 +1216,7 @@ - (IBAction) do_about_menu_item: (id) sender;
 - (void)make_about_window;
 - (void)adjustSpeed:(id)sender;
 - (IBAction)doFullGrab:(id)sender;
+- (IBAction)doSwapCommandOptionKeys:(id)sender;
 @end
 
 @implementation QemuCocoaAppController
@@ -1669,6 +1698,22 @@ - (IBAction)doFullGrab:(id) sender
     }
 }
 
+// The action method to the "Options->Swap Command and Option Keys" menu item
+- (IBAction)doSwapCommandOptionKeys:(id)sender
+{
+    // If the menu item is not checked
+    if ([sender state] == NSControlStateValueOff) {
+        swap_command_option_keys = true;
+        [sender setState: NSControlStateValueOn];
+    }
+
+    // If the menu item is checked
+    else {
+        swap_command_option_keys = false;
+        [sender setState: NSControlStateValueOff];
+    }
+}
+
 @end
 
 @interface QemuApplication : NSApplication
@@ -1762,6 +1807,11 @@ static void create_initial_menus(void)
                          @"Full Keyboard Grab" action:@selector(doFullGrab:)
                                         keyEquivalent:@""] autorelease]];
 
+    [menu addItem: [[[NSMenuItem alloc] initWithTitle:
+                                                 @"Swap Command and Option Keys"
+                                               action:@selector(doSwapCommandOptionKeys:)
+                                        keyEquivalent:@""] autorelease]];
+
     menuItem = [[[NSMenuItem alloc] initWithTitle:@"Options" action:nil
                                     keyEquivalent:@""] autorelease];
     [menuItem setSubmenu:menu];
-- 
2.24.3 (Apple Git-128)



^ permalink raw reply related	[flat|nested] 3+ messages in thread

end of thread, other threads:[~2021-07-30 17:11 UTC | newest]

Thread overview: 3+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2021-07-30 17:09 [PATCH 0/2] cocoa.m: keyboard quality of life reborn John Arbuckle
2021-07-30 17:09 ` [PATCH 1/2] ui/cocoa.m: Add full keyboard grab support John Arbuckle
2021-07-30 17:09 ` [PATCH 2/2] ui/cocoa.m: Add ability to swap command/meta and options keys John Arbuckle

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.