diff mbox

[v2] Machine menu patch for Mac OS X

Message ID A1DC71A4-977E-4F3B-A5A4-AD7D203B4A23@gmail.com
State New
Headers show

Commit Message

Programmingkid Jan. 20, 2015, 5:22 p.m. UTC
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.

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

---
Changed setting the window title to pause to
displaying the word pause on the window in big red colors.
Added "Power Down" menu item.
Dynamically adds cdrom and floppy related menu items.
Displays verify quit dialog when user pushes close button.


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

Comments

Peter Maydell Feb. 12, 2015, 2:41 a.m. UTC | #1
On 20 January 2015 at 17:22, Programmingkid <programmingkidx@gmail.com> wrote:
> 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.
>
> Signed-off-by: John Arbuckle <programmingkidx@gmail.com>

I get lots of compile warnings with this, including about
functions deprecated back in OSX 10.4, format string security
issues, and discarding of const qualifiers. These all need to
be fixed.

>
>  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;

> +/*
> +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(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)) {
> +            strcpy(official_name, block_device_data->value->device);

This is doing an unchecked strcpy into a fixed size buffer;
don't ever do this...

> +            qapi_free_BlockInfoList(block_device_data);
> +            return true;
> +        }
> +        block_device_data = block_device_data->next;
> +    }
> +    return false;
> +}

> +/* 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]];

I suggest you split this patch up. The addition of 'pause'/'resume'
is straightforward because we already do that in the GTK front end.
Adding support for 'eject floppy/cdrom' is a separate feature and
should be in its own patch for review (you should cc the block
maintainers on that to check that we have the right mechanism for
"find the cdrom drive").

thanks
-- PMM
Programmingkid Feb. 12, 2015, 4:33 a.m. UTC | #2
On Feb 11, 2015, at 9:41 PM, Peter Maydell wrote:

> On 20 January 2015 at 17:22, Programmingkid <programmingkidx@gmail.com> wrote:
>> 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.
>> 
>> Signed-off-by: John Arbuckle <programmingkidx@gmail.com>
> 
> I get lots of compile warnings with this, including about
> functions deprecated back in OSX 10.4, format string security
> issues, and discarding of const qualifiers. These all need to
> be fixed.

We could turn off the depreciation flag when compiling. I really doubt Apple is going to remove these functions because it would eliminate compatibility with a lot of Applications. But if you really want, I guess I could use all non-depreciated functions. 

> 
>> 
>> 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;
> 
>> +/*
>> +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(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)) {
>> +            strcpy(official_name, block_device_data->value->device);
> 
> This is doing an unchecked strcpy into a fixed size buffer;
> don't ever do this...

Is using strncpy() in place of strcpy() ok?

> 
>> +            qapi_free_BlockInfoList(block_device_data);
>> +            return true;
>> +        }
>> +        block_device_data = block_device_data->next;
>> +    }
>> +    return false;
>> +}
> 
>> +/* 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]];
> 
> I suggest you split this patch up. The addition of 'pause'/'resume'
> is straightforward because we already do that in the GTK front end.
> Adding support for 'eject floppy/cdrom' is a separate feature and
> should be in its own patch for review (you should cc the block
> maintainers on that to check that we have the right mechanism for
> "find the cdrom drive").

Ok.
diff mbox

Patch

diff --git a/ui/cocoa.m b/ui/cocoa.m
index d37c29b..31a2043 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
@@ -64,6 +66,9 @@  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;
 
 // keymap conversion
 int keymap[] =
@@ -239,7 +244,95 @@  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)], @"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(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)) {
+            strcpy(official_name, block_device_data->value->device);
+            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;
+    }
+}
 
+/* 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]];
+    }
+
+    if (emulatorHasFloppy()) {
+        [menu addItem: [[[NSMenuItem alloc] initWithTitle: @"Eject Floppy" action: @selector(ejectFloppy:) keyEquivalent: @""] autorelease]];
+        [menu addItem: [[[NSMenuItem alloc] initWithTitle: @"Change Floppy..." action: @selector(changeFloppy:) keyEquivalent: @""] autorelease]];
+    }
+    if (emulatorHasCDROM()) {
+        [menu addItem: [[[NSMenuItem alloc] initWithTitle: @"Eject cdrom" action: @selector(ejectCdrom:) keyEquivalent: @""] autorelease]];
+        [menu addItem: [[[NSMenuItem alloc] initWithTitle: @"Use cdrom image..." action: @selector(changeCdrom:) keyEquivalent: @""] autorelease]];
+        [menu addItem: [[[NSMenuItem alloc] initWithTitle: @"Use real cdrom drive" action: @selector(useRealCdrom:) keyEquivalent: @""] autorelease]];
+    }
+    [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];
+}
 
 /*
  ------------------------------------------------------
@@ -801,6 +894,17 @@  QemuCocoaView *cocoaView;
 - (void)toggleFullScreen:(id)sender;
 - (void)showQEMUDoc:(id)sender;
 - (void)showQEMUTec:(id)sender;
+- (void)pauseQemu:(id)sender;
+- (void)ejectFloppy:(id)sender;
+- (void)ejectCdrom:(id)sender;
+- (void)changeCdrom:(id)sender;
+- (void)changeFloppy:(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 +937,21 @@  QemuCocoaView *cocoaView;
         [normalWindow makeKeyAndOrderFront:self];
         [normalWindow center];
 
+        /* Used for displaying pause on the screen */
+        pause_label = [NSTextField new];
+        [pause_label setBezeled:NO];
+        [pause_label setDrawsBackground:NO];
+        [pause_label setEditable:NO];
+        [pause_label setSelectable:NO];
+        [pause_label setStringValue: @"Paused"];
+        [pause_label setFont: [NSFont fontWithName: @"Helvetica" size: 90]];
+        [pause_label setTextColor: [NSColor redColor]];
+        [pause_label sizeToFit];
+
+        /* Verify with the user before quitting QEMU */
+        NSButton *closeButton = [normalWindow standardWindowButton:NSWindowCloseButton];
+        [closeButton setTarget: self];
+        [closeButton setAction: @selector(verifyQuit:)];
     }
     return self;
 }
@@ -943,6 +1062,119 @@  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];
+}
+
+/* Eject the floppy0 disk */
+- (void)ejectFloppy:(id)sender
+{
+    Error *err = NULL;
+    qmp_eject(floppy_drive_name, false, false, &err);
+    handleAnyDeviceErrors(err);
+}
+
+/* Displays a dialog box asking the user to select a floppy image to load */
+- (void)changeFloppy:(id)sender
+{
+    NSOpenPanel * open_panel;
+    open_panel = [NSOpenPanel openPanel];
+    [open_panel setCanChooseFiles: YES];
+    [open_panel setAllowsMultipleSelection: NO];
+    if([open_panel runModalForDirectory: nil file: nil] == NSOKButton) {
+        Error *err = NULL;
+        NSString * file = [[open_panel filenames] objectAtIndex: 0];
+        qmp_change_blockdev(floppy_drive_name, [file cString], "raw", &err);
+        handleAnyDeviceErrors(err);
+    }
+}
+
+// Ejects the cdrom
+- (void)ejectCdrom:(id)sender
+{
+    Error *err = NULL;
+    qmp_eject(cdrom_drive_name, false, false, &err);
+    handleAnyDeviceErrors(err);
+}
+
+/* Displays a dialog box asking the user to select a CD image to load */
+- (void)changeCdrom:(id)sender
+{
+    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(cdrom_drive_name, [file cString], "raw", &err);
+        handleAnyDeviceErrors(err);
+    }
+}
+
+/* Restarts QEMU */
+- (void)restartQemu:(id)sender
+{
+    qemu_system_reset_request();
+}
+
+/* Switches QEMU to use the real cdrom drive */
+- (void)useRealCdrom:(id)sender
+{
+    Error *err = NULL;
+    qmp_change_blockdev(cdrom_drive_name, "/dev/cdrom", "raw", &err);
+    handleAnyDeviceErrors(err);
+}
+
+/* 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 +1229,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 +1360,7 @@  void cocoa_display_init(DisplayState *ds, int full_screen)
 
     // register cleanup function
     atexit(cocoa_cleanup);
+
+    /* Creates and adds the Machine menu to the menubar */
+    createMachineMenu();
 }