#include <application.h>
#include <screen.h>
#include <window.h>
#import <AppKit/NSApplication.h>
#import <Foundation/NSGeometry.h>
#import <Foundation/NSBundle.h>
#import <ApplicationServices/ApplicationServices.h>
#import <Cocoa/Cocoa.h>
#include <Carbon/Carbon.h>
#include <OpenGL/OpenGL.h>
#include <OpenGL/gl.h>
#include <GLUT/glut.h>
#include <sys/time.h>
#if __BIG_ENDIAN__
#define CLIENT_IMAGE_FORMAT GL_UNSIGNED_INT_8_8_8_8_REV
#else
#define CLIENT_IMAGE_FORMAT GL_UNSIGNED_INT_8_8_8_8
#endif
#define BUFFER_WIDTH 512
#define BUFFER_HEIGHT 512
#define ROW_BYTES BUFFER_WIDTH * 4
unsigned char textureBuffer[ROW_BYTES * BUFFER_HEIGHT];
static void drawAnObject()
{
glColor3f(1.0f, 0.85f, 0.35f);
glBegin(GL_TRIANGLES);
{
glVertex3f( 0.0, 0.6, 0.0);
glVertex3f( -0.2, -0.3, 0.0);
glVertex3f( 0.2, -0.3 ,0.0);
}
glEnd();
}
@interface MyOpenGLView : NSOpenGLView
-(void) paint;
@end
@implementation MyOpenGLView
-(void) paint
{
/*
printf("paint\n");
glClearColor(0, 0, 0, 0);
glClear(GL_COLOR_BUFFER_BIT);
//glDrawPixels(rect.width(), rect.height(), GL_RGBA, GL_UNSIGNED_INT_8_8_8_8_REV, pixels);
glDrawPixels(BUFFER_WIDTH, BUFFER_HEIGHT, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8_REV, textureBuffer);
//drawAnObject();
glFlush();
*/
}
-(void) drawRect:(NSRect)bounds
{
printf("draw rect\n");
// [self paint];
[[self openGLContext] flushBuffer];
}
-(bool) acceptsFirstResponder { return YES; }
-(bool) applicationShouldTerminateAfterLastWindowClosed:(NSApplication *)theApplication { return YES; }
-(void) mouseDown:(NSEvent *)theEvent { printf("mouse down\n"); }
-(void) mouseUp:(NSEvent *)theEvent { printf("mouse up\n"); }
-(void) mouseDragged:(NSEvent *)theEvent { printf("mouse dragged\n"); }
-(void) keyDown:(NSEvent *)theEvent { printf("key down\n"); }
-(void) keyUp:(NSEvent *)theEvent { printf("key up\n"); }
@end
MyOpenGLView *myGlView;
#define USE_GL
//#define CAPTURE_WHOLE_DISPLAY // Warning, this is dangerous and might require a reboot to get control back
//#define BUFFERED_DIRECT_DISPLAY
class MacScreen : public Screen
{
public:
MacScreen(int _x, int _y, int wi, int ht) {
if ( !app ) {
printf("Please create an Application object first before creating a Screen\n");
return;
}
x = _x;
y = _y;
_y = CGDisplayPixelsHigh(kCGDirectMainDisplay) - y - ht;
NSRect rect = { { x, _y }, { wi, ht } };
window = [[NSWindow alloc]
initWithContentRect : rect
styleMask : 0x00F
backing : 0//(NSBackingStoreType)NSBackingStoreBuffered
defer : NO ];
//[window setLevel:CGShieldingWindowLevel()];
myGlView = [[MyOpenGLView alloc] init];
[window setContentView:myGlView];
[window display];
[window makeKeyAndOrderFront:nil];
printf("Window created\n");
/*
[gc makeCurrentContext];
NSOpenGLContext *gl_context = new NSOpenGLContext;
CGLContextObj ctx;
ctx = [ gl_context cglContext ];
img = new Image(BUFFER_WIDTH, BUFFER_HEIGHT, textureBuffer, Format_RGBA_8888, BUFFER_WIDTH*4);
*/
#if defined(USE_GL)
img = new Image(wi, ht, Format_RGBA_8888);
#elif defined(BUFFERED_DIRECT_DISPLAY)
img = new Image(wi, ht, Format_RGBA_8888);
#elif defined(CAPTURE_WHOLE_DISPLAY) // Warning, this is dangerous and might require a reboot to get control back
/*
GDHandle deviceList = GetDeviceList();
unsigned char *fb = (unsigned char *)(*(*deviceList)->gdPMap)->baseAddr;
img = new Image(wi, ht, fb, Format_RGBA_8888, (*(*deviceList)->gdPMap)->rowBytes);
*/
CGDirectDisplayID display = kCGDirectMainDisplay;
CGDisplayCaptureWithOptions(display, kCGCaptureNoFill);
unsigned char *fb = (unsigned char *)CGDisplayAddressForPosition(display, x, y);
img = new Image(wi, ht, fb, Format_RGBA_8888, CGDisplayBytesPerRow(display));
//CGDisplayRelease(kCGDirectMainDisplay);
#else
img = new Image(wi, ht, Format_RGBA_8888);
#endif
}
~MacScreen() {
printf("Destruct\n");
#if defined(USE_GL)
// nothing
#elif defined(BUFFERED_DIRECT_DISPLAY)
// nothing
#elif defined(CAPTURE_WHOLE_DISPLAY)
CGDisplayRelease(kCGDirectMainDisplay);
#else
// nothing
#endif
}
NSWindow *window;
void flush() {
#if defined(USE_GL)
/*
[myGlView setNeedsDisplay:YES];
[myGlView paint];
*/
[[myGlView openGLContext] makeCurrentContext];
[[myGlView openGLContext] update];
[myGlView setNeedsDisplay:YES];
glClear(GL_COLOR_BUFFER_BIT);
// glClearColor(0, 0, 0, 0);
// glClear(GL_COLOR_BUFFER_BIT);
glDrawPixels(img->width(), img->height(), GL_RGBA, GL_UNSIGNED_INT_8_8_8_8_REV, img->bits());
//NSGraphicsContext *gfx = [NSGraphicsContext graphicsContextWithWindow:window];
//[gfx flushGraphics];
//[window flushWindow];
[[myGlView openGLContext] flushBuffer];
glutSwapBuffers();
//glFlush(); // Appears to cause delay, but might just be waiting for vertical retrace
// Flush only if haven't flushed in the last 25ms
static bool first = true;
static struct timeval lastTime;
struct timeval time;
gettimeofday(&time, 0);
if ( first ) {
lastTime = time;
first = false;
// glFinish();
//glFlush();
} else {
int diff = time.tv_sec - lastTime.tv_sec;
diff *= 1000000;
diff += time.tv_usec - lastTime.tv_usec;
if ( diff > 20000 ) {
// glFinish();
//glFlush();
lastTime = time;
}
}
#elif defined(BUFFERED_DIRECT_DISPLAY)
CGDirectDisplayID display = kCGDirectMainDisplay;
CGDisplayCaptureWithOptions(display, kCGCaptureNoFill);
unsigned char *fb = (unsigned char *)CGDisplayAddressForPosition(display, 0, 0);
Image disp(img->width(), img->height(), fb, Format_RGBA_8888, CGDisplayBytesPerRow(display));
blit(&disp, 0, 0, img, 0, 0, img->width(), img->height());
CGDisplayRelease(display); // Appears to cause repaint of everything
#elif defined(CAPTURE_WHOLE_DISPLAY)
// nothing
#else
NSGraphicsContext *gc;
CGContextRef context;
CGColorSpaceRef colorSpace;
CGBitmapInfo bitmapInfo;
CGContextRef bitmapContext;
CGImageRef image;
CGRect imageRect;
gc = [NSGraphicsContext graphicsContextWithWindow:window];
context = (CGContextRef)[gc graphicsPort];
//context = (CGContextRef)[[NSGraphicsContext currentContext] graphicsPort];
colorSpace = CGColorSpaceCreateDeviceRGB();
//bitmapInfo = kCGImageAlphaNoneSkipLast;
bitmapInfo = kCGImageAlphaPremultipliedLast;
bitmapContext = CGBitmapContextCreate(img->bits(), img->width(), img->height(), 8, img->width() * 4, colorSpace, bitmapInfo);
image = CGBitmapContextCreateImage(bitmapContext);
imageRect = CGRectMake(0, 0, img->width(), img->height());
CGContextDrawImage(context, imageRect, image);
[gc flushGraphics];
CGContextSynchronize(context);
CGContextFlush(context);
CGContextRelease(context);
CGContextRelease(bitmapContext);
CGColorSpaceRelease(colorSpace);
CFRelease(image);
delete gc;
//[[window contentView] setNeedsDisplay:YES];
// [myGlView setNeedsDisplay:YES];
#endif
}
Image *frameBuffer() { return img; }
private:
int x, y;
Image *img;
};
Screen *getScreen()
{
static Screen *s = 0;
return s ? s : s = new MacScreen(0, 0, BUFFER_WIDTH, BUFFER_HEIGHT);
}
#ifndef MIN
#define MIN(a,b) (((a)<(b))?(a):(b))
#endif
#ifndef MAX
#define MAX(a,b) (((a)>(b))?(a):(b))
#endif
void blit(Image *dst, int dstX, int dstY, Image *src, int srcX, int srcY, int w, int h)
{
bool flush = true; // XXX should check dst is the screen
w = MIN(MIN(w, src->width() - srcX), dst->width() - dstX);
h = MIN(MIN(h, src->height() - srcY), dst->height() - dstY);
unsigned char *rowDst = dst->bits() + dstX*4 + dstY*dst->step();
unsigned char *rowSrc = src->bits() + srcX*4 + srcY*src->step();
unsigned int *dstPix;
unsigned int *srcPix;
int i, j = h;
while (j--) {
dstPix = (unsigned int *)rowDst;
srcPix = (unsigned int *)rowSrc;
// memcpy(dstPix, srcPix, w*4);
size_t len = w;
while ((size_t)dstPix & 63 && len) {
*dstPix++ = *srcPix++;
len--;
}
size_t align_count = ((size_t)dstPix & 63) >> 2;
int switch_val = (len > align_count) ? align_count : len;
switch (switch_val)
{
case 15: *dstPix++ = *srcPix++;
case 14: *dstPix++ = *srcPix++;
case 13: *dstPix++ = *srcPix++;
case 12: *dstPix++ = *srcPix++;
case 11: *dstPix++ = *srcPix++;
case 10: *dstPix++ = *srcPix++;
case 9: *dstPix++ = *srcPix++;
case 8: *dstPix++ = *srcPix++;
case 7: *dstPix++ = *srcPix++;
case 6: *dstPix++ = *srcPix++;
case 5: *dstPix++ = *srcPix++;
case 4: *dstPix++ = *srcPix++;
case 3: *dstPix++ = *srcPix++;
case 2: *dstPix++ = *srcPix++;
case 1: *dstPix++ = *srcPix++;
case 0: break;
}
len -= switch_val;
unsigned long long *dstPix64 = (unsigned long long *)dstPix;
unsigned long long *srcPix64 = (unsigned long long *)srcPix;
while (len >= 8) {
dstPix64[0] = srcPix64[0];
dstPix64[1] = srcPix64[1];
dstPix64[2] = srcPix64[2];
dstPix64[3] = srcPix64[3];
//dstPix64[4] = srcPix64[4];
//dstPix64[5] = srcPix64[5];
//dstPix64[6] = srcPix64[6];
//dstPix64[7] = srcPix64[7];
dstPix64 += 4;
srcPix64 += 4;
len -= 8;
}
dstPix = (unsigned int *)dstPix64;
srcPix = (unsigned int *)srcPix64;
while (len) {
*dstPix++ = *srcPix++;
len--;
}
/*
int n = (len + 7) / 8;
// Duff's device
switch (len & 0x07)
{
case 0: do { *dstPix++ = *srcPix++;
case 7: *dstPix++ = *srcPix++;
case 6: *dstPix++ = *srcPix++;
case 5: *dstPix++ = *srcPix++;
case 4: *dstPix++ = *srcPix++;
case 3: *dstPix++ = *srcPix++;
case 2: *dstPix++ = *srcPix++;
case 1: *dstPix++ = *srcPix++;
} while (--n > 0);
}
*/
/*
int n = (w + 7) / 8;
// Duff's device
switch (w & 0x07)
{
case 0: do { *dstPix++ = *srcPix++;
case 7: *dstPix++ = *srcPix++;
case 6: *dstPix++ = *srcPix++;
case 5: *dstPix++ = *srcPix++;
case 4: *dstPix++ = *srcPix++;
case 3: *dstPix++ = *srcPix++;
case 2: *dstPix++ = *srcPix++;
case 1: *dstPix++ = *srcPix++;
} while (--n > 0);
}
*/
rowDst += dst->step();
rowSrc += src->step();
}
// if ( flush )
// getScreen()->flush();
}