2 Copyright (C) 2011 Paul Davis
3 Copyright (C) 2012 David Robillard <http://drobilla.net>
4 Copyright (C) 2017 Robin Gareus <robin@gareus.org>
6 This program is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2 of the License, or
9 (at your option) any later version.
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with this program; if not, write to the Free Software
18 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
22 /* include order matter due to apple defines */
23 #include <gtkmm/window.h>
25 #include "gtkmm2ext/cairo_canvas.h"
26 #include "gtkmm2ext/nsglview.h"
27 #include "gtkmm2ext/rgb_macros.h"
29 #include <gdk/gdkquartz.h>
31 #include <OpenGL/gl.h>
32 #import <Cocoa/Cocoa.h>
34 /* the gtk-quartz library which ardour links against
35 * is patched to pass events directly through to
36 * NSView child-views (AU Plugin GUIs).
38 * In this particular case however we do want the
39 * events to reach the GTK widget instead of the
42 * If a NSVIew tag equals to the given magic-number,
43 * Gdk events propagate.
45 #ifndef ARDOUR_CANVAS_NSVIEW_TAG
46 #define ARDOUR_CANVAS_NSVIEW_TAG 0x0
49 __attribute__ ((visibility ("hidden")))
50 @interface ArdourCanvasOpenGLView : NSOpenGLView
53 unsigned int _texture_id;
56 Cairo::RefPtr<Cairo::ImageSurface> surf;
57 Gtkmm2ext::CairoCanvas *cairo_canvas;
61 @property (readwrite) NSInteger tag;
63 - (id) initWithFrame:(NSRect)frame;
65 - (void) setCairoCanvas:(Gtkmm2ext::CairoCanvas*)c;
67 - (void) setNeedsDisplayInRect:(NSRect)rect;
68 - (void) drawRect:(NSRect)rect;
69 - (BOOL) canBecomeKeyWindow:(id)sender;
70 - (BOOL) acceptsFirstResponder:(id)sender;
74 @implementation ArdourCanvasOpenGLView
76 @synthesize tag = _tag;
78 - (id) initWithFrame:(NSRect)frame
80 NSOpenGLPixelFormatAttribute pixelAttribs[16] = {
81 NSOpenGLPFADoubleBuffer,
82 NSOpenGLPFAAccelerated,
83 NSOpenGLPFAColorSize, 32,
84 NSOpenGLPFADepthSize, 32,
85 NSOpenGLPFAMultisample,
86 NSOpenGLPFASampleBuffers, 1,
87 NSOpenGLPFASamples, 4,
91 NSOpenGLPixelFormat* pixelFormat =
92 [[NSOpenGLPixelFormat alloc] initWithAttributes:pixelAttribs];
95 self = [super initWithFrame:frame pixelFormat:pixelFormat];
96 [pixelFormat release];
98 self = [super initWithFrame:frame];
106 self.tag = ARDOUR_CANVAS_NSVIEW_TAG;
107 [[self openGLContext] makeCurrentContext];
108 glClearColor (0.0f, 0.0f, 0.0f, 0.0f);
109 glDisable (GL_DEPTH_TEST);
111 glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
112 glEnable (GL_TEXTURE_RECTANGLE_ARB);
113 [NSOpenGLContext clearCurrentContext];
122 [[self openGLContext] makeCurrentContext];
123 glDeleteTextures (1, &_texture_id);
124 [NSOpenGLContext clearCurrentContext];
129 - (void) setCairoCanvas:(Gtkmm2ext::CairoCanvas*)c
134 - (BOOL) canBecomeKeyWindow:(id)sender{
138 - (BOOL) acceptsFirstResponder:(id)sender{
144 [[self openGLContext] update];
146 NSRect bounds = [self bounds];
147 int width = bounds.size.width;
148 int height = bounds.size.height;
150 if (_width == width && _height == height) {
154 [[self openGLContext] makeCurrentContext];
156 glViewport (0, 0, width, height);
157 glMatrixMode (GL_PROJECTION);
159 glOrtho (-1.0f, 1.0f, -1.0f, 1.0f, -1.0f, 1.0f);
161 glClear (GL_COLOR_BUFFER_BIT);
163 glDeleteTextures (1, &_texture_id);
164 glGenTextures (1, &_texture_id);
165 glBindTexture (GL_TEXTURE_RECTANGLE_ARB, _texture_id);
166 glTexImage2D (GL_TEXTURE_RECTANGLE_ARB, 0, GL_RGBA8,
168 GL_BGRA, GL_UNSIGNED_BYTE, NULL);
169 glTexEnvi (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_DECAL);
171 glMatrixMode(GL_MODELVIEW);
174 [NSOpenGLContext clearCurrentContext];
180 - (void) setNeedsDisplayInRect:(NSRect)rect
182 [super setNeedsDisplayInRect:rect];
183 #ifdef DEBUG_NSVIEW_EXPOSURE
184 printf ("needsDisplay: %5.1f %5.1f %5.1f %5.1f\n",
185 rect.origin.x, rect.origin.y, rect.size.width, rect.size.height);
189 - (void) drawRect:(NSRect)rect
191 [[self openGLContext] makeCurrentContext];
193 glMatrixMode(GL_MODELVIEW);
195 glClear(GL_COLOR_BUFFER_BIT);
197 /* call back into CairoCanvas */
198 cairo_rectangle_t cairo_rect;
200 cairo_rect.x = rect.origin.x;
201 cairo_rect.y = rect.origin.y;
202 cairo_rect.width = rect.size.width;
203 cairo_rect.height = rect.size.height;
205 if (!surf || surf->get_width () != _width || surf->get_height() != _height) {
206 surf = Cairo::ImageSurface::create (Cairo::FORMAT_ARGB32, _width, _height);
210 cairo_rect.width = _width;
211 cairo_rect.height = _height;
214 Cairo::RefPtr<Cairo::Context> ctx = Cairo::Context::create (surf);
216 // TODO: check retina screen, scaling factor.
217 // cairo_surface_get_device_scale () or explicit scale
219 ctx->rectangle (cairo_rect.x, cairo_rect.y, cairo_rect.width, cairo_rect.height);
220 ctx->clip_preserve ();
222 /* draw background color */
223 uint32_t col = cairo_canvas->background_color ();
225 UINT_TO_RGBA (col, &r, &g, &b, &a);
226 ctx->set_source_rgba (r/255.0, g/255.0, b/255.0, a/255.0);
230 #ifdef DEBUG_NSVIEW_EXPOSURE
231 printf ("drawRect: %.1f %.1f %.1f %1.f\n",
232 cairo_rect.x, cairo_rect.y, cairo_rect.width, cairo_rect.height);
235 cairo_canvas->render (ctx, &cairo_rect);
238 uint8_t* imgdata = surf->get_data ();
240 /* NOTE for big-endian (PPC), we'd need to flip byte-order
241 * RGBA <> RGBA for the texture.
242 * GtkCanvas does not use this nsview for PPC builds, yet
245 /* continue OpenGL */
248 glEnable(GL_TEXTURE_2D);
249 glBindTexture(GL_TEXTURE_RECTANGLE_ARB, _texture_id);
250 glTexImage2D(GL_TEXTURE_RECTANGLE_ARB, 0, GL_RGBA8,
251 _width, _height, /*border*/ 0,
252 GL_BGRA, GL_UNSIGNED_BYTE, imgdata);
255 glTexCoord2f( 0.0f, (GLfloat) _height);
256 glVertex2f(-1.0f, -1.0f);
258 glTexCoord2f((GLfloat) _width, (GLfloat) _height);
259 glVertex2f( 1.0f, -1.0f);
261 glTexCoord2f((GLfloat) _width, 0.0f);
262 glVertex2f( 1.0f, 1.0f);
264 glTexCoord2f( 0.0f, 0.0f);
265 glVertex2f(-1.0f, 1.0f);
268 glDisable(GL_TEXTURE_2D);
275 [NSOpenGLContext clearCurrentContext];
276 [super setNeedsDisplay:NO];
282 Gtkmm2ext::nsglview_create (Gtkmm2ext::CairoCanvas* canvas)
284 /* the API is currently only used on intel mac
285 * for big-endian RGBA <> RGBA byte order of the texture
286 * will have to be swapped.
291 ArdourCanvasOpenGLView* gl_view = [ArdourCanvasOpenGLView new];
295 [gl_view setCairoCanvas:canvas];
296 [gl_view setHidden:YES];
301 Gtkmm2ext::nsglview_overlay (void* glv, GdkWindow* window)
303 ArdourCanvasOpenGLView* gl_view = (ArdourCanvasOpenGLView*) glv;
304 NSView* view = gdk_quartz_window_get_nsview (window);
305 [view addSubview:gl_view];
309 Gtkmm2ext::nsglview_resize (void* glv, int x, int y, int w, int h)
311 ArdourCanvasOpenGLView* gl_view = (ArdourCanvasOpenGLView*) glv;
312 [gl_view setFrame:NSMakeRect(x, y, w, h)];
316 Gtkmm2ext::nsglview_queue_draw (void* glv, int x, int y, int w, int h)
318 ArdourCanvasOpenGLView* gl_view = (ArdourCanvasOpenGLView*) glv;
319 [gl_view setNeedsDisplayInRect:NSMakeRect(x, y, w, h)];
320 #ifdef DEBUG_NSVIEW_EXPOSURE
321 printf ("Queue Draw %5d %5d %5d %5d\n", x, y, w, h);
326 Gtkmm2ext::nsglview_set_visible (void* glv, bool vis)
328 ArdourCanvasOpenGLView* gl_view = (ArdourCanvasOpenGLView*) glv;
330 [gl_view setHidden:NO];
332 [gl_view setHidden:YES];