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

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