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 window
normalWindow = [[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
