You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

1017 lines
30 KiB

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