Update classkeys to match new total LuaSignal count (windows only)
[ardour.git] / libs / gtkmm2ext / nsglview.mm
1 /*
2     Copyright (C) 2011 Paul Davis
3     Copyright (C) 2012 David Robillard <http://drobilla.net>
4     Copyright (C) 2017 Robin Gareus <robin@gareus.org>
5
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.
10
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.
15
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.
19
20 */
21
22 /* include order matter due to apple defines */
23 #include <gtkmm/window.h>
24
25 #include "gtkmm2ext/cairo_canvas.h"
26 #include "gtkmm2ext/nsglview.h"
27 #include "gtkmm2ext/rgb_macros.h"
28
29 #include <gdk/gdkquartz.h>
30
31 #include <OpenGL/gl.h>
32 #import  <Cocoa/Cocoa.h>
33
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).
37  *
38  * In this particular case however we do want the
39  * events to reach the GTK widget instead of the
40  * NSView subview.
41  *
42  * If a NSVIew tag equals to the given magic-number,
43  * Gdk events propagate.
44  */
45 #ifndef ARDOUR_CANVAS_NSVIEW_TAG
46 #define ARDOUR_CANVAS_NSVIEW_TAG 0x0
47 #endif
48
49 __attribute__ ((visibility ("hidden")))
50 @interface ArdourCanvasOpenGLView : NSOpenGLView
51 {
52 @private
53         unsigned int _texture_id;
54         int _width;
55         int _height;
56         Cairo::RefPtr<Cairo::ImageSurface> surf;
57         Gtkmm2ext::CairoCanvas *cairo_canvas;
58         NSInteger _tag;
59 }
60
61 @property (readwrite) NSInteger tag;
62
63 - (id) initWithFrame:(NSRect)frame;
64 - (void) dealloc;
65 - (void) setCairoCanvas:(Gtkmm2ext::CairoCanvas*)c;
66 - (void) reshape;
67 - (void) setNeedsDisplayInRect:(NSRect)rect;
68 - (void) drawRect:(NSRect)rect;
69 - (BOOL) canBecomeKeyWindow:(id)sender;
70 - (BOOL) acceptsFirstResponder:(id)sender;
71
72 @end
73
74 @implementation ArdourCanvasOpenGLView
75
76 @synthesize tag = _tag;
77
78 - (id) initWithFrame:(NSRect)frame
79 {
80         NSOpenGLPixelFormatAttribute pixelAttribs[16] = {
81                 NSOpenGLPFADoubleBuffer,
82                 NSOpenGLPFAAccelerated,
83                 NSOpenGLPFAColorSize, 32,
84                 NSOpenGLPFADepthSize, 32,
85                 NSOpenGLPFAMultisample,
86                 NSOpenGLPFASampleBuffers, 1,
87                 NSOpenGLPFASamples, 4,
88                 0
89         };
90
91         NSOpenGLPixelFormat* pixelFormat =
92                 [[NSOpenGLPixelFormat alloc] initWithAttributes:pixelAttribs];
93
94         if (pixelFormat) {
95                 self = [super initWithFrame:frame pixelFormat:pixelFormat];
96                 [pixelFormat release];
97         } else {
98                 self = [super initWithFrame:frame];
99         }
100
101         _texture_id = 0;
102         _width = 0;
103         _height = 0;
104
105         if (self) {
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);
110                 glEnable (GL_BLEND);
111                 glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
112                 glEnable (GL_TEXTURE_RECTANGLE_ARB);
113                 [NSOpenGLContext clearCurrentContext];
114
115                 [self reshape];
116         }
117
118         return self;
119 }
120
121 - (void) dealloc {
122         [[self openGLContext] makeCurrentContext];
123         glDeleteTextures (1, &_texture_id);
124         [NSOpenGLContext clearCurrentContext];
125
126         [super dealloc];
127 }
128
129 - (void) setCairoCanvas:(Gtkmm2ext::CairoCanvas*)c
130 {
131         cairo_canvas = c;
132 }
133
134 - (BOOL) canBecomeKeyWindow:(id)sender{
135         return NO;
136 }
137
138 - (BOOL) acceptsFirstResponder:(id)sender{
139         return NO;
140 }
141
142 - (void) reshape
143 {
144         [[self openGLContext] update];
145
146         NSRect bounds = [self bounds];
147         int    width  = bounds.size.width;
148         int    height = bounds.size.height;
149
150         if (_width == width && _height == height) {
151                 return;
152         }
153
154         [[self openGLContext] makeCurrentContext];
155
156         glViewport (0, 0, width, height);
157         glMatrixMode (GL_PROJECTION);
158         glLoadIdentity ();
159         glOrtho (-1.0f, 1.0f, -1.0f, 1.0f, -1.0f, 1.0f);
160
161         glClear (GL_COLOR_BUFFER_BIT);
162
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,
167                         width, height, 0,
168                         GL_BGRA, GL_UNSIGNED_BYTE, NULL);
169         glTexEnvi (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_DECAL);
170
171         glMatrixMode(GL_MODELVIEW);
172         glLoadIdentity();
173
174         [NSOpenGLContext clearCurrentContext];
175
176         _width  = width;
177         _height = height;
178 }
179
180 - (void) setNeedsDisplayInRect:(NSRect)rect
181 {
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);
186 #endif
187 }
188
189 - (void) drawRect:(NSRect)rect
190 {
191         [[self openGLContext] makeCurrentContext];
192
193         glMatrixMode(GL_MODELVIEW);
194         glLoadIdentity();
195         glClear(GL_COLOR_BUFFER_BIT);
196
197         /* call back into CairoCanvas */
198         cairo_rectangle_t cairo_rect;
199
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;
204
205         if (!surf || surf->get_width () != _width || surf->get_height() != _height) {
206                 surf = Cairo::ImageSurface::create (Cairo::FORMAT_ARGB32, _width, _height);
207
208                 cairo_rect.x = 0;
209                 cairo_rect.y = 0;
210                 cairo_rect.width = _width;
211                 cairo_rect.height = _height;
212         }
213
214         Cairo::RefPtr<Cairo::Context> ctx = Cairo::Context::create (surf);
215
216         // TODO: check retina screen, scaling factor.
217         // cairo_surface_get_device_scale () or explicit scale
218
219         ctx->rectangle (cairo_rect.x, cairo_rect.y, cairo_rect.width, cairo_rect.height);
220         ctx->clip_preserve ();
221         {
222                 /* draw background color */
223                 uint32_t col = cairo_canvas->background_color ();
224                 int r, g, b, a;
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);
227         }
228         ctx->fill ();
229
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);
233 #endif
234
235         cairo_canvas->render (ctx, &cairo_rect);
236
237         surf->flush ();
238         uint8_t* imgdata = surf->get_data ();
239
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
243          */
244
245         /* continue OpenGL */
246         glPushMatrix ();
247
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);
253
254         glBegin(GL_QUADS);
255         glTexCoord2f(           0.0f, (GLfloat) _height);
256         glVertex2f(-1.0f, -1.0f);
257
258         glTexCoord2f((GLfloat) _width, (GLfloat) _height);
259         glVertex2f( 1.0f, -1.0f);
260
261         glTexCoord2f((GLfloat) _width, 0.0f);
262         glVertex2f( 1.0f,  1.0f);
263
264         glTexCoord2f(            0.0f, 0.0f);
265         glVertex2f(-1.0f,  1.0f);
266         glEnd();
267
268         glDisable(GL_TEXTURE_2D);
269         glPopMatrix();
270
271         ///
272
273         glFlush();
274         glSwapAPPLE();
275         [NSOpenGLContext clearCurrentContext];
276         [super setNeedsDisplay:NO];
277 }
278 @end
279
280
281 void*
282 Gtkmm2ext::nsglview_create (Gtkmm2ext::CairoCanvas* canvas)
283 {
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.
287  */
288 #ifdef __ppc__
289         return 0;
290 #endif
291         ArdourCanvasOpenGLView* gl_view = [ArdourCanvasOpenGLView new];
292         if (!gl_view) {
293                 return 0;
294         }
295         [gl_view setCairoCanvas:canvas];
296         [gl_view setHidden:YES];
297         return gl_view;
298 }
299
300 void
301 Gtkmm2ext::nsglview_overlay (void* glv, GdkWindow* window)
302 {
303         ArdourCanvasOpenGLView* gl_view = (ArdourCanvasOpenGLView*) glv;
304         NSView* view = gdk_quartz_window_get_nsview (window);
305         [view addSubview:gl_view];
306 }
307
308 void
309 Gtkmm2ext::nsglview_resize (void* glv, int x, int y, int w, int h)
310 {
311         ArdourCanvasOpenGLView* gl_view = (ArdourCanvasOpenGLView*) glv;
312         [gl_view setFrame:NSMakeRect(x, y, w, h)];
313 }
314
315 void
316 Gtkmm2ext::nsglview_queue_draw (void* glv, int x, int y, int w, int h)
317 {
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);
322 #endif
323 }
324
325 void
326 Gtkmm2ext::nsglview_set_visible (void* glv, bool vis)
327 {
328         ArdourCanvasOpenGLView* gl_view = (ArdourCanvasOpenGLView*) glv;
329         if (vis) {
330                 [gl_view setHidden:NO];
331         } else {
332                 [gl_view setHidden:YES];
333         }
334 }