qemu-devel
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[Qemu-devel] [PATCH v4] ui/cocoa.m: Machine menu patch for Mac OS X


From: Programmingkid
Subject: [Qemu-devel] [PATCH v4] ui/cocoa.m: Machine menu patch for Mac OS X
Date: Mon, 16 Feb 2015 23:34:21 -0500

Added features:
Menu items to switch floppy and CD image files.
Menu items to eject floppy and CD image files.
Menu item to use /dev/cdrom.
Verifies with the user before quitting QEMU by displaying a dialog box.
Pause and resume menu items with the word pause displayed on the window.

Signed-off-by: John Arbuckle <address@hidden>

---
Other devices like SD are also given menu items. 
Menu items now support having more than one cdrom and floppy drives. 

 ui/cocoa.m |  486 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-
 1 files changed, 485 insertions(+), 1 deletions(-)

diff --git a/ui/cocoa.m b/ui/cocoa.m
index d37c29b..7c47979 100644
--- a/ui/cocoa.m
+++ b/ui/cocoa.m
@@ -29,6 +29,8 @@
 #include "ui/console.h"
 #include "ui/input.h"
 #include "sysemu/sysemu.h"
+#include "qmp-commands.h"
+#include "sysemu/blockdev.h"
 
 #ifndef MAC_OS_X_VERSION_10_4
 #define MAC_OS_X_VERSION_10_4 1040
@@ -65,6 +67,14 @@ static int last_buttons;
 int gArgc;
 char **gArgv;
 
+#define MAX_DEVICE_NAME_SIZE 10
+char floppy_drive_name[MAX_DEVICE_NAME_SIZE], 
cdrom_drive_name[MAX_DEVICE_NAME_SIZE];
+NSTextField * pause_label;
+int floppy_count = 0;
+int cd_count = 0;
+NSMutableDictionary * device_dict;
+int device_id = 0;
+
 // keymap conversion
 int keymap[] =
 {
@@ -239,9 +249,330 @@ static int cocoa_keycode_to_qemu(int keycode)
     return keymap[keycode];
 }
 
+/* Handles any errors that happen with a device transaction */
+static void handleAnyDeviceErrors(Error * err)
+{
+    if (err) {
+        NSRunAlertPanel(@"Alert", [NSString stringWithCString: 
error_get_pretty(err) encoding: NSASCIIStringEncoding], @"OK", nil, nil);
+        error_free(err);
+    }
+}
 
 
 /*
+Determine if the current emulator has the specified device.
+device_name: the name of the device you want: floppy, cd
+official_name: QEMU's name for the device: floppy0, ide-cd0
+*/
+static bool emulatorHasDevice(const char * device_name, char * official_name)
+{
+    BlockInfoList * block_device_data;
+    block_device_data = qmp_query_block(false);
+    if(block_device_data == NULL) {
+        return false;
+    }
+    while(block_device_data->next != NULL) {
+        /* If we found the device */
+        if (strstr(block_device_data->value->device, device_name)) {
+            strncpy(official_name, block_device_data->value->device, 
MAX_DEVICE_NAME_SIZE);
+            qapi_free_BlockInfoList(block_device_data);
+            return true;
+        }
+        block_device_data = block_device_data->next;
+    }
+    return false;
+}
+
+/* Determine if the current emulator has a floppy drive */
+static bool emulatorHasFloppy()
+{
+    if (emulatorHasDevice("floppy", floppy_drive_name)) {
+        return true;
+    } else {
+        return false;
+    }
+}
+
+/* Determine if the current emulator has a CDROM drive */
+static bool emulatorHasCDROM()
+{
+    if (emulatorHasDevice("cd", cdrom_drive_name)) {
+        return true;
+    } else {
+        return false;
+    }
+}
+
+/* Determines if the given device is a floppy drive */
+static bool isFloppyDevice(BlockInfo * current_device)
+{
+    if(strstr(current_device->device, "floppy")) {
+        return true;
+    } else {
+        return false;
+    }
+
+}
+
+/* Determines if the given device is a CD drive */
+static bool isCdromDevice(BlockInfo * current_device)
+{
+    if(strstr(current_device->device, "-cd")) {
+        return true;
+    } else {
+        return false;
+    }
+}
+
+/* Returns a floppy device */
+static NSString * getFloppyDevice(int index)
+{
+    int count = 0;
+    BlockInfoList *current_device;
+    current_device = qmp_query_block(false);
+    if(current_device == NULL) {
+        NSBeep();
+        NSRunAlertPanel(@"Alert", @"Could not query block devices!", @"OK", 
nil, nil);
+        printf("Error: could not query block devices!\nFunction: 
getFloppyDevice()\n");
+        return @"FAILED TO QUERY FOR FLOPPY DRIVES";
+    }
+
+    // look thru all the devices
+    while (current_device) {
+        if(isFloppyDevice(current_device->value)) {  /* If found a floppy 
drive */
+            if(count == index) { /* The drive we want */
+                return [NSString stringWithFormat: @"%s", 
current_device->value->device];
+            }
+            count++;
+        }
+        current_device = current_device->next;
+    }
+
+    /* If failed to find the drive */
+    NSRunAlertPanel(@"Alert", @"Could not find floppy drive.", @"OK", nil, 
nil);
+    printf("Error: No floppy drive found at index %d\n\a", index);
+    return [NSString stringWithFormat: @"NO FLOPPY DRIVE FOUND AT INDEX %d", 
index ];
+}
+
+/* Returns a cdrom device */
+static NSString * getCdromDevice(int index)
+{
+    int count = 0;
+    BlockInfoList *current_device;
+    current_device = qmp_query_block(false);
+    if(current_device == NULL) {
+        NSBeep();
+        NSRunAlertPanel(@"Alert", @"Could not query block devices!", @"OK", 
nil, nil);
+        printf("Error: could not query block devices!\nFunction: 
getCdromDevice()");
+        return @"FAILED TO QUERY FOR CDROM DRIVES";
+    }
+
+    // look thru all the devices
+    while (current_device) {
+        if(isCdromDevice(current_device->value)) {  /* If found a cd drive */
+            if(count == index) { /* The drive we want */
+                return [NSString stringWithFormat: @"%s", 
current_device->value->device];
+            }
+            count++;
+        }
+        current_device = current_device->next;
+    }
+
+    /* If failed to find the drive */
+    NSRunAlertPanel(@"Alert", @"Could not find cdrom.", @"OK", nil, nil);
+    printf("Error: could not find cdrom drive.\n");
+    return [NSString stringWithFormat: @"NO CDROM DRIVE FOUND AT INDEX %d", 
index ];
+}
+
+
+/* Counts the number of floppy and cdrom drives */
+static void countDevices()
+{
+    BlockInfoList *current_device;
+    current_device = qmp_query_block(false);
+    if(current_device == NULL) {
+        NSBeep();
+        NSRunAlertPanel(@"Failure", @"Could not query block devices!", @"OK", 
nil, nil);
+        return;
+    }
+
+    /* count the number of each device we have */
+    while (current_device) {
+        if(current_device->value->removable == true) {
+            if(isFloppyDevice(current_device->value))
+                floppy_count++;
+            if(isCdromDevice(current_device->value))
+                cd_count++;
+        }
+        current_device = current_device->next;
+    }
+}
+
+
+/* Create all the menu items for all floppy drives */
+static void makeFloppyDeviceMenus(NSMenu * menu)
+{
+    NSMenuItem * menu_item;
+
+    /* No floppies - no reason to continue */
+    if(floppy_count == 0) {
+        return;
+    }
+
+    /* If there is only one floppy device - don't append any number of menu 
item title */
+    else if(floppy_count == 1) {
+        menu_item = [[NSMenuItem alloc] initWithTitle: @"Change floppy..." 
action: @selector(changeDeviceMedia:) keyEquivalent: @""];
+        [menu addItem: menu_item];
+        [menu_item setTag: device_id];
+        [menu_item autorelease];
+
+        menu_item = [[NSMenuItem alloc] initWithTitle: @"Eject floppy" action: 
@selector(ejectDeviceMedia:) keyEquivalent: @""];
+        [menu addItem: menu_item];
+        [menu_item setTag: device_id];
+        [menu_item autorelease];
+
+        [device_dict setValue: getFloppyDevice(0) forKey: [[NSNumber 
numberWithInt: device_id] stringValue]];
+        device_id++;
+    }
+
+    /* If there is more than one floppy drive */
+    else if(floppy_count > 1) {
+        int i;
+        for(i = 0; i < floppy_count; i++) {
+            menu_item = [[NSMenuItem alloc] initWithTitle: [NSString 
stringWithFormat: @"Change floppy %d...", i+1] action: 
@selector(changeDeviceMedia:) keyEquivalent: @""];
+            [menu addItem: menu_item];
+            [menu_item setTag: device_id];
+            [menu_item autorelease];
+
+            menu_item = [[NSMenuItem alloc] initWithTitle: [NSString 
stringWithFormat: @"Eject floppy %d", i+1] action: @selector(ejectDeviceMedia:) 
keyEquivalent: @""];
+            [menu addItem: menu_item];
+            [menu_item setTag: device_id];
+            [menu_item autorelease];
+
+            [device_dict setValue: getFloppyDevice(i) forKey: [[NSNumber 
numberWithInt: device_id] stringValue]];
+            device_id++;
+        }
+    }
+}
+
+
+/* Makes all the CDROM menu items */
+static void makeCDROMDeviceMenus(NSMenu * menu)
+{
+    NSMenuItem * menu_item;
+
+    /* No CDROM drives for QEMU - no reason to continue */
+    if(cd_count == 0) {
+        return;
+    }
+
+    /* So we don't add a number to a menu item when there is only one of them 
*/
+    else if(cd_count == 1) {
+        menu_item = [[NSMenuItem alloc] initWithTitle: @"Change cdrom..." 
action: @selector(changeDeviceMedia:) keyEquivalent: @""];
+        [menu addItem: menu_item];
+        [menu_item setTag: device_id];
+        [menu_item autorelease];
+
+        menu_item = [[NSMenuItem alloc] initWithTitle: @"Eject cdrom" action: 
@selector(ejectDeviceMedia:) keyEquivalent: @""];
+        [menu addItem: menu_item];
+        [menu_item setTag: device_id];
+        [menu_item autorelease];
+
+        [device_dict setValue: getCdromDevice(0) forKey: [[NSNumber 
numberWithInt: device_id] stringValue]];
+        device_id++;
+    }
+
+    else if(cd_count > 1) {
+        int i;
+        for(i = 0; i < cd_count; i++) {
+            menu_item = [[NSMenuItem alloc] initWithTitle: [NSString 
stringWithFormat: @"Change cdrom (%@)...", getCdromDevice(i)] action: 
@selector(changeDeviceMedia:) keyEquivalent: @""];
+            [menu addItem: menu_item];
+            [menu_item setTag: device_id];
+            [menu_item autorelease];
+
+            menu_item = [[NSMenuItem alloc] initWithTitle: [NSString 
stringWithFormat: @"Eject cdrom (%@)", getCdromDevice(i)] action: 
@selector(ejectDeviceMedia:) keyEquivalent: @""];
+            [menu addItem: menu_item];
+            [menu_item setTag: device_id];
+            [menu_item autorelease];
+
+            [device_dict setValue: getCdromDevice(i) forKey: [[NSNumber 
numberWithInt: device_id] stringValue]];
+            device_id++;
+        }
+    }
+
+    /* This menu item should always be available. The user
+       can plug in an external optical drive and use it -
+       even after QEMU has started up. */
+    menu_item = [[NSMenuItem alloc] initWithTitle: @"Use real cdrom drive" 
action: @selector(useRealCdrom:) keyEquivalent: @""];
+    [menu addItem: menu_item];
+    [menu_item autorelease];
+}
+
+/* Make menus for other removable devices that are not floppy or cdrom */
+static void makeOtherDeviceMenus(NSMenu * menu)
+{
+    NSMenuItem * menu_item;
+    BlockInfoList * current_device;
+    current_device = qmp_query_block(false);
+    if(current_device == NULL) {
+        NSBeep();
+        NSRunAlertPanel(@"Alert", @"Failed to query for block devices!", 
@"OK", nil, nil);
+        return;
+    }
+
+    /* Loop thru all the block devices in the emulator */
+    while (current_device) {
+        if(current_device->value->removable == true) {
+            if(!isCdromDevice(current_device->value) && 
!isFloppyDevice(current_device->value)) {
+                menu_item = [[NSMenuItem alloc] initWithTitle: [NSString 
stringWithFormat: @"Change %s...", current_device->value->device] action: 
@selector(changeDeviceMedia:) keyEquivalent: @""];
+                [menu addItem: menu_item];
+                [menu_item setTag: device_id];
+                [menu_item autorelease];
+
+                menu_item = [[NSMenuItem alloc] initWithTitle: [NSString 
stringWithFormat: @"Eject %s", current_device->value->device] action: 
@selector(ejectDeviceMedia:) keyEquivalent: @""];
+                [menu addItem: menu_item];
+                [menu_item setTag: device_id];
+                [menu_item autorelease];
+
+                [device_dict setValue: [NSString stringWithFormat: @"%s", 
current_device->value->device] forKey: [[NSNumber numberWithInt: device_id] 
stringValue]];
+                device_id++;
+            }
+        }
+        current_device = current_device->next;
+    }
+}
+
+/* Adds the Machine menu to the menu bar. */
+/* Has to be added separately because QEMU needs
+   to be running to determine used devices.
+*/
+static void createMachineMenu()
+{
+    NSMenu * menu;
+    NSMenuItem * menuItem;
+
+    // Machine menu
+     menu = [[NSMenu alloc] initWithTitle: @"Machine"];
+    [menu setAutoenablesItems: NO];
+    [menu addItem: [[[NSMenuItem alloc] initWithTitle: @"Pause" action: 
@selector(pauseQemu:) keyEquivalent: @""] autorelease]];
+    [menu addItem: [[[NSMenuItem alloc] initWithTitle: @"Resume" action: 
@selector(resumeQemu:) keyEquivalent: @""] autorelease]];
+
+    if(emulatorHasFloppy() || emulatorHasCDROM()) {
+        [menu addItem: [NSMenuItem separatorItem]];
+    }
+    makeFloppyDeviceMenus(menu);
+    makeCDROMDeviceMenus(menu);
+    makeOtherDeviceMenus(menu);
+    [menu addItem: [NSMenuItem separatorItem]];
+    [menu addItem: [[[NSMenuItem alloc] initWithTitle: @"Reset" action: 
@selector(restartQemu:) keyEquivalent: @""] autorelease]];
+    [menu addItem: [[[NSMenuItem alloc] initWithTitle: @"Power Down" action: 
@selector(powerDown:) keyEquivalent: @""] autorelease]];
+    menuItem = [[[NSMenuItem alloc] initWithTitle: @"Machine" action:nil 
keyEquivalent:@""] autorelease];
+    [menuItem setSubmenu:menu];
+    [[NSApp mainMenu] insertItem: menuItem atIndex: 2]; /* Insert after View 
menu */
+    [[menu itemWithTitle: @"Resume"] setEnabled: NO];
+}
+
+/*
  ------------------------------------------------------
     QemuCocoaView
  ------------------------------------------------------
@@ -801,6 +1132,15 @@ QemuCocoaView *cocoaView;
 - (void)toggleFullScreen:(id)sender;
 - (void)showQEMUDoc:(id)sender;
 - (void)showQEMUTec:(id)sender;
+- (void)pauseQemu:(id)sender;
+- (void)ejectDeviceMedia:(id)sender;
+- (void)changeDeviceMedia:(id)sender;
+- (void)restartQemu:(id)sender;
+- (void)useRealCdrom:(id)sender;
+- (void)verifyQuit:(id)sender;
+- (void)powerDown:(id)sender;
+- (void)displayPause;
+- (void)removePause;
 @end
 
 @implementation QemuCocoaAppController
@@ -833,6 +1173,24 @@ QemuCocoaView *cocoaView;
         [normalWindow makeKeyAndOrderFront:self];
         [normalWindow center];
 
+        /* Used for displaying pause on the screen */
+        pause_label = [NSTextField new];
+        [pause_label setBezeled:YES];
+        [pause_label setDrawsBackground:YES];
+        [pause_label setBackgroundColor: [NSColor whiteColor]];
+        [pause_label setEditable:NO];
+        [pause_label setSelectable:NO];
+        [pause_label setStringValue: @"Paused"];
+        [pause_label setFont: [NSFont fontWithName: @"Helvetica" size: 90]];
+        [pause_label setTextColor: [NSColor blackColor]];
+        [pause_label sizeToFit];
+
+        /* Verify with the user before quitting QEMU */
+        NSButton *closeButton = [normalWindow 
standardWindowButton:NSWindowCloseButton];
+        [closeButton setTarget: self];
+        [closeButton setAction: @selector(verifyQuit:)];
+
+        device_dict = [NSMutableDictionary new];
     }
     return self;
 }
@@ -943,6 +1301,127 @@ QemuCocoaView *cocoaView;
     [[NSWorkspace sharedWorkspace] openFile:[NSString 
stringWithFormat:@"%@/../doc/qemu/qemu-tech.html",
         [[NSBundle mainBundle] resourcePath]] withApplication:@"Help Viewer"];
 }
+
+/* Pause the guest */
+- (void)pauseQemu:(id)sender
+{
+    qmp_stop(NULL);
+    [sender setEnabled: NO];
+    [[[sender menu] itemWithTitle: @"Resume"] setEnabled: YES];
+    [self displayPause];
+}
+
+/* Resume running the guest operating system */
+- (void)resumeQemu: (id) sender
+{
+    qmp_cont(NULL);
+    [sender setEnabled: NO];
+    [[[sender menu] itemWithTitle: @"Pause"] setEnabled: YES];
+    [self removePause];
+}
+
+
+// Ejects the media
+// Uses sender's tag to figure out device to eject
+- (void)ejectDeviceMedia:(id)sender
+{
+    NSString * drive;
+    drive = [device_dict valueForKey: [[NSNumber numberWithInt: [sender tag]] 
stringValue]];
+    if(drive == nil) {
+        NSBeep();
+        NSRunAlertPanel(@"Alert", @"Failed to find drive to eject!", @"OK", 
nil, nil);
+        printf("Error: Failed to find drive to eject!\n");
+        NSLog(@"device dictionary: %@", [device_dict description]);
+        return;
+    }
+
+    Error *err = NULL;
+    qmp_eject([drive cStringUsingEncoding: NSASCIIStringEncoding], false, 
false, &err);
+    handleAnyDeviceErrors(err);
+}
+
+/* Displays a dialog box asking the user to select an image file to load.
+   Uses sender's tag value to figure out which drive to use. */
+- (void)changeDeviceMedia:(id)sender
+{
+    /* Find the drive name */
+    NSString * drive;
+    drive = [device_dict valueForKey: [[NSNumber numberWithInt: [sender tag]] 
stringValue]];
+    if(drive == nil) {
+        NSBeep();
+        NSRunAlertPanel(@"Alert", @"Could not find drive!", @"OK", nil, nil);
+        printf("Error: Could not find drive!\n");
+        NSLog(@"device dictionary: %@", [device_dict description]);
+        return;
+    }
+
+    /* Display the file open dialog */
+    NSOpenPanel * open_panel;
+    open_panel = [NSOpenPanel openPanel];
+    [open_panel setCanChooseFiles: YES];
+    [open_panel setAllowsMultipleSelection: NO];
+    if([open_panel runModalForDirectory: nil file: nil] == NSOKButton) {
+        NSString * file = [[open_panel filenames] objectAtIndex: 0];
+        Error *err = NULL;
+        qmp_change_blockdev([drive cStringUsingEncoding: 
NSASCIIStringEncoding], [file cStringUsingEncoding: NSASCIIStringEncoding], 
"raw", &err);
+        handleAnyDeviceErrors(err);
+    }
+}
+
+/* Restarts QEMU */
+- (void)restartQemu:(id)sender
+{
+    qemu_system_reset_request();
+}
+
+/* Has QEMU use a real cdrom disc */
+- (void)useRealCdrom:(id)sender
+{
+    char *cdrom_drive_name;
+    if (emulatorHasDevice("cd", cdrom_drive_name)) {
+        Error *err = NULL;
+        qmp_change_blockdev(cdrom_drive_name, "/dev/cdrom", "raw", &err);
+        handleAnyDeviceErrors(err);
+    } else {
+        NSBeep();
+        NSRunAlertPanel(@"Alert", @"No real cdrom found.", @"OK", nil, nil);
+    }
+}
+
+/* Verifies if the user really wants to quit */
+- (void)verifyQuit:(id)sender
+{
+    NSInteger response;
+    response = NSRunAlertPanel(@"Quit?", @"Are you sure you want to quit?", 
@"Cancel", @"Quit", nil);
+    if(response == NSAlertAlternateReturn)
+        qmp_quit(NULL);
+}
+
+/* Powers down the emulator */
+- (void)powerDown:(id)sender
+{
+    qmp_system_powerdown(NULL);
+}
+
+/* Displays the word pause on the screen */
+- (void)displayPause
+{
+    /* Coordinates have to be calculated each time because the window can 
change its size */
+    int xCoord, yCoord, width, height;
+    xCoord = ([normalWindow frame].size.width - [pause_label 
frame].size.width)/2;
+    yCoord = [normalWindow frame].size.height - [pause_label 
frame].size.height - ([pause_label frame].size.height * .5);
+    width = [pause_label frame].size.width;
+    height = [pause_label frame].size.height;
+    [pause_label setFrame: NSMakeRect(xCoord, yCoord, width, height)];
+    [cocoaView addSubview: pause_label];
+}
+
+/* Removes the word pause from the screen */
+- (void)removePause
+{
+    [pause_label removeFromSuperview];
+}
+
 @end
 
 
@@ -997,7 +1476,7 @@ int main (int argc, const char * argv[]) {
     [menuItem 
setKeyEquivalentModifierMask:(NSAlternateKeyMask|NSCommandKeyMask)];
     [menu addItemWithTitle:@"Show All" 
action:@selector(unhideAllApplications:) keyEquivalent:@""]; // Show All
     [menu addItem:[NSMenuItem separatorItem]]; //Separator
-    [menu addItemWithTitle:@"Quit QEMU" action:@selector(terminate:) 
keyEquivalent:@"q"];
+    [menu addItemWithTitle:@"Quit QEMU" action:@selector(verifyQuit:) 
keyEquivalent:@"q"];
     menuItem = [[NSMenuItem alloc] initWithTitle:@"Apple" action:nil 
keyEquivalent:@""];
     [menuItem setSubmenu:menu];
     [[NSApp mainMenu] addItem:menuItem];
@@ -1128,4 +1607,9 @@ void cocoa_display_init(DisplayState *ds, int full_screen)
 
     // register cleanup function
     atexit(cocoa_cleanup);
+
+    countDevices();
+
+    /* Creates and adds the Machine menu to the menubar */
+    createMachineMenu();
 }
-- 
1.7.5.4




reply via email to

[Prev in Thread] Current Thread [Next in Thread]