From patchwork Tue Jul 16 18:09:37 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: =?utf-8?q?Philippe_Mathieu-Daud=C3=A9?= X-Patchwork-Id: 812788 Delivered-To: patch@linaro.org Received: by 2002:a5d:42c4:0:b0:367:895a:4699 with SMTP id t4csp436176wrr; Tue, 16 Jul 2024 11:12:55 -0700 (PDT) X-Forwarded-Encrypted: i=2; AJvYcCXFEeMj/hx8pXjxsesywpnVNGdMIQFX+4qTdBES3thZ3Py4wrmkFWqTX8gkcqdXMR85iCwiJUiCD2edTcTtUH96 X-Google-Smtp-Source: AGHT+IG6YSFzwa24phArb96ZeU5Z7noehVR8aUVbYdgAjTBbJOv9hBvM7UuNiTZIEfcI3Yz+v1Bg X-Received: by 2002:a05:620a:2585:b0:79f:1841:e5e2 with SMTP id af79cd13be357-7a17b6da7ddmr397465085a.41.1721153575392; Tue, 16 Jul 2024 11:12:55 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1721153575; cv=none; d=google.com; s=arc-20160816; b=c/QVpJLbN9FwwZ76KFO2zZp2Xh3I+gy8ocjyEeaJOXlggkALeWmN+yDxJKo30lQtkT 1uXMNkRpkoR6MKsaPTPCMGxdfWYra7HNL5i8GCA6TrXUqrSjxemrlEDs9C2tugXUhcEV KNlBWVaQsnvBMBu9zZabEEf72GxqCYc6EnYAs4YR9DJ+dubovtH1jQvks1+Qig6ya368 xFiGtnNwwt2qsM/BULM64Ej0KRAMqx6byWKRA3xHgQWkVctTfnT8hGQYFwn0iqUdBb1E WhECMb6M3Zq/PYeXLLhGQWfKVorM4N5INmd+5JrBI+5i6Yg79UYnku+nDE31mizfWLOa 9zbQ== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=sender:errors-to:list-subscribe:list-help:list-post:list-archive :list-unsubscribe:list-id:precedence:content-transfer-encoding :mime-version:references:in-reply-to:message-id:date:subject:cc:to :from:dkim-signature; bh=T0D9AUY7gUeEKLy7Uo83BX14QqkvgyIU5b+p9IGxUa0=; fh=wiKz0x7qvKwceerDkCg/vXelUIS3VMX4wuMEccF/XUs=; b=uFbK4Gild+Y5dJ6HKNLVH6QR1SzrB2TKDyrpWCBe/b7qwR9N3Mb98FAdaun1yFtkLU UpXpoIGFiHiibbIRxkRYFZ75LN2cgXZbRszmzyhBMhZOnh+1Ql45N07LrU+43HVpXtUX t8Cc1Ytz1DqJiEbGBLLfQGzhQUIEFhEtVTjH8RVuoLEpg4/6i1DNUZLqakpxfMmzxpM4 rB0We+ZskOAkvIQ63oxQ7j+6Z5AHjoW3RTmu4d80+5HyhTpdrrzb6eqVYgkIUAY1aHrs BnXx/dQBjaTY2sPVIUuwP72Lz2Cq2hliYTjjNLbxy713QyOGIC7Xak1oFzpPGy3xuUWw bH9A==; dara=google.com ARC-Authentication-Results: i=1; mx.google.com; dkim=pass header.i=@linaro.org header.s=google header.b=xMZRMUsU; spf=pass (google.com: domain of qemu-devel-bounces+patch=linaro.org@nongnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom="qemu-devel-bounces+patch=linaro.org@nongnu.org"; dmarc=pass (p=NONE sp=NONE dis=NONE) header.from=linaro.org; dara=neutral header.i=@linaro.org Return-Path: Received: from lists.gnu.org (lists.gnu.org. [209.51.188.17]) by mx.google.com with ESMTPS id af79cd13be357-7a160c69393si814762685a.441.2024.07.16.11.12.54 for (version=TLS1_2 cipher=ECDHE-ECDSA-CHACHA20-POLY1305 bits=256/256); Tue, 16 Jul 2024 11:12:55 -0700 (PDT) Received-SPF: pass (google.com: domain of qemu-devel-bounces+patch=linaro.org@nongnu.org designates 209.51.188.17 as permitted sender) client-ip=209.51.188.17; Authentication-Results: mx.google.com; dkim=pass header.i=@linaro.org header.s=google header.b=xMZRMUsU; spf=pass (google.com: domain of qemu-devel-bounces+patch=linaro.org@nongnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom="qemu-devel-bounces+patch=linaro.org@nongnu.org"; dmarc=pass (p=NONE sp=NONE dis=NONE) header.from=linaro.org; dara=neutral header.i=@linaro.org Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1sTme5-0004sc-28; Tue, 16 Jul 2024 14:11:25 -0400 Received: from eggs.gnu.org ([2001:470:142:3::10]) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1sTmdQ-0001hF-47 for qemu-devel@nongnu.org; Tue, 16 Jul 2024 14:10:49 -0400 Received: from mail-wm1-x329.google.com ([2a00:1450:4864:20::329]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_128_GCM_SHA256:128) (Exim 4.90_1) (envelope-from ) id 1sTmdN-0001JG-45 for qemu-devel@nongnu.org; Tue, 16 Jul 2024 14:10:43 -0400 Received: by mail-wm1-x329.google.com with SMTP id 5b1f17b1804b1-426526d30aaso41300645e9.0 for ; Tue, 16 Jul 2024 11:10:40 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linaro.org; s=google; t=1721153439; x=1721758239; darn=nongnu.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=T0D9AUY7gUeEKLy7Uo83BX14QqkvgyIU5b+p9IGxUa0=; b=xMZRMUsUoZL4CDu/F41+r6OT67G0ebFBV5WgA+EOisZeQpaMHPg8ZZ4fqM7V3+XyA4 OcZ2HfmtvMibu7mrY+6Sj5E2qXjjjjDEDzfO3xI0p/wQY5Du+7sxp2kFuO7VxKWYeho0 jsHMfFpTlBcZSsvECthJ7HRt6iY5sWWRBfmiWk5omw/J8xOcR9Ly4EVRrfmy+PZrRlxx jo9BzOzqliBUKlCiF2qMb7X8ej1Y7YZhaFC6rC+Zss0PI3NC9LZvzFBshtbNZs6otl1w W1J6ECkoYYxEFunBLNydHaG7ZC3t+NAfwui3PsHnB9HxsyYPW6nThcV3PTewN/Ky0t3C 7dBA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1721153439; x=1721758239; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=T0D9AUY7gUeEKLy7Uo83BX14QqkvgyIU5b+p9IGxUa0=; b=fwcKPm5MU1Nb8DKMXSeKJ39WAJWjJ6+XszYYMZx65EAY0Hjh3WvzxVJUm2iTdprj5N Hh6NuFSS0E7WJGTjjmiK0jVtFH30IjnJ9dwM9ikCQlQcHmzhF2JvOsVHmQ7Y5Um/S0Om GiWIeg9kVy9xxJbpS/GnmSaWPCTW+MTTcJ1YFlgr7YNObyw0dJtyWFMl2VPvMUXyvWXi 9Tgbh6yX4fSHbLLaaBRagi9Z+iGYriM3ldu1UwNOWG0EzbOUF8H0ypm/w+ZaepCq2AEm tgbAZk1jhj8wPF/7pFfAFSYur67IeGaRhou+3B62YwfC4IBvR0KO/r1H9+P1ZK3ng/0L xG2A== X-Gm-Message-State: AOJu0YwK2VFVbQMiJ37cC2bCExTpInyqiQfg+OUHGO2Lkgns/3gEFwEg YBt6CNBdV4db/u5HbPK+v48RHvPAfhUecWbP2my/Gmokk15UacgQ+VHhoh7NamisHXF0wTLusp1 iI9l96w== X-Received: by 2002:a05:600c:3b0f:b0:426:6fd3:1547 with SMTP id 5b1f17b1804b1-427ba654e7amr19745035e9.2.1721153438966; Tue, 16 Jul 2024 11:10:38 -0700 (PDT) Received: from localhost.localdomain ([176.187.209.82]) by smtp.gmail.com with ESMTPSA id 5b1f17b1804b1-427a5e8266asm137898735e9.13.2024.07.16.11.10.37 (version=TLS1_3 cipher=TLS_CHACHA20_POLY1305_SHA256 bits=256/256); Tue, 16 Jul 2024 11:10:38 -0700 (PDT) From: =?utf-8?q?Philippe_Mathieu-Daud=C3=A9?= To: qemu-devel@nongnu.org Cc: Akihiko Odaki , Phil Dennis-Jordan , =?utf-8?q?Philippe_Mathieu-Daud?= =?utf-8?q?=C3=A9?= Subject: [PULL 10/13] ui/cocoa: Add cursor composition Date: Tue, 16 Jul 2024 20:09:37 +0200 Message-ID: <20240716180941.40211-11-philmd@linaro.org> X-Mailer: git-send-email 2.41.0 In-Reply-To: <20240716180941.40211-1-philmd@linaro.org> References: <20240716180941.40211-1-philmd@linaro.org> MIME-Version: 1.0 Received-SPF: pass client-ip=2a00:1450:4864:20::329; envelope-from=philmd@linaro.org; helo=mail-wm1-x329.google.com X-Spam_score_int: -20 X-Spam_score: -2.1 X-Spam_bar: -- X-Spam_report: (-2.1 / 5.0 requ) BAYES_00=-1.9, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1, RCVD_IN_DNSWL_NONE=-0.0001, SPF_HELO_NONE=0.001, SPF_PASS=-0.001 autolearn=ham autolearn_force=no X-Spam_action: no action X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: qemu-devel-bounces+patch=linaro.org@nongnu.org Sender: qemu-devel-bounces+patch=linaro.org@nongnu.org From: Akihiko Odaki Add accelerated cursor composition to ui/cocoa. This does not only improve performance for display devices that exposes the capability to the guest according to dpy_cursor_define_supported(), but fixes the cursor display for devices that unconditionally expects the availability of the capability (e.g., virtio-gpu). The common pattern to implement accelerated cursor composition is to replace the cursor and warp it so that the replaced cursor is shown at the correct position on the guest display for relative pointer devices. Unfortunately, ui/cocoa cannot do the same because warping the cursor position interfers with the mouse input so it uses CALayer instead; although it is not specialized for cursor composition, it still can compose images with hardware acceleration. Co-authored-by: Phil Dennis-Jordan Tested-by: Phil Dennis-Jordan Signed-off-by: Akihiko Odaki Message-ID: <20240715-cursor-v3-3-afa5b9492dbf@daynix.com> Signed-off-by: Philippe Mathieu-Daudé --- meson.build | 3 +- ui/cocoa.m | 99 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 101 insertions(+), 1 deletion(-) diff --git a/meson.build b/meson.build index 6a93da48e1..a1e51277b0 100644 --- a/meson.build +++ b/meson.build @@ -1070,7 +1070,8 @@ if get_option('attr').allowed() endif endif -cocoa = dependency('appleframeworks', modules: ['Cocoa', 'CoreVideo'], +cocoa = dependency('appleframeworks', + modules: ['Cocoa', 'CoreVideo', 'QuartzCore'], required: get_option('cocoa')) vmnet = dependency('appleframeworks', modules: 'vmnet', required: get_option('vmnet')) diff --git a/ui/cocoa.m b/ui/cocoa.m index 79a054b128..4c2dd33532 100644 --- a/ui/cocoa.m +++ b/ui/cocoa.m @@ -25,6 +25,7 @@ #include "qemu/osdep.h" #import +#import #include #include "qemu/help-texts.h" @@ -79,12 +80,16 @@ static void cocoa_switch(DisplayChangeListener *dcl, DisplaySurface *surface); static void cocoa_refresh(DisplayChangeListener *dcl); +static void cocoa_mouse_set(DisplayChangeListener *dcl, int x, int y, bool on); +static void cocoa_cursor_define(DisplayChangeListener *dcl, QEMUCursor *cursor); static const DisplayChangeListenerOps dcl_ops = { .dpy_name = "cocoa", .dpy_gfx_update = cocoa_update, .dpy_gfx_switch = cocoa_switch, .dpy_refresh = cocoa_refresh, + .dpy_mouse_set = cocoa_mouse_set, + .dpy_cursor_define = cocoa_cursor_define, }; static DisplayChangeListener dcl = { .ops = &dcl_ops, @@ -308,6 +313,11 @@ @interface QemuCocoaView : NSView BOOL isAbsoluteEnabled; CFMachPortRef eventsTap; CGColorSpaceRef colorspace; + CALayer *cursorLayer; + QEMUCursor *cursor; + int mouseX; + int mouseY; + bool mouseOn; } - (void) switchSurface:(pixman_image_t *)image; - (void) grabMouse; @@ -364,6 +374,12 @@ - (id)initWithFrame:(NSRect)frameRect #if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_VERSION_14_0 [self setClipsToBounds:YES]; #endif + [self setWantsLayer:YES]; + cursorLayer = [[CALayer alloc] init]; + [cursorLayer setAnchorPoint:CGPointMake(0, 1)]; + [cursorLayer setAutoresizingMask:kCALayerMaxXMargin | + kCALayerMinYMargin]; + [[self layer] addSublayer:cursorLayer]; } return self; @@ -382,6 +398,8 @@ - (void) dealloc } CGColorSpaceRelease(colorspace); + [cursorLayer release]; + cursor_unref(cursor); [super dealloc]; } @@ -426,6 +444,72 @@ - (void) unhideCursor [NSCursor unhide]; } +- (void)setMouseX:(int)x y:(int)y on:(bool)on +{ + CGPoint position; + + mouseX = x; + mouseY = y; + mouseOn = on; + + position.x = mouseX; + position.y = screen.height - mouseY; + + [CATransaction begin]; + [CATransaction setDisableActions:YES]; + [cursorLayer setPosition:position]; + [cursorLayer setHidden:!mouseOn]; + [CATransaction commit]; +} + +- (void)setCursor:(QEMUCursor *)given_cursor +{ + CGDataProviderRef provider; + CGImageRef image; + CGRect bounds = CGRectZero; + + cursor_unref(cursor); + cursor = given_cursor; + + if (!cursor) { + return; + } + + cursor_ref(cursor); + + bounds.size.width = cursor->width; + bounds.size.height = cursor->height; + + provider = CGDataProviderCreateWithData( + NULL, + cursor->data, + cursor->width * cursor->height * 4, + NULL + ); + + image = CGImageCreate( + cursor->width, //width + cursor->height, //height + 8, //bitsPerComponent + 32, //bitsPerPixel + cursor->width * 4, //bytesPerRow + colorspace, //colorspace + kCGBitmapByteOrder32Little | kCGImageAlphaFirst, //bitmapInfo + provider, //provider + NULL, //decode + 0, //interpolate + kCGRenderingIntentDefault //intent + ); + + CGDataProviderRelease(provider); + [CATransaction begin]; + [CATransaction setDisableActions:YES]; + [cursorLayer setBounds:bounds]; + [cursorLayer setContents:(id)image]; + [CATransaction commit]; + CGImageRelease(image); +} + - (void) drawRect:(NSRect) rect { COCOA_DEBUG("QemuCocoaView: drawRect\n"); @@ -2015,6 +2099,21 @@ static void cocoa_refresh(DisplayChangeListener *dcl) [pool release]; } +static void cocoa_mouse_set(DisplayChangeListener *dcl, int x, int y, bool on) +{ + dispatch_async(dispatch_get_main_queue(), ^{ + [cocoaView setMouseX:x y:y on:on]; + }); +} + +static void cocoa_cursor_define(DisplayChangeListener *dcl, QEMUCursor *cursor) +{ + dispatch_async(dispatch_get_main_queue(), ^{ + BQL_LOCK_GUARD(); + [cocoaView setCursor:qemu_console_get_cursor(dcl->con)]; + }); +} + static void cocoa_display_init(DisplayState *ds, DisplayOptions *opts) { NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];