2 * Copyright (C) 2017 Robin Gareus <robin@gareus.org>
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License along
15 * with this program; if not, write to the Free Software Foundation, Inc.,
16 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19 /* include order matter due to apple defines */
20 #include <gtkmm/window.h>
22 #include "gtkmm2ext/cairo_canvas.h"
23 #include "gtkmm2ext/nsglview.h"
24 #include "gtkmm2ext/rgb_macros.h"
26 #include <gdk/gdkquartz.h>
28 #include <OpenGL/gl.h>
29 #import <Cocoa/Cocoa.h>
31 /* the gtk-quartz library which ardour links against
32 * is patched to pass events directly through to
33 * NSView child-views (AU Plugin GUIs).
35 * In this particular case however we do want the
36 * events to reach the GTK widget instead of the
39 * If a NSVIew tag equals to the given magic-number,
40 * Gdk events propagate.
42 #ifndef ARDOUR_CANVAS_NSVIEW_TAG
43 #define ARDOUR_CANVAS_NSVIEW_TAG 0x0
46 __attribute__ ((visibility ("hidden")))
47 @interface ArdourCanvasOpenGLView : NSOpenGLView
50 unsigned int _texture_id;
53 Cairo::RefPtr<Cairo::ImageSurface> surf;
54 Gtkmm2ext::CairoCanvas *cairo_canvas;
58 @property (readwrite) NSInteger tag;
60 - (id) initWithFrame:(NSRect)frame;
62 - (void) setCairoCanvas:(Gtkmm2ext::CairoCanvas*)c;
64 - (void) setNeedsDisplayInRect:(NSRect)rect;
65 - (void) drawRect:(NSRect)rect;
66 - (BOOL) canBecomeKeyWindow:(id)sender;
67 - (BOOL) acceptsFirstResponder:(id)sender;
71 @implementation ArdourCanvasOpenGLView
73 @synthesize tag = _tag;
75 - (id) initWithFrame:(NSRect)frame
77 NSOpenGLPixelFormatAttribute pixelAttribs[16] = {
78 NSOpenGLPFADoubleBuffer,
79 NSOpenGLPFAAccelerated,
80 NSOpenGLPFAColorSize, 32,
81 NSOpenGLPFADepthSize, 32,
82 NSOpenGLPFAMultisample,
83 NSOpenGLPFASampleBuffers, 1,
84 NSOpenGLPFASamples, 4,
88 NSOpenGLPixelFormat* pixelFormat =
89 [[NSOpenGLPixelFormat alloc] initWithAttributes:pixelAttribs];
92 self = [super initWithFrame:frame pixelFormat:pixelFormat];
93 [pixelFormat release];
95 self = [super initWithFrame:frame];
103 self.tag = ARDOUR_CANVAS_NSVIEW_TAG;
104 [[self openGLContext] makeCurrentContext];
105 glClearColor (0.0f, 0.0f, 0.0f, 0.0f);
106 glDisable (GL_DEPTH_TEST);
108 glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
109 glEnable (GL_TEXTURE_RECTANGLE_ARB);
110 [NSOpenGLContext clearCurrentContext];
119 [[self openGLContext] makeCurrentContext];
120 glDeleteTextures (1, &_texture_id);
121 [NSOpenGLContext clearCurrentContext];
126 - (void) setCairoCanvas:(Gtkmm2ext::CairoCanvas*)c
131 - (BOOL) canBecomeKeyWindow:(id)sender{
135 - (BOOL) acceptsFirstResponder:(id)sender{
141 [[self openGLContext] update];
143 NSRect bounds = [self bounds];
144 int width = bounds.size.width;
145 int height = bounds.size.height;
147 if (_width == width && _height == height) {
151 [[self openGLContext] makeCurrentContext];
153 glViewport (0, 0, width, height);
154 glMatrixMode (GL_PROJECTION);
156 glOrtho (-1.0f, 1.0f, -1.0f, 1.0f, -1.0f, 1.0f);
158 glClear (GL_COLOR_BUFFER_BIT);
160 glDeleteTextures (1, &_texture_id);
161 glGenTextures (1, &_texture_id);
162 glBindTexture (GL_TEXTURE_RECTANGLE_ARB, _texture_id);
163 glTexImage2D (GL_TEXTURE_RECTANGLE_ARB, 0, GL_RGBA8,
165 GL_BGRA, GL_UNSIGNED_BYTE, NULL);
166 glTexEnvi (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_DECAL);
168 glMatrixMode(GL_MODELVIEW);
171 [NSOpenGLContext clearCurrentContext];
177 - (void) setNeedsDisplayInRect:(NSRect)rect
179 [super setNeedsDisplayInRect:rect];
180 #ifdef DEBUG_NSVIEW_EXPOSURE
181 printf ("needsDisplay: %5.1f %5.1f %5.1f %5.1f\n",
182 rect.origin.x, rect.origin.y, rect.size.width, rect.size.height);
186 - (void) drawRect:(NSRect)rect
188 [[self openGLContext] makeCurrentContext];
190 glMatrixMode(GL_MODELVIEW);
192 glClear(GL_COLOR_BUFFER_BIT);
194 /* call back into CairoCanvas */
195 cairo_rectangle_t cairo_rect;
197 cairo_rect.x = rect.origin.x;
198 cairo_rect.y = rect.origin.y;
199 cairo_rect.width = rect.size.width;
200 cairo_rect.height = rect.size.height;
202 if (!surf || surf->get_width () != _width || surf->get_height() != _height) {
203 surf = Cairo::ImageSurface::create (Cairo::FORMAT_ARGB32, _width, _height);
207 cairo_rect.width = _width;
208 cairo_rect.height = _height;
211 Cairo::RefPtr<Cairo::Context> ctx = Cairo::Context::create (surf);
213 // TODO: check retina screen, scaling factor.
214 // cairo_surface_get_device_scale () or explicit scale
216 ctx->rectangle (cairo_rect.x, cairo_rect.y, cairo_rect.width, cairo_rect.height);
217 ctx->clip_preserve ();
219 /* draw background color */
220 uint32_t col = cairo_canvas->background_color ();
222 UINT_TO_RGBA (col, &r, &g, &b, &a);
223 ctx->set_source_rgba (r/255.0, g/255.0, b/255.0, a/255.0);
227 #ifdef DEBUG_NSVIEW_EXPOSURE
228 printf ("drawRect: %.1f %.1f %.1f %1.f\n",
229 cairo_rect.x, cairo_rect.y, cairo_rect.width, cairo_rect.height);
232 cairo_canvas->render (ctx, &cairo_rect);
235 uint8_t* imgdata = surf->get_data ();
237 /* NOTE for big-endian (PPC), we'd need to flip byte-order
238 * RGBA <> RGBA for the texture.
239 * GtkCanvas does not use this nsview for PPC builds, yet
242 /* continue OpenGL */
245 glEnable(GL_TEXTURE_2D);
246 glBindTexture(GL_TEXTURE_RECTANGLE_ARB, _texture_id);
247 glTexImage2D(GL_TEXTURE_RECTANGLE_ARB, 0, GL_RGBA8,
248 _width, _height, /*border*/ 0,
249 GL_BGRA, GL_UNSIGNED_BYTE, imgdata);
252 glTexCoord2f( 0.0f, (GLfloat) _height);
253 glVertex2f(-1.0f, -1.0f);
255 glTexCoord2f((GLfloat) _width, (GLfloat) _height);
256 glVertex2f( 1.0f, -1.0f);
258 glTexCoord2f((GLfloat) _width, 0.0f);
259 glVertex2f( 1.0f, 1.0f);
261 glTexCoord2f( 0.0f, 0.0f);
262 glVertex2f(-1.0f, 1.0f);
265 glDisable(GL_TEXTURE_2D);
272 [NSOpenGLContext clearCurrentContext];
273 [super setNeedsDisplay:NO];
279 Gtkmm2ext::nsglview_create (Gtkmm2ext::CairoCanvas* canvas)
281 /* the API is currently only used on intel mac
282 * for big-endian RGBA <> RGBA byte order of the texture
283 * will have to be swapped.
288 ArdourCanvasOpenGLView* gl_view = [ArdourCanvasOpenGLView new];
292 [gl_view setCairoCanvas:canvas];
293 [gl_view setHidden:YES];
298 Gtkmm2ext::nsglview_overlay (void* glv, GdkWindow* window)
300 ArdourCanvasOpenGLView* gl_view = (ArdourCanvasOpenGLView*) glv;
301 NSView* view = gdk_quartz_window_get_nsview (window);
302 [view addSubview:gl_view];
306 Gtkmm2ext::nsglview_resize (void* glv, int x, int y, int w, int h)
308 ArdourCanvasOpenGLView* gl_view = (ArdourCanvasOpenGLView*) glv;
309 [gl_view setFrame:NSMakeRect(x, y, w, h)];
313 Gtkmm2ext::nsglview_queue_draw (void* glv, int x, int y, int w, int h)
315 ArdourCanvasOpenGLView* gl_view = (ArdourCanvasOpenGLView*) glv;
316 [gl_view setNeedsDisplayInRect:NSMakeRect(x, y, w, h)];
317 #ifdef DEBUG_NSVIEW_EXPOSURE
318 printf ("Queue Draw %5d %5d %5d %5d\n", x, y, w, h);
323 Gtkmm2ext::nsglview_set_visible (void* glv, bool vis)
325 ArdourCanvasOpenGLView* gl_view = (ArdourCanvasOpenGLView*) glv;
327 [gl_view setHidden:NO];
329 [gl_view setHidden:YES];