Vous ne pouvez pas sélectionner plus de 25 sujets Les noms de sujets doivent commencer par une lettre ou un nombre, peuvent contenir des tirets ('-') et peuvent comporter jusqu'à 35 caractères.

driver_cocoa.m 30 KiB

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