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.
 
 
 
 
 
 

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