From patchwork Sat Dec 1 12:30:52 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Peter Maydell X-Patchwork-Id: 152625 Delivered-To: patches@linaro.org Received: by 2002:a2e:299d:0:0:0:0:0 with SMTP id p29-v6csp4762350ljp; Sat, 1 Dec 2018 04:30:59 -0800 (PST) X-Google-Smtp-Source: AFSGD/XdVbdSORTRiONcmSCS9nlnkM1F7HcMNoWACavBFqRD4E84nbGG8xZi43fBPkUAA2Cw/Ev4 X-Received: by 2002:adf:dc4e:: with SMTP id m14mr8171823wrj.107.1543667459011; Sat, 01 Dec 2018 04:30:59 -0800 (PST) ARC-Seal: i=1; a=rsa-sha256; t=1543667459; cv=none; d=google.com; s=arc-20160816; b=jPSZB8S0mvCPruju4SjScaXhBS7Rrd4QTmBoIiog2BkrtkS85sq/ZHLyVZvleju/pp vLST2Cq1Y4q/Nu/EVGvA1wZSG1bcIt9tWrckdiwntSw+TaJimkLNBjMQKKjOH6RdDwQQ QZ/yOHsZUydFDpnME5YxQShaV29SWR1btLaIWAnJSqvt7kM4uolPzm6a1qSWMbGMA2Jk qyrJFnS9HqqaWOaEP7jsldIRvMR506V9Qs/ZyW8VpmPtU0xqmw9Ot//u+w/Z15D1VoTH dtDdl7k/hFimmdoGUdXnfaAvzU8BDAycagjwEmLWK3N3mgpgiXmeTu6QtyHsICO6nBNI dEAQ== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from; bh=hLeVrNoRsvKl6ukJSSybfKgiy2H0AwsYr0188zHD1UM=; b=jrj6Re/Pv6JtSnbdfyV/VvvR5dh+Smpon0ey5ff1vw5pUdaf2yEh640X5Gi+iCYuRA MwjCN4N130TAwgJ/ORAGwbjcP9UdZrreqR1PKdpTlHnp7KyfsVCVSiBYmZb6hanumRMd NUJ1SDSM25QZbmMut3YYutvNCYhRTrZseaQ8eBQdWz9EVsbXE9cKtOPbO4CQYDFj/mRZ PI4yuNyOKzxNk7wLEHJxLBi2yM5POZELFyYOxMHYPaGwL4/Wjwzx4QFLYe+npoOG4/FU o8otBYbW9f5O66OeSQf1KCeEaaO7dHhJ1tKVchXBazD2sGyEV1DA/33Ey4iAimwiFbxP xXuA== ARC-Authentication-Results: i=1; mx.google.com; spf=pass (google.com: best guess record for domain of pm215@archaic.org.uk designates 2001:8b0:1d0::2 as permitted sender) smtp.mailfrom=pm215@archaic.org.uk; dmarc=fail (p=NONE sp=NONE dis=NONE) header.from=linaro.org Return-Path: Received: from orth.archaic.org.uk (orth.archaic.org.uk. [2001:8b0:1d0::2]) by mx.google.com with ESMTPS id r71si1635774wmd.139.2018.12.01.04.30.58 for (version=TLS1_2 cipher=ECDHE-RSA-CHACHA20-POLY1305 bits=256/256); Sat, 01 Dec 2018 04:30:58 -0800 (PST) Received-SPF: pass (google.com: best guess record for domain of pm215@archaic.org.uk designates 2001:8b0:1d0::2 as permitted sender) client-ip=2001:8b0:1d0::2; Authentication-Results: mx.google.com; spf=pass (google.com: best guess record for domain of pm215@archaic.org.uk designates 2001:8b0:1d0::2 as permitted sender) smtp.mailfrom=pm215@archaic.org.uk; dmarc=fail (p=NONE sp=NONE dis=NONE) header.from=linaro.org Received: from pm215 by orth.archaic.org.uk with local (Exim 4.89) (envelope-from ) id 1gT4QD-0008Mu-RL; Sat, 01 Dec 2018 12:30:57 +0000 From: Peter Maydell To: qemu-devel@nongnu.org Cc: patches@linaro.org, John Arbuckle , Berkus Decker , Gerd Hoffmann , Roman Bolshakov Subject: [RFC 1/5] ui/cocoa: Ensure we have the iothread lock when calling into QEMU Date: Sat, 1 Dec 2018 12:30:52 +0000 Message-Id: <20181201123056.432-2-peter.maydell@linaro.org> X-Mailer: git-send-email 2.19.2 In-Reply-To: <20181201123056.432-1-peter.maydell@linaro.org> References: <20181201123056.432-1-peter.maydell@linaro.org> MIME-Version: 1.0 The Cocoa UI should run on the main thread; this is enforced in OSX Mojave. In order to be able to run on the main thread, we need to make sure we hold the iothread lock whenever we call into various QEMU UI midlayer functions. Signed-off-by: Peter Maydell --- ui/cocoa.m | 83 ++++++++++++++++++++++++++++++++++++++---------------- 1 file changed, 59 insertions(+), 24 deletions(-) -- 2.19.2 diff --git a/ui/cocoa.m b/ui/cocoa.m index ecf12bfc2e4..9148ecdeb4c 100644 --- a/ui/cocoa.m +++ b/ui/cocoa.m @@ -117,6 +117,21 @@ bool stretch_video; NSTextField *pauseLabel; NSArray * supportedImageFileTypes; +// Utility function to run specified code block with iothread lock held +typedef void (^CodeBlock)(void); + +static void with_iothread_lock(CodeBlock block) +{ + bool locked = qemu_mutex_iothread_locked(); + if (!locked) { + qemu_mutex_lock_iothread(); + } + block(); + if (!locked) { + qemu_mutex_unlock_iothread(); + } +} + // Mac to QKeyCode conversion const int mac_to_qkeycode_map[] = { [kVK_ANSI_A] = Q_KEY_CODE_A, @@ -294,6 +309,7 @@ static void handleAnyDeviceErrors(Error * err) - (void) toggleFullScreen:(id)sender; - (void) handleMonitorInput:(NSEvent *)event; - (void) handleEvent:(NSEvent *)event; +- (void) handleEventLocked:(NSEvent *)event; - (void) setAbsoluteEnabled:(BOOL)tIsAbsoluteEnabled; /* The state surrounding mouse grabbing is potentially confusing. * isAbsoluteEnabled tracks qemu_input_is_absolute() [ie "is the emulated @@ -632,8 +648,14 @@ QemuCocoaView *cocoaView; - (void) handleEvent:(NSEvent *)event { - COCOA_DEBUG("QemuCocoaView: handleEvent\n"); + with_iothread_lock(^{ + [self handleEventLocked:event]; + }); +} +- (void) handleEventLocked:(NSEvent *)event +{ + COCOA_DEBUG("QemuCocoaView: handleEvent\n"); int buttons = 0; int keycode = 0; bool mouse_event = false; @@ -928,15 +950,18 @@ QemuCocoaView *cocoaView; */ - (void) raiseAllKeys { - int index; const int max_index = ARRAY_SIZE(modifiers_state); - for (index = 0; index < max_index; index++) { - if (modifiers_state[index]) { - modifiers_state[index] = 0; - qemu_input_event_send_key_qcode(dcl->con, index, false); - } - } + with_iothread_lock(^{ + int index; + + for (index = 0; index < max_index; index++) { + if (modifiers_state[index]) { + modifiers_state[index] = 0; + qemu_input_event_send_key_qcode(dcl->con, index, false); + } + } + }); } @end @@ -1198,13 +1223,17 @@ QemuCocoaView *cocoaView; /* Restarts QEMU */ - (void)restartQEMU:(id)sender { - qmp_system_reset(NULL); + with_iothread_lock(^{ + qmp_system_reset(NULL); + }); } /* Powers down QEMU */ - (void)powerDownQEMU:(id)sender { - qmp_system_powerdown(NULL); + with_iothread_lock(^{ + qmp_system_powerdown(NULL); + }); } /* Ejects the media. @@ -1220,9 +1249,11 @@ QemuCocoaView *cocoaView; return; } - Error *err = NULL; - qmp_eject(true, [drive cStringUsingEncoding: NSASCIIStringEncoding], - false, NULL, false, false, &err); + __block Error *err = NULL; + with_iothread_lock(^{ + qmp_eject(true, [drive cStringUsingEncoding: NSASCIIStringEncoding], + false, NULL, false, false, &err); + }); handleAnyDeviceErrors(err); } @@ -1254,16 +1285,18 @@ QemuCocoaView *cocoaView; return; } - Error *err = NULL; - qmp_blockdev_change_medium(true, - [drive cStringUsingEncoding: - NSASCIIStringEncoding], - false, NULL, - [file cStringUsingEncoding: - NSASCIIStringEncoding], - true, "raw", - false, 0, - &err); + __block Error *err = NULL; + with_iothread_lock(^{ + qmp_blockdev_change_medium(true, + [drive cStringUsingEncoding: + NSASCIIStringEncoding], + false, NULL, + [file cStringUsingEncoding: + NSASCIIStringEncoding], + true, "raw", + false, 0, + &err); + }); handleAnyDeviceErrors(err); } } @@ -1402,7 +1435,9 @@ QemuCocoaView *cocoaView; // get the throttle percentage throttle_pct = [sender tag]; - cpu_throttle_set(throttle_pct); + with_iothread_lock(^{ + cpu_throttle_set(throttle_pct); + }); COCOA_DEBUG("cpu throttling at %d%c\n", cpu_throttle_get_percentage(), '%'); } From patchwork Sat Dec 1 12:30:53 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Peter Maydell X-Patchwork-Id: 152630 Delivered-To: patches@linaro.org Received: by 2002:a2e:299d:0:0:0:0:0 with SMTP id p29-v6csp4762437ljp; Sat, 1 Dec 2018 04:31:03 -0800 (PST) X-Google-Smtp-Source: AFSGD/VHOo0ACMxRyCp626YVOuIAUsK2580Bt89uo1ZO12GHBp/UAMVC0msCxhdEofpJH1QzUpHR X-Received: by 2002:a63:111c:: with SMTP id g28mr7731079pgl.85.1543667463424; Sat, 01 Dec 2018 04:31:03 -0800 (PST) ARC-Seal: i=1; a=rsa-sha256; t=1543667463; cv=none; d=google.com; s=arc-20160816; b=pRAzH48BXE1KysODM46koH7Hp+LkXHqYdxw64GpG+Q3wBSaw18h95MogK0JZHIl6m4 EmLg7drncrr6YR2K9e89lzIFwoCKvyT3StvwL2IzpbGWfXWA95B1nfne1xTPLL8B+nRi 24g9+nleVV1xT0fFaCFfG/NLe9JiQTEwXtUxxgLFITcuV9F4mbo75MzhwI1J73uF09RR 72+msxW65c06MXZR8glkxzMCdUCAVKXTaJSgsAa2CC6HNvcAowPy+DKawtUtO1uJTHxt hWzP7M5Nn9tUk96DIFs3gKvhzKknsCyWMm6806xoAuXxyK1h/k/b3IeE2Rjmm8UgtTxu AkfA== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from; bh=tUCoK2+FxP7AGAVFFg2Bu5MbSW+hd5jAZdPtiktQsBw=; b=ak9uGwTzl5q9DO81lel1t4x1FKhHo1j1o+xv8jcXZkctt7ppnBu4dGRw/EBrWpaaFD pohiUKs9+uiv/hiObypiO4Jgs/2JNQ4O2IqKk0ies/grA/nseKLrCqXn5h3XMwhV8SPH yiUSCR8W0o2qmLeYpUKNDj/m+/M3iDu6QvpLT9gtjHUuWCJMSYAeb6jPLnoKqmHd7tVl VNHLGeDYKist6Nrs23C7FKZRIdmnlZNBjhXuH+kFSdo+80+2bu1FVqeFrkGrmEP3OAbZ ZHqGnmY0ME1t9Pu7Gt9jeUdwPNMkBO3XIpW8HNg/s/+4izyvQ23p5ZmRvqsfkVbSk0pe 2qIQ== ARC-Authentication-Results: i=1; mx.google.com; spf=pass (google.com: best guess record for domain of pm215@archaic.org.uk designates 2001:8b0:1d0::2 as permitted sender) smtp.mailfrom=pm215@archaic.org.uk; dmarc=fail (p=NONE sp=NONE dis=NONE) header.from=linaro.org Return-Path: Received: from orth.archaic.org.uk (orth.archaic.org.uk. [2001:8b0:1d0::2]) by mx.google.com with ESMTPS id q8si7712758pgc.580.2018.12.01.04.31.02 for (version=TLS1_2 cipher=ECDHE-RSA-CHACHA20-POLY1305 bits=256/256); Sat, 01 Dec 2018 04:31:02 -0800 (PST) Received-SPF: pass (google.com: best guess record for domain of pm215@archaic.org.uk designates 2001:8b0:1d0::2 as permitted sender) client-ip=2001:8b0:1d0::2; Authentication-Results: mx.google.com; spf=pass (google.com: best guess record for domain of pm215@archaic.org.uk designates 2001:8b0:1d0::2 as permitted sender) smtp.mailfrom=pm215@archaic.org.uk; dmarc=fail (p=NONE sp=NONE dis=NONE) header.from=linaro.org Received: from pm215 by orth.archaic.org.uk with local (Exim 4.89) (envelope-from ) id 1gT4QE-0008N7-FT; Sat, 01 Dec 2018 12:30:58 +0000 From: Peter Maydell To: qemu-devel@nongnu.org Cc: patches@linaro.org, John Arbuckle , Berkus Decker , Gerd Hoffmann , Roman Bolshakov Subject: [RFC 2/5] ui/cocoa: Use the pixman image directly in switchSurface Date: Sat, 1 Dec 2018 12:30:53 +0000 Message-Id: <20181201123056.432-3-peter.maydell@linaro.org> X-Mailer: git-send-email 2.19.2 In-Reply-To: <20181201123056.432-1-peter.maydell@linaro.org> References: <20181201123056.432-1-peter.maydell@linaro.org> MIME-Version: 1.0 Currently the switchSurface method takes a DisplaySurface. We want to change our DisplayChangeListener's dpy_gfx_switch callback to do this work asynchronously on a different thread. The caller of the switch callback will free the old DisplaySurface immediately the callback returns, so to ensure that the other thread doesn't access freed data we need to switch to using the underlying pixman image instead. The pixman image is reference counted, so we will be able to take a reference to it to avoid it vanishing too early. In this commit we only change the switchSurface method to take a pixman image, and keep the flow of control synchronous for now. Signed-off-by: Peter Maydell --- ui/cocoa.m | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) -- 2.19.2 diff --git a/ui/cocoa.m b/ui/cocoa.m index 9148ecdeb4c..997b0199c6c 100644 --- a/ui/cocoa.m +++ b/ui/cocoa.m @@ -303,7 +303,7 @@ static void handleAnyDeviceErrors(Error * err) BOOL isAbsoluteEnabled; BOOL isMouseDeassociated; } -- (void) switchSurface:(DisplaySurface *)surface; +- (void) switchSurface:(pixman_image_t *)image; - (void) grabMouse; - (void) ungrabMouse; - (void) toggleFullScreen:(id)sender; @@ -478,12 +478,13 @@ QemuCocoaView *cocoaView; } } -- (void) switchSurface:(DisplaySurface *)surface +- (void) switchSurface:(pixman_image_t *)image { COCOA_DEBUG("QemuCocoaView: switchSurface\n"); - int w = surface_width(surface); - int h = surface_height(surface); + int w = pixman_image_get_width(image); + int h = pixman_image_get_height(image); + pixman_format_code_t image_format = pixman_image_get_format(image); /* cdx == 0 means this is our very first surface, in which case we need * to recalculate the content dimensions even if it happens to be the size * of the initial empty window. @@ -505,10 +506,10 @@ QemuCocoaView *cocoaView; CGDataProviderRelease(dataProviderRef); //sync host window color space with guests - screen.bitsPerPixel = surface_bits_per_pixel(surface); - screen.bitsPerComponent = surface_bytes_per_pixel(surface) * 2; + screen.bitsPerPixel = PIXMAN_FORMAT_BPP(image_format); + screen.bitsPerComponent = DIV_ROUND_UP(screen.bitsPerPixel, 8) * 2; - dataProviderRef = CGDataProviderCreateWithData(NULL, surface_data(surface), w * 4 * h, NULL); + dataProviderRef = CGDataProviderCreateWithData(NULL, pixman_image_get_data(image), w * 4 * h, NULL); // update windows if (isFullscreen) { @@ -1608,7 +1609,7 @@ static void cocoa_switch(DisplayChangeListener *dcl, NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init]; COCOA_DEBUG("qemu_cocoa: cocoa_switch\n"); - [cocoaView switchSurface:surface]; + [cocoaView switchSurface:surface->image]; [pool release]; } From patchwork Sat Dec 1 12:30:54 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Peter Maydell X-Patchwork-Id: 152627 Delivered-To: patches@linaro.org Received: by 2002:a2e:299d:0:0:0:0:0 with SMTP id p29-v6csp4762384ljp; Sat, 1 Dec 2018 04:30:59 -0800 (PST) X-Google-Smtp-Source: AFSGD/UpH+w/9YQcMxA43nfir9i9f/WH4tlyNDtZXYWSGBu4BgQSEMOLgZs2cPeNJRkICYD5lXIV X-Received: by 2002:a1c:e58c:: with SMTP id c134mr2064493wmh.124.1543667459862; Sat, 01 Dec 2018 04:30:59 -0800 (PST) ARC-Seal: i=1; a=rsa-sha256; t=1543667459; cv=none; d=google.com; s=arc-20160816; b=NuOqdVKnh2lzqbwMRUP/OjubeBeaSV5qlOhxYFx/vmF+r5GYsN3MmRDlLb9QEQM2/E x/yiN0vUN1/SZKVwApGi+Cm9YPzEEKAgwXHEuG2mfdWh7NSmWNBrSJOSYcw4QBEfwZvG 4jWkUU4A6epRiUIv2+Eo4oLDfOsWiKcxNkBDGcB3gcVzd0ASJ0OTUUGyo1dKzOOFApBL 71HYXKqVovrFLZZzyGcJLyoNiZ37eiSGXyAbrftdzY3o1s/sQl8jiWuIF5jPD56d5R3L npl7sgfObtM1+A0PPls7lpmOVLjdEhUN8SOjM5LbD0b7OUMmpulzCwukIrEQ2AH1LwJU argw== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from; bh=aKFdDX/HMeBrcew1z6tiY+shi2lGG+U7ZN0NRALLBM4=; b=KRA9BQ74p5unjKky9peWsggMInDU1Ih+u6KV1E6iDWXIDtds8ZGxavL8wR48jy3PId buahF8kOlvgOpdpGv9NBuj27mYbP5TfMbN0DhdHAgkJ6ymcYIrGY8wkyvESaeUmtivBu tVW5HL3ULovwE8PXOXrMvYOckhHe4A4w26NGwaVknkDn+SGI0WaY3o96Qlc8aJWDF9Nu zY0oPJIGqSCa4rNLOuvbN+T6Gv3RDLMfLuBeU6CjvoN7hL3MEFKIYr9XInJImRGoHOih vUFLAGeWyA1vnih/r5DvD0ZFzf2O2l/y22vzh5KMe1Vi8xxwZld70jvlQ5onN5hbxFaz p8Tg== ARC-Authentication-Results: i=1; mx.google.com; spf=pass (google.com: best guess record for domain of pm215@archaic.org.uk designates 2001:8b0:1d0::2 as permitted sender) smtp.mailfrom=pm215@archaic.org.uk; dmarc=fail (p=NONE sp=NONE dis=NONE) header.from=linaro.org Return-Path: Received: from orth.archaic.org.uk (orth.archaic.org.uk. [2001:8b0:1d0::2]) by mx.google.com with ESMTPS id e6si5999417wrr.345.2018.12.01.04.30.59 for (version=TLS1_2 cipher=ECDHE-RSA-CHACHA20-POLY1305 bits=256/256); Sat, 01 Dec 2018 04:30:59 -0800 (PST) Received-SPF: pass (google.com: best guess record for domain of pm215@archaic.org.uk designates 2001:8b0:1d0::2 as permitted sender) client-ip=2001:8b0:1d0::2; Authentication-Results: mx.google.com; spf=pass (google.com: best guess record for domain of pm215@archaic.org.uk designates 2001:8b0:1d0::2 as permitted sender) smtp.mailfrom=pm215@archaic.org.uk; dmarc=fail (p=NONE sp=NONE dis=NONE) header.from=linaro.org Received: from pm215 by orth.archaic.org.uk with local (Exim 4.89) (envelope-from ) id 1gT4QF-0008NS-5k; Sat, 01 Dec 2018 12:30:59 +0000 From: Peter Maydell To: qemu-devel@nongnu.org Cc: patches@linaro.org, John Arbuckle , Berkus Decker , Gerd Hoffmann , Roman Bolshakov Subject: [RFC 3/5] ui/cocoa: Factor out initial menu creation Date: Sat, 1 Dec 2018 12:30:54 +0000 Message-Id: <20181201123056.432-4-peter.maydell@linaro.org> X-Mailer: git-send-email 2.19.2 In-Reply-To: <20181201123056.432-1-peter.maydell@linaro.org> References: <20181201123056.432-1-peter.maydell@linaro.org> MIME-Version: 1.0 Factor out the long code sequence in main() which creates the initial set of menus. This will make later patches which move initialization code around a bit clearer. Signed-off-by: Peter Maydell --- ui/cocoa.m | 78 ++++++++++++++++++++++++++++-------------------------- 1 file changed, 41 insertions(+), 37 deletions(-) -- 2.19.2 diff --git a/ui/cocoa.m b/ui/cocoa.m index 997b0199c6c..da511bfb4fa 100644 --- a/ui/cocoa.m +++ b/ui/cocoa.m @@ -1444,43 +1444,8 @@ QemuCocoaView *cocoaView; @end - -int main (int argc, const char * argv[]) { - - gArgc = argc; - gArgv = (char **)argv; - int i; - - /* In case we don't need to display a window, let's not do that */ - for (i = 1; i < argc; i++) { - const char *opt = argv[i]; - - if (opt[0] == '-') { - /* Treat --foo the same as -foo. */ - if (opt[1] == '-') { - opt++; - } - if (!strcmp(opt, "-h") || !strcmp(opt, "-help") || - !strcmp(opt, "-vnc") || - !strcmp(opt, "-nographic") || - !strcmp(opt, "-version") || - !strcmp(opt, "-curses") || - !strcmp(opt, "-display") || - !strcmp(opt, "-qtest")) { - return qemu_main(gArgc, gArgv, *_NSGetEnviron()); - } - } - } - - NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init]; - - // Pull this console process up to being a fully-fledged graphical - // app with a menubar and Dock icon - ProcessSerialNumber psn = { 0, kCurrentProcess }; - TransformProcessType(&psn, kProcessTransformToForegroundApplication); - - [NSApplication sharedApplication]; - +static void create_initial_menus(void) +{ // Add menus NSMenu *menu; NSMenuItem *menuItem; @@ -1564,6 +1529,45 @@ int main (int argc, const char * argv[]) { menuItem = [[[NSMenuItem alloc] initWithTitle:@"Window" action:nil keyEquivalent:@""] autorelease]; [menuItem setSubmenu:menu]; [[NSApp mainMenu] addItem:menuItem]; +} + +int main (int argc, const char * argv[]) { + + gArgc = argc; + gArgv = (char **)argv; + int i; + + /* In case we don't need to display a window, let's not do that */ + for (i = 1; i < argc; i++) { + const char *opt = argv[i]; + + if (opt[0] == '-') { + /* Treat --foo the same as -foo. */ + if (opt[1] == '-') { + opt++; + } + if (!strcmp(opt, "-h") || !strcmp(opt, "-help") || + !strcmp(opt, "-vnc") || + !strcmp(opt, "-nographic") || + !strcmp(opt, "-version") || + !strcmp(opt, "-curses") || + !strcmp(opt, "-display") || + !strcmp(opt, "-qtest")) { + return qemu_main(gArgc, gArgv, *_NSGetEnviron()); + } + } + } + + NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init]; + + // Pull this console process up to being a fully-fledged graphical + // app with a menubar and Dock icon + ProcessSerialNumber psn = { 0, kCurrentProcess }; + TransformProcessType(&psn, kProcessTransformToForegroundApplication); + + [NSApplication sharedApplication]; + + create_initial_menus(); // Create an Application controller QemuCocoaAppController *appController = [[QemuCocoaAppController alloc] init]; From patchwork Sat Dec 1 12:30:55 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Peter Maydell X-Patchwork-Id: 152628 Delivered-To: patches@linaro.org Received: by 2002:a2e:299d:0:0:0:0:0 with SMTP id p29-v6csp4762392ljp; Sat, 1 Dec 2018 04:31:00 -0800 (PST) X-Google-Smtp-Source: AFSGD/V5eztJzb7ae2czMyp1EXjSbw4JvUCbjiiZwiYRyENyYR9L7uQfXAL624Ll7lcXtQSVVoEx X-Received: by 2002:a1c:2c56:: with SMTP id s83mr2318654wms.48.1543667460568; Sat, 01 Dec 2018 04:31:00 -0800 (PST) ARC-Seal: i=1; a=rsa-sha256; t=1543667460; cv=none; d=google.com; s=arc-20160816; b=tgoc5d2lc8czRWpkuJshKgLVXUrpwlpP0CfB2dl29GK8SUCQLzX13w6hTB0dGaTWEy +1QUPrGrEU/kzg+h7a4TJON9NrYMQzxoSS+QeHVHKKXWeiqvwOjBU0+W7FPhCB+3QNch yaRmarr8mzGFzgNJ48ZygZW1yg0ExH+Dz3jQwTflboqe69n5xPT2tWbqGnucnITkLtrZ nVOX32+MAj3QCNfOsMPpr/gctuk4+ZnMqJstHvKqFMpF1E4nZp32QIy7WWFOumnr22ap ngGLBwxPGjQtCf2XxDZyrFZhQgK27kY8tL6Mg48tA+aaoLY9GO0kgshp5QFrKbQUDcXc nGeQ== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from; bh=MYyARyHqSztH4DXB+OUOUTwU1wGp31ywZesFNAb5r3o=; b=z2gGTgsMzbP3qE3zL4eLlJCTPqlrCKRg1J3swdnBpfE30N4delgpwJqX0ZMIbVtQGT ntpoBDms3ugam8losMJrH0EH+V9FX9th1QKZNj5fxu+MNwlxfWf5uOcLeeAX7Qev09Nm Wkh1y8jhB6AJNaBtrnItmdWHHIeMSt9iGN9ZBp40Id08rshSeq0RYIgBEz4DUBbUwOiB 73F4DX8ruiILcJtXPOpoxjoJbGm7zOg7qmU4ycxTKkg9OUHY3ezgqeEZrmEg616kbR2b awOdKpwyfRpiSv4I2i0KmLuMo6wbcP95MG5PcQn7+HJUGmrN9VAYxQd3LRanGDZgaTWv n3UQ== ARC-Authentication-Results: i=1; mx.google.com; spf=pass (google.com: best guess record for domain of pm215@archaic.org.uk designates 2001:8b0:1d0::2 as permitted sender) smtp.mailfrom=pm215@archaic.org.uk; dmarc=fail (p=NONE sp=NONE dis=NONE) header.from=linaro.org Return-Path: Received: from orth.archaic.org.uk (orth.archaic.org.uk. [2001:8b0:1d0::2]) by mx.google.com with ESMTPS id x14si5785053wrs.221.2018.12.01.04.31.00 for (version=TLS1_2 cipher=ECDHE-RSA-CHACHA20-POLY1305 bits=256/256); Sat, 01 Dec 2018 04:31:00 -0800 (PST) Received-SPF: pass (google.com: best guess record for domain of pm215@archaic.org.uk designates 2001:8b0:1d0::2 as permitted sender) client-ip=2001:8b0:1d0::2; Authentication-Results: mx.google.com; spf=pass (google.com: best guess record for domain of pm215@archaic.org.uk designates 2001:8b0:1d0::2 as permitted sender) smtp.mailfrom=pm215@archaic.org.uk; dmarc=fail (p=NONE sp=NONE dis=NONE) header.from=linaro.org Received: from pm215 by orth.archaic.org.uk with local (Exim 4.89) (envelope-from ) id 1gT4QF-0008Nm-Sk; Sat, 01 Dec 2018 12:30:59 +0000 From: Peter Maydell To: qemu-devel@nongnu.org Cc: patches@linaro.org, John Arbuckle , Berkus Decker , Gerd Hoffmann , Roman Bolshakov Subject: [RFC 4/5] ui/cocoa: Move console/device menu creation code up in file Date: Sat, 1 Dec 2018 12:30:55 +0000 Message-Id: <20181201123056.432-5-peter.maydell@linaro.org> X-Mailer: git-send-email 2.19.2 In-Reply-To: <20181201123056.432-1-peter.maydell@linaro.org> References: <20181201123056.432-1-peter.maydell@linaro.org> MIME-Version: 1.0 Move the console/device menu creation code functions further up in the source file, next to the code which creates the initial menus. We're going to want to change the location we call these functions from in the next patch. This commit is a pure code move with no other changes. Signed-off-by: Peter Maydell --- ui/cocoa.m | 184 ++++++++++++++++++++++++++--------------------------- 1 file changed, 92 insertions(+), 92 deletions(-) -- 2.19.2 diff --git a/ui/cocoa.m b/ui/cocoa.m index da511bfb4fa..2f533c77c47 100644 --- a/ui/cocoa.m +++ b/ui/cocoa.m @@ -1531,6 +1531,98 @@ static void create_initial_menus(void) [[NSApp mainMenu] addItem:menuItem]; } +/* Returns a name for a given console */ +static NSString * getConsoleName(QemuConsole * console) +{ + return [NSString stringWithFormat: @"%s", qemu_console_get_label(console)]; +} + +/* Add an entry to the View menu for each console */ +static void add_console_menu_entries(void) +{ + NSMenu *menu; + NSMenuItem *menuItem; + int index = 0; + + menu = [[[NSApp mainMenu] itemWithTitle:@"View"] submenu]; + + [menu addItem:[NSMenuItem separatorItem]]; + + while (qemu_console_lookup_by_index(index) != NULL) { + menuItem = [[[NSMenuItem alloc] initWithTitle: getConsoleName(qemu_console_lookup_by_index(index)) + action: @selector(displayConsole:) keyEquivalent: @""] autorelease]; + [menuItem setTag: index]; + [menu addItem: menuItem]; + index++; + } +} + +/* Make menu items for all removable devices. + * Each device is given an 'Eject' and 'Change' menu item. + */ +static void addRemovableDevicesMenuItems(void) +{ + NSMenu *menu; + NSMenuItem *menuItem; + BlockInfoList *currentDevice, *pointerToFree; + NSString *deviceName; + + currentDevice = qmp_query_block(NULL); + pointerToFree = currentDevice; + if(currentDevice == NULL) { + NSBeep(); + QEMU_Alert(@"Failed to query for block devices!"); + return; + } + + menu = [[[NSApp mainMenu] itemWithTitle:@"Machine"] submenu]; + + // Add a separator between related groups of menu items + [menu addItem:[NSMenuItem separatorItem]]; + + // Set the attributes to the "Removable Media" menu item + NSString *titleString = @"Removable Media"; + NSMutableAttributedString *attString=[[NSMutableAttributedString alloc] initWithString:titleString]; + NSColor *newColor = [NSColor blackColor]; + NSFontManager *fontManager = [NSFontManager sharedFontManager]; + NSFont *font = [fontManager fontWithFamily:@"Helvetica" + traits:NSBoldFontMask|NSItalicFontMask + weight:0 + size:14]; + [attString addAttribute:NSFontAttributeName value:font range:NSMakeRange(0, [titleString length])]; + [attString addAttribute:NSForegroundColorAttributeName value:newColor range:NSMakeRange(0, [titleString length])]; + [attString addAttribute:NSUnderlineStyleAttributeName value:[NSNumber numberWithInt: 1] range:NSMakeRange(0, [titleString length])]; + + // Add the "Removable Media" menu item + menuItem = [NSMenuItem new]; + [menuItem setAttributedTitle: attString]; + [menuItem setEnabled: NO]; + [menu addItem: menuItem]; + + /* Loop through all the block devices in the emulator */ + while (currentDevice) { + deviceName = [[NSString stringWithFormat: @"%s", currentDevice->value->device] retain]; + + if(currentDevice->value->removable) { + menuItem = [[NSMenuItem alloc] initWithTitle: [NSString stringWithFormat: @"Change %s...", currentDevice->value->device] + action: @selector(changeDeviceMedia:) + keyEquivalent: @""]; + [menu addItem: menuItem]; + [menuItem setRepresentedObject: deviceName]; + [menuItem autorelease]; + + menuItem = [[NSMenuItem alloc] initWithTitle: [NSString stringWithFormat: @"Eject %s", currentDevice->value->device] + action: @selector(ejectDeviceMedia:) + keyEquivalent: @""]; + [menu addItem: menuItem]; + [menuItem setRepresentedObject: deviceName]; + [menuItem autorelease]; + } + currentDevice = currentDevice->next; + } + qapi_free_BlockInfoList(pointerToFree); +} + int main (int argc, const char * argv[]) { gArgc = argc; @@ -1659,98 +1751,6 @@ static const DisplayChangeListenerOps dcl_ops = { .dpy_refresh = cocoa_refresh, }; -/* Returns a name for a given console */ -static NSString * getConsoleName(QemuConsole * console) -{ - return [NSString stringWithFormat: @"%s", qemu_console_get_label(console)]; -} - -/* Add an entry to the View menu for each console */ -static void add_console_menu_entries(void) -{ - NSMenu *menu; - NSMenuItem *menuItem; - int index = 0; - - menu = [[[NSApp mainMenu] itemWithTitle:@"View"] submenu]; - - [menu addItem:[NSMenuItem separatorItem]]; - - while (qemu_console_lookup_by_index(index) != NULL) { - menuItem = [[[NSMenuItem alloc] initWithTitle: getConsoleName(qemu_console_lookup_by_index(index)) - action: @selector(displayConsole:) keyEquivalent: @""] autorelease]; - [menuItem setTag: index]; - [menu addItem: menuItem]; - index++; - } -} - -/* Make menu items for all removable devices. - * Each device is given an 'Eject' and 'Change' menu item. - */ -static void addRemovableDevicesMenuItems(void) -{ - NSMenu *menu; - NSMenuItem *menuItem; - BlockInfoList *currentDevice, *pointerToFree; - NSString *deviceName; - - currentDevice = qmp_query_block(NULL); - pointerToFree = currentDevice; - if(currentDevice == NULL) { - NSBeep(); - QEMU_Alert(@"Failed to query for block devices!"); - return; - } - - menu = [[[NSApp mainMenu] itemWithTitle:@"Machine"] submenu]; - - // Add a separator between related groups of menu items - [menu addItem:[NSMenuItem separatorItem]]; - - // Set the attributes to the "Removable Media" menu item - NSString *titleString = @"Removable Media"; - NSMutableAttributedString *attString=[[NSMutableAttributedString alloc] initWithString:titleString]; - NSColor *newColor = [NSColor blackColor]; - NSFontManager *fontManager = [NSFontManager sharedFontManager]; - NSFont *font = [fontManager fontWithFamily:@"Helvetica" - traits:NSBoldFontMask|NSItalicFontMask - weight:0 - size:14]; - [attString addAttribute:NSFontAttributeName value:font range:NSMakeRange(0, [titleString length])]; - [attString addAttribute:NSForegroundColorAttributeName value:newColor range:NSMakeRange(0, [titleString length])]; - [attString addAttribute:NSUnderlineStyleAttributeName value:[NSNumber numberWithInt: 1] range:NSMakeRange(0, [titleString length])]; - - // Add the "Removable Media" menu item - menuItem = [NSMenuItem new]; - [menuItem setAttributedTitle: attString]; - [menuItem setEnabled: NO]; - [menu addItem: menuItem]; - - /* Loop through all the block devices in the emulator */ - while (currentDevice) { - deviceName = [[NSString stringWithFormat: @"%s", currentDevice->value->device] retain]; - - if(currentDevice->value->removable) { - menuItem = [[NSMenuItem alloc] initWithTitle: [NSString stringWithFormat: @"Change %s...", currentDevice->value->device] - action: @selector(changeDeviceMedia:) - keyEquivalent: @""]; - [menu addItem: menuItem]; - [menuItem setRepresentedObject: deviceName]; - [menuItem autorelease]; - - menuItem = [[NSMenuItem alloc] initWithTitle: [NSString stringWithFormat: @"Eject %s", currentDevice->value->device] - action: @selector(ejectDeviceMedia:) - keyEquivalent: @""]; - [menu addItem: menuItem]; - [menuItem setRepresentedObject: deviceName]; - [menuItem autorelease]; - } - currentDevice = currentDevice->next; - } - qapi_free_BlockInfoList(pointerToFree); -} - static void cocoa_display_init(DisplayState *ds, DisplayOptions *opts) { COCOA_DEBUG("qemu_cocoa: cocoa_display_init\n"); From patchwork Sat Dec 1 12:30:56 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Peter Maydell X-Patchwork-Id: 152629 Delivered-To: patches@linaro.org Received: by 2002:a2e:299d:0:0:0:0:0 with SMTP id p29-v6csp4762397ljp; Sat, 1 Dec 2018 04:31:01 -0800 (PST) X-Google-Smtp-Source: AFSGD/W7fq2Jpp+qSUSPOJVXvNsTgburUwGUBn9d6DDxsWnp+W0voqAf0QuuCAKn34KZwlVfk4Hq X-Received: by 2002:a1c:f404:: with SMTP id z4mr2173860wma.68.1543667461177; Sat, 01 Dec 2018 04:31:01 -0800 (PST) ARC-Seal: i=1; a=rsa-sha256; t=1543667461; cv=none; d=google.com; s=arc-20160816; b=qyMaJM1hx1f0eMZ8hqbD0ECK25afmHYOOwpirZPZbL8aPIBdu3wZUBZtGsEjg1hC9i 5IEalC16UTZUUrUBw9oq9iphD0EHXM1LwmR9JPnoL8MOd0x/o3dbAkmCTAhTKrLB7FN/ N9vqX3O3m8HkZtWPCH8GfKdU+whRCP1etvlWD9PyumQ6+e6YmHpQngCAgl5IILS0lb2g 3HYKYQ9K9KLvkBrcAQmHJrTTDTUgI8mDV+Bkxxy+slMXL2GMEfP6bebjENXkftHJZ9iy Mtfm98MXfpZYL86oNmbzfRaecqcMvQU2GIfNSZAxx9l6C9CYynSgmDrvpsKy00ZpLVlq iLww== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from; bh=DKd65wGJdYgiGBGn3MuhJAKrXHfuLvMkz26oD3So1m4=; b=PtjE+BA29+TUYK6rpwp174FQeEUkmF8c1L/8EoxKv0EOVyAO3kXZ7JcC6pQixNY2zP h6ALz/ShNyFBoggIL3OdcwyXplIAOI4E4+pd949yAtBuZyxev33jlFYEy8Rd9qkNYBOz 9WEkRsvZHrSKzlHkiCpb6m05dpL8z69CnB43Iwg71/0szIvc+0htxLJMf5p+pxHb+WZG jwZmNajJJWL8h7AQ0cpDWMKZlTncWrbB50X8h+KrO0maIdzpH9CEBtq+x/zM98FykkrX gbysrBRGCxBo9d0dpUrN5AengmmjDszsznddbYEdJuuuymvUXTOvTZhFKYvEUcJQ2P8y dWUw== ARC-Authentication-Results: i=1; mx.google.com; spf=pass (google.com: best guess record for domain of pm215@archaic.org.uk designates 2001:8b0:1d0::2 as permitted sender) smtp.mailfrom=pm215@archaic.org.uk; dmarc=fail (p=NONE sp=NONE dis=NONE) header.from=linaro.org Return-Path: Received: from orth.archaic.org.uk (orth.archaic.org.uk. [2001:8b0:1d0::2]) by mx.google.com with ESMTPS id j19si1777247wmc.124.2018.12.01.04.31.00 for (version=TLS1_2 cipher=ECDHE-RSA-CHACHA20-POLY1305 bits=256/256); Sat, 01 Dec 2018 04:31:01 -0800 (PST) Received-SPF: pass (google.com: best guess record for domain of pm215@archaic.org.uk designates 2001:8b0:1d0::2 as permitted sender) client-ip=2001:8b0:1d0::2; Authentication-Results: mx.google.com; spf=pass (google.com: best guess record for domain of pm215@archaic.org.uk designates 2001:8b0:1d0::2 as permitted sender) smtp.mailfrom=pm215@archaic.org.uk; dmarc=fail (p=NONE sp=NONE dis=NONE) header.from=linaro.org Received: from pm215 by orth.archaic.org.uk with local (Exim 4.89) (envelope-from ) id 1gT4QG-0008O4-Hj; Sat, 01 Dec 2018 12:31:00 +0000 From: Peter Maydell To: qemu-devel@nongnu.org Cc: patches@linaro.org, John Arbuckle , Berkus Decker , Gerd Hoffmann , Roman Bolshakov Subject: [RFC 5/5] ui/cocoa: Perform UI operations only on the main thread Date: Sat, 1 Dec 2018 12:30:56 +0000 Message-Id: <20181201123056.432-6-peter.maydell@linaro.org> X-Mailer: git-send-email 2.19.2 In-Reply-To: <20181201123056.432-1-peter.maydell@linaro.org> References: <20181201123056.432-1-peter.maydell@linaro.org> MIME-Version: 1.0 The OSX Mojave release is more picky about enforcing the Cocoa API restriction that only the main thread may perform UI calls. To accommodate this we need to restructure the Cocoa code: * the special OSX main() creates a second thread and uses that to call the vl.c qemu_main(); the original main thread goes into the OSX event loop * the refresh, switch and update callbacks asynchronously tell the main thread to do the necessary work * the refresh callback no longer does the "get events from the UI event queue and handle them" loop, since we now use the stock OSX event loop All these things have to be changed in one commit, to avoid breaking bisection. Note that since we use dispatch_get_main_queue(), this bumps our minimum version requirement to OSX 10.10 Yosemite (released in 2014, unsupported by Apple since 2017). Signed-off-by: Peter Maydell --- ui/cocoa.m | 185 +++++++++++++++++++++++++++++++---------------------- 1 file changed, 110 insertions(+), 75 deletions(-) -- 2.19.2 diff --git a/ui/cocoa.m b/ui/cocoa.m index 2f533c77c47..3897d2e370b 100644 --- a/ui/cocoa.m +++ b/ui/cocoa.m @@ -117,6 +117,9 @@ bool stretch_video; NSTextField *pauseLabel; NSArray * supportedImageFileTypes; +QemuSemaphore display_init_sem; +QemuSemaphore app_started_sem; + // Utility function to run specified code block with iothread lock held typedef void (^CodeBlock)(void); @@ -297,6 +300,7 @@ static void handleAnyDeviceErrors(Error * err) NSWindow *fullScreenWindow; float cx,cy,cw,ch,cdx,cdy; CGDataProviderRef dataProviderRef; + pixman_image_t *pixman_image; BOOL modifiers_state[256]; BOOL isMouseGrabbed; BOOL isFullscreen; @@ -502,13 +506,16 @@ QemuCocoaView *cocoaView; } // update screenBuffer - if (dataProviderRef) + if (dataProviderRef) { CGDataProviderRelease(dataProviderRef); + pixman_image_unref(pixman_image); + } //sync host window color space with guests screen.bitsPerPixel = PIXMAN_FORMAT_BPP(image_format); screen.bitsPerComponent = DIV_ROUND_UP(screen.bitsPerPixel, 8) * 2; + pixman_image = image; dataProviderRef = CGDataProviderCreateWithData(NULL, pixman_image_get_data(image), w * 4 * h, NULL); // update windows @@ -979,7 +986,6 @@ QemuCocoaView *cocoaView; #endif { } -- (void)startEmulationWithArgc:(int)argc argv:(char**)argv; - (void)doToggleFullScreen:(id)sender; - (void)toggleFullScreen:(id)sender; - (void)showQEMUDoc:(id)sender; @@ -1067,8 +1073,8 @@ QemuCocoaView *cocoaView; - (void)applicationDidFinishLaunching: (NSNotification *) note { COCOA_DEBUG("QemuCocoaAppController: applicationDidFinishLaunching\n"); - // launch QEMU, with the global args - [self startEmulationWithArgc:gArgc argv:(char **)gArgv]; + /* Tell cocoa_display_init to proceed */ + qemu_sem_post(&app_started_sem); } - (void)applicationWillTerminate:(NSNotification *)aNotification @@ -1111,15 +1117,6 @@ QemuCocoaView *cocoaView; [cocoaView raiseAllKeys]; } -- (void)startEmulationWithArgc:(int)argc argv:(char**)argv -{ - COCOA_DEBUG("QemuCocoaAppController: startEmulationWithArgc\n"); - - int status; - status = qemu_main(argc, argv, *_NSGetEnviron()); - exit(status); -} - /* We abstract the method called by the Enter Fullscreen menu item * because Mac OS 10.7 and higher disables it. This is because of the * menu item's old selector's name toggleFullScreen: @@ -1623,32 +1620,59 @@ static void addRemovableDevicesMenuItems(void) qapi_free_BlockInfoList(pointerToFree); } -int main (int argc, const char * argv[]) { +/* + * The startup process for the OSX/Cocoa UI is complicated, because + * OSX insists that the UI runs on the initial main thread, and so we + * need to start a second thread which runs the vl.c qemu_main(): + * + * Initial thread: 2nd thread: + * in main(): + * create qemu-main thread + * wait on display_init semaphore + * call qemu_main() + * ... + * in cocoa_display_init(): + * post the display_init semaphore + * wait on app_started semaphore + * create application, menus, etc + * enter OSX run loop + * in applicationDidFinishLaunching: + * post app_started semaphore + * tell main thread to fullscreen if needed + * [...] + * run qemu main-loop + * + * We do this in two stages so that we don't do the creation of the + * GUI application menus and so on for command line options like --help + * where we want to just print text to stdout and exit immediately. + */ +static void *call_qemu_main(void *opaque) +{ + int status; + + COCOA_DEBUG("Second thread: calling qemu_main()\n"); + status = qemu_main(gArgc, gArgv, *_NSGetEnviron()); + COCOA_DEBUG("Second thread: qemu_main() returned, exiting\n"); + exit(status); +} + +int main (int argc, const char * argv[]) { + QemuThread thread; + + COCOA_DEBUG("Entered main()\n"); gArgc = argc; gArgv = (char **)argv; - int i; - /* In case we don't need to display a window, let's not do that */ - for (i = 1; i < argc; i++) { - const char *opt = argv[i]; + qemu_sem_init(&display_init_sem, 0); + qemu_sem_init(&app_started_sem, 0); - if (opt[0] == '-') { - /* Treat --foo the same as -foo. */ - if (opt[1] == '-') { - opt++; - } - if (!strcmp(opt, "-h") || !strcmp(opt, "-help") || - !strcmp(opt, "-vnc") || - !strcmp(opt, "-nographic") || - !strcmp(opt, "-version") || - !strcmp(opt, "-curses") || - !strcmp(opt, "-display") || - !strcmp(opt, "-qtest")) { - return qemu_main(gArgc, gArgv, *_NSGetEnviron()); - } - } - } + qemu_thread_create(&thread, "qemu_main", call_qemu_main, + NULL, QEMU_THREAD_DETACHED); + + COCOA_DEBUG("Main thread: waiting for display_init_sem\n"); + qemu_sem_wait(&display_init_sem); + COCOA_DEBUG("Main thread: initializing app\n"); NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init]; @@ -1661,12 +1685,24 @@ int main (int argc, const char * argv[]) { create_initial_menus(); + /* + * Create the menu entries which depend on QEMU state (for consoles + * and removeable devices). These make calls back into QEMU functions, + * which is OK because at this point we know that the second thread + * holds the iothread lock and is synchronously waiting for us to + * finish. + */ + add_console_menu_entries(); + addRemovableDevicesMenuItems(); + // Create an Application controller QemuCocoaAppController *appController = [[QemuCocoaAppController alloc] init]; [NSApp setDelegate:appController]; // Start the main event loop + COCOA_DEBUG("Main thread: entering OSX run loop\n"); [NSApp run]; + COCOA_DEBUG("Main thread: left OSX run loop, exiting\n"); [appController release]; [pool release]; @@ -1684,17 +1720,19 @@ static void cocoa_update(DisplayChangeListener *dcl, COCOA_DEBUG("qemu_cocoa: cocoa_update\n"); - NSRect rect; - if ([cocoaView cdx] == 1.0) { - rect = NSMakeRect(x, [cocoaView gscreen].height - y - h, w, h); - } else { - rect = NSMakeRect( - x * [cocoaView cdx], - ([cocoaView gscreen].height - y - h) * [cocoaView cdy], - w * [cocoaView cdx], - h * [cocoaView cdy]); - } - [cocoaView setNeedsDisplayInRect:rect]; + dispatch_async(dispatch_get_main_queue(), ^{ + NSRect rect; + if ([cocoaView cdx] == 1.0) { + rect = NSMakeRect(x, [cocoaView gscreen].height - y - h, w, h); + } else { + rect = NSMakeRect( + x * [cocoaView cdx], + ([cocoaView gscreen].height - y - h) * [cocoaView cdy], + w * [cocoaView cdx], + h * [cocoaView cdy]); + } + [cocoaView setNeedsDisplayInRect:rect]; + }); [pool release]; } @@ -1703,9 +1741,19 @@ static void cocoa_switch(DisplayChangeListener *dcl, DisplaySurface *surface) { NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init]; + pixman_image_t *image = surface->image; COCOA_DEBUG("qemu_cocoa: cocoa_switch\n"); - [cocoaView switchSurface:surface->image]; + + // The DisplaySurface will be freed as soon as this callback returns. + // We take a reference to the underlying pixman image here so it does + // not disappear from under our feet; the switchSurface method will + // deref the old image when it is done with it. + pixman_image_ref(image); + + dispatch_async(dispatch_get_main_queue(), ^{ + [cocoaView switchSurface:image]; + }); [pool release]; } @@ -1717,24 +1765,15 @@ static void cocoa_refresh(DisplayChangeListener *dcl) graphic_hw_update(NULL); if (qemu_input_is_absolute()) { - if (![cocoaView isAbsoluteEnabled]) { - if ([cocoaView isMouseGrabbed]) { - [cocoaView ungrabMouse]; + dispatch_async(dispatch_get_main_queue(), ^{ + if (![cocoaView isAbsoluteEnabled]) { + if ([cocoaView isMouseGrabbed]) { + [cocoaView ungrabMouse]; + } } - } - [cocoaView setAbsoluteEnabled:YES]; + [cocoaView setAbsoluteEnabled:YES]; + }); } - - NSDate *distantPast; - NSEvent *event; - distantPast = [NSDate distantPast]; - do { - event = [NSApp nextEventMatchingMask:NSEventMaskAny untilDate:distantPast - inMode: NSDefaultRunLoopMode dequeue:YES]; - if (event != nil) { - [cocoaView handleEvent:event]; - } - } while(event != nil); [pool release]; } @@ -1755,10 +1794,17 @@ static void cocoa_display_init(DisplayState *ds, DisplayOptions *opts) { COCOA_DEBUG("qemu_cocoa: cocoa_display_init\n"); + /* Tell main thread to go ahead and create the app and enter the run loop */ + qemu_sem_post(&display_init_sem); + qemu_sem_wait(&app_started_sem); + COCOA_DEBUG("cocoa_display_init: app start completed\n"); + /* if fullscreen mode is to be used */ if (opts->has_full_screen && opts->full_screen) { - [NSApp activateIgnoringOtherApps: YES]; - [(QemuCocoaAppController *)[[NSApplication sharedApplication] delegate] toggleFullScreen: nil]; + dispatch_async(dispatch_get_main_queue(), ^{ + [NSApp activateIgnoringOtherApps: YES]; + [(QemuCocoaAppController *)[[NSApplication sharedApplication] delegate] toggleFullScreen: nil]; + }); } dcl = g_malloc0(sizeof(DisplayChangeListener)); @@ -1769,17 +1815,6 @@ static void cocoa_display_init(DisplayState *ds, DisplayOptions *opts) // register cleanup function atexit(cocoa_cleanup); - - /* At this point QEMU has created all the consoles, so we can add View - * menu entries for them. - */ - add_console_menu_entries(); - - /* Give all removable devices a menu item. - * Has to be called after QEMU has started to - * find out what removable devices it has. - */ - addRemovableDevicesMenuItems(); } static QemuDisplay qemu_display_cocoa = {