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.
 
 
 
 
 
 

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