|
@@ -78,6 +78,10 @@
|
|
|
|
|
|
typedef struct CocoaListener {
|
|
typedef struct CocoaListener {
|
|
DisplayChangeListener dcl;
|
|
DisplayChangeListener dcl;
|
|
|
|
+ QEMUCursor *cursor;
|
|
|
|
+ int mouse_x;
|
|
|
|
+ int mouse_y;
|
|
|
|
+ int mouse_on;
|
|
#ifdef CONFIG_OPENGL
|
|
#ifdef CONFIG_OPENGL
|
|
uint32_t gl_scanout_id;
|
|
uint32_t gl_scanout_id;
|
|
bool gl_scanout_y0_top;
|
|
bool gl_scanout_y0_top;
|
|
@@ -95,12 +99,15 @@ @interface QemuCocoaPasteboardTypeOwner : NSObject<NSPasteboardTypeOwner>
|
|
static void cocoa_switch(DisplayChangeListener *dcl,
|
|
static void cocoa_switch(DisplayChangeListener *dcl,
|
|
DisplaySurface *surface);
|
|
DisplaySurface *surface);
|
|
|
|
|
|
|
|
+static void cocoa_cursor_update(void);
|
|
|
|
+
|
|
static NSWindow *normalWindow;
|
|
static NSWindow *normalWindow;
|
|
static CocoaListener *active_listener;
|
|
static CocoaListener *active_listener;
|
|
static CocoaListener *listeners;
|
|
static CocoaListener *listeners;
|
|
static size_t listeners_count;
|
|
static size_t listeners_count;
|
|
static DisplaySurface *surface;
|
|
static DisplaySurface *surface;
|
|
-static QemuMutex surface_mutex;
|
|
|
|
|
|
+static QemuMutex draw_mutex;
|
|
|
|
+static CGImageRef cursor_cgimage;
|
|
static QKbdState *kbd;
|
|
static QKbdState *kbd;
|
|
static int cursor_hide = 1;
|
|
static int cursor_hide = 1;
|
|
static int left_command_key_enabled = 1;
|
|
static int left_command_key_enabled = 1;
|
|
@@ -119,7 +126,8 @@ static void cocoa_switch(DisplayChangeListener *dcl,
|
|
|
|
|
|
#ifdef CONFIG_OPENGL
|
|
#ifdef CONFIG_OPENGL
|
|
|
|
|
|
-static bool gl_surface_dirty;
|
|
|
|
|
|
+static GLuint cursor_texture;
|
|
|
|
+static bool gl_dirty;
|
|
static QEMUGLContext view_ctx;
|
|
static QEMUGLContext view_ctx;
|
|
|
|
|
|
#ifdef CONFIG_EGL
|
|
#ifdef CONFIG_EGL
|
|
@@ -129,6 +137,8 @@ static void cocoa_switch(DisplayChangeListener *dcl,
|
|
static void cocoa_gl_switch(DisplayChangeListener *dcl,
|
|
static void cocoa_gl_switch(DisplayChangeListener *dcl,
|
|
DisplaySurface *new_surface);
|
|
DisplaySurface *new_surface);
|
|
|
|
|
|
|
|
+static void cocoa_gl_cursor_update(void);
|
|
|
|
+
|
|
static bool cocoa_gl_is_compatible_dcl(DisplayGLCtx *dgc,
|
|
static bool cocoa_gl_is_compatible_dcl(DisplayGLCtx *dgc,
|
|
DisplayChangeListener *dcl);
|
|
DisplayChangeListener *dcl);
|
|
|
|
|
|
@@ -208,6 +218,7 @@ static bool bool_with_iothread_lock(BoolCodeBlock block)
|
|
qemu_cleanup();
|
|
qemu_cleanup();
|
|
qkbd_state_free(kbd);
|
|
qkbd_state_free(kbd);
|
|
[cbowner release];
|
|
[cbowner release];
|
|
|
|
+ CGImageRelease(cursor_cgimage);
|
|
#ifdef CONFIG_OPENGL
|
|
#ifdef CONFIG_OPENGL
|
|
qemu_gl_fini_shader(dgc.gls);
|
|
qemu_gl_fini_shader(dgc.gls);
|
|
if (view_ctx) {
|
|
if (view_ctx) {
|
|
@@ -245,6 +256,20 @@ static void handleAnyDeviceErrors(Error * err)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+static CGRect compute_cursor_clip_rect(int screen_height,
|
|
|
|
+ int given_mouse_x, int given_mouse_y,
|
|
|
|
+ int cursor_width, int cursor_height)
|
|
|
|
+{
|
|
|
|
+ CGRect rect;
|
|
|
|
+
|
|
|
|
+ rect.origin.x = MAX(0, -given_mouse_x);
|
|
|
|
+ rect.origin.y = 0;
|
|
|
|
+ rect.size.width = MIN(cursor_width, cursor_width + given_mouse_x);
|
|
|
|
+ rect.size.height = cursor_height - rect.origin.x;
|
|
|
|
+
|
|
|
|
+ return rect;
|
|
|
|
+}
|
|
|
|
+
|
|
/*
|
|
/*
|
|
------------------------------------------------------
|
|
------------------------------------------------------
|
|
QemuCocoaView
|
|
QemuCocoaView
|
|
@@ -376,11 +401,13 @@ - (void) selectConsoleLocked:(unsigned int)index
|
|
|
|
|
|
if (display_opengl) {
|
|
if (display_opengl) {
|
|
#ifdef CONFIG_OPENGL
|
|
#ifdef CONFIG_OPENGL
|
|
|
|
+ cocoa_gl_cursor_update();
|
|
cocoa_gl_switch(&active_listener->dcl, new_surface);
|
|
cocoa_gl_switch(&active_listener->dcl, new_surface);
|
|
#else
|
|
#else
|
|
g_assert_not_reached();
|
|
g_assert_not_reached();
|
|
#endif
|
|
#endif
|
|
} else {
|
|
} else {
|
|
|
|
+ cocoa_cursor_update();
|
|
cocoa_switch(&active_listener->dcl, new_surface);
|
|
cocoa_switch(&active_listener->dcl, new_surface);
|
|
}
|
|
}
|
|
|
|
|
|
@@ -403,6 +430,21 @@ - (void) unhideCursor
|
|
[NSCursor unhide];
|
|
[NSCursor unhide];
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+- (CGRect) convertCursorClipRectToDraw:(CGRect)rect
|
|
|
|
+ screenHeight:(int)screen_height
|
|
|
|
+ mouseX:(int)given_mouse_x
|
|
|
|
+ mouseY:(int)given_mouse_y
|
|
|
|
+{
|
|
|
|
+ CGFloat d = [self frame].size.height / (CGFloat)screen_height;
|
|
|
|
+
|
|
|
|
+ rect.origin.x = (rect.origin.x + given_mouse_x) * d;
|
|
|
|
+ rect.origin.y = (screen_height - rect.origin.y - given_mouse_y - rect.size.height) * d;
|
|
|
|
+ rect.size.width *= d;
|
|
|
|
+ rect.size.height *= d;
|
|
|
|
+
|
|
|
|
+ return rect;
|
|
|
|
+}
|
|
|
|
+
|
|
- (void) drawRect:(NSRect) rect
|
|
- (void) drawRect:(NSRect) rect
|
|
{
|
|
{
|
|
COCOA_DEBUG("QemuCocoaView: drawRect\n");
|
|
COCOA_DEBUG("QemuCocoaView: drawRect\n");
|
|
@@ -419,7 +461,7 @@ - (void) drawRect:(NSRect) rect
|
|
CGContextSetInterpolationQuality (viewContextRef, kCGInterpolationNone);
|
|
CGContextSetInterpolationQuality (viewContextRef, kCGInterpolationNone);
|
|
CGContextSetShouldAntialias (viewContextRef, NO);
|
|
CGContextSetShouldAntialias (viewContextRef, NO);
|
|
|
|
|
|
- qemu_mutex_lock(&surface_mutex);
|
|
|
|
|
|
+ qemu_mutex_lock(&draw_mutex);
|
|
|
|
|
|
// draw screen bitmap directly to Core Graphics context
|
|
// draw screen bitmap directly to Core Graphics context
|
|
if (!surface) {
|
|
if (!surface) {
|
|
@@ -475,9 +517,29 @@ - (void) drawRect:(NSRect) rect
|
|
}
|
|
}
|
|
CGImageRelease (imageRef);
|
|
CGImageRelease (imageRef);
|
|
CGDataProviderRelease(dataProviderRef);
|
|
CGDataProviderRelease(dataProviderRef);
|
|
|
|
+
|
|
|
|
+ if (active_listener->mouse_on) {
|
|
|
|
+ size_t cursor_width = CGImageGetWidth(cursor_cgimage);
|
|
|
|
+ size_t cursor_height = CGImageGetHeight(cursor_cgimage);
|
|
|
|
+ int mouse_x = active_listener->mouse_x;
|
|
|
|
+ int mouse_y = active_listener->mouse_y;
|
|
|
|
+ clipRect = compute_cursor_clip_rect(h, mouse_x, mouse_y,
|
|
|
|
+ cursor_width,
|
|
|
|
+ cursor_height);
|
|
|
|
+ CGRect drawRect = [self convertCursorClipRectToDraw:clipRect
|
|
|
|
+ screenHeight:h
|
|
|
|
+ mouseX:mouse_x
|
|
|
|
+ mouseY:mouse_y];
|
|
|
|
+ clipImageRef = CGImageCreateWithImageInRect(
|
|
|
|
+ cursor_cgimage,
|
|
|
|
+ clipRect
|
|
|
|
+ );
|
|
|
|
+ CGContextDrawImage(viewContextRef, drawRect, clipImageRef);
|
|
|
|
+ CGImageRelease (clipImageRef);
|
|
|
|
+ }
|
|
}
|
|
}
|
|
|
|
|
|
- qemu_mutex_unlock(&surface_mutex);
|
|
|
|
|
|
+ qemu_mutex_unlock(&draw_mutex);
|
|
}
|
|
}
|
|
|
|
|
|
- (NSSize) computeUnzoomedSize
|
|
- (NSSize) computeUnzoomedSize
|
|
@@ -1872,13 +1934,13 @@ static void cocoa_update(DisplayChangeListener *dcl,
|
|
COCOA_DEBUG("qemu_cocoa: cocoa_update\n");
|
|
COCOA_DEBUG("qemu_cocoa: cocoa_update\n");
|
|
|
|
|
|
dispatch_async(dispatch_get_main_queue(), ^{
|
|
dispatch_async(dispatch_get_main_queue(), ^{
|
|
- qemu_mutex_lock(&surface_mutex);
|
|
|
|
|
|
+ qemu_mutex_lock(&draw_mutex);
|
|
if (updated != surface) {
|
|
if (updated != surface) {
|
|
- qemu_mutex_unlock(&surface_mutex);
|
|
|
|
|
|
+ qemu_mutex_unlock(&draw_mutex);
|
|
return;
|
|
return;
|
|
}
|
|
}
|
|
int full_height = surface_height(surface);
|
|
int full_height = surface_height(surface);
|
|
- qemu_mutex_unlock(&surface_mutex);
|
|
|
|
|
|
+ qemu_mutex_unlock(&draw_mutex);
|
|
|
|
|
|
CGFloat d = [cocoaView frame].size.height / full_height;
|
|
CGFloat d = [cocoaView frame].size.height / full_height;
|
|
NSRect rect = NSMakeRect(x * d, (full_height - y - h) * d, w * d, h * d);
|
|
NSRect rect = NSMakeRect(x * d, (full_height - y - h) * d, w * d, h * d);
|
|
@@ -1895,15 +1957,15 @@ static void cocoa_switch(DisplayChangeListener *dcl,
|
|
return;
|
|
return;
|
|
}
|
|
}
|
|
|
|
|
|
- qemu_mutex_lock(&surface_mutex);
|
|
|
|
|
|
+ qemu_mutex_lock(&draw_mutex);
|
|
surface = new_surface;
|
|
surface = new_surface;
|
|
- qemu_mutex_unlock(&surface_mutex);
|
|
|
|
|
|
+ qemu_mutex_unlock(&draw_mutex);
|
|
|
|
|
|
dispatch_async(dispatch_get_main_queue(), ^{
|
|
dispatch_async(dispatch_get_main_queue(), ^{
|
|
- qemu_mutex_lock(&surface_mutex);
|
|
|
|
|
|
+ qemu_mutex_lock(&draw_mutex);
|
|
int w = surface_width(surface);
|
|
int w = surface_width(surface);
|
|
int h = surface_height(surface);
|
|
int h = surface_height(surface);
|
|
- qemu_mutex_unlock(&surface_mutex);
|
|
|
|
|
|
+ qemu_mutex_unlock(&draw_mutex);
|
|
|
|
|
|
[cocoaView updateScreenWidth:w height:h];
|
|
[cocoaView updateScreenWidth:w height:h];
|
|
});
|
|
});
|
|
@@ -1946,11 +2008,136 @@ static void cocoa_refresh(DisplayChangeListener *dcl)
|
|
[pool release];
|
|
[pool release];
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+static void cocoa_mouse_set(DisplayChangeListener *dcl, int x, int y, int on)
|
|
|
|
+{
|
|
|
|
+ CocoaListener *listener = container_of(dcl, CocoaListener, dcl);
|
|
|
|
+
|
|
|
|
+ qemu_mutex_lock(&draw_mutex);
|
|
|
|
+ int full_height = surface_height(surface);
|
|
|
|
+ int old_x = listener->mouse_x;
|
|
|
|
+ int old_y = listener->mouse_y;
|
|
|
|
+ listener->mouse_x = x;
|
|
|
|
+ listener->mouse_y = y;
|
|
|
|
+ listener->mouse_on = on;
|
|
|
|
+ qemu_mutex_unlock(&draw_mutex);
|
|
|
|
+
|
|
|
|
+ if (listener == active_listener && cursor_cgimage) {
|
|
|
|
+ size_t cursor_width = CGImageGetWidth(cursor_cgimage);
|
|
|
|
+ size_t cursor_height = CGImageGetHeight(cursor_cgimage);
|
|
|
|
+
|
|
|
|
+ dispatch_async(dispatch_get_main_queue(), ^{
|
|
|
|
+ CGRect clip_rect = compute_cursor_clip_rect(full_height,
|
|
|
|
+ old_x, old_y,
|
|
|
|
+ cursor_width,
|
|
|
|
+ cursor_height);
|
|
|
|
+ CGRect draw_rect =
|
|
|
|
+ [cocoaView convertCursorClipRectToDraw:clip_rect
|
|
|
|
+ screenHeight:full_height
|
|
|
|
+ mouseX:old_x
|
|
|
|
+ mouseY:old_y];
|
|
|
|
+ [cocoaView setNeedsDisplayInRect:draw_rect];
|
|
|
|
+
|
|
|
|
+ clip_rect = compute_cursor_clip_rect(full_height, x, y,
|
|
|
|
+ cursor_width,
|
|
|
|
+ cursor_height);
|
|
|
|
+ draw_rect =
|
|
|
|
+ [cocoaView convertCursorClipRectToDraw:clip_rect
|
|
|
|
+ screenHeight:full_height
|
|
|
|
+ mouseX:x
|
|
|
|
+ mouseY:y];
|
|
|
|
+ [cocoaView setNeedsDisplayInRect:draw_rect];
|
|
|
|
+ });
|
|
|
|
+ }
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static void cocoa_cursor_update()
|
|
|
|
+{
|
|
|
|
+ CGImageRef old_image = cursor_cgimage;
|
|
|
|
+ CGImageRef new_image;
|
|
|
|
+
|
|
|
|
+ if (active_listener->cursor) {
|
|
|
|
+ CGDataProviderRef provider = CGDataProviderCreateWithData(
|
|
|
|
+ NULL,
|
|
|
|
+ active_listener->cursor->data,
|
|
|
|
+ active_listener->cursor->width * active_listener->cursor->height * 4,
|
|
|
|
+ NULL
|
|
|
|
+ );
|
|
|
|
+
|
|
|
|
+ new_image = CGImageCreate(
|
|
|
|
+ active_listener->cursor->width, //width
|
|
|
|
+ active_listener->cursor->height, //height
|
|
|
|
+ 8, //bitsPerComponent
|
|
|
|
+ 32, //bitsPerPixel
|
|
|
|
+ active_listener->cursor->width * 4, //bytesPerRow
|
|
|
|
+ CGColorSpaceCreateWithName(kCGColorSpaceSRGB), //colorspace
|
|
|
|
+ kCGBitmapByteOrder32Little | kCGImageAlphaFirst, //bitmapInfo
|
|
|
|
+ provider, //provider
|
|
|
|
+ NULL, //decode
|
|
|
|
+ 0, //interpolate
|
|
|
|
+ kCGRenderingIntentDefault //intent
|
|
|
|
+ );
|
|
|
|
+
|
|
|
|
+ CGDataProviderRelease(provider);
|
|
|
|
+ } else {
|
|
|
|
+ new_image = NULL;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ qemu_mutex_lock(&draw_mutex);
|
|
|
|
+ cursor_cgimage = new_image;
|
|
|
|
+ qemu_mutex_unlock(&draw_mutex);
|
|
|
|
+
|
|
|
|
+ CGImageRelease(old_image);
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static void cocoa_cursor_define(DisplayChangeListener *dcl, QEMUCursor *cursor)
|
|
|
|
+{
|
|
|
|
+ CocoaListener *listener = container_of(dcl, CocoaListener, dcl);
|
|
|
|
+
|
|
|
|
+ listener->cursor = cursor;
|
|
|
|
+
|
|
|
|
+ if (listener == active_listener) {
|
|
|
|
+ int full_height = surface_height(surface);
|
|
|
|
+ int width = cursor->width;
|
|
|
|
+ int height = cursor->height;
|
|
|
|
+ int x = listener->mouse_x;
|
|
|
|
+ int y = listener->mouse_y;
|
|
|
|
+ size_t old_width;
|
|
|
|
+ size_t old_height;
|
|
|
|
+
|
|
|
|
+ if (cursor_cgimage) {
|
|
|
|
+ old_width = CGImageGetWidth(cursor_cgimage);
|
|
|
|
+ old_height = CGImageGetHeight(cursor_cgimage);
|
|
|
|
+ } else {
|
|
|
|
+ old_width = 0;
|
|
|
|
+ old_height = 0;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ cocoa_cursor_update();
|
|
|
|
+
|
|
|
|
+ dispatch_async(dispatch_get_main_queue(), ^{
|
|
|
|
+ CGFloat d = [cocoaView frame].size.height / full_height;
|
|
|
|
+ NSRect rect;
|
|
|
|
+
|
|
|
|
+ rect.origin.x = d * x;
|
|
|
|
+ rect.origin.y = d * (full_height - y - old_height);
|
|
|
|
+ rect.size.width = d * old_width;
|
|
|
|
+ rect.size.height = d * old_height;
|
|
|
|
+ [cocoaView setNeedsDisplayInRect:rect];
|
|
|
|
+
|
|
|
|
+ rect.size.width = d * width;
|
|
|
|
+ rect.size.height = d * height;
|
|
|
|
+ [cocoaView setNeedsDisplayInRect:rect];
|
|
|
|
+ });
|
|
|
|
+ }
|
|
|
|
+}
|
|
|
|
+
|
|
static const DisplayChangeListenerOps dcl_ops = {
|
|
static const DisplayChangeListenerOps dcl_ops = {
|
|
.dpy_name = "cocoa",
|
|
.dpy_name = "cocoa",
|
|
.dpy_gfx_update = cocoa_update,
|
|
.dpy_gfx_update = cocoa_update,
|
|
.dpy_gfx_switch = cocoa_switch,
|
|
.dpy_gfx_switch = cocoa_switch,
|
|
.dpy_refresh = cocoa_refresh,
|
|
.dpy_refresh = cocoa_refresh,
|
|
|
|
+ .dpy_mouse_set = cocoa_mouse_set,
|
|
|
|
+ .dpy_cursor_define = cocoa_cursor_define,
|
|
};
|
|
};
|
|
|
|
|
|
#ifdef CONFIG_OPENGL
|
|
#ifdef CONFIG_OPENGL
|
|
@@ -2058,16 +2245,38 @@ static void cocoa_gl_update(DisplayChangeListener *dcl,
|
|
{
|
|
{
|
|
CocoaListener *listener = container_of(dcl, CocoaListener, dcl);
|
|
CocoaListener *listener = container_of(dcl, CocoaListener, dcl);
|
|
|
|
|
|
- if (listener != active_listener || listener->gl_scanout_id) {
|
|
|
|
|
|
+ if (listener != active_listener) {
|
|
return;
|
|
return;
|
|
}
|
|
}
|
|
|
|
|
|
with_view_ctx(^{
|
|
with_view_ctx(^{
|
|
surface_gl_update_texture(dgc.gls, surface, x, y, w, h);
|
|
surface_gl_update_texture(dgc.gls, surface, x, y, w, h);
|
|
- gl_surface_dirty = true;
|
|
|
|
|
|
+ gl_dirty = true;
|
|
});
|
|
});
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+static void cocoa_gl_cursor_render()
|
|
|
|
+{
|
|
|
|
+ if (!active_listener->mouse_on) {
|
|
|
|
+ return;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ NSSize size = [cocoaView convertSizeToBacking:[cocoaView frame].size];
|
|
|
|
+ CGFloat d = size.height / surface_height(surface);
|
|
|
|
+
|
|
|
|
+ glViewport(
|
|
|
|
+ d * active_listener->mouse_x,
|
|
|
|
+ size.height - d * (active_listener->mouse_y + active_listener->cursor->height),
|
|
|
|
+ d * active_listener->cursor->width,
|
|
|
|
+ d * active_listener->cursor->height
|
|
|
|
+ );
|
|
|
|
+ glBindTexture(GL_TEXTURE_2D, cursor_texture);
|
|
|
|
+ glEnable(GL_BLEND);
|
|
|
|
+ glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
|
|
|
+ qemu_gl_run_texture_blit(dgc.gls, false);
|
|
|
|
+ glDisable(GL_BLEND);
|
|
|
|
+}
|
|
|
|
+
|
|
static void cocoa_gl_switch(DisplayChangeListener *dcl,
|
|
static void cocoa_gl_switch(DisplayChangeListener *dcl,
|
|
DisplaySurface *new_surface)
|
|
DisplaySurface *new_surface)
|
|
{
|
|
{
|
|
@@ -2083,21 +2292,39 @@ static void cocoa_gl_switch(DisplayChangeListener *dcl,
|
|
});
|
|
});
|
|
|
|
|
|
cocoa_switch(dcl, new_surface);
|
|
cocoa_switch(dcl, new_surface);
|
|
- gl_surface_dirty = true;
|
|
|
|
|
|
+ gl_dirty = true;
|
|
}
|
|
}
|
|
|
|
|
|
static void cocoa_gl_refresh(DisplayChangeListener *dcl)
|
|
static void cocoa_gl_refresh(DisplayChangeListener *dcl)
|
|
{
|
|
{
|
|
|
|
+ CocoaListener *listener = container_of(dcl, CocoaListener, dcl);
|
|
|
|
+
|
|
|
|
+ if (listener != active_listener) {
|
|
|
|
+ return;
|
|
|
|
+ }
|
|
|
|
+
|
|
cocoa_refresh(dcl);
|
|
cocoa_refresh(dcl);
|
|
|
|
|
|
- if (gl_surface_dirty) {
|
|
|
|
- gl_surface_dirty = false;
|
|
|
|
|
|
+ if (gl_dirty) {
|
|
|
|
+ gl_dirty = false;
|
|
|
|
|
|
with_view_ctx(^{
|
|
with_view_ctx(^{
|
|
NSSize size = [cocoaView convertSizeToBacking:[cocoaView frame].size];
|
|
NSSize size = [cocoaView convertSizeToBacking:[cocoaView frame].size];
|
|
|
|
|
|
- surface_gl_setup_viewport(dgc.gls, surface, size.width, size.height);
|
|
|
|
- surface_gl_render_texture(dgc.gls, surface);
|
|
|
|
|
|
+ if (listener->gl_scanout_id) {
|
|
|
|
+ glBindFramebuffer(GL_FRAMEBUFFER_EXT, 0);
|
|
|
|
+ glViewport(0, 0, size.width, size.height);
|
|
|
|
+ glBindTexture(GL_TEXTURE_2D, active_listener->gl_scanout_id);
|
|
|
|
+ qemu_gl_run_texture_blit(dgc.gls,
|
|
|
|
+ active_listener->gl_scanout_y0_top);
|
|
|
|
+ } else {
|
|
|
|
+ surface_gl_setup_viewport(dgc.gls, surface,
|
|
|
|
+ size.width, size.height);
|
|
|
|
+ glBindTexture(GL_TEXTURE_2D, surface->texture);
|
|
|
|
+ surface_gl_render_texture(dgc.gls, surface);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ cocoa_gl_cursor_render();
|
|
cocoa_gl_flush();
|
|
cocoa_gl_flush();
|
|
});
|
|
});
|
|
}
|
|
}
|
|
@@ -2109,13 +2336,40 @@ static void cocoa_gl_scanout_disable(DisplayChangeListener *dcl)
|
|
|
|
|
|
listener->gl_scanout_id = 0;
|
|
listener->gl_scanout_id = 0;
|
|
|
|
|
|
- if (listener == active_listener && surface) {
|
|
|
|
|
|
+ if (listener == active_listener) {
|
|
|
|
+ gl_dirty = surface != NULL;
|
|
|
|
+ }
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static void cocoa_gl_cursor_update()
|
|
|
|
+{
|
|
|
|
+ if (active_listener->cursor) {
|
|
with_view_ctx(^{
|
|
with_view_ctx(^{
|
|
- surface_gl_destroy_texture(dgc.gls, surface);
|
|
|
|
- surface_gl_create_texture(dgc.gls, surface);
|
|
|
|
|
|
+ glBindTexture(GL_TEXTURE_2D, cursor_texture);
|
|
|
|
+ glPixelStorei(GL_UNPACK_ROW_LENGTH_EXT,
|
|
|
|
+ active_listener->cursor->width);
|
|
|
|
+ glTexImage2D(GL_TEXTURE_2D, 0,
|
|
|
|
+ epoxy_is_desktop_gl() ? GL_RGBA : GL_BGRA,
|
|
|
|
+ active_listener->cursor->width,
|
|
|
|
+ active_listener->cursor->height,
|
|
|
|
+ 0, GL_BGRA, GL_UNSIGNED_BYTE,
|
|
|
|
+ active_listener->cursor->data);
|
|
|
|
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
|
|
|
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
|
});
|
|
});
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ gl_dirty = true;
|
|
|
|
+}
|
|
|
|
|
|
- gl_surface_dirty = true;
|
|
|
|
|
|
+static void cocoa_gl_cursor_define(DisplayChangeListener *dcl, QEMUCursor *cursor)
|
|
|
|
+{
|
|
|
|
+ CocoaListener *listener = container_of(dcl, CocoaListener, dcl);
|
|
|
|
+
|
|
|
|
+ listener->cursor = cursor;
|
|
|
|
+
|
|
|
|
+ if (listener == active_listener) {
|
|
|
|
+ cocoa_gl_cursor_update();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
@@ -2131,35 +2385,28 @@ static void cocoa_gl_scanout_texture(DisplayChangeListener *dcl,
|
|
|
|
|
|
listener->gl_scanout_id = backing_id;
|
|
listener->gl_scanout_id = backing_id;
|
|
listener->gl_scanout_y0_top = backing_y_0_top;
|
|
listener->gl_scanout_y0_top = backing_y_0_top;
|
|
-
|
|
|
|
- if (listener == active_listener) {
|
|
|
|
- gl_surface_dirty = false;
|
|
|
|
- }
|
|
|
|
|
|
+ gl_dirty = true;
|
|
}
|
|
}
|
|
|
|
|
|
-static void cocoa_gl_scanout_flush()
|
|
|
|
|
|
+static void cocoa_gl_scanout_flush(DisplayChangeListener *dcl,
|
|
|
|
+ uint32_t x, uint32_t y,
|
|
|
|
+ uint32_t w, uint32_t h)
|
|
{
|
|
{
|
|
- if (!active_listener->gl_scanout_id) {
|
|
|
|
- return;
|
|
|
|
|
|
+ if (container_of(dcl, CocoaListener, dcl) == active_listener) {
|
|
|
|
+ gl_dirty = true;
|
|
}
|
|
}
|
|
-
|
|
|
|
- with_view_ctx(^{
|
|
|
|
- NSSize size = [cocoaView convertSizeToBacking:[cocoaView frame].size];
|
|
|
|
-
|
|
|
|
- glBindFramebuffer(GL_FRAMEBUFFER_EXT, 0);
|
|
|
|
- glViewport(0, 0, size.width, size.height);
|
|
|
|
- glBindTexture(GL_TEXTURE_2D, active_listener->gl_scanout_id);
|
|
|
|
- qemu_gl_run_texture_blit(dgc.gls, active_listener->gl_scanout_y0_top);
|
|
|
|
- cocoa_gl_flush();
|
|
|
|
- });
|
|
|
|
}
|
|
}
|
|
|
|
|
|
-static void cocoa_gl_scanout_flush_proxy(DisplayChangeListener *dcl,
|
|
|
|
- uint32_t x, uint32_t y,
|
|
|
|
- uint32_t w, uint32_t h)
|
|
|
|
|
|
+static void cocoa_gl_mouse_set(DisplayChangeListener *dcl, int x, int y, int on)
|
|
{
|
|
{
|
|
- if (container_of(dcl, CocoaListener, dcl) == active_listener) {
|
|
|
|
- cocoa_gl_scanout_flush();
|
|
|
|
|
|
+ CocoaListener *listener = container_of(dcl, CocoaListener, dcl);
|
|
|
|
+
|
|
|
|
+ listener->mouse_x = x;
|
|
|
|
+ listener->mouse_y = y;
|
|
|
|
+ listener->mouse_on = on;
|
|
|
|
+
|
|
|
|
+ if (listener == active_listener) {
|
|
|
|
+ gl_dirty = true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
@@ -2169,10 +2416,12 @@ static void cocoa_gl_scanout_flush_proxy(DisplayChangeListener *dcl,
|
|
.dpy_gfx_switch = cocoa_gl_switch,
|
|
.dpy_gfx_switch = cocoa_gl_switch,
|
|
.dpy_gfx_check_format = console_gl_check_format,
|
|
.dpy_gfx_check_format = console_gl_check_format,
|
|
.dpy_refresh = cocoa_gl_refresh,
|
|
.dpy_refresh = cocoa_gl_refresh,
|
|
|
|
+ .dpy_mouse_set = cocoa_gl_mouse_set,
|
|
|
|
+ .dpy_cursor_define = cocoa_gl_cursor_define,
|
|
|
|
|
|
.dpy_gl_scanout_disable = cocoa_gl_scanout_disable,
|
|
.dpy_gl_scanout_disable = cocoa_gl_scanout_disable,
|
|
.dpy_gl_scanout_texture = cocoa_gl_scanout_texture,
|
|
.dpy_gl_scanout_texture = cocoa_gl_scanout_texture,
|
|
- .dpy_gl_update = cocoa_gl_scanout_flush_proxy,
|
|
|
|
|
|
+ .dpy_gl_update = cocoa_gl_scanout_flush,
|
|
};
|
|
};
|
|
|
|
|
|
static bool cocoa_gl_is_compatible_dcl(DisplayGLCtx *dgc,
|
|
static bool cocoa_gl_is_compatible_dcl(DisplayGLCtx *dgc,
|
|
@@ -2212,7 +2461,7 @@ static void cocoa_display_init(DisplayState *ds, DisplayOptions *opts)
|
|
appController = [[QemuCocoaAppController alloc] init];
|
|
appController = [[QemuCocoaAppController alloc] init];
|
|
[NSApp setDelegate:appController];
|
|
[NSApp setDelegate:appController];
|
|
|
|
|
|
- qemu_mutex_init(&surface_mutex);
|
|
|
|
|
|
+ qemu_mutex_init(&draw_mutex);
|
|
|
|
|
|
if (display_opengl) {
|
|
if (display_opengl) {
|
|
#ifdef CONFIG_OPENGL
|
|
#ifdef CONFIG_OPENGL
|
|
@@ -2249,6 +2498,7 @@ static void cocoa_display_init(DisplayState *ds, DisplayOptions *opts)
|
|
}
|
|
}
|
|
|
|
|
|
dgc.gls = qemu_gl_init_shader();
|
|
dgc.gls = qemu_gl_init_shader();
|
|
|
|
+ glGenTextures(1, &cursor_texture);
|
|
|
|
|
|
// register vga output callbacks
|
|
// register vga output callbacks
|
|
ops = &dcl_gl_ops;
|
|
ops = &dcl_gl_ops;
|