Audio plugin host https://kx.studio/carla
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.

580 lines
13KB

  1. /*
  2. Copyright 2012 David Robillard <http://drobilla.net>
  3. Permission to use, copy, modify, and/or distribute this software for any
  4. purpose with or without fee is hereby granted, provided that the above
  5. copyright notice and this permission notice appear in all copies.
  6. THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
  7. WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
  8. MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
  9. ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
  10. WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
  11. ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
  12. OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  13. */
  14. /**
  15. @file pugl_osx.m OSX/Cocoa Pugl Implementation.
  16. */
  17. #include <stdlib.h>
  18. #import <Cocoa/Cocoa.h>
  19. #include "pugl_internal.h"
  20. #define PuglWindow PuglWindow ## DGL_NAMESPACE
  21. #define PuglOpenGLView PuglOpenGLView ## DGL_NAMESPACE
  22. @interface PuglWindow : NSWindow
  23. {
  24. @public
  25. PuglView* puglview;
  26. }
  27. - (id) initWithContentRect:(NSRect)contentRect
  28. styleMask:(unsigned int)aStyle
  29. backing:(NSBackingStoreType)bufferingType
  30. defer:(BOOL)flag;
  31. - (void) setPuglview:(PuglView*)view;
  32. - (BOOL) canBecomeKeyWindow;
  33. - (BOOL) windowShouldClose:(id)sender;
  34. @end
  35. @implementation PuglWindow
  36. - (id)initWithContentRect:(NSRect)contentRect
  37. styleMask:(unsigned int)aStyle
  38. backing:(NSBackingStoreType)bufferingType
  39. defer:(BOOL)flag
  40. {
  41. NSWindow* result = [super initWithContentRect:contentRect
  42. styleMask:(NSClosableWindowMask |
  43. NSTitledWindowMask |
  44. NSResizableWindowMask)
  45. backing:NSBackingStoreBuffered defer:NO];
  46. [result setAcceptsMouseMovedEvents:YES];
  47. [result setLevel: CGShieldingWindowLevel() + 1];
  48. return (PuglWindow*)result;
  49. // unused
  50. (void)aStyle; (void)bufferingType; (void)flag;
  51. }
  52. - (void)setPuglview:(PuglView*)view
  53. {
  54. puglview = view;
  55. [self setContentSize:NSMakeSize(view->width, view->height)];
  56. }
  57. - (BOOL)canBecomeKeyWindow
  58. {
  59. return YES;
  60. }
  61. - (BOOL)windowShouldClose:(id)sender
  62. {
  63. if (puglview->closeFunc)
  64. puglview->closeFunc(puglview);
  65. return YES;
  66. // unused
  67. (void)sender;
  68. }
  69. @end
  70. static void
  71. puglDisplay(PuglView* view)
  72. {
  73. view->redisplay = false;
  74. if (view->displayFunc) {
  75. view->displayFunc(view);
  76. }
  77. }
  78. @interface PuglOpenGLView : NSOpenGLView
  79. {
  80. @public
  81. PuglView* puglview;
  82. NSTrackingArea* trackingArea;
  83. bool doubleBuffered;
  84. }
  85. - (BOOL) acceptsFirstMouse:(NSEvent*)e;
  86. - (BOOL) acceptsFirstResponder;
  87. - (BOOL) isFlipped;
  88. - (BOOL) isOpaque;
  89. - (BOOL) preservesContentInLiveResize;
  90. - (id) initWithFrame:(NSRect)frame;
  91. - (void) reshape;
  92. - (void) drawRect:(NSRect)r;
  93. - (void) cursorUpdate:(NSEvent*)e;
  94. - (void) updateTrackingAreas;
  95. - (void) viewWillMoveToWindow:(NSWindow*)newWindow;
  96. - (void) mouseMoved:(NSEvent*)event;
  97. - (void) mouseDragged:(NSEvent*)event;
  98. - (void) rightMouseDragged:(NSEvent*)event;
  99. - (void) otherMouseDragged:(NSEvent*)event;
  100. - (void) mouseDown:(NSEvent*)event;
  101. - (void) rightMouseDown:(NSEvent*)event;
  102. - (void) otherMouseDown:(NSEvent*)event;
  103. - (void) mouseUp:(NSEvent*)event;
  104. - (void) rightMouseUp:(NSEvent*)event;
  105. - (void) otherMouseUp:(NSEvent*)event;
  106. - (void) scrollWheel:(NSEvent*)event;
  107. - (void) keyDown:(NSEvent*)event;
  108. - (void) keyUp:(NSEvent*)event;
  109. - (void) flagsChanged:(NSEvent*)event;
  110. @end
  111. @implementation PuglOpenGLView
  112. - (BOOL) acceptsFirstMouse:(NSEvent*)e
  113. {
  114. return YES;
  115. // unused
  116. (void)e;
  117. }
  118. - (BOOL) acceptsFirstResponder
  119. {
  120. return YES;
  121. }
  122. - (BOOL) isFlipped
  123. {
  124. return YES;
  125. }
  126. - (BOOL) isOpaque
  127. {
  128. return YES;
  129. }
  130. - (BOOL) preservesContentInLiveResize
  131. {
  132. return NO;
  133. }
  134. - (id) initWithFrame:(NSRect)frame
  135. {
  136. puglview = nil;
  137. trackingArea = nil;
  138. doubleBuffered = true;
  139. NSOpenGLPixelFormatAttribute pixelAttribs[] = {
  140. NSOpenGLPFAColorSize, 24,
  141. NSOpenGLPFAAlphaSize, 8,
  142. NSOpenGLPFADepthSize, 16,
  143. NSOpenGLPFADoubleBuffer,
  144. NSOpenGLPFAAccelerated,
  145. 0
  146. };
  147. NSOpenGLPixelFormat* pixelFormat = [[NSOpenGLPixelFormat alloc]
  148. initWithAttributes:pixelAttribs];
  149. if (pixelFormat) {
  150. self = [super initWithFrame:frame pixelFormat:pixelFormat];
  151. [pixelFormat release];
  152. printf("Is doubleBuffered? TRUE\n");
  153. } else {
  154. self = [super initWithFrame:frame];
  155. doubleBuffered = false;
  156. printf("Is doubleBuffered? FALSE\n");
  157. }
  158. if (self) {
  159. GLint swapInterval = 1;
  160. [[self openGLContext] setValues:&swapInterval forParameter:NSOpenGLCPSwapInterval];
  161. [self reshape];
  162. }
  163. return self;
  164. }
  165. - (void) reshape
  166. {
  167. if (!puglview) {
  168. /* NOTE: Apparently reshape gets called when the GC gets around to
  169. deleting the view (?), so we must have reset puglview to NULL when
  170. this comes around.
  171. */
  172. return;
  173. }
  174. [[self openGLContext] update];
  175. NSRect bounds = [self bounds];
  176. int width = bounds.size.width;
  177. int height = bounds.size.height;
  178. puglEnterContext(puglview);
  179. if (puglview->reshapeFunc) {
  180. puglview->reshapeFunc(puglview, width, height);
  181. } else {
  182. puglDefaultReshape(puglview, width, height);
  183. }
  184. puglLeaveContext(puglview, false);
  185. puglview->width = width;
  186. puglview->height = height;
  187. }
  188. - (void) drawRect:(NSRect)r
  189. {
  190. puglEnterContext(puglview);
  191. puglDisplay(puglview);
  192. puglLeaveContext(puglview, true);
  193. // unused
  194. return; (void)r;
  195. }
  196. - (void) cursorUpdate:(NSEvent*)e
  197. {
  198. [[NSCursor arrowCursor] set];
  199. // unused
  200. return; (void)e;
  201. }
  202. - (void) updateTrackingAreas
  203. {
  204. static const int opts = NSTrackingMouseEnteredAndExited
  205. | NSTrackingMouseMoved
  206. | NSTrackingEnabledDuringMouseDrag
  207. | NSTrackingInVisibleRect
  208. | NSTrackingActiveAlways
  209. | NSTrackingCursorUpdate;
  210. if (trackingArea != nil) {
  211. [self removeTrackingArea:trackingArea];
  212. [trackingArea release];
  213. }
  214. trackingArea = [[NSTrackingArea alloc] initWithRect:[self bounds]
  215. options:opts
  216. owner:self
  217. userInfo:nil];
  218. [self addTrackingArea:trackingArea];
  219. [super updateTrackingAreas];
  220. }
  221. - (void) viewWillMoveToWindow:(NSWindow*)newWindow
  222. {
  223. if (newWindow != nil) {
  224. [newWindow setAcceptsMouseMovedEvents:YES];
  225. [newWindow makeFirstResponder:self];
  226. }
  227. [super viewWillMoveToWindow:newWindow];
  228. }
  229. static unsigned
  230. getModifiers(PuglView* view, NSEvent* ev)
  231. {
  232. const unsigned modifierFlags = [ev modifierFlags];
  233. view->event_timestamp_ms = fmod([ev timestamp] * 1000.0, UINT32_MAX);
  234. unsigned mods = 0;
  235. mods |= (modifierFlags & NSShiftKeyMask) ? PUGL_MOD_SHIFT : 0;
  236. mods |= (modifierFlags & NSControlKeyMask) ? PUGL_MOD_CTRL : 0;
  237. mods |= (modifierFlags & NSAlternateKeyMask) ? PUGL_MOD_ALT : 0;
  238. mods |= (modifierFlags & NSCommandKeyMask) ? PUGL_MOD_SUPER : 0;
  239. return mods;
  240. }
  241. static int
  242. getFixedAppKitButton(NSInteger button)
  243. {
  244. switch (button) {
  245. case 0: return 1;
  246. case 1: return 3;
  247. case 2: return 2;
  248. default: return button;
  249. }
  250. }
  251. - (void) mouseMoved:(NSEvent*)event
  252. {
  253. if (puglview->motionFunc) {
  254. NSPoint loc = [self convertPoint:[event locationInWindow] fromView:nil];
  255. puglview->mods = getModifiers(puglview, event);
  256. puglview->motionFunc(puglview, loc.x, loc.y);
  257. }
  258. }
  259. - (void) mouseDragged:(NSEvent*)event
  260. {
  261. [self mouseMoved:event];
  262. }
  263. - (void) rightMouseDragged:(NSEvent*)event
  264. {
  265. [self mouseDragged:event];
  266. }
  267. - (void) otherMouseDragged:(NSEvent*)event
  268. {
  269. [self mouseDragged:event];
  270. }
  271. - (void) mouseDown:(NSEvent*)event
  272. {
  273. if (puglview->mouseFunc) {
  274. NSPoint loc = [self convertPoint:[event locationInWindow] fromView:nil];
  275. puglview->mods = getModifiers(puglview, event);
  276. puglview->mouseFunc(puglview, getFixedAppKitButton([event buttonNumber]), true, loc.x, loc.y);
  277. }
  278. }
  279. - (void) rightMouseDown:(NSEvent*)event
  280. {
  281. [self mouseDown:event];
  282. }
  283. - (void) otherMouseDown:(NSEvent*)event
  284. {
  285. [self mouseDown:event];
  286. }
  287. - (void) mouseUp:(NSEvent*)event
  288. {
  289. if (puglview->mouseFunc) {
  290. NSPoint loc = [self convertPoint:[event locationInWindow] fromView:nil];
  291. puglview->mods = getModifiers(puglview, event);
  292. puglview->mouseFunc(puglview, getFixedAppKitButton([event buttonNumber]), false, loc.x, loc.y);
  293. }
  294. }
  295. - (void) rightMouseUp:(NSEvent*)event
  296. {
  297. [self mouseUp:event];
  298. }
  299. - (void) otherMouseUp:(NSEvent*)event
  300. {
  301. [self mouseUp:event];
  302. }
  303. - (void) scrollWheel:(NSEvent*)event
  304. {
  305. if (puglview->scrollFunc) {
  306. NSPoint loc = [self convertPoint:[event locationInWindow] fromView:nil];
  307. puglview->mods = getModifiers(puglview, event);
  308. puglview->scrollFunc(puglview,
  309. loc.x, loc.y,
  310. [event deltaX], [event deltaY]);
  311. }
  312. }
  313. - (void) keyDown:(NSEvent*)event
  314. {
  315. if (puglview->keyboardFunc && !(puglview->ignoreKeyRepeat && [event isARepeat])) {
  316. NSString* chars = [event characters];
  317. puglview->mods = getModifiers(puglview, event);
  318. puglview->keyboardFunc(puglview, true, [chars characterAtIndex:0]);
  319. }
  320. }
  321. - (void) keyUp:(NSEvent*)event
  322. {
  323. if (puglview->keyboardFunc) {
  324. NSString* chars = [event characters];
  325. puglview->mods = getModifiers(puglview, event);
  326. puglview->keyboardFunc(puglview, false, [chars characterAtIndex:0]);
  327. }
  328. }
  329. - (void) flagsChanged:(NSEvent*)event
  330. {
  331. if (puglview->specialFunc) {
  332. const unsigned mods = getModifiers(puglview, event);
  333. if ((mods & PUGL_MOD_SHIFT) != (puglview->mods & PUGL_MOD_SHIFT)) {
  334. puglview->specialFunc(puglview, mods & PUGL_MOD_SHIFT, PUGL_KEY_SHIFT);
  335. } else if ((mods & PUGL_MOD_CTRL) != (puglview->mods & PUGL_MOD_CTRL)) {
  336. puglview->specialFunc(puglview, mods & PUGL_MOD_CTRL, PUGL_KEY_CTRL);
  337. } else if ((mods & PUGL_MOD_ALT) != (puglview->mods & PUGL_MOD_ALT)) {
  338. puglview->specialFunc(puglview, mods & PUGL_MOD_ALT, PUGL_KEY_ALT);
  339. } else if ((mods & PUGL_MOD_SUPER) != (puglview->mods & PUGL_MOD_SUPER)) {
  340. puglview->specialFunc(puglview, mods & PUGL_MOD_SUPER, PUGL_KEY_SUPER);
  341. }
  342. puglview->mods = mods;
  343. }
  344. }
  345. @end
  346. struct PuglInternalsImpl {
  347. PuglOpenGLView* glview;
  348. id window;
  349. };
  350. PuglInternals*
  351. puglInitInternals()
  352. {
  353. return (PuglInternals*)calloc(1, sizeof(PuglInternals));
  354. }
  355. void
  356. puglEnterContext(PuglView* view)
  357. {
  358. #ifdef PUGL_HAVE_GL
  359. if (view->ctx_type == PUGL_GL) {
  360. [[view->impl->glview openGLContext] makeCurrentContext];
  361. }
  362. #endif
  363. }
  364. void
  365. puglLeaveContext(PuglView* view, bool flush)
  366. {
  367. #ifdef PUGL_HAVE_GL
  368. if (view->ctx_type == PUGL_GL && flush) {
  369. if (view->impl->glview->doubleBuffered) {
  370. [[view->impl->glview openGLContext] flushBuffer];
  371. } else {
  372. glFlush();
  373. }
  374. //[NSOpenGLContext clearCurrentContext];
  375. }
  376. #endif
  377. }
  378. int
  379. puglCreateWindow(PuglView* view, const char* title)
  380. {
  381. PuglInternals* impl = view->impl;
  382. [NSAutoreleasePool new];
  383. [NSApplication sharedApplication];
  384. impl->glview = [PuglOpenGLView new];
  385. if (!impl->glview) {
  386. return 1;
  387. }
  388. impl->glview->puglview = view;
  389. if (view->resizable) {
  390. [impl->glview setAutoresizingMask:NSViewWidthSizable|NSViewHeightSizable];
  391. }
  392. if (view->parent) {
  393. [impl->glview retain];
  394. NSView* pview = (NSView*)view->parent;
  395. [pview addSubview:impl->glview];
  396. return 0;
  397. }
  398. id window = [[PuglWindow new]retain];
  399. if (title) {
  400. NSString* titleString = [[NSString alloc]
  401. initWithBytes:title
  402. length:strlen(title)
  403. encoding:NSUTF8StringEncoding];
  404. [window setTitle:titleString];
  405. }
  406. [window setPuglview:view];
  407. [window setContentView:impl->glview];
  408. [window makeFirstResponder:impl->glview];
  409. [window makeKeyAndOrderFront:window];
  410. // wait for first puglShowWindow
  411. [window setIsVisible:NO];
  412. [NSApp activateIgnoringOtherApps:YES];
  413. [window center];
  414. impl->window = window;
  415. return 0;
  416. }
  417. void
  418. puglShowWindow(PuglView* view)
  419. {
  420. PuglInternals* impl = view->impl;
  421. if (impl->window) {
  422. [impl->window setIsVisible:YES];
  423. } else {
  424. [view->impl->glview setHidden:NO];
  425. }
  426. }
  427. void
  428. puglHideWindow(PuglView* view)
  429. {
  430. PuglInternals* impl = view->impl;
  431. if (impl->window) {
  432. [impl->window setIsVisible:NO];
  433. } else {
  434. [impl->glview setHidden:YES];
  435. }
  436. }
  437. void
  438. puglDestroy(PuglView* view)
  439. {
  440. view->impl->glview->puglview = NULL;
  441. if (view->impl->window) {
  442. [view->impl->window close];
  443. [view->impl->glview release];
  444. [view->impl->window release];
  445. } else {
  446. [view->impl->glview release];
  447. }
  448. free(view->impl);
  449. free(view);
  450. }
  451. PuglStatus
  452. puglProcessEvents(PuglView* view)
  453. {
  454. return PUGL_SUCCESS;
  455. // unused
  456. (void)view;
  457. }
  458. void
  459. puglPostRedisplay(PuglView* view)
  460. {
  461. view->redisplay = true;
  462. [view->impl->glview setNeedsDisplay:YES];
  463. }
  464. PuglNativeWindow
  465. puglGetNativeWindow(PuglView* view)
  466. {
  467. return (PuglNativeWindow)view->impl->glview;
  468. }
  469. void*
  470. puglGetContext(PuglView* view)
  471. {
  472. return NULL;
  473. // unused
  474. (void)view;
  475. }