Du kan inte välja fler än 25 ämnen Ämnen måste starta med en bokstav eller siffra, kan innehålla bindestreck ('-') och vara max 35 tecken långa.
 
 
 
 
 
 

1018 rader
30 KiB

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