All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH] ui/cocoa: Implement dcl operators for guest cursor
@ 2021-08-10  9:17 Zhang Chen
  0 siblings, 0 replies; 2+ messages in thread
From: Zhang Chen @ 2021-08-10  9:17 UTC (permalink / raw)
  To: qemu-devel; +Cc: Zhang Chen, Peter Maydell, Gerd Hoffmann

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 --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");
-- 
2.30.2



^ permalink raw reply related	[flat|nested] 2+ messages in thread

* [PATCH] ui/cocoa: Implement dcl operators for guest cursor
@ 2021-08-10  8:37 Zhang Chen via
  0 siblings, 0 replies; 2+ messages in thread
From: Zhang Chen via @ 2021-08-10  8:37 UTC (permalink / raw)
  To: qemu-devel; +Cc: Zhang Chen, Peter Maydell, Gerd Hoffmann

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.
---
 ui/cocoa.m | 99 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 99 insertions(+)

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");
-- 
2.30.2



^ permalink raw reply related	[flat|nested] 2+ messages in thread

end of thread, other threads:[~2021-08-10  9:18 UTC | newest]

Thread overview: 2+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2021-08-10  9:17 [PATCH] ui/cocoa: Implement dcl operators for guest cursor Zhang Chen
  -- strict thread matches above, loose matches on Subject: below --
2021-08-10  8:37 Zhang Chen via

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.