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

Last change on this file since 4333 was 4333, checked in by Sam Hocevar, 10 years ago

Large source code cleanup, getting rid of spaces, tabs, and svn keywords.

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