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

Last change on this file since 2305 was 2305, checked in by Sam Hocevar, 12 years ago
  • Remove all unsigned ints from exported functions. Signed arithmetic is far better for error checking.
  • 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 2305 2008-04-19 19:25:52Z 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 "cucul.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 != cucul_get_canvas_width(dp->cv)
199        || _h != cucul_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 = cucul_get_canvas_width(dp->cv);
205        _h = cucul_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(cucul_get_canvas_width(dp->cv) * _font_rect.size.width,
220                                                  cucul_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, cucul_get_canvas_attrs(dp->cv),
232               _w * _h * sizeof(uint32_t));
233        memcpy(_chars, cucul_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 = cucul_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                cucul_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 == CUCUL_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 = cucul_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                    cucul_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 & CUCUL_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, cucul_get_canvas_width(dp->cv) * fontRect.size.width,
576                                           cucul_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 = cucul_get_canvas_width(dp->cv);
830    int height = cucul_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    cucul_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              cucul_get_canvas_width(dp->cv), cucul_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 = cucul_get_canvas_width(dp->cv);
962    dp->resize.h = cucul_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.