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