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 }