Você não pode selecionar mais de 25 tópicos Os tópicos devem começar com uma letra ou um número, podem incluir traços ('-') e podem ter até 35 caracteres.
 
 
 
 
 
 

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