From patchwork Tue Feb 17 04:55:19 2015 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Programmingkid X-Patchwork-Id: 440453 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from lists.gnu.org (lists.gnu.org [IPv6:2001:4830:134:3::11]) (using TLSv1 with cipher AES256-SHA (256/256 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id E05B0140291 for ; Tue, 17 Feb 2015 15:55:55 +1100 (AEDT) Received: from localhost ([::1]:43844 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1YNaCN-0001WI-W9 for incoming@patchwork.ozlabs.org; Mon, 16 Feb 2015 23:55:52 -0500 Received: from eggs.gnu.org ([2001:4830:134:3::10]:37492) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1YNaBx-0000zE-Ue for qemu-devel@nongnu.org; Mon, 16 Feb 2015 23:55:28 -0500 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1YNaBu-00016S-NT for qemu-devel@nongnu.org; Mon, 16 Feb 2015 23:55:25 -0500 Received: from mail-qc0-x22b.google.com ([2607:f8b0:400d:c01::22b]:38822) by eggs.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1YNaBu-00016N-FB for qemu-devel@nongnu.org; Mon, 16 Feb 2015 23:55:22 -0500 Received: by mail-qc0-f171.google.com with SMTP id l6so27371877qcy.2 for ; Mon, 16 Feb 2015 20:55:22 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20120113; h=from:content-type:content-transfer-encoding:subject:date:message-id :cc:to:mime-version; bh=D5AWpueANaLDMqtgUVEgeK+NJnWJO7gTuzdY1o89qmE=; b=PDRQe3hibVGzjLy46kXvES38YxmNNYOa8iz+Pz+mWyha2WVHd61SgMO8ANXPTX9lAD 1zBJ4esH+VLbPSNhN5Ivth93hJNzl6bgBpT70zKQRBGZzJ3KvlQIV22JUtSKn6ssoFGQ oY8JxdtV5JmSKgJnweiowlhjSsXYjZQT10DDiyuiW8xOFIyu6CBS6oRATz4BgVlSq39e /aJyzh0aB4yVW7IF9ElZNgX6VAamYT4ffmp2lyoVA6fnNZ+dCVwshWA6ZAHyTbz9TRD/ +HgF67+X2Sq7E2fLPdC+HNlMJVuWvfzMzJ/darNb0GQ4rzgeJwwOoQFRQzJX8dp3suOr VlBQ== X-Received: by 10.140.133.69 with SMTP id 66mr82565qhf.17.1424148921949; Mon, 16 Feb 2015 20:55:21 -0800 (PST) Received: from [192.168.0.4] (d199-74-164-53.col.wideopenwest.com. [74.199.53.164]) by mx.google.com with ESMTPSA id e7sm15300472qag.10.2015.02.16.20.55.20 (version=TLSv1 cipher=ECDHE-RSA-RC4-SHA bits=128/128); Mon, 16 Feb 2015 20:55:21 -0800 (PST) From: Programmingkid Date: Mon, 16 Feb 2015 23:55:19 -0500 Message-Id: <3528E407-0F10-47A3-B18E-827C0D80F2A2@gmail.com> To: Peter Maydell , Kevin Wolf Mime-Version: 1.0 (Apple Message framework v1084) X-Mailer: Apple Mail (2.1084) X-detected-operating-system: by eggs.gnu.org: Error: Malformed IPv6 address (bad octet value). X-Received-From: 2607:f8b0:400d:c01::22b Cc: qemu-devel qemu-devel Subject: [Qemu-devel] [PATCH v5] ui/cocoa.m: Machine menu patch for Mac OS X X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.14 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org Sender: qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org 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 paused displayed on the window. Signed-off-by: John Arbuckle --- Fixed a small bug with the useRealCdrom: method. ui/cocoa.m | 486 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 files changed, 485 insertions(+), 1 deletions(-) diff --git a/ui/cocoa.m b/ui/cocoa.m index d37c29b..00148c1 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[MAX_DEVICE_NAME_SIZE]; + 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(); }