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 }