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