diff mbox

Adds the ability to use the command key in the guest OS.

Message ID F51CACE0-E635-4DF5-920C-C470CBD7D599@gmail.com
State New
Headers show

Commit Message

Programmingkid July 12, 2013, 8:41 p.m. UTC
This patch adds the ability to use the command key in the guest OS. This patch will allow you to send keyboard shortcuts to Macintosh applications running in QEMU. 

signed-off-by: John Arbuckle <programmingkidx@gmail.com>

---
 ui/cocoa.m |  123 +++++++++++++++++++++++++++++++++++++++++++++++++++++------
 1 files changed, 110 insertions(+), 13 deletions(-)
diff mbox

Patch

diff --git a/ui/cocoa.m b/ui/cocoa.m
index be49179..4884ccf 100644
--- a/ui/cocoa.m
+++ b/ui/cocoa.m
@@ -70,6 +70,7 @@  static DisplayChangeListener *dcl;
 
 int gArgc;
 char **gArgv;
+bool substitutingForCommandKey = false; 
 
 // keymap conversion
 int keymap[] =
@@ -129,8 +130,8 @@  int keymap[] =
     14, //  51      0x33    0x0e            BKSP    QZ_BACKSPACE
     0,  //  52      0x34    Undefined
     1,  //  53      0x35    0x01            ESC     QZ_ESCAPE
-    0,  //  54      0x36                            QZ_RMETA
-    0,  //  55      0x37                            QZ_LMETA
+    220, //  54      0x36                            QZ_RMETA
+    219, //  55      0x37                            QZ_LMETA
     42, //  56      0x38    0x2a            L SHFT  QZ_LSHIFT
     58, //  57      0x39    0x3a            CAPS    QZ_CAPSLOCK
     56, //  58      0x3A    0x38            L ALT   QZ_LALT
@@ -249,6 +250,72 @@  static int cocoa_keycode_to_qemu(int keycode)
 }
 
 
+// Used to map the guest OS's command key to a key on the host keyboard.
+// Uses the -command-key option.
+static void handleCommandKeyOption(int * argc, char * argv[])
+{
+	bool foundOption = false;
+	int newCommandKeyButtonValue, i;	
+
+	#define BUFFER_SIZE 10
+	#define LEFT_COMMAND_KEY 0x37
+	#define RIGHT_COMMAND_KEY 0x36
+	#define GUEST_COMMAND_KEY 219
+	
+	char keyValueString[BUFFER_SIZE];
+
+	for(i = 0; i < *argc; i++) {
+	    if(strcmp(argv[i], "-command-key") == 0) {
+	        foundOption = true;
+		break;
+	    }
+	}
+
+	// if the -command-key option is found
+	if(foundOption == true)
+	{
+		snprintf(keyValueString, BUFFER_SIZE, "%s", argv[i+1]); 
+		if(strlen(keyValueString) == 0) {
+			printf("Usage: -command-key <host keyboard key value>\n");
+			printf("This page will help: http://boredzo.org/blog/wp-content/uploads/2007/05/imtx-virtual-keycodes.png\n");
+			exit(-1000);
+		}
+		
+		// if using hexadecimal notation (e.g. 0x37)
+		if(keyValueString[0] == '0' && toupper(keyValueString[1]) == 'X') {
+			sscanf(keyValueString, "%x", &newCommandKeyButtonValue);
+		}
+		
+		// for decimal notation
+		else {
+			newCommandKeyButtonValue = atoi(keyValueString);
+		}
+		
+		//in case the key specified is a negative value
+		if(newCommandKeyButtonValue < 0) {
+			printf("\aCan't use a negative value for the command key!\n");
+			exit(-1001);
+		}
+		
+		// if the guest OS command key is set to the host keyboard's left or right command key
+		if(newCommandKeyButtonValue == LEFT_COMMAND_KEY || newCommandKeyButtonValue == RIGHT_COMMAND_KEY) {
+			substitutingForCommandKey = true;
+			printf("\nNote: since you are using the host command key, the ALT key can be used to send QEMU commands.\n");
+			printf("Example: use ALT-q to quit QEMU.\n\n");
+		}
+		
+		// do the mapping
+		keymap[newCommandKeyButtonValue] = GUEST_COMMAND_KEY;
+		
+		// Remove -command-key from the argument list.
+		// QEMU will complain if we don't.
+		for(int x = i; x < *argc - 2; x=x+2) {
+			argv[x] = argv[x+2];
+			argv[x+1] = argv[x+3];
+		}
+		*argc = *argc - 2;
+	}
+}
 
 /*
  ------------------------------------------------------
@@ -491,20 +558,27 @@  QemuCocoaView *cocoaView;
     int keycode;
     NSPoint p = [event locationInWindow];
 
+    // The key used to send QEMU commands (e.g. Quit, Full Screen).
+    // Change this if you don't like using the ALT key.
+    // Possible values: NSShiftKeyMask, NSControlKeyMask, NSFunctionKeyMask, NSAlternateKeyMask
+    const int substituteKeyMask = NSAlternateKeyMask; 
+	 
     switch ([event type]) {
         case NSFlagsChanged:
-            keycode = cocoa_keycode_to_qemu([event keyCode]);
+            keycode = cocoa_keycode_to_qemu([event keyCode]);			
             if (keycode) {
                 if (keycode == 58 || keycode == 69) { // emulate caps lock and num lock keydown and keyup
                     kbd_put_keycode(keycode);
                     kbd_put_keycode(keycode | 0x80);
                 } else if (qemu_console_is_graphic(NULL)) {
-                    if (keycode & 0x80)
-                        kbd_put_keycode(0xe0);
-                    if (modifiers_state[keycode] == 0) { // keydown
+		    if (keycode & 0x80)  // if keycode >= 0x80, for those keycodes that need a 0xe0 sent first 
+		    {
+		       kbd_put_keycode(0xe0);
+		    }
+                    if (modifiers_state[keycode] == 0) {     // keydown
                         kbd_put_keycode(keycode & 0x7f);
                         modifiers_state[keycode] = 1;
-                    } else { // keyup
+                    } else {                                 // keyup
                         kbd_put_keycode(keycode | 0x80);
                         modifiers_state[keycode] = 0;
                     }
@@ -516,17 +590,37 @@  QemuCocoaView *cocoaView;
                 [self ungrabMouse];
             }
             break;
-        case NSKeyDown:
+        case NSKeyDown:				
+            // if substituting for the host command key and the substitute key is being held down - have QEMU handle it
+            if((substitutingForCommandKey == true) && ([event modifierFlags] & substituteKeyMask)) { 
+               // recreate the event with the command key as the modifier
+               int modifiers = [event modifierFlags];
+               modifiers = modifiers | NSCommandKeyMask;  // set the command key 
+               modifiers = modifiers ^ substituteKeyMask; // unset the substitute key
+
+               event = [NSEvent keyEventWithType: [event type] location: [event locationInWindow] modifierFlags: modifiers
+                       timestamp: [event timestamp] windowNumber: [event windowNumber] context: [event context] characters: [event characters] 
+                       charactersIgnoringModifiers: [event charactersIgnoringModifiers] isARepeat: [event isARepeat] keyCode: [event keyCode] ];
+               [NSApp sendEvent:event];
+               return;
+            }
 
-            // forward command Key Combos
-            if ([event modifierFlags] & NSCommandKeyMask) {
+            // if the command key is held down and we want QEMU to handle the event - not the guest OS
+            else if([event modifierFlags] & NSCommandKeyMask && substitutingForCommandKey == false) {
                 [NSApp sendEvent:event];
                 return;
             }
 
+            // if the command key is held down and we want the guest OS to handle it
+            if(([event modifierFlags] & NSCommandKeyMask) && (substitutingForCommandKey == true)) {
+                kbd_put_keycode(219); // command key
+                kbd_put_keycode(cocoa_keycode_to_qemu([event keyCode])); // any other keys
+                return;
+            }
+
             // default
             keycode = cocoa_keycode_to_qemu([event keyCode]);
-
+				
             // handle control + alt Key Combos (ctrl+alt is reserved for QEMU)
             if (([event modifierFlags] & NSControlKeyMask) && ([event modifierFlags] & NSAlternateKeyMask)) {
                 switch (keycode) {
@@ -584,7 +678,7 @@  QemuCocoaView *cocoaView;
             if (qemu_console_is_graphic(NULL)) {
                 if (keycode & 0x80)
                     kbd_put_keycode(0xe0);
-                kbd_put_keycode(keycode | 0x80); //add 128 to signal release of key
+                kbd_put_keycode(keycode | 0x80); //add 128 (0x80) to signal release of key
             }
             break;
         case NSMouseMoved:
@@ -810,8 +904,9 @@  QemuCocoaView *cocoaView;
 - (void)startEmulationWithArgc:(int)argc argv:(char**)argv
 {
     COCOA_DEBUG("QemuCocoaAppController: startEmulationWithArgc\n");
-
+	
     int status;
+    handleCommandKeyOption(&argc, argv);
     status = qemu_main(argc, argv, *_NSGetEnviron());
     exit(status);
 }
@@ -1047,3 +1142,5 @@  void cocoa_display_init(DisplayState *ds, int full_screen)
     // register cleanup function
     atexit(cocoa_cleanup);
 }
+
+