NSGLView: default to hidden, add API to un/hide
[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 #ifndef ARDOUR_CANVAS_NSVIEW_TAG
35 #define ARDOUR_CANVAS_NSVIEW_TAG 0xa2d0c2c4
36 #endif
37
38 __attribute__ ((visibility ("hidden")))
39 @interface ArdourCanvasOpenGLView : NSOpenGLView
40 {
41 @private
42         unsigned int _texture_id;
43         int _width;
44         int _height;
45         Cairo::RefPtr<Cairo::ImageSurface> surf;
46         Gtkmm2ext::CairoCanvas *cairo_canvas;
47 }
48
49 @property (readwrite) NSInteger tag;
50
51 - (id) initWithFrame:(NSRect)frame;
52 - (void) dealloc;
53 - (void) setCairoCanvas:(Gtkmm2ext::CairoCanvas*)c;
54 - (void) reshape;
55 - (void) drawRect:(NSRect)rect;
56 - (BOOL) canBecomeKeyWindow:(id)sender;
57 - (BOOL) acceptsFirstResponder:(id)sender;
58
59 @end
60
61 @implementation ArdourCanvasOpenGLView
62
63 @synthesize tag = _tag;
64
65 - (id) initWithFrame:(NSRect)frame
66 {
67         NSOpenGLPixelFormatAttribute pixelAttribs[16] = {
68                 NSOpenGLPFADoubleBuffer,
69                 NSOpenGLPFAAccelerated,
70                 NSOpenGLPFAColorSize, 32,
71                 NSOpenGLPFADepthSize, 32,
72                 NSOpenGLPFAMultisample,
73                 NSOpenGLPFASampleBuffers, 1,
74                 NSOpenGLPFASamples, 4,
75                 0
76         };
77
78         NSOpenGLPixelFormat* pixelFormat =
79                 [[NSOpenGLPixelFormat alloc] initWithAttributes:pixelAttribs];
80
81         if (pixelFormat) {
82                 self = [super initWithFrame:frame pixelFormat:pixelFormat];
83                 [pixelFormat release];
84         } else {
85                 self = [super initWithFrame:frame];
86         }
87
88         _texture_id = 0;
89         _width = 0;
90         _height = 0;
91
92         if (self) {
93                 self.tag = ARDOUR_CANVAS_NSVIEW_TAG;
94                 [[self openGLContext] makeCurrentContext];
95                 glClearColor (0.0f, 0.0f, 0.0f, 0.0f);
96                 glDisable (GL_DEPTH_TEST);
97                 glEnable (GL_BLEND);
98                 glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
99                 glEnable (GL_TEXTURE_RECTANGLE_ARB);
100                 [NSOpenGLContext clearCurrentContext];
101
102                 [self reshape];
103         }
104
105         return self;
106 }
107
108 - (void) dealloc {
109         [[self openGLContext] makeCurrentContext];
110         glDeleteTextures (1, &_texture_id);
111         [NSOpenGLContext clearCurrentContext];
112
113         [super dealloc];
114 }
115
116 - (void) setCairoCanvas:(Gtkmm2ext::CairoCanvas*)c
117 {
118         cairo_canvas = c;
119 }
120
121 - (BOOL) canBecomeKeyWindow:(id)sender{
122         return NO;
123 }
124
125 - (BOOL) acceptsFirstResponder:(id)sender{
126         return NO;
127 }
128
129 - (void) reshape
130 {
131         [[self openGLContext] update];
132
133         NSRect bounds = [self bounds];
134         int    width  = bounds.size.width;
135         int    height = bounds.size.height;
136
137         if (_width == width && _height == height) {
138                 return;
139         }
140
141         [[self openGLContext] makeCurrentContext];
142
143         glViewport (0, 0, width, height);
144         glMatrixMode (GL_PROJECTION);
145         glLoadIdentity ();
146         glOrtho (-1.0f, 1.0f, -1.0f, 1.0f, -1.0f, 1.0f);
147
148         glClear (GL_COLOR_BUFFER_BIT);
149
150         glDeleteTextures (1, &_texture_id);
151         glGenTextures (1, &_texture_id);
152         glBindTexture (GL_TEXTURE_RECTANGLE_ARB, _texture_id);
153         glTexImage2D (GL_TEXTURE_RECTANGLE_ARB, 0, GL_RGBA8,
154                         width, height, 0,
155                         GL_BGRA, GL_UNSIGNED_BYTE, NULL);
156         glTexEnvi (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_DECAL);
157
158         glMatrixMode(GL_MODELVIEW);
159         glLoadIdentity();
160
161         [NSOpenGLContext clearCurrentContext];
162
163         _width  = width;
164         _height = height;
165 }
166
167 - (void) drawRect:(NSRect)rect
168 {
169         [[self openGLContext] makeCurrentContext];
170
171         glMatrixMode(GL_MODELVIEW);
172         glLoadIdentity();
173         glClear(GL_COLOR_BUFFER_BIT);
174
175         /* call back into CairoCanvas */
176         cairo_rectangle_t cairo_rect;
177
178         cairo_rect.x = rect.origin.x;
179         cairo_rect.y = rect.origin.y;
180         cairo_rect.width = rect.size.width;
181         cairo_rect.height = rect.size.height;
182
183         if (!surf || surf->get_width () != _width || surf->get_height() != _height) {
184                 surf = Cairo::ImageSurface::create (Cairo::FORMAT_ARGB32, _width, _height);
185
186                 cairo_rect.x = 0;
187                 cairo_rect.y = 0;
188                 cairo_rect.width = _width;
189                 cairo_rect.height = _height;
190         }
191
192         Cairo::RefPtr<Cairo::Context> ctx = Cairo::Context::create (surf);
193
194         // TODO: check retina screen, scaling factor.
195         // cairo_surface_get_device_scale () or explicit scale
196
197         ctx->rectangle (cairo_rect.x, cairo_rect.y, cairo_rect.width, cairo_rect.height);
198         ctx->clip_preserve ();
199         {
200                 /* draw background color */
201                 uint32_t col = cairo_canvas->background_color ();
202                 int r, g, b, a;
203                 UINT_TO_RGBA (col, &r, &g, &b, &a);
204                 ctx->set_source_rgba (r/255.0, g/255.0, b/255.0, a/255.0);
205         }
206         ctx->fill ();
207
208         cairo_canvas->render (ctx, &cairo_rect);
209
210         surf->flush ();
211         uint8_t* imgdata = surf->get_data ();
212
213         /* NOTE for big-endian (PPC), we'd need to flip byte-order
214          * RGBA <> RGBA for the texture.
215          * GtkCanvas does not use this nsview for PPC builds, yet
216          */
217
218         /* continue OpenGL */
219         glPushMatrix ();
220
221         glEnable(GL_TEXTURE_2D);
222         glBindTexture(GL_TEXTURE_RECTANGLE_ARB, _texture_id);
223         glTexImage2D(GL_TEXTURE_RECTANGLE_ARB, 0, GL_RGBA8,
224                         _width, _height, /*border*/ 0,
225                         GL_BGRA, GL_UNSIGNED_BYTE, imgdata);
226
227         glBegin(GL_QUADS);
228         glTexCoord2f(           0.0f, (GLfloat) _height);
229         glVertex2f(-1.0f, -1.0f);
230
231         glTexCoord2f((GLfloat) _width, (GLfloat) _height);
232         glVertex2f( 1.0f, -1.0f);
233
234         glTexCoord2f((GLfloat) _width, 0.0f);
235         glVertex2f( 1.0f,  1.0f);
236
237         glTexCoord2f(            0.0f, 0.0f);
238         glVertex2f(-1.0f,  1.0f);
239         glEnd();
240
241         glDisable(GL_TEXTURE_2D);
242         glPopMatrix();
243
244         ///
245
246         glFlush();
247         glSwapAPPLE();
248         [NSOpenGLContext clearCurrentContext];
249 }
250 @end
251
252
253 void*
254 Gtkmm2ext::nsglview_create (Gtkmm2ext::CairoCanvas* canvas)
255 {
256         ArdourCanvasOpenGLView* gl_view = [ArdourCanvasOpenGLView new];
257         if (!gl_view) {
258                 return 0;
259         }
260         [gl_view setCairoCanvas:canvas];
261         [gl_view setHidden:YES];
262         return gl_view;
263 }
264
265 void
266 Gtkmm2ext::nsglview_overlay (void* glv, GdkWindow* window)
267 {
268         ArdourCanvasOpenGLView* gl_view = (ArdourCanvasOpenGLView*) glv;
269         NSView* view = gdk_quartz_window_get_nsview (window);
270         [view addSubview:gl_view];
271 }
272
273 void
274 Gtkmm2ext::nsglview_resize (void* glv, int x, int y, int w, int h)
275 {
276         ArdourCanvasOpenGLView* gl_view = (ArdourCanvasOpenGLView*) glv;
277         [gl_view setFrame:NSMakeRect(x, y, w, h)];
278 }
279
280 void
281 Gtkmm2ext::nsglview_queue_draw (void* glv, int x, int y, int w, int h)
282 {
283         ArdourCanvasOpenGLView* gl_view = (ArdourCanvasOpenGLView*) glv;
284         [gl_view setNeedsDisplayInRect:NSMakeRect(x, y, w, h)];
285 }
286
287 void
288 Gtkmm2ext::nsglview_set_visible (void* glv, bool vis)
289 {
290         ArdourCanvasOpenGLView* gl_view = (ArdourCanvasOpenGLView*) glv;
291         if (vis) {
292                 [gl_view setHidden:NO];
293         } else {
294                 [gl_view setHidden:YES];
295         }
296 }