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

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