diff mbox series

ui/cocoa: Implement dcl operators for guest cursor

Message ID 20210810091706.65576-1-tgfbeta@me.com
State New
Headers show
Series ui/cocoa: Implement dcl operators for guest cursor | expand

Commit Message

Chen Zhang Aug. 10, 2021, 9:17 a.m. UTC
In this patch, two dcl operators were implemented for Cocoa
 display, to prepare, update and draw guest cursor on screen canvas.

After this implementation, Cocoa display could support virtio-vga
device, which is better supported in guest side, especially for Linux.

In contrast to the default vga device, virtio-vga with dcl operators
for cursors showed less flicker in cursor drawing.

Two fields were added in the struct QemuScreen to pass dimensions to
dcl operators.

Signed-off-by: Zhang Chen <tgfbeta@me.com>
---
 ui/cocoa.m | 99 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 99 insertions(+)
diff mbox series

Patch

diff --git a/ui/cocoa.m b/ui/cocoa.m
index 68a6302184..9d3a8eac28 100644
--- a/ui/cocoa.m
+++ b/ui/cocoa.m
@@ -73,6 +73,8 @@ 
 typedef struct {
     int width;
     int height;
+    int bitsPerComponent;
+    int bitsPerPixel;
 } QEMUScreen;
 
 static void cocoa_update(DisplayChangeListener *dcl,
@@ -83,12 +85,19 @@  static void cocoa_switch(DisplayChangeListener *dcl,
 
 static void cocoa_refresh(DisplayChangeListener *dcl);
 
+static void cocoa_mouse_set(DisplayChangeListener *dcl,
+                            int x, int y, int visible);
+
+static void cocoa_cursor_define(DisplayChangeListener *dcl, QEMUCursor *c);
+
 static NSWindow *normalWindow, *about_window;
 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,
@@ -309,6 +318,9 @@  static void handleAnyDeviceErrors(Error * err)
     BOOL isMouseGrabbed;
     BOOL isFullscreen;
     BOOL isAbsoluteEnabled;
+    CGRect cursorRect;
+    CGImageRef cursorImage;
+    BOOL cursorVisible;
 }
 - (void) switchSurface:(pixman_image_t *)image;
 - (void) grabMouse;
@@ -344,6 +356,8 @@  QemuCocoaView *cocoaView;
     self = [super initWithFrame:frameRect];
     if (self) {
 
+        screen.bitsPerComponent = 8;
+        screen.bitsPerPixel = 32;
         screen.width = frameRect.size.width;
         screen.height = frameRect.size.height;
         kbd = qkbd_state_init(dcl.con);
@@ -484,6 +498,12 @@  QemuCocoaView *cocoaView;
                                                         );
             CGContextDrawImage (viewContextRef, cgrect(rectList[i]), clipImageRef);
             CGImageRelease (clipImageRef);
+
+        }
+        CGRect cursorDrawRect = stretch_video ?
+                                    [self convertRectFromQemuScreen:cursorRect] : cursorRect;
+        if (cursorVisible && cursorImage && NSIntersectsRect(rect, cursorDrawRect)) {
+            CGContextDrawImage (viewContextRef, cursorDrawRect, cursorImage);
         }
         CGImageRelease (imageRef);
         CGDataProviderRelease(dataProviderRef);
@@ -1075,6 +1095,28 @@  QemuCocoaView *cocoaView;
 - (float) cdy {return cdy;}
 - (QEMUScreen) gscreen {return screen;}
 
+- (CGRect) cursorRect {return cursorRect;}
+- (void) setCursorRect:(CGRect)rect {cursorRect = rect;}
+- (CGImageRef) cursorImage {return cursorImage;}
+- (void) setCursorImage:(CGImageRef)image
+{
+    if (cursorImage && cursorImage != image) {
+        CGImageRelease(cursorImage);
+    }
+    cursorImage = image;
+}
+- (BOOL) isCursorVisible {return cursorVisible;}
+- (void) setCursorVisible:(BOOL)visible {cursorVisible = visible;}
+
+- (CGRect) convertRectFromQemuScreen:(CGRect)rect
+{
+    CGRect viewRect = rect;
+    viewRect.origin.x *= cdx;
+    viewRect.origin.y *= cdy;
+    viewRect.size.width *= cdx;
+    viewRect.size.height *= cdy;
+    return viewRect;
+}
 /*
  * Makes the target think all down keys are being released.
  * This prevents a stuck key problem, since we will not see
@@ -2022,6 +2064,63 @@  static void cocoa_refresh(DisplayChangeListener *dcl)
     [pool release];
 }
 
+static void cocoa_cursor_define(DisplayChangeListener *dcl, QEMUCursor *c)
+{
+    NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
+    int bitsPerComponent = [cocoaView gscreen].bitsPerComponent;
+    int bitsPerPixel = [cocoaView gscreen].bitsPerPixel;
+    int stride = c->width * bitsPerComponent / 2;
+    CGDataProviderRef provider = CGDataProviderCreateWithData(NULL, c->data, c->width * 4 * c->height, NULL);
+
+    CGImageRef img = CGImageCreate(
+        c->width,
+        c->height,
+        bitsPerComponent,
+        bitsPerPixel,
+        stride,
+        CGColorSpaceCreateWithName(kCGColorSpaceGenericRGB), //colorspace
+        kCGBitmapByteOrder32Little | kCGImageAlphaFirst,
+        provider,
+        NULL,
+        0,
+        kCGRenderingIntentDefault
+    );
+
+    CGDataProviderRelease(provider);
+    CGFloat width = c->width;
+    CGFloat height = c->height;
+    dispatch_async(dispatch_get_main_queue(), ^{
+        [cocoaView setCursorImage:img];
+        CGRect rect = [cocoaView cursorRect];
+        rect.size = CGSizeMake(width, height);
+        [cocoaView setCursorRect:rect];
+    });
+    [pool release];
+}
+
+static void cocoa_mouse_set(DisplayChangeListener *dcl,
+                            int x, int y, int visible)
+{
+    NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
+    dispatch_async(dispatch_get_main_queue(), ^{
+        QEMUScreen screen = [cocoaView gscreen];
+        // Mark old cursor rect as dirty
+        CGRect rect = [cocoaView cursorRect];
+        CGRect dirtyRect = stretch_video ?
+                        [cocoaView convertRectFromQemuScreen:rect] : rect;
+        [cocoaView setNeedsDisplayInRect:dirtyRect];
+        // Update rect for cursor sprite
+        rect.origin = CGPointMake(x, screen.height - (y + rect.size.height));
+        [cocoaView setCursorRect:rect];
+        [cocoaView setCursorVisible:visible ? YES : NO];
+        // Mark new cursor rect as dirty
+        dirtyRect = stretch_video ?
+                        [cocoaView convertRectFromQemuScreen:rect] : rect;
+        [cocoaView setNeedsDisplayInRect:dirtyRect];
+    });
+    [pool release];
+}
+
 static void cocoa_display_init(DisplayState *ds, DisplayOptions *opts)
 {
     COCOA_DEBUG("qemu_cocoa: cocoa_display_init\n");