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