It's way faster than CG, but it requires a Mac with OpenGL capable Graphics Card and at least 8mb of VRAM.
I think starting with G4 and Highend G3, this requirements are met.
features:[new] draws dirty lines of the window as needed, implemented with OpenGL (used the extensions as proposed by Pierre)
[new] window can be resized [fix] conditional builds for Leopard, without linking to a specific sdk [fix] lineflicker in fullscreen modeThe Question is, where to draw the line - or - if it needs a switch for CG/OpenGL support for cocoa.
Please test and comment. Mike [1] http://www.kberg.ch/qemu/091patches/cocoa_m_OpenGL.diff.gz--- /Users/mike/Documents/Qemu091gcc4/qemu/cocoa.m_original.m 2008-01-21 17:11:30.000000000 +0100 +++ /Users/mike/Documents/Qemu091gcc4/qemu/cocoa.m 2008-02-01 17:11:41.000000000 +0100
@@ -22,12 +22,17 @@ * THE SOFTWARE. */ +#include <AvailabilityMacros.h> + #import <Cocoa/Cocoa.h> +#import <OpenGL/gl.h> + #include "qemu-common.h" #include "console.h" #include "sysemu.h" +#define titleBarHeight 21.0 //#define DEBUG @@ -54,6 +59,16 @@ int bitsPerPixel; } QEMUScreen; +typedef struct { + float x; + float y; + float width; + float height; + float dx; + float dy; + float zoom; +} COCOADisplayProperties; + int qemu_main(int argc, char **argv); // main defined in qemu/vl.c NSWindow *normalWindow; id cocoaView; @@ -246,18 +261,19 @@ QemuCocoaView ------------------------------------------------------ */ [EMAIL PROTECTED] QemuCocoaView : NSView [EMAIL PROTECTED] QemuCocoaView : NSOpenGLView { QEMUScreen screen; + COCOADisplayProperties displayProperties; NSWindow *fullScreenWindow; - float cx,cy,cw,ch,cdx,cdy; - CGDataProviderRef dataProviderRef; + GLuint screen_tex; int modifiers_state[256]; BOOL isMouseGrabed; BOOL isFullscreen; BOOL isAbsoluteEnabled; BOOL isTabletEnabled; } +- (void) setContentDimensionsForFrame:(NSRect)rect;- (void) resizeContentToWidth:(int)w height:(int)h displayState: (DisplayState *)ds;
- (void) grabMouse; - (void) ungrabMouse; @@ -266,17 +282,16 @@ - (void) setAbsoluteEnabled:(BOOL)tIsAbsoluteEnabled; - (BOOL) isMouseGrabed; - (BOOL) isAbsoluteEnabled; -- (float) cdx; -- (float) cdy; - (QEMUScreen) gscreen; +- (COCOADisplayProperties) displayProperties; @end @implementation QemuCocoaView -- (id)initWithFrame:(NSRect)frameRect+- (id)initWithFrame:(NSRect)frameRect pixelFormat: (NSOpenGLPixelFormat *)format
{ - COCOA_DEBUG("QemuCocoaView: initWithFrame\n"); + COCOA_DEBUG("QemuCocoaView: initWithFrame:pixelFormat\n"); - self = [super initWithFrame:frameRect]; + self = [super initWithFrame:frameRect pixelFormat:format]; if (self) { screen.bitsPerComponent = 8; @@ -284,6 +299,8 @@ screen.width = frameRect.size.width; screen.height = frameRect.size.height; + displayProperties.zoom = 1.0; + } return self; } @@ -295,110 +312,118 @@ if (screenBuffer) free(screenBuffer); - if (dataProviderRef) - CGDataProviderRelease(dataProviderRef); - [super dealloc]; } - (void) drawRect:(NSRect) rect { - COCOA_DEBUG("QemuCocoaView: drawRect\n");+ COCOA_DEBUG("QemuCocoaView: drawRect: NSRect(%f, %f, %f, %f)\n", rect.origin.x, rect.origin.y, rect.size.width, rect.size.height);
if ((int)screenBuffer == -1) return; - // get CoreGraphic context- CGContextRef viewContextRef = [[NSGraphicsContext currentContext] graphicsPort]; - CGContextSetInterpolationQuality (viewContextRef, kCGInterpolationNone);
- CGContextSetShouldAntialias (viewContextRef, NO); - - // draw screen bitmap directly to Core Graphics context - if (dataProviderRef) { - CGImageRef imageRef = CGImageCreate( - screen.width, //width - screen.height, //height - screen.bitsPerComponent, //bitsPerComponent - screen.bitsPerPixel, //bitsPerPixel - (screen.width * 4), //bytesPerRow + // remove old texture + if( screen_tex != 0) { + glDeleteTextures(1, &screen_tex); + } + + screen_tex = 1; + float onePixel[2]; + onePixel[0] = 2.0 / displayProperties.width; + onePixel[1] = 2.0 / displayProperties.height; + + //calculate the texure rect + NSRect clipRect; + clipRect = NSMakeRect(+ 0.0, // we update the whole width, as QEMU in vga is always updating whole memory pages) + floor((float)screen.height - (rect.origin.y + rect.size.height) / displayProperties.dy),
+ (float)screen.width, + ceil(rect.size.height / displayProperties.dy)); + int start = (int)clipRect.origin.y * screen.width * 4; + unsigned char *startPointer = screenBuffer; + + //adapt the drawRect to the textureRect + rect = NSMakeRect(+ 0.0, // we update the whole width, as QEMU in vga is always updating whole memory pages) + (screen.height - (clipRect.origin.y + clipRect.size.height)) * displayProperties.dy,
+ displayProperties.width, + clipRect.size.height * displayProperties.dy); + + glEnable(GL_TEXTURE_RECTANGLE_ARB); // enable rectangle textures + + // bind screenBuffer to texture+ glPixelStorei(GL_UNPACK_ROW_LENGTH, screen.width); // Sets the appropriate unpacking row length for the bitmap. + glPixelStorei(GL_UNPACK_ALIGNMENT, 1); // Sets the byte-aligned unpacking that's needed for bitmaps that are 3 bytes per pixel.
++ glBindTexture (GL_TEXTURE_RECTANGLE_ARB, screen_tex); // Binds the texture name to the texture target. + glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MIN_FILTER, GL_LINEAR); // Sets filtering so that it does not use a mipmap, which would be redundant for the texture rectangle extension
+ + // optimize loading of texture+ glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_STORAGE_HINT_APPLE, GL_STORAGE_SHARED_APPLE); // + glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, GL_TRUE); // bypass OpenGL framework + glTextureRangeAPPLE(GL_TEXTURE_RECTANGLE_EXT, (int)clipRect.size.height * screen.width * 4, &startPointer[start]); // bypass OpenGL driver
+ + glTexImage2D( + GL_TEXTURE_RECTANGLE_ARB, + 0, + GL_RGBA, + screen.width, + (int)clipRect.size.height, + 0, #if __LITTLE_ENDIAN__- CGColorSpaceCreateWithName(kCGColorSpaceGenericRGB), // colorspace for OS X >= 10.4
- kCGImageAlphaNoneSkipLast, + GL_RGBA, + GL_UNSIGNED_BYTE, #else- CGColorSpaceCreateDeviceRGB(), //colorspace for OS X < 10.4 (actually ppc)
- kCGImageAlphaNoneSkipFirst, //bitmapInfo -#endif - dataProviderRef, //provider - NULL, //decode - 0, //interpolate - kCGRenderingIntentDefault //intent - ); -// test if host support "CGImageCreateWithImageInRect" at compiletime -#if (MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_4)- if (CGImageCreateWithImageInRect == NULL) { // test if "CGImageCreateWithImageInRect" is supported on host at runtime
+ GL_BGRA, + GL_UNSIGNED_INT_8_8_8_8_REV, #endif- // compatibility drawing code (draws everything) (OS X < 10.4) - CGContextDrawImage (viewContextRef, CGRectMake(0, 0, [self bounds].size.width, [self bounds].size.height), imageRef);
-#if (MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_4) - } else {- // selective drawing code (draws only dirty rectangles) (OS X >= 10.4)
- const NSRect *rectList; - int rectCount; - int i; - CGImageRef clipImageRef; - CGRect clipRect; - - [self getRectsBeingDrawn:&rectList count:&rectCount]; - for (i = 0; i < rectCount; i++) { - clipRect.origin.x = rectList[i].origin.x / cdx;- clipRect.origin.y = (float)screen.height - (rectList[i].origin.y + rectList[i].size.height) / cdy;
- clipRect.size.width = rectList[i].size.width / cdx; - clipRect.size.height = rectList[i].size.height / cdy; - clipImageRef = CGImageCreateWithImageInRect( - imageRef, - clipRect - );- CGContextDrawImage (viewContextRef, cgrect(rectList[i]), clipImageRef);
- CGImageRelease (clipImageRef); - } - } -#endif - CGImageRelease (imageRef); + &startPointer[start]); + + glBegin(GL_QUADS); + { + glTexCoord2f(0.0f, 0.0f);+ glVertex2f(-1.0f, (GLfloat)(onePixel[1] * (rect.origin.y + rect.size.height) - 1.0));
+ + glTexCoord2f(0.0f, (GLfloat)clipRect.size.height); + glVertex2f(-1.0f, (GLfloat)(onePixel[1] * rect.origin.y - 1.0)); ++ glTexCoord2f((GLfloat)clipRect.size.width, (GLfloat)clipRect.size.height);
+ glVertex2f(1.0f, (GLfloat)(onePixel[1] * rect.origin.y - 1.0)); + + glTexCoord2f((GLfloat)clipRect.size.width, 0.0f);+ glVertex2f(1.0f, (GLfloat)(onePixel[1] * (rect.origin.y + rect.size.height) - 1.0));
} + glEnd(); + + glFlush(); } -- (void) setContentDimensions +- (void) setContentDimensionsForFrame:(NSRect)rect { - COCOA_DEBUG("QemuCocoaView: setContentDimensions\n");+ COCOA_DEBUG("QemuCocoaView: setContentDimensionsForFrame: NSRect(%f, %f, %f, %f)\n", rect.origin.x, rect.origin.y, rect.size.width, rect.size.height);
- if (isFullscreen) {- cdx = [[NSScreen mainScreen] frame].size.width / (float)screen.width; - cdy = [[NSScreen mainScreen] frame].size.height / (float)screen.height;
- cw = screen.width * cdx; - ch = screen.height * cdy; - cx = ([[NSScreen mainScreen] frame].size.width - cw) / 2.0; - cy = ([[NSScreen mainScreen] frame].size.height - ch) / 2.0; - } else { - cx = 0; - cy = 0; - cw = screen.width; - ch = screen.height; - cdx = 1.0; - cdy = 1.0; - } + displayProperties.dx = rect.size.width / (float)screen.width; + displayProperties.dy = rect.size.height / (float)screen.height; + displayProperties.width = rect.size.width; + displayProperties.height = rect.size.height; + displayProperties.x = 0.0;//([self bounds].size.width - cw) / 2.0; + displayProperties.y = 0.0;//([self bounds].size.height - ch) / 2.0; + + [[self openGLContext] makeCurrentContext];+ glViewport(displayProperties.x, displayProperties.y, displayProperties.width, displayProperties.height);
+ [self update]; }- (void) resizeContentToWidth:(int)w height:(int)h displayState: (DisplayState *)ds
{ - COCOA_DEBUG("QemuCocoaView: resizeContent\n");+ COCOA_DEBUG("QemuCocoaView: resizeContentToWidth:%i height:%i\n", w, h);
// update screenBuffer - if (dataProviderRef) - CGDataProviderRelease(dataProviderRef); if (screenBuffer) free(screenBuffer); screenBuffer = malloc( w * 4 * h ); + // update display state ds->data = screenBuffer; ds->linesize = (w * 4); ds->depth = 32; @@ -410,21 +435,31 @@ ds->bgr = 0; #endif- dataProviderRef = CGDataProviderCreateWithData(NULL, screenBuffer, w * 4 * h, NULL);
+ // update screen state + screen.width = w; + screen.height = h; + + NSSize normalWindowSize; + normalWindowSize = NSMakeSize( + (float)w * displayProperties.zoom, + (float)h * displayProperties.zoom + titleBarHeight + ); + + // keep Window in correct aspect ratio+ [normalWindow setMaxSize:NSMakeSize(normalWindowSize.width, normalWindowSize.height)]; + [normalWindow setAspectRatio:NSMakeSize(normalWindowSize.width, normalWindowSize.height)];
// update windows if (isFullscreen) {- [[fullScreenWindow contentView] setFrame:[[NSScreen mainScreen] frame]]; - [normalWindow setFrame:NSMakeRect([normalWindow frame].origin.x, [normalWindow frame].origin.y - h + screen.height, w, h + [normalWindow frame].size.height - screen.height) display:NO animate:NO]; + [self setContentDimensionsForFrame:[[NSScreen mainScreen] frame]]; + [normalWindow setFrame:NSMakeRect([normalWindow frame].origin.x, [normalWindow frame].origin.y + [normalWindow frame].size.height - normalWindowSize.height, normalWindowSize.width, normalWindowSize.height) display:NO animate:NO];
} else { if (qemu_name)[normalWindow setTitle:[NSString stringWithFormat:@"QEMU %s", qemu_name]]; - [normalWindow setFrame:NSMakeRect([normalWindow frame].origin.x, [normalWindow frame].origin.y - h + screen.height, w, h + [normalWindow frame].size.height - screen.height) display:YES animate:YES]; + [self setContentDimensionsForFrame:NSMakeRect(0, 0, w * displayProperties.zoom, h * displayProperties.zoom)]; + [self setFrame:NSMakeRect(0, 0, w * displayProperties.zoom, h * displayProperties.zoom)]; + [normalWindow setFrame:NSMakeRect([normalWindow frame].origin.x, [normalWindow frame].origin.y + [normalWindow frame].size.height - normalWindowSize.height, normalWindowSize.width, normalWindowSize.height) display:YES animate:YES];
} - screen.width = w; - screen.height = h; - [self setContentDimensions]; - [self setFrame:NSMakeRect(cx, cy, cw, ch)]; } - (void) toggleFullScreen:(id)sender @@ -434,9 +469,8 @@ if (isFullscreen) { // switch from fullscreen to desktop isFullscreen = FALSE; [self ungrabMouse]; - [self setContentDimensions];// test if host support "enterFullScreenMode:withOptions" at compiletime
-#if (MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_4) +#ifdef AVAILABLE_MAC_OS_X_VERSION_10_5_AND_LATERif ([NSView respondsToSelector:@selector(exitFullScreenModeWithOptions:)]) { // test if "exitFullScreenModeWithOptions" is supported on host at runtime
[self exitFullScreenModeWithOptions:nil]; } else { @@ -445,15 +479,16 @@ [normalWindow setContentView: self]; [normalWindow makeKeyAndOrderFront: self]; [NSMenu setMenuBarVisible:YES]; -#if (MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_4) +#ifdef AVAILABLE_MAC_OS_X_VERSION_10_5_AND_LATER } #endif+ [self setContentDimensionsForFrame:NSMakeRect(0.0, 0.0, screen.width * displayProperties.zoom, screen.height * displayProperties.zoom)];
} else { // switch from desktop to fullscreen isFullscreen = TRUE; [self grabMouse]; - [self setContentDimensions];+ [self setContentDimensionsForFrame:NSMakeRect(0.0, 0.0, [[NSScreen mainScreen] frame].size.width, [[NSScreen mainScreen] frame].size.height)]; // test if host support "enterFullScreenMode:withOptions" at compiletime
-#if (MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_4) +#ifdef AVAILABLE_MAC_OS_X_VERSION_10_5_AND_LATERif ([NSView respondsToSelector:@selector(enterFullScreenMode:withOptions:)]) { // test if "enterFullScreenMode:withOptions" is supported on host at runtime [self enterFullScreenMode:[NSScreen mainScreen] withOptions:[NSDictionary dictionaryWithObjectsAndKeys: [NSNumber numberWithBool:NO], NSFullScreenModeAllScreens,
@@ -469,7 +504,7 @@ [fullScreenWindow setHasShadow:NO]; [fullScreenWindow setContentView:self]; [fullScreenWindow makeKeyAndOrderFront:self]; -#if (MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_4) +#ifdef AVAILABLE_MAC_OS_X_VERSION_10_5_AND_LATER } #endif } @@ -581,7 +616,7 @@ break; case NSMouseMoved: if (isAbsoluteEnabled) {- if (p.x < 0 || p.x > screen.width || p.y < 0 || p.y > screen.height || ![[self window] isKeyWindow]) { + if (p.x < 0 || p.x > (screen.width * displayProperties.zoom) || p.y < 0 || p.y > (screen.height * displayProperties.zoom) || ![[self window] isKeyWindow]) { if (isTabletEnabled) { // if we leave the window, deactivate the tablet
[NSCursor unhide]; isTabletEnabled = FALSE; @@ -688,12 +723,42 @@ isMouseGrabed = FALSE; }+- (NSSize)windowWillResize:(NSWindow *)window toSize: (NSSize)proposedFrameSize
+{+ COCOA_DEBUG("QemuCocoaView: windowWillResize: toSize: NSSize(%f, %f)\n", proposedFrameSize.width, proposedFrameSize.height);
+ + // update zoom+ displayProperties.zoom = proposedFrameSize.width / (float)screen.width;
++ // Update the content to new size before window is resized, if the new size is bigger + if (proposedFrameSize.width > [window frame].size.width || proposedFrameSize.height > [window frame].size.height) { + [self setContentDimensionsForFrame:NSMakeRect(0, 0, proposedFrameSize.width, proposedFrameSize.height - titleBarHeight)]; + [self setFrame:NSMakeRect(displayProperties.x, displayProperties.y, displayProperties.width, displayProperties.height - titleBarHeight)];
+ } + + return proposedFrameSize; +} + +- (void)windowDidResize:(NSNotification *)notification +{ + COCOA_DEBUG("QemuCocoaView: windowDidResize\n"); + + // update the content, if the size has changed+ if (displayProperties.width != [[self window] frame].size.width || displayProperties.height != [[self window] frame].size.height - titleBarHeight) {
+ if (isFullscreen) {+ [self setContentDimensionsForFrame:NSMakeRect(0, 0, [[self window] frame].size.width, [[self window] frame].size.height)];
+ } else {+ [self setContentDimensionsForFrame:NSMakeRect(0, 0, [[self window] frame].size.width, [[self window] frame].size.height - titleBarHeight)];
+ }+ [self setFrame:NSMakeRect(displayProperties.x, displayProperties.y, displayProperties.width, displayProperties.height)];
+ } +} +- (void) setAbsoluteEnabled:(BOOL)tIsAbsoluteEnabled {isAbsoluteEnabled = tIsAbsoluteEnabled;}
- (BOOL) isMouseGrabed {return isMouseGrabed;} - (BOOL) isAbsoluteEnabled {return isAbsoluteEnabled;} -- (float) cdx {return cdx;} -- (float) cdy {return cdy;} - (QEMUScreen) gscreen {return screen;}+- (COCOADisplayProperties) displayProperties {return displayProperties;}
@end @@ -722,7 +787,7 @@ if (self) { // create a view and add it to the window- cocoaView = [[QemuCocoaView alloc] initWithFrame:NSMakeRect(0.0, 0.0, 640.0, 480.0)]; + cocoaView = [[QemuCocoaView alloc] initWithFrame:NSMakeRect(0.0, 0.0, 640.0, 480.0) pixelFormat: [NSOpenGLView defaultPixelFormat]];
if(!cocoaView) { fprintf(stderr, "(cocoa) can't create a view\n"); exit(1); @@ -730,7 +795,7 @@ // create a windownormalWindow = [[NSWindow alloc] initWithContentRect: [cocoaView frame] - styleMask:NSTitledWindowMask|NSMiniaturizableWindowMask| NSClosableWindowMask + styleMask:NSTitledWindowMask|NSMiniaturizableWindowMask| NSClosableWindowMask|NSResizableWindowMask
backing:NSBackingStoreBuffered defer:NO]; if(!normalWindow) { fprintf(stderr, "(cocoa) can't create window\n"); @@ -740,6 +805,7 @@ [normalWindow setTitle:[NSString stringWithFormat:@"QEMU"]]; [normalWindow setContentView:cocoaView]; [normalWindow makeKeyAndOrderFront:self]; + [normalWindow setDelegate:cocoaView]; } return self; @@ -927,15 +993,12 @@ 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]); - } + rect = NSMakeRect( + x * [cocoaView displayProperties].dx,+ ([cocoaView gscreen].height - y - h) * [cocoaView displayProperties].dy,
+ w * [cocoaView displayProperties].dx, + h * [cocoaView displayProperties].dy); + [cocoaView displayRect:rect]; }--- /Users/mike/Documents/Qemu091gcc4/qemu/configure_original 2008-02-01 09:27:49.000000000 +0100 +++ /Users/mike/Documents/Qemu091gcc4/qemu/configure 2008-02-01 09:33:37.000000000 +0100
@@ -154,7 +154,7 @@ cocoa="yes" coreaudio="yes" OS_CFLAGS="-mdynamic-no-pic" -OS_LDFLAGS="-framework CoreFoundation -framework IOKit"+OS_LDFLAGS="-framework CoreFoundation -framework IOKit -framework OpenGL"
;; SunOS) solaris="yes"--- /Users/mike/Documents/Qemu091gcc4/qemu/Makefile.target_original 2008-02-01 09:38:48.000000000 +0100 +++ /Users/mike/Documents/Qemu091gcc4/qemu/Makefile.target 2008-02-01 09:38:32.000000000 +0100
@@ -525,7 +525,7 @@ VL_OBJS+=gdbstub.o endif ifdef CONFIG_COCOA-COCOA_LIBS=-F/System/Library/Frameworks -framework Cocoa -framework IOKit +COCOA_LIBS=-F/System/Library/Frameworks -framework Cocoa -framework IOKit -framework OpenGL
ifdef CONFIG_COREAUDIO COCOA_LIBS+=-framework CoreAudio endif
smime.p7s
Description: S/MIME cryptographic signature