"Fossies" - the Fresh Open Source Software Archive

Member "libcaca-0.99.beta20/caca/driver/cocoa.m" (19 Oct 2021, 30551 Bytes) of package /linux/privat/libcaca-0.99.beta20.tar.bz2:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) Matlab source code syntax highlighting (style: standard) with prefixed line numbers. Alternatively you can here view or download the uninterpreted source code file. For more information about "cocoa.m" see the Fossies "Dox" file reference documentation and the latest Fossies "Diffs" side-by-side code changes report: 0.99.beta19_vs_0.99.beta20.

    1 /*
    2  *  libcaca     Colour ASCII-Art library
    3  *  Copyright © 2021 Sam Hocevar <sam@hocevar.net>
    4  *              2006 Colin Delacroix <colin@zoy.org>
    5  *              All Rights Reserved
    6  *
    7  *  This library is free software. It comes without any warranty, to
    8  *  the extent permitted by applicable law. You can redistribute it
    9  *  and/or modify it under the terms of the Do What the Fuck You Want
   10  *  to Public License, Version 2, as published by Sam Hocevar. See
   11  *  http://www.wtfpl.net/ for more details.
   12  */
   13 
   14 /*
   15  *  This file contains the libcaca Cocoa input and output driver
   16  */
   17 
   18 #include "config.h"
   19 
   20 #if defined USE_COCOA
   21 
   22 #import <Cocoa/Cocoa.h>
   23 
   24 #include "caca.h"
   25 #include "caca.h"
   26 #include "caca_internals.h"
   27 
   28 //#define COCOA_DEBUG
   29 
   30 // many ways to draw the chars :
   31 // - NSString and drawInRect:withAttributes: or drawWithRect:options:attributes:
   32 // - NSAttributedString and drawInRect: or drawWithRect:options:
   33 // - NSTextLayout and co.
   34 // - Quartz 2D
   35 // - ATSUI (more accessible from carbon)
   36 // 2 firsts are high level cocoa, 3rd is low-level cocoa, other are untested
   37 // also see http://www.cocoabuilder.com/archive/message/cocoa/2004/11/18/121928
   38 // update: actually high-level is faster, so keep it like that
   39 //#define USE_LOWLEVEL_COCOA 1
   40 
   41 // build a complete color table cache for the view
   42 #define PRECACHE_WHOLE_COLOR_TABLE 1
   43 
   44 //#define USE_RGB12_FGBG 1
   45 
   46 //#define USE_GLOBAL_AUTORELEASE_POOL 1
   47 
   48 #ifdef COCOA_DEBUG
   49 #define debug_log NSLog
   50 #else
   51 #define debug_log(...)
   52 #endif
   53 
   54 #define NCOLORS 0x1000
   55 
   56 static BOOL s_quit = NO;
   57 static BOOL s_quitting = NO;
   58 
   59 @interface CacaView : NSView
   60 {
   61     //NSFont* _font;
   62     NSRect _font_rect;
   63     int _h, _w;
   64     uint32_t* _attrs;
   65     uint32_t* _chars;
   66     NSRect*   _bkg_rects;
   67     NSColor** _bkg_colors;
   68 #ifdef PRECACHE_WHOLE_COLOR_TABLE
   69     NSColor* _colorCache[NCOLORS];
   70 #else
   71     NSMutableDictionary* _colorCache;
   72 #endif
   73     NSMutableDictionary* _attrDict;
   74     NSMutableDictionary* _attrDictUnderline; // lame optim
   75 #ifdef USE_LOWLEVEL_COCOA
   76     NSTextStorage* _textStorage;
   77     NSLayoutManager* _layoutManager;
   78     NSTextContainer* _textContainer;
   79 #endif
   80 }
   81 
   82 - (void)setFont:(NSFont *)aFont;
   83 - (void)updateBuffersFromCaca:(caca_display_t *)dp;
   84 @end
   85 
   86 @interface NSColor(Caca)
   87 + (NSColor *)colorFromRgb12:(uint16_t) ui_rgb12;
   88 @end
   89 
   90 @implementation CacaView
   91 - (id)initWithFrame:(NSRect)frameRect
   92 {
   93     self = [super initWithFrame:frameRect];
   94     if(!self)
   95         return nil;
   96 
   97     [[self window] makeFirstResponder:self];
   98 
   99 #ifdef PRECACHE_WHOLE_COLOR_TABLE
  100     int i;
  101     for(i = 0; i < NCOLORS; i++)
  102         _colorCache[i] = [[NSColor colorFromRgb12:i] retain];
  103 #else
  104     _colorCache = [[NSMutableDictionary alloc] initWithCapacity:NCOLORS];
  105 #endif
  106     _attrDict = [[NSMutableDictionary alloc] initWithCapacity:3];
  107     _attrDictUnderline = [[NSMutableDictionary alloc] initWithCapacity:3];
  108     [_attrDictUnderline setObject:[NSNumber numberWithInt:NSUnderlineStyleSingle]
  109                         forKey:NSUnderlineStyleAttributeName];
  110 #ifdef USE_LOWLEVEL_COCOA
  111     _textStorage = [[NSTextStorage alloc] init];
  112     _layoutManager = [[NSLayoutManager alloc] init];
  113     _textContainer = [[NSTextContainer alloc] init];
  114     [_textContainer setLineFragmentPadding:0.0];
  115     [_layoutManager addTextContainer:_textContainer];
  116     [_textStorage addLayoutManager:_layoutManager];
  117 #endif
  118 
  119     return self;
  120 }
  121 
  122 - (void)dealloc
  123 {
  124     //[_font release];
  125 #ifdef PRECACHE_WHOLE_COLOR_TABLE
  126     short i;
  127     for(i = 0; i < NCOLORS; i++)
  128         [_colorCache[i] release];
  129 #else
  130     [_colorCache release];
  131 #endif
  132     [_attrDict release];
  133     [_attrDictUnderline release];
  134 #ifdef USE_LOWLEVEL_COCOA
  135     [_textStorage release];
  136     [_layoutManager release];
  137     [_textContainer release];
  138 #endif
  139     if(_attrs)
  140         free(_attrs);
  141     if(_bkg_rects)
  142         free(_bkg_rects);
  143     if(_bkg_colors)
  144         free(_bkg_colors);
  145 
  146     [super dealloc];
  147 }
  148 
  149 // to accelerate the window drawing speed
  150 - (BOOL)isOpaque
  151 {
  152     return YES;
  153 }
  154 
  155 - (BOOL)isFlipped
  156 {
  157     return YES;
  158 }
  159 
  160 - (void)setupNewSize
  161 {
  162     float fw = _font_rect.size.width;
  163     float fh = _font_rect.size.height;
  164     _w = ceilf([self bounds].size.width / fw);
  165     _h = ceilf([self bounds].size.height / fh);
  166     debug_log(@"fw=%f selfw=%f %u %f", fw, [self bounds].size.width,
  167               _w, [self bounds].size.width-(_w*fw));
  168     debug_log(@"fh=%f selfh=%f %u %f", fh, [self bounds].size.height,
  169               _h, [self bounds].size.height-(_h*fh));
  170 }
  171 
  172 - (void)keyDown:(NSEvent *)theEvent
  173 {
  174     NSLog(@"key %@", theEvent);
  175 }
  176 
  177 - (void)mouseMoved:(NSEvent *)theEvent
  178 {
  179     NSLog(@"mouse %@", theEvent);
  180 }
  181 
  182 - (void)setFont:(NSFont *)aFont
  183 {
  184     //[_font release];
  185     //_font = [aFont retain];
  186     _font_rect = [aFont boundingRectForFont];
  187     _font_rect = NSMakeRect(0, 0, ceilf(_font_rect.size.width), ceilf(_font_rect.size.height));
  188     [self setupNewSize];
  189     [_attrDict setObject:aFont forKey:NSFontAttributeName];
  190     [_attrDictUnderline setObject:aFont forKey:NSFontAttributeName];
  191     [aFont set];
  192 }
  193 
  194 - (void)resizeIfNeeded:(caca_display_t *)dp
  195 {
  196     if(_w != caca_get_canvas_width(dp->cv)
  197         || _h != caca_get_canvas_height(dp->cv)
  198         || !_attrs || !_bkg_rects || !_bkg_colors)
  199     {
  200         debug_log(@"%s resize to %ux%u", _cmd, _w, _h);
  201 
  202         _w = caca_get_canvas_width(dp->cv);
  203         _h = caca_get_canvas_height(dp->cv);
  204 
  205         if(_attrs)
  206             free(_attrs);
  207         _attrs = _caca_alloc2d(_w , _h, sizeof(uint32_t) * 2);
  208 
  209         if(_bkg_rects)
  210             free(_bkg_rects);
  211         _bkg_rects = _caca_alloc2d(_w, _h, sizeof(NSRect));
  212 
  213         if(_bkg_colors)
  214             free(_bkg_colors);
  215         _bkg_colors = _caca_alloc2d(_w, _h, sizeof(NSColor*));
  216 
  217       //  [[self window] setContentSize: NSMakeSize(caca_get_canvas_width(dp->cv) * _font_rect.size.width,
  218      //                                             caca_get_canvas_height(dp->cv) * _font_rect.size.height)];
  219           [[self window] setContentSize: NSMakeSize(caca_get_canvas_width(dp->cv) * 8,
  220                                                     caca_get_canvas_height(dp->cv) * 13)];
  221 
  222     }
  223 }
  224 
  225 - (void)updateBuffersFromCaca:(caca_display_t *)dp
  226 {
  227     [self resizeIfNeeded:dp];
  228 
  229     if(_attrs)
  230     {
  231         _chars = _attrs + _w * _h;
  232         memcpy(_attrs, caca_get_canvas_attrs(dp->cv),
  233                _w * _h * sizeof(uint32_t));
  234         memcpy(_chars, caca_get_canvas_chars(dp->cv),
  235                _w * _h * sizeof(uint32_t));
  236 
  237         [self setNeedsDisplay:TRUE];
  238     }
  239 }
  240 
  241 - (void)drawRect:(NSRect)rect
  242 {
  243     //if([self inLiveResize]) [self setupNewSize];
  244 
  245     if(!_attrs || !_chars)
  246     {
  247         [[NSColor blueColor] set];
  248         NSRectFill(rect);
  249         return;
  250     }
  251 
  252     int x, y;
  253     float fw = 8;//_font_rect.size.width;
  254     float fh = 13;//_font_rect.size.height;
  255     uint32_t* attrs;
  256     uint32_t* chars = _chars;
  257 
  258     /* first take care of the background */
  259     [[NSColor blackColor] set];
  260     NSRectFill(rect);
  261 
  262     int arrayLength = 0;
  263     for(y = 0; y < _h; y++)
  264     {
  265         int yoff = y * fh;
  266         for(x = 0; x < _w; x++)
  267         {
  268             NSRect r = NSMakeRect(x * fw, yoff, fw, fh);
  269             if(NSIntersectsRect(r, rect))
  270             {
  271                 attrs = _attrs + x + y * _w;
  272                 NSColor* color = nil;
  273 #if USE_RGB12_FGBG
  274                 uint16_t bg = caca_attr_to_rgb12_bg(*attrs);
  275                 if(bg)
  276                 {
  277 #   ifdef PRECACHE_WHOLE_COLOR_TABLE
  278                     color = _colorCache[bg];
  279 #   else
  280                     NSNumber* numberBg = [NSNumber numberWithInt:bg];
  281                     color = [_colorCache objectForKey:numberBg];
  282                     if(!color)
  283                     {
  284                         color = [NSColor colorFromRgb12:bg];
  285                         if(color)
  286                             [_colorCache setObject:color forKey:numberBg];
  287                     }
  288 #   endif
  289                 }
  290 #else
  291                 uint8_t argb[8];
  292                 caca_attr_to_argb64(*attrs, argb);
  293                 color =  [NSColor colorWithCalibratedRed:((float)argb[1]) / 15.0
  294                                   green:((float)argb[2]) / 15.0
  295                                   blue:((float)argb[3]) / 15.0
  296                                   alpha:1.0];
  297 #endif
  298                 if(color)
  299                 {
  300                     _bkg_colors[arrayLength] = color;
  301                     _bkg_rects[arrayLength++] = r;
  302                 }
  303             }
  304         }
  305     }
  306     NSRectFillListWithColors(_bkg_rects, _bkg_colors, arrayLength);
  307 
  308     /* Then print the foreground characters */
  309     for(y = 0; y < _h; y++)
  310     {
  311         int yoff = y * fh;
  312         for(x = 0; x < _w; x++, chars++)
  313         {
  314             attrs = _attrs + x + y * _w;
  315 
  316             /* Skip spaces */
  317             if(*chars <= 0x00000020)
  318                 continue;
  319 
  320             if(*chars == CACA_MAGIC_FULLWIDTH)
  321                 continue;
  322 
  323             /* Plain ASCII, no problem. */
  324             // TODO: test me with wide chars
  325             //if(*chars > 0x00000020 && *chars < 0x00000080)
  326             {
  327                 NSRect r = NSMakeRect(x * fw + 1, yoff, fw - 1, fh);
  328                 if(NSIntersectsRect(r, rect))
  329                 {
  330                     NSColor* color = nil;
  331 #if USE_RGB12_FGBG
  332                     uint16_t fg = caca_attr_to_rgb12_fg(*attrs);
  333 #   ifdef PRECACHE_WHOLE_COLOR_TABLE
  334                     color = _colorCache[fg];
  335 #   else // PRECACHE_WHOLE_COLOR_TABLE
  336                     NSNumber* numberFg = [NSNumber numberWithInt:fg];
  337                     color = [_colorCache objectForKey:numberFg];
  338                     if(!color)
  339                     {
  340                         color = [NSColor colorFromRgb12:fg];
  341                         if(color)
  342                             [_colorCache setObject:color forKey:numberFg];
  343                     }
  344 #   endif // PRECACHE_WHOLE_COLOR_TABLE
  345 #else // USE_RGB12_FGBG
  346                     uint8_t argb[8];
  347                     caca_attr_to_argb64(*attrs, argb);
  348                     debug_log(@"x,y=[%d,%d] r,g,b back=[%u %u %u] front=[%u %u %u]",
  349                               x, y, argb[1], argb[2], argb[3], argb[5], argb[6], argb[7]);
  350                     color =  [NSColor colorWithCalibratedRed:((float)argb[5]) / 15.0
  351                                       green:((float)argb[6]) / 15.0
  352                                       blue:((float)argb[7]) / 15.0
  353                                       alpha:1.0];
  354 #endif // USE_RGB12_FGBG
  355 
  356                     if(color)
  357                     {
  358                         NSMutableDictionary* attrDict = (*attrs & CACA_UNDERLINE) ?
  359                                                         _attrDictUnderline : _attrDict;
  360                         [attrDict setObject:color forKey:NSForegroundColorAttributeName];
  361 
  362                         unichar ch = *chars;
  363                         NSString* str = [[NSString alloc] initWithCharacters:&ch length:1];
  364 
  365 #ifdef USE_LOWLEVEL_COCOA
  366                         [[_textStorage mutableString] setString:str];
  367                         [_textStorage setAttributes:attrDict range:NSMakeRange(0, 1)];
  368                         [_layoutManager drawGlyphsForGlyphRange:NSMakeRange(0, 1) atPoint:r.origin];
  369 #else
  370                         [str drawInRect:r withAttributes:attrDict];
  371 #endif
  372                         [str release];
  373                     }
  374                 }
  375                 continue;
  376             }
  377         }
  378     }
  379 }
  380 
  381 @end
  382 
  383 struct driver_private
  384 {
  385     NSWindow* window;
  386     CacaView* view;
  387 #ifdef USE_GLOBAL_AUTORELEASE_POOL
  388     NSAutoreleasePool* pool;
  389 #endif
  390 };
  391 
  392 //============================================================================
  393 // NSApplication(Caca)
  394 //============================================================================
  395 
  396 @implementation NSApplication(Caca)
  397 - (void)setRunning
  398 {
  399     _running = 1;
  400 }
  401 @end
  402 
  403 //============================================================================
  404 // NSColor(Caca)
  405 //============================================================================
  406 
  407 @implementation NSColor(Caca)
  408 + (NSColor *)colorFromRgb12:(uint16_t)ui_rgb12
  409 {
  410     float red   = ((float)((ui_rgb12 & 0x0f00) >> 3)) / 15.0,
  411           green = ((float)((ui_rgb12 & 0x00f0) >> 2)) / 15.0,
  412           blue  = ((float)( ui_rgb12 & 0x000f)      ) / 15.0;
  413     return [NSColor colorWithDeviceRed:red green:green
  414                     blue:blue alpha:1.0];
  415 }
  416 @end
  417 
  418 //============================================================================
  419 // CacaWindowDelegate
  420 //============================================================================
  421 
  422 @interface CacaWindowDelegate : NSObject
  423 @end
  424 
  425 @implementation CacaWindowDelegate
  426 - (BOOL)windowShouldClose:(id)sender
  427 {
  428     debug_log(@"%s", _cmd);
  429     [NSApp terminate:self];
  430     return NO;
  431 }
  432 @end
  433 
  434 //============================================================================
  435 // CacaAppDelegate
  436 //============================================================================
  437 
  438 @interface CacaAppDelegate : NSObject
  439 @end
  440 
  441 @implementation CacaAppDelegate : NSObject
  442 - (NSApplicationTerminateReply)applicationShouldTerminate:(NSApplication *)sender
  443 {
  444     s_quit = YES;
  445     return NSTerminateCancel;
  446 }
  447 @end
  448 
  449 /* setAppleMenu disappeared from the headers in 10.4 */
  450 #if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_4
  451 @interface NSApplication(NSAppleMenu)
  452 - (void)setAppleMenu:(NSMenu *)menu;
  453 @end
  454 #endif
  455 
  456 //============================================================================
  457 // utility methods
  458 //============================================================================
  459 
  460 static NSString* get_application_name()
  461 {
  462     NSString* appName = [[NSBundle mainBundle] objectForInfoDictionaryKey:
  463                                                    @"CFBundleName"];
  464     if(![appName length])
  465         appName = [[NSProcessInfo processInfo] processName];
  466 
  467     return appName;
  468 }
  469 
  470 static void create_application_menus()
  471 {
  472     /* Create the main menu bar */
  473     [NSApp setMainMenu:[[NSMenu alloc] init]];
  474 
  475     /* Create the application menu */
  476     NSString* appName = get_application_name();
  477     NSMenu* appleMenu = [[NSMenu alloc] initWithTitle:@""];
  478 
  479     /* Add menu items */
  480     NSString* title = [@"About " stringByAppendingString:appName];
  481     [appleMenu addItemWithTitle:title
  482                action:@selector(orderFrontStandardAboutPanel:)
  483                keyEquivalent:@""];
  484     [appleMenu addItem:[NSMenuItem separatorItem]];
  485 
  486     title = [@"Hide " stringByAppendingString:appName];
  487     [appleMenu addItemWithTitle:title action:@selector(hide:)
  488                keyEquivalent:@"h"];
  489 
  490     id<NSMenuItem> menuItem = [appleMenu addItemWithTitle:@"Hide Others"
  491                                          action:@selector(hideOtherApplications:)
  492                                          keyEquivalent:@"h"];
  493     [menuItem setKeyEquivalentModifierMask:(NSAlternateKeyMask|NSCommandKeyMask)];
  494 
  495     [appleMenu addItemWithTitle:@"Show All"
  496                action:@selector(unhideAllApplications:)
  497                keyEquivalent:@""];
  498     [appleMenu addItem:[NSMenuItem separatorItem]];
  499 
  500     title = [@"Quit " stringByAppendingString:appName];
  501     [appleMenu addItemWithTitle:title action:@selector(terminate:)
  502                keyEquivalent:@"q"];
  503 
  504     /* Put menu into the menubar */
  505     menuItem = [[NSMenuItem alloc] initWithTitle:@"" action:nil keyEquivalent:@""];
  506     [menuItem setSubmenu:appleMenu];
  507     [[NSApp mainMenu] addItem:menuItem];
  508     [menuItem release];
  509 
  510     /* Tell the application object that this is now the application menu */
  511     [NSApp setAppleMenu:appleMenu];
  512     [appleMenu release];
  513 
  514     /* Create the window menu */
  515     NSMenu* windowMenu = [[NSMenu alloc] initWithTitle:@"Window"];
  516 
  517     /* "Minimize" item */
  518     menuItem = [[NSMenuItem alloc] initWithTitle:@"Minimize"
  519                                    action:@selector(performMiniaturize:)
  520                                    keyEquivalent:@"m"];
  521     [windowMenu addItem:menuItem];
  522     [menuItem release];
  523 
  524     /* Put menu into the menubar */
  525     menuItem = [[NSMenuItem alloc] initWithTitle:@"Window" action:nil keyEquivalent:@""];
  526     [menuItem setSubmenu:windowMenu];
  527     [[NSApp mainMenu] addItem:menuItem];
  528     [menuItem release];
  529 
  530     /* Tell the application object that this is now the window menu */
  531     [NSApp setWindowsMenu:windowMenu];
  532     [windowMenu release];
  533 }
  534 
  535 static void register_cocoa_app(caca_display_t *dp)
  536 {
  537     ProcessSerialNumber psn;
  538     if(!GetCurrentProcess(&psn))
  539     {
  540         TransformProcessType(&psn, kProcessTransformToForegroundApplication);
  541         SetFrontProcess(&psn);
  542     }
  543 
  544     if(NSApp == nil)
  545     {
  546         [NSApplication sharedApplication];
  547 
  548         if(![NSApp mainMenu])
  549             create_application_menus();
  550 
  551         [NSApp finishLaunching];
  552     }
  553 
  554     if ([NSApp delegate] == nil)
  555         [NSApp setDelegate:[[CacaAppDelegate alloc] init]];
  556 
  557     [NSApp setRunning];
  558 }
  559 
  560 static __inline__ void convert_NSRect(NSRect *r)
  561 {
  562     float mb_height = 38.0; // [[NSApp mainMenu] menuBarHeight] is 0 - wtf ?
  563     /*debug_log(@"%@ %f %f %d %d %d", [NSApp mainMenu],
  564              [[NSApp mainMenu] menuBarHeight], mb_height,
  565              (int)CGDisplayPixelsHigh(kCGDirectMainDisplay),
  566              (int)r->origin.y, (int)r->size.height);*/
  567     r->origin.y = CGDisplayPixelsHigh(kCGDirectMainDisplay) - mb_height
  568                   - r->origin.y - r->size.height;
  569 }
  570 
  571 static void create_first_window(caca_display_t *dp)
  572 {
  573     NSFont* font = [NSFont fontWithName:@"Monaco" size:10];
  574     NSRect fontRect = [font boundingRectForFont];
  575     fontRect = NSMakeRect(0, 0, ceilf(fontRect.size.width), ceilf(fontRect.size.height));
  576     NSRect windowRect = NSMakeRect(20, 20, caca_get_canvas_width(dp->cv) * fontRect.size.width,
  577                                            caca_get_canvas_height(dp->cv) * fontRect.size.height);
  578     convert_NSRect(&windowRect);
  579 
  580     CacaView* view = [[CacaView alloc] initWithFrame:windowRect];
  581     NSWindow* win = [[NSWindow alloc] initWithContentRect:windowRect
  582                                       styleMask:  NSTitledWindowMask
  583                                               //| NSResizableWindowMask
  584                                                 | NSClosableWindowMask
  585                                                 | NSWindowMiniaturizeButton
  586                                       backing:NSBackingStoreBuffered
  587                                       defer:NO];
  588 
  589     NSString* appName = get_application_name();
  590     if(appName)
  591         [win setTitle: appName];
  592     [win setDelegate:[CacaWindowDelegate new]];
  593     [win setContentView:view];
  594     [view setFont:font];
  595     [win makeKeyAndOrderFront:nil];
  596 
  597     dp->drv.p->window = win;
  598     dp->drv.p->view = view;
  599 }
  600 
  601 static int get_caca_keycode(NSEvent* event)
  602 {
  603     int caca_keycode = 0;
  604     /*
  605     unsigned short mac_keycode = [event keyCode];
  606     debug_log(@"keycode %u (%x)", mac_keycode, mac_keycode);
  607     switch(mac_keycode)
  608     {
  609     }
  610     */
  611     if(/*!caca_keycode &&*/ ([event modifierFlags] & NSControlKeyMask))
  612     {
  613         NSString *chars = [event charactersIgnoringModifiers];
  614         unichar ch = [chars characterAtIndex: 0];
  615         // CACA_KEY_CTRL_A -> CACA_KEY_CTRL_Z
  616         if(ch >= 'a' && ch <= 'z')
  617             caca_keycode = CACA_KEY_CTRL_A + ch - 'a';
  618     }
  619 
  620     if(!caca_keycode)
  621     {
  622         NSString *chars = [event characters];
  623         unichar ch = 0;
  624         if([chars length])
  625             ch = [chars characterAtIndex: 0];
  626         switch(ch)
  627         {
  628             case NSUpArrowFunctionKey:
  629                 caca_keycode = CACA_KEY_UP;
  630                 break;
  631             case NSDownArrowFunctionKey:
  632                 caca_keycode = CACA_KEY_DOWN;
  633                 break;
  634             case NSLeftArrowFunctionKey:
  635                 caca_keycode = CACA_KEY_LEFT;
  636                 break;
  637             case NSRightArrowFunctionKey:
  638                 caca_keycode = CACA_KEY_RIGHT;
  639                 break;
  640             case 27:
  641                 caca_keycode = CACA_KEY_ESCAPE;
  642                 break;
  643             case NSDeleteCharacter:
  644                 caca_keycode = CACA_KEY_DELETE;
  645                 break;
  646             case NSBackspaceCharacter:
  647                 caca_keycode = CACA_KEY_BACKSPACE;
  648                 break;
  649             case NSTabCharacter:
  650                 caca_keycode = CACA_KEY_TAB;
  651                 break;
  652             case NSNewlineCharacter:
  653             case NSCarriageReturnCharacter:
  654                 caca_keycode = CACA_KEY_RETURN;
  655                 break;
  656             case NSPageUpFunctionKey:
  657                 caca_keycode = CACA_KEY_PAGEUP;
  658                 break;
  659             case NSPageDownFunctionKey:
  660                 caca_keycode = CACA_KEY_PAGEDOWN;
  661                 break;
  662             case NSF1FunctionKey:
  663                 caca_keycode = CACA_KEY_F1;
  664                 break;
  665             case NSF2FunctionKey:
  666                 caca_keycode = CACA_KEY_F2;
  667                 break;
  668             case NSF3FunctionKey:
  669                 caca_keycode = CACA_KEY_F3;
  670                 break;
  671             case NSF4FunctionKey:
  672                 caca_keycode = CACA_KEY_F4;
  673                 break;
  674             case NSF5FunctionKey:
  675                 caca_keycode = CACA_KEY_F5;
  676                 break;
  677             case NSF6FunctionKey:
  678                 caca_keycode = CACA_KEY_F6;
  679                 break;
  680             case NSF7FunctionKey:
  681                 caca_keycode = CACA_KEY_F7;
  682                 break;
  683             case NSF8FunctionKey:
  684                 caca_keycode = CACA_KEY_F8;
  685                 break;
  686             case NSF9FunctionKey:
  687                 caca_keycode = CACA_KEY_F9;
  688                 break;
  689             case NSF10FunctionKey:
  690                 caca_keycode = CACA_KEY_F10;
  691                 break;
  692             case NSF11FunctionKey:
  693                 caca_keycode = CACA_KEY_F11;
  694                 break;
  695             case NSF12FunctionKey:
  696                 caca_keycode = CACA_KEY_F12;
  697                 break;
  698             case NSF13FunctionKey:
  699                 caca_keycode = CACA_KEY_F13;
  700                 break;
  701             case NSF14FunctionKey:
  702                 caca_keycode = CACA_KEY_F14;
  703                 break;
  704             case NSF15FunctionKey:
  705                 caca_keycode = CACA_KEY_F15;
  706                 break;
  707             case NSPauseFunctionKey:
  708                 caca_keycode = CACA_KEY_PAUSE;
  709                 break;
  710             case NSInsertFunctionKey:
  711                 debug_log(@"insert key");
  712                 caca_keycode = CACA_KEY_INSERT;
  713                 break;
  714             case NSHomeFunctionKey:
  715                 caca_keycode = CACA_KEY_HOME;
  716                 break;
  717             case NSEndFunctionKey:
  718                 caca_keycode = CACA_KEY_END;
  719                 break;
  720         }
  721     }
  722 
  723     return caca_keycode;
  724 }
  725 
  726 static BOOL handle_key_event(caca_privevent_t *ev, NSEvent* event)
  727 {
  728     if(!ev || !event)
  729         return NO;
  730 
  731     BOOL eventHandled = NO;
  732 
  733     if([event modifierFlags] & NSCommandKeyMask)
  734     {
  735         // let the system handle the Apple-commands for now
  736         return NO;
  737     }
  738 
  739     switch ([event type]) {
  740         case NSKeyDown:
  741             /* test [event isARepeat] ? */
  742             ev->type = CACA_EVENT_KEY_PRESS;
  743             break;
  744         case NSKeyUp:
  745             ev->type = CACA_EVENT_KEY_RELEASE;
  746             break;
  747         default:
  748             ;
  749     }
  750 
  751     int caca_keycode = get_caca_keycode(event);
  752     if(caca_keycode)
  753     {
  754         ev->data.key.ch = caca_keycode;
  755         eventHandled = YES;
  756     }
  757     else
  758     {
  759         NSString *chars = [event characters];
  760         unichar mac_keycode = 0;
  761         if([chars length])
  762             mac_keycode = [chars characterAtIndex: 0];
  763         if(mac_keycode)
  764         {
  765             ev->data.key.ch = mac_keycode;
  766             ev->data.key.utf32 = (uint32_t)mac_keycode;
  767             ev->data.key.utf8[0] = mac_keycode & 0x00ff; // FIXME: endianness
  768             ev->data.key.utf8[1] = mac_keycode & 0xff00;
  769 
  770             eventHandled = YES;
  771         }
  772     }
  773 
  774     return eventHandled;
  775 }
  776 
  777 // TODO: handle CACA_EVENT_RESIZE
  778 static BOOL handle_mouse_event(caca_display_t *dp, caca_privevent_t *ev,
  779                                NSEvent* event)
  780 {
  781     if(!ev || !event)
  782         return NO;
  783 
  784     switch ([event type]) {
  785         case NSLeftMouseDown:
  786             ev->type = CACA_EVENT_MOUSE_PRESS;
  787             ev->data.mouse.button = 1;
  788             break;
  789         case NSLeftMouseUp:
  790             ev->type = CACA_EVENT_MOUSE_RELEASE;
  791             ev->data.mouse.button = 1;
  792             break;
  793         case NSRightMouseDown:
  794             ev->type = CACA_EVENT_MOUSE_PRESS;
  795             ev->data.mouse.button = 2;
  796             break;
  797         case NSRightMouseUp:
  798             ev->type = CACA_EVENT_MOUSE_RELEASE;
  799             ev->data.mouse.button = 2;
  800             break;
  801         case NSMouseMoved:
  802         {
  803             NSPoint mouseLoc = [NSEvent mouseLocation];
  804             int mouse_x = round(mouseLoc.x);
  805             int mouse_y = round(mouseLoc.y);
  806             if(dp->mouse.x == mouse_x && dp->mouse.y == mouse_y)
  807                 break;
  808 
  809             dp->mouse.x = mouse_x;
  810             dp->mouse.y = mouse_y;
  811 
  812             ev->type = CACA_EVENT_MOUSE_MOTION;
  813             ev->data.mouse.x = dp->mouse.x;
  814             ev->data.mouse.y = dp->mouse.y;
  815             break;
  816         }
  817         default:
  818             ;
  819     }
  820 
  821     return YES;
  822 }
  823 
  824 //============================================================================
  825 // caca driver methods
  826 //============================================================================
  827 
  828 static int cocoa_init_graphics(caca_display_t *dp)
  829 {
  830     int width = caca_get_canvas_width(dp->cv);
  831     int height = caca_get_canvas_height(dp->cv);
  832 
  833     debug_log(@"%s dp->cv: %ux%u", __PRETTY_FUNCTION__, width, height);
  834 
  835     NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
  836 
  837     dp->drv.p = malloc(sizeof(struct driver_private));
  838     if(dp->drv.p == NULL)
  839         return -1;
  840 
  841     dp->resize.allow = 1;
  842     caca_set_canvas_size(dp->cv, width ? width : 80, height ? height : 32);
  843     dp->resize.allow = 0;
  844 
  845     // first create a full cocoa app if the host has no bundle
  846     if(![[NSBundle mainBundle] bundleIdentifier])
  847         register_cocoa_app(dp);
  848     create_first_window(dp);
  849 
  850 #ifdef USE_GLOBAL_AUTORELEASE_POOL
  851     dp->drv.p->pool = pool;
  852 #else
  853     [pool release];
  854 #endif
  855 
  856     return 0;
  857 }
  858 
  859 static int cocoa_end_graphics(caca_display_t *dp)
  860 {
  861     debug_log(@"%s dp->cv: %ux%u", __PRETTY_FUNCTION__,
  862               caca_get_canvas_width(dp->cv), caca_get_canvas_height(dp->cv));
  863 
  864     NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
  865     [dp->drv.p->window close];
  866     CacaWindowDelegate* delegate = [dp->drv.p->window delegate];
  867     [dp->drv.p->window setDelegate:nil];
  868     [delegate release];
  869     // don't release the window yourself
  870     //[dp->drv.p->window release];
  871 #ifdef USE_GLOBAL_AUTORELEASE_POOL
  872     [dp->drv.p->pool release];
  873 #endif
  874     free(dp->drv.p);
  875     debug_log(@"%s end", __PRETTY_FUNCTION__);
  876     [pool release];
  877 
  878     return 0;
  879 }
  880 
  881 static void cocoa_display(caca_display_t *dp)
  882 {
  883     NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
  884     [dp->drv.p->view updateBuffersFromCaca:dp];
  885     [pool release];
  886 }
  887 
  888 static int cocoa_get_event(caca_display_t *dp, caca_privevent_t *ev)
  889 {
  890     if(s_quit)
  891     {
  892         if(s_quitting)
  893         {
  894             // host app isn't handling the quit event properly, aborting
  895             debug_log(@"duplicate quit event, aborting.");
  896             abort();
  897         }
  898         debug_log(@"posting quit event.");
  899         ev->type = CACA_EVENT_QUIT;
  900         s_quitting = YES;
  901         return 1;
  902     }
  903 
  904     BOOL eventHandled = NO, forceRedispatch = NO;
  905     ev->type = CACA_EVENT_NONE;
  906     NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
  907 
  908     if([NSApp isRunning])
  909     {
  910         NSEvent *event = [NSApp nextEventMatchingMask:NSAnyEventMask
  911                                 untilDate:[NSDate distantPast]
  912                                 inMode:NSDefaultRunLoopMode
  913                                 dequeue:YES];
  914         if(event)
  915         {
  916             switch([event type])
  917             {
  918                 case NSKeyDown:
  919                 case NSKeyUp:
  920                     eventHandled = handle_key_event(ev, event);
  921                     break;
  922 
  923                 case NSFlagsChanged:
  924                     break;
  925 
  926                 case NSLeftMouseDown:
  927                 case NSLeftMouseUp:
  928                 case NSRightMouseDown:
  929                 case NSRightMouseUp:
  930                 case NSMouseMoved:
  931                     if([NSApp isActive])
  932                     {
  933                         eventHandled = handle_mouse_event(dp, ev, event);
  934                         forceRedispatch = YES;
  935                     }
  936                     else
  937                     {
  938                         [NSApp sendEvent:event];
  939                         eventHandled = YES;
  940                     }
  941                     break;
  942 
  943                 default:
  944                     ;
  945             }
  946 
  947             if(!eventHandled || forceRedispatch)
  948                 [NSApp sendEvent:event];
  949         }
  950     }
  951     [pool release];
  952 
  953     if(eventHandled)
  954         return 1;
  955 
  956     return 0;
  957 }
  958 
  959 static void cocoa_handle_resize(caca_display_t *dp)
  960 {
  961     debug_log(@"%s", __PRETTY_FUNCTION__);
  962     dp->resize.w = caca_get_canvas_width(dp->cv);
  963     dp->resize.h = caca_get_canvas_height(dp->cv);
  964 }
  965 
  966 static int cocoa_set_display_title(caca_display_t *dp, char const *title)
  967 {
  968     NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
  969     [dp->drv.p->window setTitle:[NSString stringWithUTF8String:title]];
  970     [pool release];
  971     return 0;
  972 }
  973 
  974 static int cocoa_get_display_width(caca_display_t const *dp)
  975 {
  976     return [dp->drv.p->window frame].size.width;
  977 }
  978 
  979 static int cocoa_get_display_height(caca_display_t const *dp)
  980 {
  981     return [dp->drv.p->window frame].size.height;
  982 }
  983 
  984 static void cocoa_set_mouse(caca_display_t *dp, int flag)
  985 {
  986     NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
  987     if(flag)
  988         [[NSCursor arrowCursor] set];
  989     else {
  990         [[NSCursor disappearingItemCursor] set];
  991     }
  992     [pool release];
  993 }
  994 
  995 /*
  996  * Driver initialisation
  997  */
  998 
  999 int cocoa_install(caca_display_t *dp)
 1000 {
 1001     dp->drv.id = CACA_DRIVER_COCOA;
 1002     dp->drv.driver = "cocoa";
 1003 
 1004     dp->drv.init_graphics = cocoa_init_graphics;
 1005     dp->drv.end_graphics = cocoa_end_graphics;
 1006     dp->drv.set_display_title = cocoa_set_display_title;
 1007     dp->drv.get_display_width = cocoa_get_display_width;
 1008     dp->drv.get_display_height = cocoa_get_display_height;
 1009     dp->drv.display = cocoa_display;
 1010     dp->drv.handle_resize = cocoa_handle_resize;
 1011     dp->drv.get_event = cocoa_get_event;
 1012     dp->drv.set_mouse = cocoa_set_mouse;
 1013 
 1014     return 0;
 1015 }
 1016 
 1017 #endif /* USE_COCOA */