source: libcaca/trunk/caca/driver_cocoa.m @ 2821

Last change on this file since 2821 was 2821, checked in by Sam Hocevar, 11 years ago

Starting refactoring to get rid of libcucul. The initial reason for the
split is rendered moot by the plugin system: when enabled, binaries do
not link directly with libX11 or libGL. I hope this is a step towards
more consisteny and clarity.

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