source: libcaca/trunk/caca/driver/cocoa.m @ 3046

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