close Warning: Failed to sync with repository "(default)": database is locked; repository information may be out of date. Look in the Trac log for more information including mitigation strategies.

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

Last change on this file since 2962 was 2962, checked in by Sam Hocevar, 12 years ago

libcaca: move codecs and import/export functions into subdirectories.

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