1 /*
2  * This file is part of gtkD.
3  *
4  * gtkD is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU Lesser General Public License
6  * as published by the Free Software Foundation; either version 3
7  * of the License, or (at your option) any later version, with
8  * some exceptions, please read the COPYING file.
9  *
10  * gtkD is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU Lesser General Public License for more details.
14  *
15  * You should have received a copy of the GNU Lesser General Public License
16  * along with gtkD; if not, write to the Free Software
17  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110, USA
18  */
19 
20 // generated automatically - do not change
21 // find conversion definition on APILookup.txt
22 // implement new conversion functionalities on the wrap.utils pakage
23 
24 
25 module gtk.GLArea;
26 
27 private import gdk.GLContext;
28 private import glib.ConstructionException;
29 private import glib.ErrorG;
30 private import gobject.ObjectG;
31 private import gobject.Signals;
32 private import gtk.Widget;
33 private import gtkc.gtk;
34 public  import gtkc.gtktypes;
35 private import std.algorithm;
36 
37 
38 /**
39  * #GtkGLArea is a widget that allows drawing with OpenGL.
40  * 
41  * #GtkGLArea sets up its own #GdkGLContext for the window it creates, and
42  * creates a custom GL framebuffer that the widget will do GL rendering onto.
43  * It also ensures that this framebuffer is the default GL rendering target
44  * when rendering.
45  * 
46  * In order to draw, you have to connect to the #GtkGLArea::render signal,
47  * or subclass #GtkGLArea and override the @GtkGLAreaClass.render() virtual
48  * function.
49  * 
50  * The #GtkGLArea widget ensures that the #GdkGLContext is associated with
51  * the widget's drawing area, and it is kept updated when the size and
52  * position of the drawing area changes.
53  * 
54  * ## Drawing with GtkGLArea ##
55  * 
56  * The simplest way to draw using OpenGL commands in a #GtkGLArea is to
57  * create a widget instance and connect to the #GtkGLArea::render signal:
58  * 
59  * |[<!-- language="C" -->
60  * // create a GtkGLArea instance
61  * GtkWidget *gl_area = gtk_gl_area_new ();
62  * 
63  * // connect to the "render" signal
64  * g_signal_connect (gl_area, "render", G_CALLBACK (render), NULL);
65  * ]|
66  * 
67  * The `render()` function will be called when the #GtkGLArea is ready
68  * for you to draw its content:
69  * 
70  * |[<!-- language="C" -->
71  * static gboolean
72  * render (GtkGLArea *area, GdkGLContext *context)
73  * {
74  * // inside this function it's safe to use GL; the given
75  * // #GdkGLContext has been made current to the drawable
76  * // surface used by the #GtkGLArea and the viewport has
77  * // already been set to be the size of the allocation
78  * 
79  * // we can start by clearing the buffer
80  * glClearColor (0, 0, 0, 0);
81  * glClear (GL_COLOR_BUFFER_BIT);
82  * 
83  * // draw your object
84  * draw_an_object ();
85  * 
86  * // we completed our drawing; the draw commands will be
87  * // flushed at the end of the signal emission chain, and
88  * // the buffers will be drawn on the window
89  * return TRUE;
90  * }
91  * ]|
92  * 
93  * If you need to initialize OpenGL state, e.g. buffer objects or
94  * shaders, you should use the #GtkWidget::realize signal; you
95  * can use the #GtkWidget::unrealize signal to clean up. Since the
96  * #GdkGLContext creation and initialization may fail, you will
97  * need to check for errors, using gtk_gl_area_get_error(). An example
98  * of how to safely initialize the GL state is:
99  * 
100  * |[<!-- language="C" -->
101  * static void
102  * on_realize (GtkGLarea *area)
103  * {
104  * // We need to make the context current if we want to
105  * // call GL API
106  * gtk_gl_area_make_current (area);
107  * 
108  * // If there were errors during the initialization or
109  * // when trying to make the context current, this
110  * // function will return a #GError for you to catch
111  * if (gtk_gl_area_get_error (area) != NULL)
112  * return;
113  * 
114  * // You can also use gtk_gl_area_set_error() in order
115  * // to show eventual initialization errors on the
116  * // GtkGLArea widget itself
117  * GError *internal_error = NULL;
118  * init_buffer_objects (&error);
119  * if (error != NULL)
120  * {
121  * gtk_gl_area_set_error (area, error);
122  * g_error_free (error);
123  * return;
124  * }
125  * 
126  * init_shaders (&error);
127  * if (error != NULL)
128  * {
129  * gtk_gl_area_set_error (area, error);
130  * g_error_free (error);
131  * return;
132  * }
133  * }
134  * ]|
135  * 
136  * If you need to change the options for creating the #GdkGLContext
137  * you should use the #GtkGLArea::create-context signal.
138  *
139  * Since: 3.16
140  */
141 public class GLArea : Widget
142 {
143 	/** the main Gtk struct */
144 	protected GtkGLArea* gtkGLArea;
145 
146 	/** Get the main Gtk struct */
147 	public GtkGLArea* getGLAreaStruct(bool transferOwnership = false)
148 	{
149 		if (transferOwnership)
150 			ownedRef = false;
151 		return gtkGLArea;
152 	}
153 
154 	/** the main Gtk struct as a void* */
155 	protected override void* getStruct()
156 	{
157 		return cast(void*)gtkGLArea;
158 	}
159 
160 	protected override void setStruct(GObject* obj)
161 	{
162 		gtkGLArea = cast(GtkGLArea*)obj;
163 		super.setStruct(obj);
164 	}
165 
166 	/**
167 	 * Sets our main struct and passes it to the parent class.
168 	 */
169 	public this (GtkGLArea* gtkGLArea, bool ownedRef = false)
170 	{
171 		this.gtkGLArea = gtkGLArea;
172 		super(cast(GtkWidget*)gtkGLArea, ownedRef);
173 	}
174 
175 
176 	/** */
177 	public static GType getType()
178 	{
179 		return gtk_gl_area_get_type();
180 	}
181 
182 	/**
183 	 * Creates a new #GtkGLArea widget.
184 	 *
185 	 * Returns: the newly created #GtkGLArea
186 	 *
187 	 * Since: 3.16
188 	 *
189 	 * Throws: ConstructionException GTK+ fails to create the object.
190 	 */
191 	public this()
192 	{
193 		auto p = gtk_gl_area_new();
194 		
195 		if(p is null)
196 		{
197 			throw new ConstructionException("null returned by new");
198 		}
199 		
200 		this(cast(GtkGLArea*) p, true);
201 	}
202 
203 	/**
204 	 * Ensures that the @area framebuffer object is made the current draw
205 	 * and read target, and that all the required buffers for the @area
206 	 * are created and bound to the frambuffer.
207 	 *
208 	 * This function is automatically called before emitting the
209 	 * #GtkGLArea::render signal, and doesn't normally need to be called
210 	 * by application code.
211 	 *
212 	 * Since: 3.16
213 	 */
214 	public void attachBuffers()
215 	{
216 		gtk_gl_area_attach_buffers(gtkGLArea);
217 	}
218 
219 	/**
220 	 * Returns whether the area is in auto render mode or not.
221 	 *
222 	 * Returns: %TRUE if the @area is auto rendering, %FALSE otherwise
223 	 *
224 	 * Since: 3.16
225 	 */
226 	public bool getAutoRender()
227 	{
228 		return gtk_gl_area_get_auto_render(gtkGLArea) != 0;
229 	}
230 
231 	/**
232 	 * Retrieves the #GdkGLContext used by @area.
233 	 *
234 	 * Returns: the #GdkGLContext
235 	 *
236 	 * Since: 3.16
237 	 */
238 	public GLContext getContext()
239 	{
240 		auto p = gtk_gl_area_get_context(gtkGLArea);
241 		
242 		if(p is null)
243 		{
244 			return null;
245 		}
246 		
247 		return ObjectG.getDObject!(GLContext)(cast(GdkGLContext*) p);
248 	}
249 
250 	/**
251 	 * Gets the current error set on the @area.
252 	 *
253 	 * Returns: the #GError or %NULL
254 	 *
255 	 * Since: 3.16
256 	 */
257 	public ErrorG getError()
258 	{
259 		auto p = gtk_gl_area_get_error(gtkGLArea);
260 		
261 		if(p is null)
262 		{
263 			return null;
264 		}
265 		
266 		return new ErrorG(cast(GError*) p);
267 	}
268 
269 	/**
270 	 * Returns whether the area has an alpha component.
271 	 *
272 	 * Returns: %TRUE if the @area has an alpha component, %FALSE otherwise
273 	 *
274 	 * Since: 3.16
275 	 */
276 	public bool getHasAlpha()
277 	{
278 		return gtk_gl_area_get_has_alpha(gtkGLArea) != 0;
279 	}
280 
281 	/**
282 	 * Returns whether the area has a depth buffer.
283 	 *
284 	 * Returns: %TRUE if the @area has a depth buffer, %FALSE otherwise
285 	 *
286 	 * Since: 3.16
287 	 */
288 	public bool getHasDepthBuffer()
289 	{
290 		return gtk_gl_area_get_has_depth_buffer(gtkGLArea) != 0;
291 	}
292 
293 	/**
294 	 * Returns whether the area has a stencil buffer.
295 	 *
296 	 * Returns: %TRUE if the @area has a stencil buffer, %FALSE otherwise
297 	 *
298 	 * Since: 3.16
299 	 */
300 	public bool getHasStencilBuffer()
301 	{
302 		return gtk_gl_area_get_has_stencil_buffer(gtkGLArea) != 0;
303 	}
304 
305 	/**
306 	 * Retrieves the required version of OpenGL set
307 	 * using gtk_gl_area_set_required_version().
308 	 *
309 	 * Params:
310 	 *     major = return location for the required major version
311 	 *     minor = return location for the required minor version
312 	 *
313 	 * Since: 3.16
314 	 */
315 	public void getRequiredVersion(out int major, out int minor)
316 	{
317 		gtk_gl_area_get_required_version(gtkGLArea, &major, &minor);
318 	}
319 
320 	/**
321 	 * Retrieves the value set by gtk_gl_area_set_use_es().
322 	 *
323 	 * Returns: %TRUE if the #GtkGLArea should create an OpenGL ES context
324 	 *     and %FALSE otherwise
325 	 *
326 	 * Since: 3.22
327 	 */
328 	public bool getUseEs()
329 	{
330 		return gtk_gl_area_get_use_es(gtkGLArea) != 0;
331 	}
332 
333 	/**
334 	 * Ensures that the #GdkGLContext used by @area is associated with
335 	 * the #GtkGLArea.
336 	 *
337 	 * This function is automatically called before emitting the
338 	 * #GtkGLArea::render signal, and doesn't normally need to be called
339 	 * by application code.
340 	 *
341 	 * Since: 3.16
342 	 */
343 	public void makeCurrent()
344 	{
345 		gtk_gl_area_make_current(gtkGLArea);
346 	}
347 
348 	/**
349 	 * Marks the currently rendered data (if any) as invalid, and queues
350 	 * a redraw of the widget, ensuring that the #GtkGLArea::render signal
351 	 * is emitted during the draw.
352 	 *
353 	 * This is only needed when the gtk_gl_area_set_auto_render() has
354 	 * been called with a %FALSE value. The default behaviour is to
355 	 * emit #GtkGLArea::render on each draw.
356 	 *
357 	 * Since: 3.16
358 	 */
359 	public void queueRender()
360 	{
361 		gtk_gl_area_queue_render(gtkGLArea);
362 	}
363 
364 	/**
365 	 * If @auto_render is %TRUE the #GtkGLArea::render signal will be
366 	 * emitted every time the widget draws. This is the default and is
367 	 * useful if drawing the widget is faster.
368 	 *
369 	 * If @auto_render is %FALSE the data from previous rendering is kept
370 	 * around and will be used for drawing the widget the next time,
371 	 * unless the window is resized. In order to force a rendering
372 	 * gtk_gl_area_queue_render() must be called. This mode is useful when
373 	 * the scene changes seldomly, but takes a long time to redraw.
374 	 *
375 	 * Params:
376 	 *     autoRender = a boolean
377 	 *
378 	 * Since: 3.16
379 	 */
380 	public void setAutoRender(bool autoRender)
381 	{
382 		gtk_gl_area_set_auto_render(gtkGLArea, autoRender);
383 	}
384 
385 	/**
386 	 * Sets an error on the area which will be shown instead of the
387 	 * GL rendering. This is useful in the #GtkGLArea::create-context
388 	 * signal if GL context creation fails.
389 	 *
390 	 * Params:
391 	 *     error = a new #GError, or %NULL to unset the error
392 	 *
393 	 * Since: 3.16
394 	 */
395 	public void setError(ErrorG error)
396 	{
397 		gtk_gl_area_set_error(gtkGLArea, (error is null) ? null : error.getErrorGStruct());
398 	}
399 
400 	/**
401 	 * If @has_alpha is %TRUE the buffer allocated by the widget will have
402 	 * an alpha channel component, and when rendering to the window the
403 	 * result will be composited over whatever is below the widget.
404 	 *
405 	 * If @has_alpha is %FALSE there will be no alpha channel, and the
406 	 * buffer will fully replace anything below the widget.
407 	 *
408 	 * Params:
409 	 *     hasAlpha = %TRUE to add an alpha component
410 	 *
411 	 * Since: 3.16
412 	 */
413 	public void setHasAlpha(bool hasAlpha)
414 	{
415 		gtk_gl_area_set_has_alpha(gtkGLArea, hasAlpha);
416 	}
417 
418 	/**
419 	 * If @has_depth_buffer is %TRUE the widget will allocate and
420 	 * enable a depth buffer for the target framebuffer. Otherwise
421 	 * there will be none.
422 	 *
423 	 * Params:
424 	 *     hasDepthBuffer = %TRUE to add a depth buffer
425 	 *
426 	 * Since: 3.16
427 	 */
428 	public void setHasDepthBuffer(bool hasDepthBuffer)
429 	{
430 		gtk_gl_area_set_has_depth_buffer(gtkGLArea, hasDepthBuffer);
431 	}
432 
433 	/**
434 	 * If @has_stencil_buffer is %TRUE the widget will allocate and
435 	 * enable a stencil buffer for the target framebuffer. Otherwise
436 	 * there will be none.
437 	 *
438 	 * Params:
439 	 *     hasStencilBuffer = %TRUE to add a stencil buffer
440 	 *
441 	 * Since: 3.16
442 	 */
443 	public void setHasStencilBuffer(bool hasStencilBuffer)
444 	{
445 		gtk_gl_area_set_has_stencil_buffer(gtkGLArea, hasStencilBuffer);
446 	}
447 
448 	/**
449 	 * Sets the required version of OpenGL to be used when creating the context
450 	 * for the widget.
451 	 *
452 	 * This function must be called before the area has been realized.
453 	 *
454 	 * Params:
455 	 *     major = the major version
456 	 *     minor = the minor version
457 	 *
458 	 * Since: 3.16
459 	 */
460 	public void setRequiredVersion(int major, int minor)
461 	{
462 		gtk_gl_area_set_required_version(gtkGLArea, major, minor);
463 	}
464 
465 	/**
466 	 * Sets whether the @area should create an OpenGL or an OpenGL ES context.
467 	 *
468 	 * You should check the capabilities of the #GdkGLContext before drawing
469 	 * with either API.
470 	 *
471 	 * Params:
472 	 *     useEs = whether to use OpenGL or OpenGL ES
473 	 *
474 	 * Since: 3.22
475 	 */
476 	public void setUseEs(bool useEs)
477 	{
478 		gtk_gl_area_set_use_es(gtkGLArea, useEs);
479 	}
480 
481 	protected class OnCreateContextDelegateWrapper
482 	{
483 		static OnCreateContextDelegateWrapper[] listeners;
484 		GLContext delegate(GLArea) dlg;
485 		gulong handlerId;
486 		
487 		this(GLContext delegate(GLArea) dlg)
488 		{
489 			this.dlg = dlg;
490 			this.listeners ~= this;
491 		}
492 		
493 		void remove(OnCreateContextDelegateWrapper source)
494 		{
495 			foreach(index, wrapper; listeners)
496 			{
497 				if (wrapper.handlerId == source.handlerId)
498 				{
499 					listeners[index] = null;
500 					listeners = std.algorithm.remove(listeners, index);
501 					break;
502 				}
503 			}
504 		}
505 	}
506 
507 	/**
508 	 * The ::create-context signal is emitted when the widget is being
509 	 * realized, and allows you to override how the GL context is
510 	 * created. This is useful when you want to reuse an existing GL
511 	 * context, or if you want to try creating different kinds of GL
512 	 * options.
513 	 *
514 	 * If context creation fails then the signal handler can use
515 	 * gtk_gl_area_set_error() to register a more detailed error
516 	 * of how the construction failed.
517 	 *
518 	 * Returns: a newly created #GdkGLContext;
519 	 *     the #GtkGLArea widget will take ownership of the returned value.
520 	 *
521 	 * Since: 3.16
522 	 */
523 	gulong addOnCreateContext(GLContext delegate(GLArea) dlg, ConnectFlags connectFlags=cast(ConnectFlags)0)
524 	{
525 		auto wrapper = new OnCreateContextDelegateWrapper(dlg);
526 		wrapper.handlerId = Signals.connectData(
527 			this,
528 			"create-context",
529 			cast(GCallback)&callBackCreateContext,
530 			cast(void*)wrapper,
531 			cast(GClosureNotify)&callBackCreateContextDestroy,
532 			connectFlags);
533 		return wrapper.handlerId;
534 	}
535 	
536 	extern(C) static GdkGLContext* callBackCreateContext(GtkGLArea* glareaStruct, OnCreateContextDelegateWrapper wrapper)
537 	{
538 		auto r = wrapper.dlg(wrapper.outer);
539 		return r.getGLContextStruct();
540 	}
541 	
542 	extern(C) static void callBackCreateContextDestroy(OnCreateContextDelegateWrapper wrapper, GClosure* closure)
543 	{
544 		wrapper.remove(wrapper);
545 	}
546 
547 	protected class OnRenderDelegateWrapper
548 	{
549 		static OnRenderDelegateWrapper[] listeners;
550 		bool delegate(GLContext, GLArea) dlg;
551 		gulong handlerId;
552 		
553 		this(bool delegate(GLContext, GLArea) dlg)
554 		{
555 			this.dlg = dlg;
556 			this.listeners ~= this;
557 		}
558 		
559 		void remove(OnRenderDelegateWrapper source)
560 		{
561 			foreach(index, wrapper; listeners)
562 			{
563 				if (wrapper.handlerId == source.handlerId)
564 				{
565 					listeners[index] = null;
566 					listeners = std.algorithm.remove(listeners, index);
567 					break;
568 				}
569 			}
570 		}
571 	}
572 
573 	/**
574 	 * The ::render signal is emitted every time the contents
575 	 * of the #GtkGLArea should be redrawn.
576 	 *
577 	 * The @context is bound to the @area prior to emitting this function,
578 	 * and the buffers are painted to the window once the emission terminates.
579 	 *
580 	 * Params:
581 	 *     context = the #GdkGLContext used by @area
582 	 *
583 	 * Returns: %TRUE to stop other handlers from being invoked for the event.
584 	 *     %FALSE to propagate the event further.
585 	 *
586 	 * Since: 3.16
587 	 */
588 	gulong addOnRender(bool delegate(GLContext, GLArea) dlg, ConnectFlags connectFlags=cast(ConnectFlags)0)
589 	{
590 		auto wrapper = new OnRenderDelegateWrapper(dlg);
591 		wrapper.handlerId = Signals.connectData(
592 			this,
593 			"render",
594 			cast(GCallback)&callBackRender,
595 			cast(void*)wrapper,
596 			cast(GClosureNotify)&callBackRenderDestroy,
597 			connectFlags);
598 		return wrapper.handlerId;
599 	}
600 	
601 	extern(C) static int callBackRender(GtkGLArea* glareaStruct, GdkGLContext* context, OnRenderDelegateWrapper wrapper)
602 	{
603 		return wrapper.dlg(ObjectG.getDObject!(GLContext)(context), wrapper.outer);
604 	}
605 	
606 	extern(C) static void callBackRenderDestroy(OnRenderDelegateWrapper wrapper, GClosure* closure)
607 	{
608 		wrapper.remove(wrapper);
609 	}
610 
611 	protected class OnResizeDelegateWrapper
612 	{
613 		static OnResizeDelegateWrapper[] listeners;
614 		void delegate(int, int, GLArea) dlg;
615 		gulong handlerId;
616 		
617 		this(void delegate(int, int, GLArea) dlg)
618 		{
619 			this.dlg = dlg;
620 			this.listeners ~= this;
621 		}
622 		
623 		void remove(OnResizeDelegateWrapper source)
624 		{
625 			foreach(index, wrapper; listeners)
626 			{
627 				if (wrapper.handlerId == source.handlerId)
628 				{
629 					listeners[index] = null;
630 					listeners = std.algorithm.remove(listeners, index);
631 					break;
632 				}
633 			}
634 		}
635 	}
636 
637 	/**
638 	 * The ::resize signal is emitted once when the widget is realized, and
639 	 * then each time the widget is changed while realized. This is useful
640 	 * in order to keep GL state up to date with the widget size, like for
641 	 * instance camera properties which may depend on the width/height ratio.
642 	 *
643 	 * The GL context for the area is guaranteed to be current when this signal
644 	 * is emitted.
645 	 *
646 	 * The default handler sets up the GL viewport.
647 	 *
648 	 * Params:
649 	 *     width = the width of the viewport
650 	 *     height = the height of the viewport
651 	 *
652 	 * Since: 3.16
653 	 */
654 	gulong addOnResize(void delegate(int, int, GLArea) dlg, ConnectFlags connectFlags=cast(ConnectFlags)0)
655 	{
656 		auto wrapper = new OnResizeDelegateWrapper(dlg);
657 		wrapper.handlerId = Signals.connectData(
658 			this,
659 			"resize",
660 			cast(GCallback)&callBackResize,
661 			cast(void*)wrapper,
662 			cast(GClosureNotify)&callBackResizeDestroy,
663 			connectFlags);
664 		return wrapper.handlerId;
665 	}
666 	
667 	extern(C) static void callBackResize(GtkGLArea* glareaStruct, int width, int height, OnResizeDelegateWrapper wrapper)
668 	{
669 		wrapper.dlg(width, height, wrapper.outer);
670 	}
671 	
672 	extern(C) static void callBackResizeDestroy(OnResizeDelegateWrapper wrapper, GClosure* closure)
673 	{
674 		wrapper.remove(wrapper);
675 	}
676 }