hackthelobby/libcaca/caca/driver/cocoa.m

1018 lines
30 KiB
Objective-C

/*
* libcaca Colour ASCII-Art library
* Copyright © 2021 Sam Hocevar <sam@hocevar.net>
* 2006 Colin Delacroix <colin@zoy.org>
* All Rights Reserved
*
* This library is free software. It comes without any warranty, to
* the extent permitted by applicable law. You can redistribute it
* and/or modify it under the terms of the Do What the Fuck You Want
* to Public License, Version 2, as published by Sam Hocevar. See
* http://www.wtfpl.net/ for more details.
*/
/*
* This file contains the libcaca Cocoa input and output driver
*/
#include "config.h"
#if defined USE_COCOA
#import <Cocoa/Cocoa.h>
#include "caca.h"
#include "caca.h"
#include "caca_internals.h"
//#define COCOA_DEBUG
// many ways to draw the chars :
// - NSString and drawInRect:withAttributes: or drawWithRect:options:attributes:
// - NSAttributedString and drawInRect: or drawWithRect:options:
// - NSTextLayout and co.
// - Quartz 2D
// - ATSUI (more accessible from carbon)
// 2 firsts are high level cocoa, 3rd is low-level cocoa, other are untested
// also see http://www.cocoabuilder.com/archive/message/cocoa/2004/11/18/121928
// update: actually high-level is faster, so keep it like that
//#define USE_LOWLEVEL_COCOA 1
// build a complete color table cache for the view
#define PRECACHE_WHOLE_COLOR_TABLE 1
//#define USE_RGB12_FGBG 1
//#define USE_GLOBAL_AUTORELEASE_POOL 1
#ifdef COCOA_DEBUG
#define debug_log NSLog
#else
#define debug_log(...)
#endif
#define NCOLORS 0x1000
static BOOL s_quit = NO;
static BOOL s_quitting = NO;
@interface CacaView : NSView
{
//NSFont* _font;
NSRect _font_rect;
int _h, _w;
uint32_t* _attrs;
uint32_t* _chars;
NSRect* _bkg_rects;
NSColor** _bkg_colors;
#ifdef PRECACHE_WHOLE_COLOR_TABLE
NSColor* _colorCache[NCOLORS];
#else
NSMutableDictionary* _colorCache;
#endif
NSMutableDictionary* _attrDict;
NSMutableDictionary* _attrDictUnderline; // lame optim
#ifdef USE_LOWLEVEL_COCOA
NSTextStorage* _textStorage;
NSLayoutManager* _layoutManager;
NSTextContainer* _textContainer;
#endif
}
- (void)setFont:(NSFont *)aFont;
- (void)updateBuffersFromCaca:(caca_display_t *)dp;
@end
@interface NSColor(Caca)
+ (NSColor *)colorFromRgb12:(uint16_t) ui_rgb12;
@end
@implementation CacaView
- (id)initWithFrame:(NSRect)frameRect
{
self = [super initWithFrame:frameRect];
if(!self)
return nil;
[[self window] makeFirstResponder:self];
#ifdef PRECACHE_WHOLE_COLOR_TABLE
int i;
for(i = 0; i < NCOLORS; i++)
_colorCache[i] = [[NSColor colorFromRgb12:i] retain];
#else
_colorCache = [[NSMutableDictionary alloc] initWithCapacity:NCOLORS];
#endif
_attrDict = [[NSMutableDictionary alloc] initWithCapacity:3];
_attrDictUnderline = [[NSMutableDictionary alloc] initWithCapacity:3];
[_attrDictUnderline setObject:[NSNumber numberWithInt:NSUnderlineStyleSingle]
forKey:NSUnderlineStyleAttributeName];
#ifdef USE_LOWLEVEL_COCOA
_textStorage = [[NSTextStorage alloc] init];
_layoutManager = [[NSLayoutManager alloc] init];
_textContainer = [[NSTextContainer alloc] init];
[_textContainer setLineFragmentPadding:0.0];
[_layoutManager addTextContainer:_textContainer];
[_textStorage addLayoutManager:_layoutManager];
#endif
return self;
}
- (void)dealloc
{
//[_font release];
#ifdef PRECACHE_WHOLE_COLOR_TABLE
short i;
for(i = 0; i < NCOLORS; i++)
[_colorCache[i] release];
#else
[_colorCache release];
#endif
[_attrDict release];
[_attrDictUnderline release];
#ifdef USE_LOWLEVEL_COCOA
[_textStorage release];
[_layoutManager release];
[_textContainer release];
#endif
if(_attrs)
free(_attrs);
if(_bkg_rects)
free(_bkg_rects);
if(_bkg_colors)
free(_bkg_colors);
[super dealloc];
}
// to accelerate the window drawing speed
- (BOOL)isOpaque
{
return YES;
}
- (BOOL)isFlipped
{
return YES;
}
- (void)setupNewSize
{
float fw = _font_rect.size.width;
float fh = _font_rect.size.height;
_w = ceilf([self bounds].size.width / fw);
_h = ceilf([self bounds].size.height / fh);
debug_log(@"fw=%f selfw=%f %u %f", fw, [self bounds].size.width,
_w, [self bounds].size.width-(_w*fw));
debug_log(@"fh=%f selfh=%f %u %f", fh, [self bounds].size.height,
_h, [self bounds].size.height-(_h*fh));
}
- (void)keyDown:(NSEvent *)theEvent
{
NSLog(@"key %@", theEvent);
}
- (void)mouseMoved:(NSEvent *)theEvent
{
NSLog(@"mouse %@", theEvent);
}
- (void)setFont:(NSFont *)aFont
{
//[_font release];
//_font = [aFont retain];
_font_rect = [aFont boundingRectForFont];
_font_rect = NSMakeRect(0, 0, ceilf(_font_rect.size.width), ceilf(_font_rect.size.height));
[self setupNewSize];
[_attrDict setObject:aFont forKey:NSFontAttributeName];
[_attrDictUnderline setObject:aFont forKey:NSFontAttributeName];
[aFont set];
}
- (void)resizeIfNeeded:(caca_display_t *)dp
{
if(_w != caca_get_canvas_width(dp->cv)
|| _h != caca_get_canvas_height(dp->cv)
|| !_attrs || !_bkg_rects || !_bkg_colors)
{
debug_log(@"%s resize to %ux%u", _cmd, _w, _h);
_w = caca_get_canvas_width(dp->cv);
_h = caca_get_canvas_height(dp->cv);
if(_attrs)
free(_attrs);
_attrs = _caca_alloc2d(_w , _h, sizeof(uint32_t) * 2);
if(_bkg_rects)
free(_bkg_rects);
_bkg_rects = _caca_alloc2d(_w, _h, sizeof(NSRect));
if(_bkg_colors)
free(_bkg_colors);
_bkg_colors = _caca_alloc2d(_w, _h, sizeof(NSColor*));
// [[self window] setContentSize: NSMakeSize(caca_get_canvas_width(dp->cv) * _font_rect.size.width,
// caca_get_canvas_height(dp->cv) * _font_rect.size.height)];
[[self window] setContentSize: NSMakeSize(caca_get_canvas_width(dp->cv) * 8,
caca_get_canvas_height(dp->cv) * 13)];
}
}
- (void)updateBuffersFromCaca:(caca_display_t *)dp
{
[self resizeIfNeeded:dp];
if(_attrs)
{
_chars = _attrs + _w * _h;
memcpy(_attrs, caca_get_canvas_attrs(dp->cv),
_w * _h * sizeof(uint32_t));
memcpy(_chars, caca_get_canvas_chars(dp->cv),
_w * _h * sizeof(uint32_t));
[self setNeedsDisplay:TRUE];
}
}
- (void)drawRect:(NSRect)rect
{
//if([self inLiveResize]) [self setupNewSize];
if(!_attrs || !_chars)
{
[[NSColor blueColor] set];
NSRectFill(rect);
return;
}
int x, y;
float fw = 8;//_font_rect.size.width;
float fh = 13;//_font_rect.size.height;
uint32_t* attrs;
uint32_t* chars = _chars;
/* first take care of the background */
[[NSColor blackColor] set];
NSRectFill(rect);
int arrayLength = 0;
for(y = 0; y < _h; y++)
{
int yoff = y * fh;
for(x = 0; x < _w; x++)
{
NSRect r = NSMakeRect(x * fw, yoff, fw, fh);
if(NSIntersectsRect(r, rect))
{
attrs = _attrs + x + y * _w;
NSColor* color = nil;
#if USE_RGB12_FGBG
uint16_t bg = caca_attr_to_rgb12_bg(*attrs);
if(bg)
{
# ifdef PRECACHE_WHOLE_COLOR_TABLE
color = _colorCache[bg];
# else
NSNumber* numberBg = [NSNumber numberWithInt:bg];
color = [_colorCache objectForKey:numberBg];
if(!color)
{
color = [NSColor colorFromRgb12:bg];
if(color)
[_colorCache setObject:color forKey:numberBg];
}
# endif
}
#else
uint8_t argb[8];
caca_attr_to_argb64(*attrs, argb);
color = [NSColor colorWithCalibratedRed:((float)argb[1]) / 15.0
green:((float)argb[2]) / 15.0
blue:((float)argb[3]) / 15.0
alpha:1.0];
#endif
if(color)
{
_bkg_colors[arrayLength] = color;
_bkg_rects[arrayLength++] = r;
}
}
}
}
NSRectFillListWithColors(_bkg_rects, _bkg_colors, arrayLength);
/* Then print the foreground characters */
for(y = 0; y < _h; y++)
{
int yoff = y * fh;
for(x = 0; x < _w; x++, chars++)
{
attrs = _attrs + x + y * _w;
/* Skip spaces */
if(*chars <= 0x00000020)
continue;
if(*chars == CACA_MAGIC_FULLWIDTH)
continue;
/* Plain ASCII, no problem. */
// TODO: test me with wide chars
//if(*chars > 0x00000020 && *chars < 0x00000080)
{
NSRect r = NSMakeRect(x * fw + 1, yoff, fw - 1, fh);
if(NSIntersectsRect(r, rect))
{
NSColor* color = nil;
#if USE_RGB12_FGBG
uint16_t fg = caca_attr_to_rgb12_fg(*attrs);
# ifdef PRECACHE_WHOLE_COLOR_TABLE
color = _colorCache[fg];
# else // PRECACHE_WHOLE_COLOR_TABLE
NSNumber* numberFg = [NSNumber numberWithInt:fg];
color = [_colorCache objectForKey:numberFg];
if(!color)
{
color = [NSColor colorFromRgb12:fg];
if(color)
[_colorCache setObject:color forKey:numberFg];
}
# endif // PRECACHE_WHOLE_COLOR_TABLE
#else // USE_RGB12_FGBG
uint8_t argb[8];
caca_attr_to_argb64(*attrs, argb);
debug_log(@"x,y=[%d,%d] r,g,b back=[%u %u %u] front=[%u %u %u]",
x, y, argb[1], argb[2], argb[3], argb[5], argb[6], argb[7]);
color = [NSColor colorWithCalibratedRed:((float)argb[5]) / 15.0
green:((float)argb[6]) / 15.0
blue:((float)argb[7]) / 15.0
alpha:1.0];
#endif // USE_RGB12_FGBG
if(color)
{
NSMutableDictionary* attrDict = (*attrs & CACA_UNDERLINE) ?
_attrDictUnderline : _attrDict;
[attrDict setObject:color forKey:NSForegroundColorAttributeName];
unichar ch = *chars;
NSString* str = [[NSString alloc] initWithCharacters:&ch length:1];
#ifdef USE_LOWLEVEL_COCOA
[[_textStorage mutableString] setString:str];
[_textStorage setAttributes:attrDict range:NSMakeRange(0, 1)];
[_layoutManager drawGlyphsForGlyphRange:NSMakeRange(0, 1) atPoint:r.origin];
#else
[str drawInRect:r withAttributes:attrDict];
#endif
[str release];
}
}
continue;
}
}
}
}
@end
struct driver_private
{
NSWindow* window;
CacaView* view;
#ifdef USE_GLOBAL_AUTORELEASE_POOL
NSAutoreleasePool* pool;
#endif
};
//============================================================================
// NSApplication(Caca)
//============================================================================
@implementation NSApplication(Caca)
- (void)setRunning
{
_running = 1;
}
@end
//============================================================================
// NSColor(Caca)
//============================================================================
@implementation NSColor(Caca)
+ (NSColor *)colorFromRgb12:(uint16_t)ui_rgb12
{
float red = ((float)((ui_rgb12 & 0x0f00) >> 3)) / 15.0,
green = ((float)((ui_rgb12 & 0x00f0) >> 2)) / 15.0,
blue = ((float)( ui_rgb12 & 0x000f) ) / 15.0;
return [NSColor colorWithDeviceRed:red green:green
blue:blue alpha:1.0];
}
@end
//============================================================================
// CacaWindowDelegate
//============================================================================
@interface CacaWindowDelegate : NSObject
@end
@implementation CacaWindowDelegate
- (BOOL)windowShouldClose:(id)sender
{
debug_log(@"%s", _cmd);
[NSApp terminate:self];
return NO;
}
@end
//============================================================================
// CacaAppDelegate
//============================================================================
@interface CacaAppDelegate : NSObject
@end
@implementation CacaAppDelegate : NSObject
- (NSApplicationTerminateReply)applicationShouldTerminate:(NSApplication *)sender
{
s_quit = YES;
return NSTerminateCancel;
}
@end
/* setAppleMenu disappeared from the headers in 10.4 */
#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_4
@interface NSApplication(NSAppleMenu)
- (void)setAppleMenu:(NSMenu *)menu;
@end
#endif
//============================================================================
// utility methods
//============================================================================
static NSString* get_application_name()
{
NSString* appName = [[NSBundle mainBundle] objectForInfoDictionaryKey:
@"CFBundleName"];
if(![appName length])
appName = [[NSProcessInfo processInfo] processName];
return appName;
}
static void create_application_menus()
{
/* Create the main menu bar */
[NSApp setMainMenu:[[NSMenu alloc] init]];
/* Create the application menu */
NSString* appName = get_application_name();
NSMenu* appleMenu = [[NSMenu alloc] initWithTitle:@""];
/* Add menu items */
NSString* title = [@"About " stringByAppendingString:appName];
[appleMenu addItemWithTitle:title
action:@selector(orderFrontStandardAboutPanel:)
keyEquivalent:@""];
[appleMenu addItem:[NSMenuItem separatorItem]];
title = [@"Hide " stringByAppendingString:appName];
[appleMenu addItemWithTitle:title action:@selector(hide:)
keyEquivalent:@"h"];
id<NSMenuItem> menuItem = [appleMenu addItemWithTitle:@"Hide Others"
action:@selector(hideOtherApplications:)
keyEquivalent:@"h"];
[menuItem setKeyEquivalentModifierMask:(NSAlternateKeyMask|NSCommandKeyMask)];
[appleMenu addItemWithTitle:@"Show All"
action:@selector(unhideAllApplications:)
keyEquivalent:@""];
[appleMenu addItem:[NSMenuItem separatorItem]];
title = [@"Quit " stringByAppendingString:appName];
[appleMenu addItemWithTitle:title action:@selector(terminate:)
keyEquivalent:@"q"];
/* Put menu into the menubar */
menuItem = [[NSMenuItem alloc] initWithTitle:@"" action:nil keyEquivalent:@""];
[menuItem setSubmenu:appleMenu];
[[NSApp mainMenu] addItem:menuItem];
[menuItem release];
/* Tell the application object that this is now the application menu */
[NSApp setAppleMenu:appleMenu];
[appleMenu release];
/* Create the window menu */
NSMenu* windowMenu = [[NSMenu alloc] initWithTitle:@"Window"];
/* "Minimize" item */
menuItem = [[NSMenuItem alloc] initWithTitle:@"Minimize"
action:@selector(performMiniaturize:)
keyEquivalent:@"m"];
[windowMenu addItem:menuItem];
[menuItem release];
/* Put menu into the menubar */
menuItem = [[NSMenuItem alloc] initWithTitle:@"Window" action:nil keyEquivalent:@""];
[menuItem setSubmenu:windowMenu];
[[NSApp mainMenu] addItem:menuItem];
[menuItem release];
/* Tell the application object that this is now the window menu */
[NSApp setWindowsMenu:windowMenu];
[windowMenu release];
}
static void register_cocoa_app(caca_display_t *dp)
{
ProcessSerialNumber psn;
if(!GetCurrentProcess(&psn))
{
TransformProcessType(&psn, kProcessTransformToForegroundApplication);
SetFrontProcess(&psn);
}
if(NSApp == nil)
{
[NSApplication sharedApplication];
if(![NSApp mainMenu])
create_application_menus();
[NSApp finishLaunching];
}
if ([NSApp delegate] == nil)
[NSApp setDelegate:[[CacaAppDelegate alloc] init]];
[NSApp setRunning];
}
static __inline__ void convert_NSRect(NSRect *r)
{
float mb_height = 38.0; // [[NSApp mainMenu] menuBarHeight] is 0 - wtf ?
/*debug_log(@"%@ %f %f %d %d %d", [NSApp mainMenu],
[[NSApp mainMenu] menuBarHeight], mb_height,
(int)CGDisplayPixelsHigh(kCGDirectMainDisplay),
(int)r->origin.y, (int)r->size.height);*/
r->origin.y = CGDisplayPixelsHigh(kCGDirectMainDisplay) - mb_height
- r->origin.y - r->size.height;
}
static void create_first_window(caca_display_t *dp)
{
NSFont* font = [NSFont fontWithName:@"Monaco" size:10];
NSRect fontRect = [font boundingRectForFont];
fontRect = NSMakeRect(0, 0, ceilf(fontRect.size.width), ceilf(fontRect.size.height));
NSRect windowRect = NSMakeRect(20, 20, caca_get_canvas_width(dp->cv) * fontRect.size.width,
caca_get_canvas_height(dp->cv) * fontRect.size.height);
convert_NSRect(&windowRect);
CacaView* view = [[CacaView alloc] initWithFrame:windowRect];
NSWindow* win = [[NSWindow alloc] initWithContentRect:windowRect
styleMask: NSTitledWindowMask
//| NSResizableWindowMask
| NSClosableWindowMask
| NSWindowMiniaturizeButton
backing:NSBackingStoreBuffered
defer:NO];
NSString* appName = get_application_name();
if(appName)
[win setTitle: appName];
[win setDelegate:[CacaWindowDelegate new]];
[win setContentView:view];
[view setFont:font];
[win makeKeyAndOrderFront:nil];
dp->drv.p->window = win;
dp->drv.p->view = view;
}
static int get_caca_keycode(NSEvent* event)
{
int caca_keycode = 0;
/*
unsigned short mac_keycode = [event keyCode];
debug_log(@"keycode %u (%x)", mac_keycode, mac_keycode);
switch(mac_keycode)
{
}
*/
if(/*!caca_keycode &&*/ ([event modifierFlags] & NSControlKeyMask))
{
NSString *chars = [event charactersIgnoringModifiers];
unichar ch = [chars characterAtIndex: 0];
// CACA_KEY_CTRL_A -> CACA_KEY_CTRL_Z
if(ch >= 'a' && ch <= 'z')
caca_keycode = CACA_KEY_CTRL_A + ch - 'a';
}
if(!caca_keycode)
{
NSString *chars = [event characters];
unichar ch = 0;
if([chars length])
ch = [chars characterAtIndex: 0];
switch(ch)
{
case NSUpArrowFunctionKey:
caca_keycode = CACA_KEY_UP;
break;
case NSDownArrowFunctionKey:
caca_keycode = CACA_KEY_DOWN;
break;
case NSLeftArrowFunctionKey:
caca_keycode = CACA_KEY_LEFT;
break;
case NSRightArrowFunctionKey:
caca_keycode = CACA_KEY_RIGHT;
break;
case 27:
caca_keycode = CACA_KEY_ESCAPE;
break;
case NSDeleteCharacter:
caca_keycode = CACA_KEY_DELETE;
break;
case NSBackspaceCharacter:
caca_keycode = CACA_KEY_BACKSPACE;
break;
case NSTabCharacter:
caca_keycode = CACA_KEY_TAB;
break;
case NSNewlineCharacter:
case NSCarriageReturnCharacter:
caca_keycode = CACA_KEY_RETURN;
break;
case NSPageUpFunctionKey:
caca_keycode = CACA_KEY_PAGEUP;
break;
case NSPageDownFunctionKey:
caca_keycode = CACA_KEY_PAGEDOWN;
break;
case NSF1FunctionKey:
caca_keycode = CACA_KEY_F1;
break;
case NSF2FunctionKey:
caca_keycode = CACA_KEY_F2;
break;
case NSF3FunctionKey:
caca_keycode = CACA_KEY_F3;
break;
case NSF4FunctionKey:
caca_keycode = CACA_KEY_F4;
break;
case NSF5FunctionKey:
caca_keycode = CACA_KEY_F5;
break;
case NSF6FunctionKey:
caca_keycode = CACA_KEY_F6;
break;
case NSF7FunctionKey:
caca_keycode = CACA_KEY_F7;
break;
case NSF8FunctionKey:
caca_keycode = CACA_KEY_F8;
break;
case NSF9FunctionKey:
caca_keycode = CACA_KEY_F9;
break;
case NSF10FunctionKey:
caca_keycode = CACA_KEY_F10;
break;
case NSF11FunctionKey:
caca_keycode = CACA_KEY_F11;
break;
case NSF12FunctionKey:
caca_keycode = CACA_KEY_F12;
break;
case NSF13FunctionKey:
caca_keycode = CACA_KEY_F13;
break;
case NSF14FunctionKey:
caca_keycode = CACA_KEY_F14;
break;
case NSF15FunctionKey:
caca_keycode = CACA_KEY_F15;
break;
case NSPauseFunctionKey:
caca_keycode = CACA_KEY_PAUSE;
break;
case NSInsertFunctionKey:
debug_log(@"insert key");
caca_keycode = CACA_KEY_INSERT;
break;
case NSHomeFunctionKey:
caca_keycode = CACA_KEY_HOME;
break;
case NSEndFunctionKey:
caca_keycode = CACA_KEY_END;
break;
}
}
return caca_keycode;
}
static BOOL handle_key_event(caca_privevent_t *ev, NSEvent* event)
{
if(!ev || !event)
return NO;
BOOL eventHandled = NO;
if([event modifierFlags] & NSCommandKeyMask)
{
// let the system handle the Apple-commands for now
return NO;
}
switch ([event type]) {
case NSKeyDown:
/* test [event isARepeat] ? */
ev->type = CACA_EVENT_KEY_PRESS;
break;
case NSKeyUp:
ev->type = CACA_EVENT_KEY_RELEASE;
break;
default:
;
}
int caca_keycode = get_caca_keycode(event);
if(caca_keycode)
{
ev->data.key.ch = caca_keycode;
eventHandled = YES;
}
else
{
NSString *chars = [event characters];
unichar mac_keycode = 0;
if([chars length])
mac_keycode = [chars characterAtIndex: 0];
if(mac_keycode)
{
ev->data.key.ch = mac_keycode;
ev->data.key.utf32 = (uint32_t)mac_keycode;
ev->data.key.utf8[0] = mac_keycode & 0x00ff; // FIXME: endianness
ev->data.key.utf8[1] = mac_keycode & 0xff00;
eventHandled = YES;
}
}
return eventHandled;
}
// TODO: handle CACA_EVENT_RESIZE
static BOOL handle_mouse_event(caca_display_t *dp, caca_privevent_t *ev,
NSEvent* event)
{
if(!ev || !event)
return NO;
switch ([event type]) {
case NSLeftMouseDown:
ev->type = CACA_EVENT_MOUSE_PRESS;
ev->data.mouse.button = 1;
break;
case NSLeftMouseUp:
ev->type = CACA_EVENT_MOUSE_RELEASE;
ev->data.mouse.button = 1;
break;
case NSRightMouseDown:
ev->type = CACA_EVENT_MOUSE_PRESS;
ev->data.mouse.button = 2;
break;
case NSRightMouseUp:
ev->type = CACA_EVENT_MOUSE_RELEASE;
ev->data.mouse.button = 2;
break;
case NSMouseMoved:
{
NSPoint mouseLoc = [NSEvent mouseLocation];
int mouse_x = round(mouseLoc.x);
int mouse_y = round(mouseLoc.y);
if(dp->mouse.x == mouse_x && dp->mouse.y == mouse_y)
break;
dp->mouse.x = mouse_x;
dp->mouse.y = mouse_y;
ev->type = CACA_EVENT_MOUSE_MOTION;
ev->data.mouse.x = dp->mouse.x;
ev->data.mouse.y = dp->mouse.y;
break;
}
default:
;
}
return YES;
}
//============================================================================
// caca driver methods
//============================================================================
static int cocoa_init_graphics(caca_display_t *dp)
{
int width = caca_get_canvas_width(dp->cv);
int height = caca_get_canvas_height(dp->cv);
debug_log(@"%s dp->cv: %ux%u", __PRETTY_FUNCTION__, width, height);
NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
dp->drv.p = malloc(sizeof(struct driver_private));
if(dp->drv.p == NULL)
return -1;
dp->resize.allow = 1;
caca_set_canvas_size(dp->cv, width ? width : 80, height ? height : 32);
dp->resize.allow = 0;
// first create a full cocoa app if the host has no bundle
if(![[NSBundle mainBundle] bundleIdentifier])
register_cocoa_app(dp);
create_first_window(dp);
#ifdef USE_GLOBAL_AUTORELEASE_POOL
dp->drv.p->pool = pool;
#else
[pool release];
#endif
return 0;
}
static int cocoa_end_graphics(caca_display_t *dp)
{
debug_log(@"%s dp->cv: %ux%u", __PRETTY_FUNCTION__,
caca_get_canvas_width(dp->cv), caca_get_canvas_height(dp->cv));
NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
[dp->drv.p->window close];
CacaWindowDelegate* delegate = [dp->drv.p->window delegate];
[dp->drv.p->window setDelegate:nil];
[delegate release];
// don't release the window yourself
//[dp->drv.p->window release];
#ifdef USE_GLOBAL_AUTORELEASE_POOL
[dp->drv.p->pool release];
#endif
free(dp->drv.p);
debug_log(@"%s end", __PRETTY_FUNCTION__);
[pool release];
return 0;
}
static void cocoa_display(caca_display_t *dp)
{
NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
[dp->drv.p->view updateBuffersFromCaca:dp];
[pool release];
}
static int cocoa_get_event(caca_display_t *dp, caca_privevent_t *ev)
{
if(s_quit)
{
if(s_quitting)
{
// host app isn't handling the quit event properly, aborting
debug_log(@"duplicate quit event, aborting.");
abort();
}
debug_log(@"posting quit event.");
ev->type = CACA_EVENT_QUIT;
s_quitting = YES;
return 1;
}
BOOL eventHandled = NO, forceRedispatch = NO;
ev->type = CACA_EVENT_NONE;
NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
if([NSApp isRunning])
{
NSEvent *event = [NSApp nextEventMatchingMask:NSAnyEventMask
untilDate:[NSDate distantPast]
inMode:NSDefaultRunLoopMode
dequeue:YES];
if(event)
{
switch([event type])
{
case NSKeyDown:
case NSKeyUp:
eventHandled = handle_key_event(ev, event);
break;
case NSFlagsChanged:
break;
case NSLeftMouseDown:
case NSLeftMouseUp:
case NSRightMouseDown:
case NSRightMouseUp:
case NSMouseMoved:
if([NSApp isActive])
{
eventHandled = handle_mouse_event(dp, ev, event);
forceRedispatch = YES;
}
else
{
[NSApp sendEvent:event];
eventHandled = YES;
}
break;
default:
;
}
if(!eventHandled || forceRedispatch)
[NSApp sendEvent:event];
}
}
[pool release];
if(eventHandled)
return 1;
return 0;
}
static void cocoa_handle_resize(caca_display_t *dp)
{
debug_log(@"%s", __PRETTY_FUNCTION__);
dp->resize.w = caca_get_canvas_width(dp->cv);
dp->resize.h = caca_get_canvas_height(dp->cv);
}
static int cocoa_set_display_title(caca_display_t *dp, char const *title)
{
NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
[dp->drv.p->window setTitle:[NSString stringWithUTF8String:title]];
[pool release];
return 0;
}
static int cocoa_get_display_width(caca_display_t const *dp)
{
return [dp->drv.p->window frame].size.width;
}
static int cocoa_get_display_height(caca_display_t const *dp)
{
return [dp->drv.p->window frame].size.height;
}
static void cocoa_set_mouse(caca_display_t *dp, int flag)
{
NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
if(flag)
[[NSCursor arrowCursor] set];
else {
[[NSCursor disappearingItemCursor] set];
}
[pool release];
}
/*
* Driver initialisation
*/
int cocoa_install(caca_display_t *dp)
{
dp->drv.id = CACA_DRIVER_COCOA;
dp->drv.driver = "cocoa";
dp->drv.init_graphics = cocoa_init_graphics;
dp->drv.end_graphics = cocoa_end_graphics;
dp->drv.set_display_title = cocoa_set_display_title;
dp->drv.get_display_width = cocoa_get_display_width;
dp->drv.get_display_height = cocoa_get_display_height;
dp->drv.display = cocoa_display;
dp->drv.handle_resize = cocoa_handle_resize;
dp->drv.get_event = cocoa_get_event;
dp->drv.set_mouse = cocoa_set_mouse;
return 0;
}
#endif /* USE_COCOA */