"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.
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 */