"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.

    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 */