Compare commits

...

40 Commits

Author SHA1 Message Date
Emmanuele Bassi
6d22f2a46a glarea: Ensure that the viewport is correctly set
Call gdk_gl_context_update() once we create the GdkWindow inside the
::realize implementation of GtkGLArea, to ensure that the GL viewport is
updated with the size of the GDK window.
2014-08-18 18:07:45 +01:00
Emmanuele Bassi
4d07a9ad3a gdk/x11: Trap glXDestroyWindow() 2014-08-18 18:07:22 +01:00
Emmanuele Bassi
a8b172b803 glarea: Temporarily disable double-buffering
Double-buffering inside GDK breaks the GL drawing model; GL expects to
handle the output surface in its own way, so the double buffering done
by GTK produces flickering and bad frames.

We need to teach to GDK to ignore windows with native surfaces
associated with a GdkGLContext, while still allowing the ability to draw
with Cairo on a valid surface, but that requires some serious rework of
the drawing code.
2014-08-18 18:01:34 +01:00
Emmanuele Bassi
78f84f42c5 demos: Add GtkGLArea to gtk-demo 2014-08-18 17:55:51 +01:00
Emmanuele Bassi
06d48d0e9a gl: Drop the async viewport update
It is not the cause of the flickering, and after checking with the
current implementation of GL widgets like GtkClutterEmbed, it's not
necessary to update the viewport in response to a ConfigureNotify
for child windows.
2014-08-15 12:44:53 +01:00
Emmanuele Bassi
a7f3f94985 glarea: Clean up the ::realize implementation 2014-08-15 12:44:13 +01:00
Emmanuele Bassi
06bd4aba2b gdk/x11: Plug a leak in create_gl_context()
The valid GdkGLPixelFormat is owned by the GdkGLContext using it.
2014-08-15 12:42:55 +01:00
Emmanuele Bassi
5cba7de332 tests/glarea: Make the UI a bit more complex
Add sliders to control rotation angles on all three axis.
2014-08-15 12:40:40 +01:00
Emmanuele Bassi
be97c75a01 gdk: Add missing declaration in GdkGLPixelFormat 2014-08-15 12:40:11 +01:00
Emmanuele Bassi
a98d8fd8df glarea: Set app-paintable
The GL viewport is responsible for painting everything, including the
backgound.
2014-08-14 12:10:47 +01:00
Emmanuele Bassi
7622a36c7d gdk: Document pixel format getters
And add the implementation of get_aux_buffers().
2014-08-14 11:38:54 +01:00
Emmanuele Bassi
fb996eec50 gdk: Add GdkGLPixelFormat:stereo
Missing pixel format attribute for stereoscopic buffers.
2014-08-14 11:30:20 +01:00
Emmanuele Bassi
cf0b1771da gdk/x11: Respect GdkGLPixelFormat:accum-size
We are currently ignoring it.
2014-08-14 11:29:43 +01:00
Emmanuele Bassi
19a0a6196b gl: Rework pixel format validation
Instead of modifying the pixel format in place, we allow separating the
pixel format from the effective pixel format we found.

This means that caller code can keep a reference to the pixel format it
creates, and re-use it to create new contexts on different displays,
while the GL context can keep a reference to a valid pixel format to let
the use query it.

This allows GtkGLArea to keep its own user-supplied pixel format and
reuse it without adding additional constraints as a result of a
validation on a specific GdkDisplay.
2014-08-14 11:20:37 +01:00
Emmanuele Bassi
3398e2ea87 gdk: Add getters for GdkGLPixelFormat properties 2014-08-14 11:20:37 +01:00
Emmanuele Bassi
9082c07b91 gdk/x11: Adjust the pixel format's color size
The color size is the minimum value between all the color buffers.
2014-08-12 16:36:07 +01:00
Emmanuele Bassi
fd24c996df gdk/x11: Plug a leak
XVisualInfo objects returned by glXGetVisualFromFBConfig() need to be
freed.
2014-08-12 16:33:48 +01:00
Emmanuele Bassi
4e84bf5263 docs: Add GtkGLArea to the API reference 2014-08-12 12:43:14 +01:00
Emmanuele Bassi
0f47f8bcae docs: Add Gdk OpenGL API to the reference 2014-08-12 12:40:01 +01:00
Emmanuele Bassi
4b5f540f11 gdk: Remove unused function
The clear_window() function is replaced by the set_window() function
called with a NULL window.
2014-08-12 12:39:26 +01:00
Emmanuele Bassi
b7a5369f01 gtk: Document GtkGLArea 2014-08-12 12:30:48 +01:00
Emmanuele Bassi
30548946ee gdk: Add licensing notes for GdkGL* 2014-08-12 12:30:30 +01:00
Emmanuele Bassi
63130913ca docs: Add some more documentation to GdkGLContext 2014-08-12 11:56:47 +01:00
Emmanuele Bassi
3d2237865c gdk/x11: Add API to query the GLX version 2014-08-12 11:55:04 +01:00
Emmanuele Bassi
54edcca4d5 glarea: Unconditionally call gdk_gl_context_update()
There's no need to check for different sizes.
2014-08-12 11:54:08 +01:00
Emmanuele Bassi
ca380707e3 tests/glarea: Remove explcit glFlush()
It's really not needed.
2014-08-12 11:53:29 +01:00
Emmanuele Bassi
a71cec0119 gdk: Rework the GL context update
Ensure that gdk_gl_context_update() schedules an update of the viewport,
and then perform the actual update when the backend says it should. This
makes the code a bit more readable, and we can keep asking the user to
call gdk_gl_context_update() after gdk_window_move_resize() without
having to care about asynchronous updates.
2014-08-12 11:51:45 +01:00
Emmanuele Bassi
d713ae6321 gdk: Add missing GDK_DEBUG token 2014-08-12 11:24:51 +01:00
Emmanuele Bassi
e18136f16b tests/glarea: Add a button
Make the test a bit more friendly.
2014-08-12 10:39:29 +01:00
Emmanuele Bassi
686f9b4607 x11: Update GL viewport on ConfigureNotify
On X11, by virtue of the asynchronous nature of the protocol, we have to
wait until the ConfigureNotify event to resize the GL viewport. If the
GdkWindow is a top-level one, then we'll get a GDK_CONFIGURE event, but
if we're using a GDK_WINDOW_CHILD the event will be dropped on the
floor.
2014-08-12 10:39:29 +01:00
Emmanuele Bassi
90cc7743d0 x11: Add missing initialization 2014-08-12 10:39:29 +01:00
Emmanuele Bassi
eb0e790a21 x11: Update pixel format after validation
The GdkGLPixelFormat after validation contains the values of the
attributes that were found.
2014-08-12 10:39:29 +01:00
Emmanuele Bassi
0500963db9 x11: Fix conversion of pixel format to GL attributes
The color-size pixel format property is the size of each color buffer
channel.

We also need to set a default for the samples per sample buffer.
2014-08-12 10:39:29 +01:00
Emmanuele Bassi
bc9180d610 Improve documentation of GdkGLContext 2014-08-12 10:39:29 +01:00
Emmanuele Bassi
6b0fc7c4d8 gl: Add a back pointer from the window to the context
This way we can check if a GdkWindow has a GdkGLContext associated to
it.
2014-08-12 10:39:29 +01:00
Emmanuele Bassi
6d71c440e9 Improve documentation of GdkGLPixelFormat 2014-08-12 10:39:28 +01:00
Emmanuele Bassi
ffcd7983aa gtk: Add a GL drawing widget
https://bugzilla.gnome.org/show_bug.cgi?id=119189
2014-08-12 10:39:28 +01:00
Emmanuele Bassi
a5da2eff7d gdk: Make GdkGLPixelFormat less smart
We use GdkGLPixelFormat only as a storage for GL configuration options.
The validation is only made when creating a GdkGLContext.

Validation for feature discovery and fallback code paths can be done
directly using gdk_display_validate_gl_pixel_format() instead.
2014-08-12 10:39:28 +01:00
Emmanuele Bassi
5052aa4a9a Implement GdkGLContext and GdkGLPixelFormat
GdkGLPixelFormat is an ancillary class to specify pixel formats, buffer
types, and buffer sizes for GL context.

GdkGLContext is the wrapper around platform-specific GL context API.

This commit adds the base classes, and an implementation on X11.

https://bugzilla.gnome.org/show_bug.cgi?id=119189
2014-08-12 10:39:28 +01:00
Emmanuele Bassi
a6ee04781f build: Require libepoxy
If we want to use OpenGL in GDK then we have two choices; either:

  - find the GL headers on each platform
  - do extension discovery
  - implement all the crazy dlopen()/dlsym() dispatch tables

*or* use libepoxy, which shields us from all this madness and provides a
decent layer for GL clients to use, without creating its own namespace.

Epoxy is also used by other projects, like Xorg and piglit, and it's
portable to all the platforms GDK cares about.

https://bugzilla.gnome.org/show_bug.cgi?id=119189
2014-08-12 10:39:28 +01:00
37 changed files with 4156 additions and 9 deletions

View File

@@ -48,6 +48,7 @@ m4_define([cairo_required_version], [1.12.0])
m4_define([gdk_pixbuf_required_version], [2.27.1])
m4_define([introspection_required_version], [1.39.0])
m4_define([wayland_required_version], [1.3.90])
m4_define([epoxy_required_version], [1.0])
GLIB_REQUIRED_VERSION=glib_required_version
PANGO_REQUIRED_VERSION=pango_required_version
ATK_REQUIRED_VERSION=atk_required_version
@@ -1325,7 +1326,7 @@ CFLAGS="$saved_cflags"
LDFLAGS="$saved_ldflags"
GDK_PACKAGES="$PANGO_PACKAGES gdk-pixbuf-2.0 >= gdk_pixbuf_required_version cairo >= cairo_required_version cairo-gobject >= cairo_required_version"
GDK_PRIVATE_PACKAGES="$GDK_GIO_PACKAGE $X_PACKAGES $WAYLAND_PACKAGES $cairo_backends"
GDK_PRIVATE_PACKAGES="$GDK_GIO_PACKAGE $X_PACKAGES $WAYLAND_PACKAGES $cairo_backends epoxy >= epoxy_required_version"
if test "x$enable_x11_backend" = xyes; then
GDK_PRIVATE_PACKAGES="$GDK_PRIVATE_PACKAGES pangoft2"
fi

View File

@@ -25,6 +25,7 @@ demos = \
event_axes.c \
expander.c \
gestures.c \
glarea.c \
headerbar.c \
hypertext.c \
iconview.c \

View File

@@ -95,6 +95,7 @@
<file>expander.c</file>
<file>flowbox.c</file>
<file>gestures.c</file>
<file>glarea.c</file>
<file>headerbar.c</file>
<file>hypertext.c</file>
<file>iconview.c</file>

230
demos/gtk-demo/glarea.c Normal file
View File

@@ -0,0 +1,230 @@
/* OpenGL Area
*
* GtkGLArea is a widget that allows custom drawing using OpenGL calls.
*/
#include <gtk/gtk.h>
#include <epoxy/gl.h>
static GtkWidget *demo_window = NULL;
/* the GtkGLArea widget */
static GtkWidget *gl_area = NULL;
enum {
X_AXIS,
Y_AXIS,
Z_AXIS,
N_AXIS
};
/* Rotation angles on each axis */
static float rotation_angles[N_AXIS] = { 0.0 };
/* The object we are drawing */
static void
draw_triangle (void)
{
glColor3f (1.0f, 0.85f, 0.35f);
glBegin (GL_TRIANGLES);
{
glVertex3f ( 0.0, 0.6, 0.0);
glVertex3f (-0.2, -0.3, 0.0);
glVertex3f ( 0.2, -0.3, 0.0);
}
glEnd ();
}
/* The main rendering callback */
static gboolean
render (GtkGLArea *area,
GdkGLContext *context)
{
glClearColor (0.5, 0.5, 0.5, 1.0);
glClear (GL_COLOR_BUFFER_BIT);
glMatrixMode (GL_MODELVIEW);
glLoadIdentity ();
glRotatef (rotation_angles[X_AXIS], 1, 0, 0);
glRotatef (rotation_angles[Y_AXIS], 0, 1, 0);
glRotatef (rotation_angles[Z_AXIS], 0, 0, 1);
draw_triangle ();
glFlush ();
return TRUE;
}
/* Initialize the GL state */
static void
init_gl_state (GtkWidget *widget)
{
GdkGLContext *context = gtk_gl_area_get_context (GTK_GL_AREA (widget));
GdkGLPixelFormat *format = gdk_gl_context_get_pixel_format (context);
g_print ("GL Pixel format:\n"
" - double-buffer: %s\n"
" - multi-sample: %s\n"
" - stereo: %s\n"
" - color-size: %d, alpha-size: %d\n"
" - depth-size: %d\n"
" - stencil-size: %d\n"
" - aux-buffers: %d\n"
" - accum-size: %d\n"
" - sample-buffers: %d\n"
" - samples: %d\n\n",
gdk_gl_pixel_format_get_double_buffer (format) ? "yes" : "no",
gdk_gl_pixel_format_get_multi_sample (format) ? "yes" : "no",
gdk_gl_pixel_format_get_stereo (format) ? "yes" : "no",
gdk_gl_pixel_format_get_color_size (format),
gdk_gl_pixel_format_get_alpha_size (format),
gdk_gl_pixel_format_get_depth_size (format),
gdk_gl_pixel_format_get_stencil_size (format),
gdk_gl_pixel_format_get_aux_buffers (format),
gdk_gl_pixel_format_get_accum_size (format),
gdk_gl_pixel_format_get_sample_buffers (format),
gdk_gl_pixel_format_get_samples (format));
}
static void
on_axis_value_change (GtkAdjustment *adjustment,
gpointer data)
{
int axis = GPOINTER_TO_INT (data);
g_assert (axis >= 0 && axis < N_AXIS);
/* Update the rotation angle */
rotation_angles[axis] = gtk_adjustment_get_value (adjustment);
/* Update the contents of the GL drawing area */
gtk_widget_queue_draw (gl_area);
}
static GtkWidget *
create_axis_slider (int axis)
{
GtkWidget *box, *label, *slider;
GtkAdjustment *adj;
const char *text;
box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, FALSE);
switch (axis)
{
case X_AXIS:
text = "X axis";
break;
case Y_AXIS:
text = "Y axis";
break;
case Z_AXIS:
text = "Z axis";
break;
default:
g_assert_not_reached ();
}
label = gtk_label_new (text);
gtk_container_add (GTK_CONTAINER (box), label);
gtk_widget_show (label);
adj = gtk_adjustment_new (0.0, 0.0, 360.0, 1.0, 12.0, 0.0);
g_signal_connect (adj, "value-changed",
G_CALLBACK (on_axis_value_change),
GINT_TO_POINTER (axis));
slider = gtk_scale_new (GTK_ORIENTATION_HORIZONTAL, adj);
gtk_container_add (GTK_CONTAINER (box), slider);
gtk_widget_set_hexpand (slider, TRUE);
gtk_widget_show (slider);
gtk_widget_show (box);
return box;
}
static void
close_window (GtkWidget *widget)
{
/* Reset the state */
demo_window = NULL;
gl_area = NULL;
rotation_angles[X_AXIS] = 0.0;
rotation_angles[Y_AXIS] = 0.0;
rotation_angles[Z_AXIS] = 0.0;
}
GtkWidget *
create_glarea_window (GtkWidget *do_widget)
{
GtkWidget *window, *box, *button, *controls;
GdkGLPixelFormat *pixel_format;
int i;
window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
gtk_window_set_screen (GTK_WINDOW (window), gtk_widget_get_screen (do_widget));
gtk_window_set_title (GTK_WINDOW (window), "GtkGLArea - Golden Triangle");
gtk_window_set_default_size (GTK_WINDOW (window), 400, 400);
gtk_container_set_border_width (GTK_CONTAINER (window), 12);
g_signal_connect (window, "destroy", G_CALLBACK (close_window), NULL);
box = gtk_box_new (GTK_ORIENTATION_VERTICAL, FALSE);
gtk_box_set_spacing (GTK_BOX (box), 6);
gtk_container_add (GTK_CONTAINER (window), box);
/* Create a new pixel format; we use this to configure the
* GL context, and to check for features.
*
* We only need it to be double-buffered.
*/
pixel_format = gdk_gl_pixel_format_new ("double-buffer", TRUE, NULL);
gl_area = gtk_gl_area_new (pixel_format);
gtk_widget_set_hexpand (gl_area, TRUE);
gtk_widget_set_vexpand (gl_area, TRUE);
gtk_container_add (GTK_CONTAINER (box), gl_area);
/* we use ::realize to initialize our GL state, because at that
* point we know that the GtkGLArea is associated with windowing
* system resources like a display, window, and GL context.
*/
g_signal_connect (gl_area, "realize", G_CALLBACK (init_gl_state), NULL);
/* the main "draw" call for GtkGLArea */
g_signal_connect (gl_area, "render", G_CALLBACK (render), NULL);
g_object_unref (pixel_format);
controls = gtk_box_new (GTK_ORIENTATION_VERTICAL, FALSE);
gtk_container_add (GTK_CONTAINER (box), controls);
gtk_widget_set_hexpand (controls, TRUE);
for (i = 0; i < N_AXIS; i++)
gtk_container_add (GTK_CONTAINER (controls), create_axis_slider (i));
button = gtk_button_new_with_label ("Quit");
gtk_widget_set_hexpand (button, TRUE);
gtk_container_add (GTK_CONTAINER (box), button);
g_signal_connect_swapped (button, "clicked", G_CALLBACK (gtk_widget_destroy), window);
return window;
}
GtkWidget*
do_glarea (GtkWidget *do_widget)
{
if (demo_window == NULL)
demo_window = create_glarea_window (do_widget);
if (!gtk_widget_get_visible (demo_window))
gtk_widget_show_all (demo_window);
else
gtk_widget_destroy (demo_window);
return demo_window;
}

View File

@@ -32,6 +32,8 @@
<xi:include href="xml/windows.xml" />
<xi:include href="xml/gdkframeclock.xml" />
<xi:include href="xml/gdkframetimings.xml" />
<xi:include href="xml/gdkglcontext.xml" />
<xi:include href="xml/gdkglpixelformat.xml" />
<xi:include href="xml/events.xml" />
<xi:include href="xml/event_structs.xml" />
<xi:include href="xml/keys.xml" />

View File

@@ -156,6 +156,8 @@ gdk_display_supports_input_shapes
gdk_display_supports_composite
gdk_display_get_app_launch_context
gdk_display_notify_startup_complete
gdk_display_validate_gl_pixel_format
gdk_display_get_gl_context
<SUBSECTION Standard>
GDK_DISPLAY
@@ -1004,6 +1006,7 @@ gdk_x11_display_error_trap_pop
gdk_x11_display_error_trap_pop_ignored
gdk_x11_display_set_cursor_theme
gdk_x11_display_set_window_scale
gdk_x11_display_get_glx_version
gdk_x11_register_standard_event_type
gdk_x11_screen_get_screen_number
gdk_x11_screen_get_xscreen
@@ -1123,6 +1126,11 @@ GDK_X11_KEYMAP_CLASS
GDK_IS_X11_KEYMAP
GDK_IS_X11_KEYMAP_CLASS
GDK_X11_KEYMAP_GET_CLASS
GDK_TYPE_X11_GL_CONTEXT
GDK_X11_GL_CONTEXT
GDK_X11_GL_CONTEXT_CLASS
GDK_IS_X11_GL_CONTEXT
GDK_IS_X11_GL_CONTEXT_CLASS
GDK_TYPE_X11_SCREEN
GDK_X11_SCREEN
GDK_X11_SCREEN_CLASS
@@ -1273,3 +1281,50 @@ gdk_frame_timings_get_predicted_presentation_time
<SUBSECTION Private>
gdk_frame_get_type
</SECTION>
<SECTION>
<FILE>gdkglcontext</FILE>
GdkGLContext
gdk_gl_context_get_display
gdk_gl_context_get_pixel_format
gdk_gl_context_get_visual
gdk_gl_context_set_window
gdk_gl_context_get_window
gdk_gl_context_make_current
gdk_gl_context_flush_buffer
gdk_gl_context_update
gdk_gl_context_clear_current
gdk_gl_context_get_current
<SUBSECTION Standard>
GDK_GL_CONTEXT
GDK_IS_GL_CONTEXT
GDK_TYPE_GL_CONTEXT
gdk_gl_context_get_type
</SECTION>
<SECTION>
<FILE>gdkglpixelformat</FILE>
GdkGLPixelFormat
GdkGLPixelFormatProfile
gdk_gl_pixel_format_new
<SUBSECTION>
gdk_gl_pixel_format_get_double_buffer
gdk_gl_pixel_format_get_multi_sample
gdk_gl_pixel_format_get_color_size
gdk_gl_pixel_format_get_alpha_size
gdk_gl_pixel_format_get_depth_size
gdk_gl_pixel_format_get_stencil_size
gdk_gl_pixel_format_get_accum_size
gdk_gl_pixel_format_get_aux_buffers
gdk_gl_pixel_format_get_sample_buffers
gdk_gl_pixel_format_get_samples
<SUBSECTION>
GDK_GL_PIXEL_FORMAT_ERROR
GdkGLPixelFormatError
<SUBSECTION Standard>
GDK_GL_PIXEL_FORMAT
GDK_IS_GL_PIXEL_FORMAT
GDK_TYPE_GL_PIXEL_FORMAT
gdk_gl_pixel_format_error_quark
gdk_gl_pixel_format_get_type
</SECTION>

View File

@@ -8,6 +8,8 @@ gdk_display_get_type
gdk_display_manager_get_type
gdk_drag_context_get_type
gdk_frame_clock_get_type
gdk_gl_context_get_type
gdk_gl_pixel_format_get_type
gdk_keymap_get_type
gdk_screen_get_type
gdk_visual_get_type

View File

@@ -243,6 +243,7 @@
<xi:include href="xml/gtkadjustment.xml" />
<xi:include href="xml/gtkcalendar.xml" />
<xi:include href="xml/gtkdrawingarea.xml" />
<xi:include href="xml/gtkglarea.xml" />
<xi:include href="xml/gtkeventbox.xml" />
<xi:include href="xml/gtkhandlebox.xml" />
<xi:include href="xml/gtkimcontextsimple.xml" />

View File

@@ -8153,3 +8153,22 @@ GTK_GESTURE_ZOOM_GET_CLASS
<SUBSECTION Private>
gtk_gesture_zoom_get_type
</SECTION>
<SECTION>
<FILE>gtkglarea</FILE>
GtkGLArea
GtkGLAreaClass
gtk_gl_area_new
gtk_gl_area_get_context
<SUBSECTION>
gtk_gl_area_make_current
gtk_gl_area_flush_buffer
<SUBSECTION Standard>
GTK_TYPE_GL_AREA
GTK_GL_AREA
GTK_GL_AREA_CLASS
GTK_IS_GL_AREA
GTK_IS_GL_AREA_CLASS
<SUBSECTION Private>
gtk_gl_area_get_type
</SECTION>

View File

@@ -75,6 +75,8 @@ gdk_public_h_sources = \
gdkdnd.h \
gdkevents.h \
gdkframetimings.h \
gdkglcontext.h \
gdkglpixelformat.h \
gdkkeys.h \
gdkkeysyms.h \
gdkkeysyms-compat.h \
@@ -111,6 +113,8 @@ gdk_private_headers = \
gdkdndprivate.h \
gdkframeclockidle.h \
gdkframeclockprivate.h \
gdkglcontextprivate.h \
gdkglpixelformatprivate.h \
gdkscreenprivate.h \
gdkinternals.h \
gdkintl.h \
@@ -135,6 +139,8 @@ gdk_c_sources = \
gdkdnd.c \
gdkevents.c \
gdkframetimings.c \
gdkglcontext.c \
gdkglpixelformat.c \
gdkglobals.c \
gdkkeys.c \
gdkkeyuni.c \

View File

@@ -148,7 +148,8 @@ static const GDebugKey gdk_debug_keys[] = {
{"draw", GDK_DEBUG_DRAW},
{"eventloop", GDK_DEBUG_EVENTLOOP},
{"frames", GDK_DEBUG_FRAMES},
{"settings", GDK_DEBUG_SETTINGS}
{"settings", GDK_DEBUG_SETTINGS},
{"opengl", GDK_DEBUG_OPENGL},
};
static gboolean

View File

@@ -41,6 +41,8 @@
#include <gdk/gdkevents.h>
#include <gdk/gdkframeclock.h>
#include <gdk/gdkframetimings.h>
#include <gdk/gdkglpixelformat.h>
#include <gdk/gdkglcontext.h>
#include <gdk/gdkkeys.h>
#include <gdk/gdkkeysyms.h>
#include <gdk/gdkmain.h>

View File

@@ -2225,3 +2225,163 @@ gdk_error_trap_pop (void)
{
return gdk_error_trap_pop_internal (TRUE);
}
/*< private >
* gdk_display_destroy_gl_context:
* @display: a #GdkDisplay
* @context: a #GdkGLContext
*
* Destroys the platform-specific parts of the @context.
*
* The @context instance is still valid, though inert, after
* this functionr returns.
*/
void
gdk_display_destroy_gl_context (GdkDisplay *display,
GdkGLContext *context)
{
GDK_DISPLAY_GET_CLASS (display)->destroy_gl_context (display, context);
}
/*< private >
* gdk_display_make_gl_context_current:
* @display: a #GdkDisplay
* @context: (optional): a #GdkGLContext, or %NULL
* @window: (optional): a #GdkWindow, or %NULL
*
* Makes the given @context the current GL context, or unsets
* the current GL context if @context is %NULL.
*
* Returns: %TRUE if successful
*/
gboolean
gdk_display_make_gl_context_current (GdkDisplay *display,
GdkGLContext *context,
GdkWindow *window)
{
GdkGLContext *current = gdk_display_get_current_gl_context (display);
if (current == context)
return TRUE;
if (context == NULL)
g_object_set_data (G_OBJECT (display), "-gdk-gl-current-context", NULL);
else
g_object_set_data_full (G_OBJECT (display), "-gdk-gl-current-context",
g_object_ref (context),
(GDestroyNotify) g_object_unref);
return GDK_DISPLAY_GET_CLASS (display)->make_gl_context_current (display, context, window);
}
/*< private >
* gdk_display_get_current_gl_context:
* @display: a #GdkDisplay
*
* Retrieves the current #GdkGLContext associated with @display.
*
* Returns: (transfer none): the current #GdkGLContext or %NULL
*/
GdkGLContext *
gdk_display_get_current_gl_context (GdkDisplay *display)
{
return g_object_get_data (G_OBJECT (display), "-gdk-gl-current-context");
}
/**
* gdk_display_validate_gl_pixel_format:
* @display: a #GdkDisplay
* @format: a #GdkGLPixelFormat
* @validated_format: (out callee-allocates) (transfer full) (optional): return location for
* the validated #GdkGLPixelFormat
* @error: return location for a #GError
*
* Validates a #GdkGLPixelFormat for the given display.
*
* If the pixel format is valid, and @validated_format is not %NULL, the
* validated pixel format will be stored into @validated_format.
*
* If the pixel format is invalid, @error will be set.
*
* Returns: %TRUE if the pixel format is valid
*
* Since: 3.14
*/
gboolean
gdk_display_validate_gl_pixel_format (GdkDisplay *display,
GdkGLPixelFormat *format,
GdkGLPixelFormat **validated_format,
GError **error)
{
g_return_val_if_fail (GDK_IS_DISPLAY (display), FALSE);
g_return_val_if_fail (GDK_IS_GL_PIXEL_FORMAT (format), FALSE);
g_return_val_if_fail (validated_format == NULL || *validated_format == NULL, FALSE);
return GDK_DISPLAY_GET_CLASS (display)->validate_gl_pixel_format (display,
format, validated_format,
error);
}
/**
* gdk_display_create_gl_context:
* @display: a #GdkDisplay
* @format: a #GdkGLPixelFormat
* @error: return location for a #GError
*
* Creates a new #GdkGLContext for the given display, with the given
* pixel format.
*
* If the @format is invalid, or in case the creation of the #GdkGLContext
* failed, @error will be set.
*
* Returns: (transfer full): the newly created #GdkGLContext, or
* %NULL on error
*
* Since: 3.14
*/
GdkGLContext *
gdk_display_create_gl_context (GdkDisplay *display,
GdkGLPixelFormat *format,
GError **error)
{
g_return_val_if_fail (GDK_IS_DISPLAY (display), NULL);
g_return_val_if_fail (GDK_IS_GL_PIXEL_FORMAT (format), NULL);
g_return_val_if_fail (error == NULL || *error == NULL, NULL);
return GDK_DISPLAY_GET_CLASS (display)->create_gl_context (display, format, NULL, error);
}
/**
* gdk_display_create_shared_gl_context:
* @display: a #GdkDisplay
* @format: a #GdkGLPixelFormat
* @shared_context: shared #GdkGLContext
* @error: return location for a #GError
*
* Creates a new #GdkGLContext for the given display, with the given
* pixel format; the newly created #GdkGLContext will share resources
* like the texture namespace and display lists with @shared_context.
*
* If the @format is invalid, or in case the creation of the #GdkGLContext
* failed, @error will be set.
*
* Returns: (transfer full): the newly created #GdkGLContext, or
* %NULL on error
*
* Since: 3.14
*/
GdkGLContext *
gdk_display_create_shared_gl_context (GdkDisplay *display,
GdkGLPixelFormat *format,
GdkGLContext *shared_context,
GError **error)
{
g_return_val_if_fail (GDK_IS_DISPLAY (display), NULL);
g_return_val_if_fail (GDK_IS_GL_PIXEL_FORMAT (format), NULL);
g_return_val_if_fail (GDK_IS_GL_CONTEXT (shared_context), NULL);
g_return_val_if_fail (error == NULL || *error == NULL, NULL);
return GDK_DISPLAY_GET_CLASS (display)->create_gl_context (display, format,
shared_context,
error);
}

View File

@@ -171,6 +171,21 @@ GdkDeviceManager * gdk_display_get_device_manager (GdkDisplay *display);
GDK_AVAILABLE_IN_ALL
GdkAppLaunchContext *gdk_display_get_app_launch_context (GdkDisplay *display);
GDK_AVAILABLE_IN_3_14
gboolean gdk_display_validate_gl_pixel_format (GdkDisplay *display,
GdkGLPixelFormat *format,
GdkGLPixelFormat **validated_format,
GError **error);
GDK_AVAILABLE_IN_3_14
GdkGLContext * gdk_display_create_gl_context (GdkDisplay *display,
GdkGLPixelFormat *format,
GError **error);
GDK_AVAILABLE_IN_3_14
GdkGLContext * gdk_display_create_shared_gl_context (GdkDisplay *display,
GdkGLPixelFormat *format,
GdkGLContext *shared_context,
GError **error);
G_END_DECLS
#endif /* __GDK_DISPLAY_H__ */

View File

@@ -225,6 +225,20 @@ struct _GdkDisplayClass
gchar * (*utf8_to_string_target) (GdkDisplay *display,
const gchar *text);
GdkGLContext * (*create_gl_context) (GdkDisplay *display,
GdkGLPixelFormat *format,
GdkGLContext *share,
GError **error);
gboolean (*make_gl_context_current) (GdkDisplay *display,
GdkGLContext *context,
GdkWindow *drawable);
void (*destroy_gl_context) (GdkDisplay *display,
GdkGLContext *context);
gboolean (*validate_gl_pixel_format) (GdkDisplay *display,
GdkGLPixelFormat *format,
GdkGLPixelFormat **valid_format,
GError **error);
/* Signals */
void (*opened) (GdkDisplay *display);
void (*closed) (GdkDisplay *display,
@@ -303,6 +317,17 @@ void _gdk_display_create_window_impl (GdkDisplay *display
gint attributes_mask);
GdkWindow * _gdk_display_create_window (GdkDisplay *display);
gboolean gdk_display_validate_gl_pixel_format (GdkDisplay *display,
GdkGLPixelFormat *format,
GdkGLPixelFormat **validated_format,
GError **error);
void gdk_display_destroy_gl_context (GdkDisplay *display,
GdkGLContext *context);
gboolean gdk_display_make_gl_context_current (GdkDisplay *display,
GdkGLContext *context,
GdkWindow *window);
GdkGLContext * gdk_display_get_current_gl_context (GdkDisplay *display);
G_END_DECLS
#endif /* __GDK_DISPLAY_PRIVATE_H__ */

648
gdk/gdkglcontext.c Normal file
View File

@@ -0,0 +1,648 @@
/* GDK - The GIMP Drawing Kit
*
* gdkglcontext.c: GL context abstraction
*
* Copyright © 2014 Emmanuele Bassi
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
*/
/**
* SECTION:gdkglcontext
* @Title: GdkGLContext
* @Short_description: OpenGL context
*
* #GdkGLContext is an object representing the platform-specific
* OpenGL drawing context.
*
* #GdkGLContexts are created via a #GdkDisplay by specifying a
* #GdkGLPixelFormat to be used by the OpenGL context.
*
* Support for #GdkGLContext is platform specific; in order to
* discover if the platform supports OpenGL, you should use the
* #GdkGLPixelFormat class.
*
* A #GdkGLContext has to be associated with a #GdkWindow and
* made "current", otherwise any OpenGL call will be ignored.
*
* ## Creating a new OpenGL context ##
*
* In order to create a new #GdkGLContext instance you need a
* #GdkGLPixelFormat instance and a #GdkDisplay.
*
* The #GdkGLPixelFormat class contains configuration option that
* you require from the windowing system to be available on the
* #GdkGLContext.
*
* |[<!-- language="C" -->
* GdkGLPixelFormat *format;
*
* format = gdk_gl_pixel_format_new ("double-buffer", TRUE,
* "depth-size", 32,
* NULL);
* ]|
*
* The example above will create a pixel format with double buffering
* and a depth buffer size of 32 bits.
*
* You can either choose to validate the pixel format, in case you
* have the ability to change your drawing code depending on it, or
* just ask the #GdkDisplay to create the #GdkGLContext with it, which
* will implicitly validate the pixel format and return an error if it
* could not find an OpenGL context that satisfied the requirements
* of the pixel format:
*
* |[<!-- language="C" -->
* GError *error = NULL;
*
* // the "display" variable has been set elsewhere
* GdkGLContext *context =
* gdk_display_create_gl_context (display, format, &error);
*
* if (error != NULL)
* {
* // handle error condition
* }
*
* // you can release the reference on the pixel format at
* // this point
* g_object_unref (format);
* ]|
*
* ## Using a GdkGLContext ##
*
* In order to use a #GdkGLContext to draw with OpenGL commands
* on a #GdkWindow, it's necessary to bind the context to the
* window:
*
* |[<!-- language="C" -->
* // associates the window to the context
* gdk_gl_context_set_window (context, window);
* ]|
*
* This ensures that the #GdkGLContext can refer to the #GdkWindow,
* as well as the #GdkWindow can present the result of the OpenGL
* commands.
*
* You will also need to make the #GdkGLContext the current context
* before issuing OpenGL calls; the system sends OpenGL commands to
* whichever context is current. It is possible to have multiple
* contexts, so you always need to ensure that the one which you
* want to draw with is the current one before issuing commands:
*
* |[<!-- language="C" -->
* gdk_gl_context_make_current (context);
* ]|
*
* You can now perform your drawing using OpenGL commands.
*
* Once you finished drawing your frame, and you want to present the
* result on the window bound to the #GdkGLContext, you should call
* gdk_gl_context_flush_buffer().
*
* If the #GdkWindow bound to the #GdkGLContext changes size, you
* will need to call gdk_gl_context_update() to ensure that the OpenGL
* viewport is kept in sync with the size of the window.
*
* You can detach the currently bound #GdkWindow from a #GdkGLContext
* by using gdk_gl_context_set_window() with a %NULL argument.
*
* You can check which #GdkGLContext is the current one by using
* gdk_gl_context_get_current(); you can also unset any #GdkGLContext
* that is currently set by calling gdk_gl_context_clear_current().
*/
#include "config.h"
#include "gdkglcontextprivate.h"
#include "gdkdisplayprivate.h"
#include "gdkglpixelformat.h"
#include "gdkvisual.h"
#include "gdkinternals.h"
#include "gdkintl.h"
typedef struct {
GdkDisplay *display;
GdkGLPixelFormat *pixel_format;
GdkWindow *window;
GdkVisual *visual;
gboolean swap_interval;
} GdkGLContextPrivate;
enum {
PROP_0,
PROP_DISPLAY,
PROP_PIXEL_FORMAT,
PROP_WINDOW,
PROP_VISUAL,
PROP_SWAP_INTERVAL,
LAST_PROP
};
static GParamSpec *obj_pspecs[LAST_PROP] = { NULL, };
G_DEFINE_ABSTRACT_TYPE_WITH_PRIVATE (GdkGLContext, gdk_gl_context, G_TYPE_OBJECT)
static void
gdk_gl_context_dispose (GObject *gobject)
{
GdkGLContext *context = GDK_GL_CONTEXT (gobject);
GdkGLContextPrivate *priv = gdk_gl_context_get_instance_private (context);
gdk_display_destroy_gl_context (priv->display, context);
g_clear_object (&priv->display);
g_clear_object (&priv->pixel_format);
g_clear_object (&priv->window);
g_clear_object (&priv->visual);
G_OBJECT_CLASS (gdk_gl_context_parent_class)->dispose (gobject);
}
static void
gdk_gl_context_set_property (GObject *gobject,
guint prop_id,
const GValue *value,
GParamSpec *pspec)
{
GdkGLContextPrivate *priv = gdk_gl_context_get_instance_private ((GdkGLContext *) gobject);
switch (prop_id)
{
case PROP_DISPLAY:
priv->display = g_object_ref (g_value_get_object (value));
break;
case PROP_PIXEL_FORMAT:
priv->pixel_format = g_object_ref (g_value_get_object (value));
break;
case PROP_WINDOW:
{
GdkGLContext *context = GDK_GL_CONTEXT (gobject);
GdkWindow *window = g_value_get_object (value);
gdk_gl_context_set_window (context, window);
}
break;
case PROP_VISUAL:
{
GdkVisual *visual = g_value_get_object (value);
if (visual != NULL)
priv->visual = g_object_ref (visual);
}
break;
case PROP_SWAP_INTERVAL:
priv->swap_interval = g_value_get_boolean (value);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
}
}
static void
gdk_gl_context_get_property (GObject *gobject,
guint prop_id,
GValue *value,
GParamSpec *pspec)
{
GdkGLContextPrivate *priv = gdk_gl_context_get_instance_private ((GdkGLContext *) gobject);
switch (prop_id)
{
case PROP_DISPLAY:
g_value_set_object (value, priv->display);
break;
case PROP_PIXEL_FORMAT:
g_value_set_object (value, priv->pixel_format);
break;
case PROP_WINDOW:
g_value_set_object (value, priv->window);
break;
case PROP_VISUAL:
g_value_set_object (value, priv->visual);
break;
case PROP_SWAP_INTERVAL:
g_value_set_boolean (value, priv->swap_interval);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
}
}
static void
gdk_gl_context_class_init (GdkGLContextClass *klass)
{
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
/**
* GdkGLContext:display:
*
* The #GdkDisplay used by the context.
*
* Since: 3.14
*/
obj_pspecs[PROP_DISPLAY] =
g_param_spec_object ("display",
"Display",
"The GDK display used by the GL context",
GDK_TYPE_DISPLAY,
G_PARAM_READWRITE |
G_PARAM_CONSTRUCT_ONLY |
G_PARAM_STATIC_STRINGS);
/**
* GdkGLContext:pixel-format:
*
* The #GdkGLPixelFormat used to create the context.
*
* Since: 3.14
*/
obj_pspecs[PROP_PIXEL_FORMAT] =
g_param_spec_object ("pixel-format",
"Pixel Format",
"The GDK pixel format used by the GL context",
GDK_TYPE_GL_PIXEL_FORMAT,
G_PARAM_READWRITE |
G_PARAM_CONSTRUCT_ONLY |
G_PARAM_STATIC_STRINGS);
/**
* GdkGLContext:window:
*
* The #GdkWindow currently bound to the context.
*
* You typically need to bind a #GdkWindow to a #GdkGLContext prior
* to calling gdk_gl_context_make_current().
*
* Since: 3.14
*/
obj_pspecs[PROP_WINDOW] =
g_param_spec_object ("window",
P_("Window"),
P_("The GDK window currently bound to the GL context"),
GDK_TYPE_WINDOW,
G_PARAM_READWRITE |
G_PARAM_STATIC_STRINGS);
/**
* GdkGLContext:visual:
*
* The #GdkVisual matching the #GdkGLPixelFormat used by the context.
*
* Since: 3.14
*/
obj_pspecs[PROP_VISUAL] =
g_param_spec_object ("visual",
P_("Visual"),
P_("The GDK visual used by the GL context"),
GDK_TYPE_VISUAL,
G_PARAM_READWRITE |
G_PARAM_CONSTRUCT_ONLY |
G_PARAM_STATIC_STRINGS);
/**
* GdkGLContext:swap-interval:
*
* The swap interval of the context.
*
* If set to %TRUE (the default), buffers will be flushed only during
* the vertical refresh of the display.
*
* If set to %FALSE, gdk_gl_context_flush_buffer() will execute
* the buffer flush as soon as possible.
*
* Since: 3.14
*/
obj_pspecs[PROP_SWAP_INTERVAL] =
g_param_spec_boolean ("swap-interval",
P_("Swap Interval"),
P_("The swap interval of the GL context"),
TRUE,
G_PARAM_READWRITE |
G_PARAM_STATIC_STRINGS);
gobject_class->set_property = gdk_gl_context_set_property;
gobject_class->get_property = gdk_gl_context_get_property;
gobject_class->dispose = gdk_gl_context_dispose;
g_object_class_install_properties (gobject_class, LAST_PROP, obj_pspecs);
}
static void
gdk_gl_context_init (GdkGLContext *self)
{
GdkGLContextPrivate *priv = gdk_gl_context_get_instance_private (self);
priv->swap_interval = TRUE;
}
/**
* gdk_gl_context_get_display:
* @context: a #GdkGLContext
*
* Retrieves the #GdkDisplay associated with the @context.
*
* Returns: (transfer none): the #GdkDisplay
*
* Since: 3.14
*/
GdkDisplay *
gdk_gl_context_get_display (GdkGLContext *context)
{
GdkGLContextPrivate *priv = gdk_gl_context_get_instance_private (context);
g_return_val_if_fail (GDK_IS_GL_CONTEXT (context), NULL);
return priv->display;
}
/**
* gdk_gl_context_get_pixel_format:
* @context: a #GdkGLContext
*
* Retrieves the #GdkGLPixelFormat associated with the @context.
*
* Returns: (transfer none): the #GdkDisplay
*
* Since: 3.14
*/
GdkGLPixelFormat *
gdk_gl_context_get_pixel_format (GdkGLContext *context)
{
GdkGLContextPrivate *priv = gdk_gl_context_get_instance_private (context);
g_return_val_if_fail (GDK_IS_GL_CONTEXT (context), NULL);
return priv->pixel_format;
}
/**
* gdk_gl_context_get_visual:
* @context: a #GdkGLContext
*
* Retrieves the #GdkVisual associated with the @context.
*
* Returns: (transfer none): the #GdkVisual
*
* Since: 3.14
*/
GdkVisual *
gdk_gl_context_get_visual (GdkGLContext *context)
{
GdkGLContextPrivate *priv = gdk_gl_context_get_instance_private (context);
g_return_val_if_fail (GDK_IS_GL_CONTEXT (context), NULL);
return priv->visual;
}
/**
* gdk_gl_context_flush_buffer:
* @context: a #GdkGLContext
*
* Copies the back buffer to the front buffer.
*
* If the #GdkGLContext is not double buffered, this function does not
* do anything.
*
* Depending on the value of the #GdkGLContext:swap-interval property,
* the copy may take place during the vertical refresh of the display
* rather than immediately.
*
* This function may call `glFlush()` implicitly before returning; it
* is not recommended to call `glFlush()` explicitly before calling
* this function.
*
* Since: 3.14
*/
void
gdk_gl_context_flush_buffer (GdkGLContext *context)
{
g_return_if_fail (GDK_IS_GL_CONTEXT (context));
GDK_GL_CONTEXT_GET_CLASS (context)->flush_buffer (context);
}
/**
* gdk_gl_context_make_current:
* @context: a #GdkGLContext
*
* Makes the @context the current one.
*
* Returns: %TRUE if the context is current
*
* Since: 3.14
*/
gboolean
gdk_gl_context_make_current (GdkGLContext *context)
{
GdkGLContextPrivate *priv = gdk_gl_context_get_instance_private (context);
g_return_val_if_fail (GDK_IS_GL_CONTEXT (context), FALSE);
return gdk_display_make_gl_context_current (priv->display, context, priv->window);
}
/**
* gdk_gl_context_set_window:
* @context: a #GdkGLContext
* @window: (optional): a #GdkWindow, or %NULL
*
* Sets the #GdkWindow used to display the draw commands.
*
* If @window is %NULL, the @context is detached from the window.
*
* Since: 3.14
*/
void
gdk_gl_context_set_window (GdkGLContext *context,
GdkWindow *window)
{
GdkGLContextPrivate *priv = gdk_gl_context_get_instance_private (context);
g_return_if_fail (GDK_IS_GL_CONTEXT (context));
g_return_if_fail (window == NULL || (GDK_IS_WINDOW (window) && !GDK_WINDOW_DESTROYED (window)));
if (priv->window == window)
return;
if (priv->window != NULL)
gdk_window_set_gl_context (priv->window, NULL);
g_clear_object (&priv->window);
if (window != NULL)
{
priv->window = g_object_ref (window);
gdk_window_set_gl_context (window, context);
}
GDK_GL_CONTEXT_GET_CLASS (context)->set_window (context, window);
}
/**
* gdk_gl_context_get_window:
* @context: a #GdkGLContext
*
* Retrieves the #GdkWindow used by the @context.
*
* Returns: (transfer none): a #GdkWindow or %NULL
*
* Since: 3.14
*/
GdkWindow *
gdk_gl_context_get_window (GdkGLContext *context)
{
GdkGLContextPrivate *priv = gdk_gl_context_get_instance_private (context);
g_return_val_if_fail (GDK_IS_GL_CONTEXT (context), NULL);
return priv->window;
}
/**
* gdk_gl_context_update:
* @context: a #GdkGLContext
*
* Updates the @context when the #GdkWindow used to display the
* rendering changes size or position.
*
* Typically, you will call this function after calling
* gdk_window_resize() or gdk_window_move_resize().
*
* Since: 3.14
*/
void
gdk_gl_context_update (GdkGLContext *context)
{
GdkGLContextPrivate *priv = gdk_gl_context_get_instance_private (context);
g_return_if_fail (GDK_IS_GL_CONTEXT (context));
if (priv->window == NULL)
return;
GDK_GL_CONTEXT_GET_CLASS (context)->update (context, priv->window);
}
/**
* gdk_gl_context_clear_current:
*
* Clears the current #GdkGLContext.
*
* Any OpenGL call after this function returns will be ignored
* until gdk_gl_context_make_current() is called.
*
* Since: 3.14
*/
void
gdk_gl_context_clear_current (void)
{
GdkDisplay *display = gdk_display_get_default ();
gdk_display_make_gl_context_current (display, NULL, NULL);
}
/**
* gdk_gl_context_get_current:
*
* Retrieves the current #GdkGLContext.
*
* Returns: (transfer none): the current #GdkGLContext, or %NULL
*
* Since: 3.14
*/
GdkGLContext *
gdk_gl_context_get_current (void)
{
GdkDisplay *display = gdk_display_get_default ();
return gdk_display_get_current_gl_context (display);
}
/*< private >
* gdk_gl_context_get_swap_interval:
* @context: a #GdkGLContext
*
* Retrieves the swap interval of the context.
*
* Returns: the swap interval
*/
gboolean
gdk_gl_context_get_swap_interval (GdkGLContext *context)
{
GdkGLContextPrivate *priv = gdk_gl_context_get_instance_private (context);
return priv->swap_interval;
}
/*< private >
* gdk_window_has_gl_context:
* @window: a #GdkWindow
*
* Checks whether a #GdkWindow has a #GdkGLContext associated to it.
*
* Returns: %TRUE if the window has a GL context
*/
gboolean
gdk_window_has_gl_context (GdkWindow *window)
{
return g_object_get_data (G_OBJECT (window), "-gdk-gl-context") != NULL;
}
/*< private >
* gdk_window_set_gl_context:
* @window: a #GdkWindow
* @context: a #GdkGLContext
*
* Sets a back pointer to a #GdkGLContext on @window.
*
* This function should only be called by gdk_gl_context_set_window().
*/
void
gdk_window_set_gl_context (GdkWindow *window,
GdkGLContext *context)
{
g_object_set_data (G_OBJECT (window), "-gdk-gl-context", context);
}
/*< private >
* gdk_window_get_gl_context:
* @window: a #GdkWindow
*
* Retrieves a pointer to the #GdkGLContext associated to
* the @window.
*
* Returns: (transfer none): a #GdkGLContext, or %NULL
*/
GdkGLContext *
gdk_window_get_gl_context (GdkWindow *window)
{
return g_object_get_data (G_OBJECT (window), "-gdk-gl-context");
}

66
gdk/gdkglcontext.h Normal file
View File

@@ -0,0 +1,66 @@
/* GDK - The GIMP Drawing Kit
*
* gdkglcontext.h: GL context abstraction
*
* Copyright © 2014 Emmanuele Bassi
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef __GDK_GL_CONTEXT_H__
#define __GDK_GL_CONTEXT_H__
#if !defined (__GDK_H_INSIDE__) && !defined (GDK_COMPILATION)
#error "Only <gdk/gdk.h> can be included directly."
#endif
#include <gdk/gdkversionmacros.h>
#include <gdk/gdktypes.h>
G_BEGIN_DECLS
#define GDK_TYPE_GL_CONTEXT (gdk_gl_context_get_type ())
#define GDK_GL_CONTEXT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GDK_TYPE_GL_CONTEXT, GdkGLContext))
#define GDK_IS_GL_CONTEXT(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GDK_TYPE_GL_CONTEXT))
GDK_AVAILABLE_IN_3_14
GType gdk_gl_context_get_type (void) G_GNUC_CONST;
GDK_AVAILABLE_IN_3_14
GdkDisplay * gdk_gl_context_get_display (GdkGLContext *context);
GDK_AVAILABLE_IN_3_14
GdkGLPixelFormat * gdk_gl_context_get_pixel_format (GdkGLContext *context);
GDK_AVAILABLE_IN_3_14
GdkVisual * gdk_gl_context_get_visual (GdkGLContext *context);
GDK_AVAILABLE_IN_3_14
void gdk_gl_context_flush_buffer (GdkGLContext *context);
GDK_AVAILABLE_IN_3_14
gboolean gdk_gl_context_make_current (GdkGLContext *context);
GDK_AVAILABLE_IN_3_14
void gdk_gl_context_set_window (GdkGLContext *context,
GdkWindow *window);
GDK_AVAILABLE_IN_3_14
GdkWindow * gdk_gl_context_get_window (GdkGLContext *context);
GDK_AVAILABLE_IN_3_14
void gdk_gl_context_update (GdkGLContext *context);
GDK_AVAILABLE_IN_3_14
void gdk_gl_context_clear_current (void);
GDK_AVAILABLE_IN_3_14
GdkGLContext * gdk_gl_context_get_current (void);
G_END_DECLS
#endif /* __GDK_GL_CONTEXT_H__ */

54
gdk/gdkglcontextprivate.h Normal file
View File

@@ -0,0 +1,54 @@
/* GDK - The GIMP Drawing Kit
*
* gdkglcontextprivate.h: GL context abstraction
*
* Copyright © 2014 Emmanuele Bassi
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef __GDK_GL_CONTEXT_PRIVATE_H__
#define __GDK_GL_CONTEXT_PRIVATE_H__
#include "gdkglcontext.h"
G_BEGIN_DECLS
#define GDK_GL_CONTEXT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GDK_TYPE_GL_CONTEXT, GdkGLContextClass))
#define GDK_IS_GL_CONTEXT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GDK_TYPE_GL_CONTEXT))
#define GDK_GL_CONTEXT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GDK_TYPE_GL_CONTEXT, GdkGLContextClass))
typedef struct _GdkGLContextClass GdkGLContextClass;
struct _GdkGLContext
{
GObject parent_instance;
};
struct _GdkGLContextClass
{
GObjectClass parent_class;
void (* set_window) (GdkGLContext *context,
GdkWindow *window);
void (* update) (GdkGLContext *context,
GdkWindow *window);
void (* flush_buffer) (GdkGLContext *context);
};
gboolean gdk_gl_context_get_swap_interval (GdkGLContext *context);
G_END_DECLS
#endif /* __GDK_GL_CONTEXT_PRIVATE_H__ */

664
gdk/gdkglpixelformat.c Normal file
View File

@@ -0,0 +1,664 @@
/* GDK - The GIMP Drawing Kit
*
* gdkglpixelformat.c: GL pixel formats
*
* Copyright © 2014 Emmanuele Bassi
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
*/
/**
* SECTION:gdkglpixelformat
* @Title: GdkGLPixelFormat
* @Short_description: Specify the pixel format for GL contexts
*
* The #GdkGLPixelFormat class is used to specify the types and sizes of
* buffers to be used by a #GdkGLContext, as well as other configuration
* parameters.
*
* Once created, a #GdkGLPixelFormat is immutable, and can only be used
* to create a #GdkGLContext, or for validation.
*
* Once a #GdkGLPixelFormat has been validated, either directly through
* gdk_display_validate_gl_pixel_format() or indirectly through the
* creation of a #GdkGLContext, it is possible to query its properties
* for the values found during validation.
*
* ## Using GdkGLPixelFormat ##
*
* Typically, you will create a new #GdkGLPixelFormat using the given
* constructor and properties:
*
* |[<!-- language="C" -->
* GdkGLPixelFormat *format;
*
* // Ask for a pixel format with double buffering and
* // a depth buffer with a size of 32 bits
* format = gdk_gl_pixel_format_new ("double-buffer", TRUE,
* "depth-size", 32,
* NULL);
* ]|
*
* After creating a pixel format, you can validate it using a #GdkDisplay
* and use the result of gdk_display_validate_gl_pixel_format() to provide
* an alternative pixel format:
*
* |[<!-- language="C" -->
* GError *error = NULL;
* GdkGLPixelFormat *valid = NULL;
*
* // The "display" variable is set elsewhere.
* // The "format" variable is the one we set previously.
* if (!gdk_display_validate_gl_pixel_format (display, format, &valid, &error))
* {
* // print "error" or create a new pixel format to validate
* }
* ]|
*
* You can also create a #GdkGLContext for the given display and
* pixel format:
*
* |[<!-- language="C" -->
* GdkGLContext *context;
* GError *error = NULL;
*
* context = gdk_display_create_gl_context (display, format, &error);
* if (error != NULL)
* {
* // print error
* }
*
* g_object_unref (format);
* ]|
*
* Once a #GdkGLContext has been created with a #GdkGLPixelFormat, the
* context will acquire a reference on the pixel format, so it's safe to
* release the reference created at construction.
*/
#include "config.h"
#include <gio/gio.h>
#include "gdkglpixelformatprivate.h"
#include "gdkdisplayprivate.h"
#include "gdkenumtypes.h"
#include "gdkintl.h"
enum {
PROP_0,
/* bool */
PROP_DOUBLE_BUFFER,
PROP_MULTI_SAMPLE,
PROP_STEREO,
/* uint */
PROP_AUX_BUFFERS,
PROP_COLOR_SIZE,
PROP_ALPHA_SIZE,
PROP_DEPTH_SIZE,
PROP_STENCIL_SIZE,
PROP_ACCUM_SIZE,
PROP_SAMPLE_BUFFERS,
PROP_SAMPLES,
/* enum */
PROP_PROFILE,
LAST_PROP
};
static GParamSpec *obj_props[LAST_PROP] = { NULL, };
G_DEFINE_QUARK (gdk-gl-pixel-format-error-quark, gdk_gl_pixel_format_error)
G_DEFINE_TYPE (GdkGLPixelFormat, gdk_gl_pixel_format, G_TYPE_OBJECT)
static void
gdk_gl_pixel_format_set_property (GObject *gobject,
guint prop_id,
const GValue *value,
GParamSpec *pspec)
{
GdkGLPixelFormat *self = GDK_GL_PIXEL_FORMAT (gobject);
switch (prop_id)
{
case PROP_DOUBLE_BUFFER:
self->double_buffer = g_value_get_boolean (value);
break;
case PROP_MULTI_SAMPLE:
self->multi_sample = g_value_get_boolean (value);
break;
case PROP_STEREO:
self->stereo = g_value_get_boolean (value);
break;
case PROP_AUX_BUFFERS:
self->aux_buffers = g_value_get_int (value);
break;
case PROP_COLOR_SIZE:
self->color_size = g_value_get_int (value);
break;
case PROP_ALPHA_SIZE:
self->alpha_size = g_value_get_int (value);
break;
case PROP_DEPTH_SIZE:
self->depth_size = g_value_get_int (value);
break;
case PROP_STENCIL_SIZE:
self->stencil_size = g_value_get_int (value);
break;
case PROP_ACCUM_SIZE:
self->accum_size = g_value_get_int (value);
break;
case PROP_SAMPLE_BUFFERS:
self->sample_buffers = g_value_get_int (value);
break;
case PROP_SAMPLES:
self->samples = g_value_get_int (value);
break;
case PROP_PROFILE:
self->profile = g_value_get_enum (value);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
}
}
static void
gdk_gl_pixel_format_get_property (GObject *gobject,
guint prop_id,
GValue *value,
GParamSpec *pspec)
{
GdkGLPixelFormat *self = GDK_GL_PIXEL_FORMAT (gobject);
switch (prop_id)
{
case PROP_DOUBLE_BUFFER:
g_value_set_boolean (value, self->double_buffer);
break;
case PROP_MULTI_SAMPLE:
g_value_set_boolean (value, self->multi_sample);
break;
case PROP_STEREO:
g_value_set_boolean (value, self->stereo);
break;
case PROP_AUX_BUFFERS:
g_value_set_int (value, self->aux_buffers);
break;
case PROP_COLOR_SIZE:
g_value_set_int (value, self->color_size);
break;
case PROP_ALPHA_SIZE:
g_value_set_int (value, self->alpha_size);
break;
case PROP_DEPTH_SIZE:
g_value_set_int (value, self->depth_size);
break;
case PROP_STENCIL_SIZE:
g_value_set_int (value, self->stencil_size);
break;
case PROP_ACCUM_SIZE:
g_value_set_int (value, self->accum_size);
break;
case PROP_SAMPLE_BUFFERS:
g_value_set_int (value, self->sample_buffers);
break;
case PROP_SAMPLES:
g_value_set_int (value, self->samples);
break;
case PROP_PROFILE:
g_value_set_enum (value, self->profile);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
}
}
static void
gdk_gl_pixel_format_class_init (GdkGLPixelFormatClass *klass)
{
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
gobject_class->set_property = gdk_gl_pixel_format_set_property;
gobject_class->get_property = gdk_gl_pixel_format_get_property;
/**
* GdkGLPixelFormat:double-buffer:
*
* Whether the pixel format should enable double buffering.
*
* Since: 3.14
*/
obj_props[PROP_DOUBLE_BUFFER] =
g_param_spec_boolean ("double-buffer",
P_("Double Buffer"),
P_("Whether the pixel format should ask for double buffering"),
FALSE,
G_PARAM_READWRITE |
G_PARAM_CONSTRUCT_ONLY |
G_PARAM_STATIC_STRINGS);
/**
* GdkGLPixelFormat:multi-sample:
*
* Whether the pixel format should enable multi-sampling.
*
* See also the #GdkGLPixelFormat:sample-buffers and
* #GdkGLPixelFormat:samples properties.
*
* Since: 3.14
*/
obj_props[PROP_MULTI_SAMPLE] =
g_param_spec_boolean ("multi-sample",
P_("Multi Sample"),
P_("Whether the pixel format should enable multi-sampling"),
FALSE,
G_PARAM_READWRITE |
G_PARAM_CONSTRUCT_ONLY |
G_PARAM_STATIC_STRINGS);
/**
* GdkGLPixelFormat:stereo:
*
* Whether the pixel format should support stereoscopic buffers.
*
* Since: 3.14
*/
obj_props[PROP_STEREO] =
g_param_spec_boolean ("stereo",
P_("Stereo"),
P_("Whether the pixel format should support stereoscopic buffers"),
FALSE,
G_PARAM_READWRITE |
G_PARAM_CONSTRUCT_ONLY |
G_PARAM_STATIC_STRINGS);
/**
* GdkGLPixelFormat:aux-buffers:
*
* A positive integer indicating the number of auxiliary buffers
* for the pixel format.
*
* If set to -1, the default will be used.
*
* Since: 3.14
*/
obj_props[PROP_AUX_BUFFERS] =
g_param_spec_int ("aux-buffers",
P_("Auxiliary Buffers"),
P_("The number of auxiliary buffers"),
-1, G_MAXINT, -1,
G_PARAM_READWRITE |
G_PARAM_CONSTRUCT_ONLY |
G_PARAM_STATIC_STRINGS);
/**
* GdkGLPixelFormat:color-size:
*
* A positive integer indicating the size of each color buffer.
*
* If set to -1, the default will be used.
*
* Since: 3.14
*/
obj_props[PROP_COLOR_SIZE] =
g_param_spec_int ("color-size",
P_("Color Size"),
P_("The size of each color buffer"),
-1, G_MAXINT, -1,
G_PARAM_READWRITE |
G_PARAM_CONSTRUCT_ONLY |
G_PARAM_STATIC_STRINGS);
/**
* GdkGLPixelFormat:alpha-size:
*
* A positive integer indicating the size of the alpha buffer.
*
* If set to 0, the alpha channel will be ignored.
*
* If set the -1, the default will be used.
*
* Since: 3.14
*/
obj_props[PROP_ALPHA_SIZE] =
g_param_spec_int ("alpha-size",
P_("Alpha Size"),
P_("The size of the alpha buffer"),
-1, G_MAXINT, -1,
G_PARAM_READWRITE |
G_PARAM_CONSTRUCT_ONLY |
G_PARAM_STATIC_STRINGS);
/**
* GdkGLPixelFormat:depth-size:
*
* A positive integer indicating the size of the depth buffer.
*
* If set to -1, the default will be used.
*
* Since: 3.14
*/
obj_props[PROP_DEPTH_SIZE] =
g_param_spec_int ("depth-size",
P_("Depth Size"),
P_("The size of the depth buffer"),
-1, G_MAXINT, -1,
G_PARAM_READWRITE |
G_PARAM_CONSTRUCT_ONLY |
G_PARAM_STATIC_STRINGS);
/**
* GdkGLPixelFormat:stencil-size:
*
* A positive integer indicating the size of the stencil buffer.
*
* If set to -1, the default will be used.
*
* Since: 3.14
*/
obj_props[PROP_STENCIL_SIZE] =
g_param_spec_int ("stencil-size",
P_("Stencil Size"),
P_("The size of the stencil buffer"),
-1, G_MAXINT, -1,
G_PARAM_READWRITE |
G_PARAM_CONSTRUCT_ONLY |
G_PARAM_STATIC_STRINGS);
/**
* GdkGLPixelFormat:accum-size:
*
* A positive integer indicating the size of the accumulation buffer.
*
* If set to -1, the default will be used.
*
* Since: 3.14
*/
obj_props[PROP_ACCUM_SIZE] =
g_param_spec_int ("accum-size",
P_("Accumulation Size"),
P_("The size of the accumulation buffer"),
-1, G_MAXINT, -1,
G_PARAM_READWRITE |
G_PARAM_CONSTRUCT_ONLY |
G_PARAM_STATIC_STRINGS);
/**
* GdkGLPixelFormat:sample-buffers:
*
* A positive integer indicating the number of multi-sample buffers.
*
* If set to -1, the default will be used.
*
* This property is only used if #GdkGLPixelFormat:multi-sample is set
* to %TRUE.
*
* Since: 3.14
*/
obj_props[PROP_SAMPLE_BUFFERS] =
g_param_spec_int ("sample-buffers",
P_("Sample Buffers"),
P_("The number of multi-sample buffers"),
-1, G_MAXINT, -1,
G_PARAM_READWRITE |
G_PARAM_CONSTRUCT_ONLY |
G_PARAM_STATIC_STRINGS);
/**
* GdkGLPixelFormat:samples:
*
* A positive integer indicating the number of samples for each
* multi-sample buffer.
*
* If set to -1, the default will be used.
*
* This property is only used if #GdkGLPixelFormat:multi-sample is set
* to %TRUE.
*
* Since: 3.14
*/
obj_props[PROP_SAMPLES] =
g_param_spec_int ("samples",
P_("Samples"),
P_(""),
-1, G_MAXINT, -1,
G_PARAM_READWRITE |
G_PARAM_CONSTRUCT_ONLY |
G_PARAM_STATIC_STRINGS);
/**
* GdkGLPixelFormat:profile:
*
* The GL profile to be used when creating a #GdkGLContext.
*
* Since: 3.14
*/
obj_props[PROP_PROFILE] =
g_param_spec_enum ("profile",
P_("Profile"),
P_(""),
GDK_TYPE_GL_PIXEL_FORMAT_PROFILE,
GDK_GL_PIXEL_FORMAT_PROFILE_DEFAULT,
G_PARAM_READWRITE |
G_PARAM_CONSTRUCT_ONLY |
G_PARAM_STATIC_STRINGS);
g_object_class_install_properties (gobject_class, LAST_PROP, obj_props);
}
static void
gdk_gl_pixel_format_init (GdkGLPixelFormat *self)
{
}
/**
* gdk_gl_pixel_format_new:
* @first_property: the first property to set
* @...: the value of the @first_property, followed by a %NULL terminated
* set of property, value pairs
*
* Creates a new #GdkGLPixelFormat with the given list of properties.
*
* Returns: the newly created #GdkGLPixelFormat
*
* Since: 3.14
*/
GdkGLPixelFormat *
gdk_gl_pixel_format_new (const char *first_property,
...)
{
GdkGLPixelFormat *res;
va_list args;
va_start (args, first_property);
res = (GdkGLPixelFormat *) g_object_new_valist (GDK_TYPE_GL_PIXEL_FORMAT, first_property, args);
va_end (args);
return res;
}
#define GDK_GL_PIXEL_FORMAT_GET(CType,FieldName,DefaultValue) \
CType \
gdk_gl_pixel_format_get_ ## FieldName (GdkGLPixelFormat *format) \
{ \
g_return_val_if_fail (GDK_IS_GL_PIXEL_FORMAT (format), DefaultValue); \
\
return format->FieldName; \
}
/**
* gdk_gl_pixel_format_get_double_buffer:
* @format: a #GdkGLPixelFormat
*
* Retrieves the value of the #GdkGLPixelFormat:double-buffer property.
*
* Returns: %TRUE if the pixel format is double buffered
*
* Since: 3.14
*/
GDK_GL_PIXEL_FORMAT_GET (gboolean, double_buffer, FALSE)
/**
* gdk_gl_pixel_format_get_multi_sample:
* @format: a #GdkGLPixelFormat
*
* Retrieves the value of the #GdkGLPixelFormat:multi-sample property.
*
* Returns: %TRUE if the pixel format supports multi-sampling
*
* Since: 3.14
*/
GDK_GL_PIXEL_FORMAT_GET (gboolean, multi_sample, FALSE)
/**
* gdk_gl_pixel_format_get_stereo:
* @format: a #GdkGLPixelFormat
*
* Retrieves the value of the #GdkGLPixelFormat:stereo property.
*
* Returns: %TRUE if the pixel format supports stereoscopic buffers
*
* Since: 3.14
*/
GDK_GL_PIXEL_FORMAT_GET (gboolean, stereo, FALSE)
/**
* gdk_gl_pixel_format_get_color_size:
* @format: a #GdkGLPixelFormat
*
* Retrieves the value of the #GdkGLPixelFormat:color-size property.
*
* Returns: the size of the RGB components of the color buffer
*
* Since: 3.14
*/
GDK_GL_PIXEL_FORMAT_GET (gint, color_size, 0)
/**
* gdk_gl_pixel_format_get_alpha_size:
* @format: a #GdkGLPixelFormat
*
* Retrieves the value of the #GdkGLPixelFormat:alpha-size property.
*
* Returns: the size of the alpha component of the color buffer
*
* Since: 3.14
*/
GDK_GL_PIXEL_FORMAT_GET (gint, alpha_size, 0)
/**
* gdk_gl_pixel_format_get_depth_size:
* @format: a #GdkGLPixelFormat
*
* Retrieves the value of the #GdkGLPixelFormat:depth-size property.
*
* Returns: the size of the depth buffer
*
* Since: 3.14
*/
GDK_GL_PIXEL_FORMAT_GET (gint, depth_size, 0)
/**
* gdk_gl_pixel_format_get_stencil_size:
* @format: a #GdkGLPixelFormat
*
* Retrieves the value of the #GdkGLPixelFormat:stencil-size property.
*
* Returns: the size of the stencil buffer
*
* Since: 3.14
*/
GDK_GL_PIXEL_FORMAT_GET (gint, stencil_size, 0)
/**
* gdk_gl_pixel_format_get_aux_buffers:
* @format: a #GdkGLPixelFormat
*
* Retrieves the value of the #GdkGLPixelFormat:aux-buffers property.
*
* Returns: the number of auxiliary buffers.
*
* Since: 3.14
*/
GDK_GL_PIXEL_FORMAT_GET (gint, aux_buffers, 0);
/**
* gdk_gl_pixel_format_get_accum_size:
* @format: a #GdkGLPixelFormat
*
* Retrieves the value of the #GdkGLPixelFormat:accum-size property.
*
* Returns: the size of the accumulation buffers
*
* Since: 3.14
*/
GDK_GL_PIXEL_FORMAT_GET (gint, accum_size, 0)
/**
* gdk_gl_pixel_format_get_sample_buffers:
* @format: a #GdkGLPixelFormat
*
* Retrieves the value of the #GdkGLPixelFormat:sample-buffers property.
*
* Returns: the number of multi-sample buffers
*
* Since: 3.14
*/
GDK_GL_PIXEL_FORMAT_GET (gint, sample_buffers, 0)
/**
* gdk_gl_pixel_format_get_samples:
* @format: a #GdkGLPixelFormat
*
* Retrieves the value of the #GdkGLPixelFormat:samples property.
*
* Returns: the number of samples for each multi-sample buffer
*
* Since: 3.14
*/
GDK_GL_PIXEL_FORMAT_GET (gint, samples, 0)
#undef GDK_GL_PIXEL_FORMAT_GET

75
gdk/gdkglpixelformat.h Normal file
View File

@@ -0,0 +1,75 @@
/* GDK - The GIMP Drawing Kit
*
* gdkglpixelformat.h: GL pixel formats
*
* Copyright © 2014 Emmanuele Bassi
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef __GDK_GL_PIXEL_FORMAT_H__
#define __GDK_GL_PIXEL_FORMAT_H__
#if !defined (__GDK_H_INSIDE__) && !defined (GDK_COMPILATION)
#error "Only <gdk/gdk.h> can be included directly."
#endif
#include <gdk/gdkversionmacros.h>
#include <gdk/gdktypes.h>
G_BEGIN_DECLS
#define GDK_TYPE_GL_PIXEL_FORMAT (gdk_gl_pixel_format_get_type ())
#define GDK_GL_PIXEL_FORMAT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GDK_TYPE_GL_PIXEL_FORMAT, GdkGLPixelFormat))
#define GDK_IS_GL_PIXEL_FORMAT(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GDK_TYPE_GL_PIXEL_FORMAT))
#define GDK_GL_PIXEL_FORMAT_ERROR (gdk_gl_pixel_format_error_quark ())
typedef struct _GdkGLPixelFormatClass GdkGLPixelFormatClass;
GDK_AVAILABLE_IN_3_14
GType gdk_gl_pixel_format_get_type (void) G_GNUC_CONST;
GDK_AVAILABLE_IN_3_14
GQuark gdk_gl_pixel_format_error_quark (void);
GDK_AVAILABLE_IN_3_14
GdkGLPixelFormat * gdk_gl_pixel_format_new (const char *first_property,
...);
GDK_AVAILABLE_IN_3_14
gboolean gdk_gl_pixel_format_get_double_buffer (GdkGLPixelFormat *format);
GDK_AVAILABLE_IN_3_14
gboolean gdk_gl_pixel_format_get_multi_sample (GdkGLPixelFormat *format);
GDK_AVAILABLE_IN_3_14
gboolean gdk_gl_pixel_format_get_stereo (GdkGLPixelFormat *format);
GDK_AVAILABLE_IN_3_14
gint gdk_gl_pixel_format_get_color_size (GdkGLPixelFormat *format);
GDK_AVAILABLE_IN_3_14
gint gdk_gl_pixel_format_get_alpha_size (GdkGLPixelFormat *format);
GDK_AVAILABLE_IN_3_14
gint gdk_gl_pixel_format_get_depth_size (GdkGLPixelFormat *format);
GDK_AVAILABLE_IN_3_14
gint gdk_gl_pixel_format_get_stencil_size (GdkGLPixelFormat *format);
GDK_AVAILABLE_IN_3_14
gint gdk_gl_pixel_format_get_accum_size (GdkGLPixelFormat *format);
GDK_AVAILABLE_IN_3_14
gint gdk_gl_pixel_format_get_aux_buffers (GdkGLPixelFormat *format);
GDK_AVAILABLE_IN_3_14
gint gdk_gl_pixel_format_get_sample_buffers (GdkGLPixelFormat *format);
GDK_AVAILABLE_IN_3_14
gint gdk_gl_pixel_format_get_samples (GdkGLPixelFormat *format);
G_END_DECLS
#endif /* __GDK_GL_PIXEL_FORMAT_H__ */

View File

@@ -0,0 +1,59 @@
/* GDK - The GIMP Drawing Kit
*
* gdkglpixelformatprivate.h: GL pixel formats private API
*
* Copyright © 2014 Emmanuele Bassi
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef __GDK_GL_PIXEL_FORMAT_PRIVATE_H__
#define __GDK_GL_PIXEL_FORMAT_PRIVATE_H__
#include "gdkglpixelformat.h"
G_BEGIN_DECLS
#define GDK_GL_PIXEL_FORMAT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GDK_TYPE_GL_PIXEL_FORMAT, GdkGLPixelFormatClass))
#define GDK_IS_GL_PIXEL_FORMAT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GDK_TYPE_GL_PIXEL_FORMAT))
#define GDK_GL_PIXEL_FORMAT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GDK_TYPE_GL_PIXEL_FORMAT, GdkGLPixelFormatClass))
struct _GdkGLPixelFormat
{
GObject parent_instance;
gboolean double_buffer;
gboolean multi_sample;
gboolean stereo;
int aux_buffers;
int color_size;
int alpha_size;
int depth_size;
int stencil_size;
int accum_size;
int sample_buffers;
int samples;
GdkGLPixelFormatProfile profile;
};
struct _GdkGLPixelFormatClass
{
GObjectClass parent_class;
};
G_END_DECLS
#endif /* __GDK_GL_PIXEL_FORMAT_PRIVATE_H__ */

View File

@@ -84,7 +84,8 @@ typedef enum {
GDK_DEBUG_DRAW = 1 << 9,
GDK_DEBUG_EVENTLOOP = 1 << 10,
GDK_DEBUG_FRAMES = 1 << 11,
GDK_DEBUG_SETTINGS = 1 << 12
GDK_DEBUG_SETTINGS = 1 << 12,
GDK_DEBUG_OPENGL = 1 << 13
} GdkDebugFlag;
typedef enum {
@@ -406,6 +407,11 @@ void _gdk_synthesize_crossing_events_for_geometry_change (GdkWindow *changed_win
gboolean _gdk_window_has_impl (GdkWindow *window);
GdkWindow * _gdk_window_get_impl_window (GdkWindow *window);
gboolean gdk_window_has_gl_context (GdkWindow *window);
void gdk_window_set_gl_context (GdkWindow *window,
GdkGLContext *context);
GdkGLContext * gdk_window_get_gl_context (GdkWindow *window);
/*****************************
* offscreen window routines *
*****************************/

View File

@@ -128,6 +128,9 @@ typedef struct _GdkWindow GdkWindow;
typedef struct _GdkKeymap GdkKeymap;
typedef struct _GdkAppLaunchContext GdkAppLaunchContext;
typedef struct _GdkGLPixelFormat GdkGLPixelFormat;
typedef struct _GdkGLContext GdkGLContext;
/**
* GdkByteOrder:
* @GDK_LSB_FIRST: The values are stored with the least-significant byte
@@ -429,8 +432,25 @@ struct _GdkPoint
gint y;
};
/**
* GdkGLPixelFormatProfile:
* @GDK_GL_PIXEL_FORMAT_PROFILE_DEFAULT: ...
* @GDK_GL_PIXEL_FORMAT_PROFILE_LEGACY: ...
* @GDK_GL_PIXEL_FORMAT_PROFILE_3_2_CORE: ...
*
* ...
*/
typedef enum {
GDK_GL_PIXEL_FORMAT_PROFILE_DEFAULT,
GDK_GL_PIXEL_FORMAT_PROFILE_LEGACY,
GDK_GL_PIXEL_FORMAT_PROFILE_3_2_CORE
} GdkGLPixelFormatProfile;
typedef enum {
GDK_GL_PIXEL_FORMAT_ERROR_INVALID_FORMAT,
GDK_GL_PIXEL_FORMAT_ERROR_NOT_AVAILABLE
} GdkGLPixelFormatError;
G_END_DECLS
#endif /* __GDK_TYPES_H__ */

View File

@@ -39,6 +39,8 @@ libgdk_x11_la_SOURCES = \
gdkeventtranslator.c \
gdkeventtranslator.h \
gdkgeometry-x11.c \
gdkglcontext-x11.c \
gdkglcontext-x11.h \
gdkkeys-x11.c \
gdkmain-x11.c \
gdkproperty-x11.c \
@@ -71,6 +73,7 @@ libgdkx11include_HEADERS = \
gdkx11display.h \
gdkx11displaymanager.h \
gdkx11dnd.h \
gdkx11glcontext.h \
gdkx11keys.h \
gdkx11property.h \
gdkx11screen.h \

View File

@@ -37,6 +37,7 @@
#include "gdkdisplay-x11.h"
#include "gdkprivate-x11.h"
#include "gdkscreen-x11.h"
#include "gdkglcontext-x11.h"
#include <glib.h>
#include <glib/gprintf.h>
@@ -781,11 +782,13 @@ gdk_x11_display_translate_event (GdkEventTranslator *translator,
}
#endif
if (!window ||
xevent->xconfigure.event != xevent->xconfigure.window ||
GDK_WINDOW_TYPE (window) == GDK_WINDOW_CHILD ||
GDK_WINDOW_TYPE (window) == GDK_WINDOW_ROOT)
return_val = FALSE;
if (!window ||
xevent->xconfigure.event != xevent->xconfigure.window ||
GDK_WINDOW_TYPE (window) == GDK_WINDOW_ROOT ||
GDK_WINDOW_TYPE (window) == GDK_WINDOW_CHILD)
{
return_val = FALSE;
}
else
{
event->configure.type = GDK_CONFIGURE;
@@ -2903,5 +2906,10 @@ gdk_x11_display_class_init (GdkX11DisplayClass * class)
display_class->text_property_to_utf8_list = _gdk_x11_display_text_property_to_utf8_list;
display_class->utf8_to_string_target = _gdk_x11_display_utf8_to_string_target;
display_class->validate_gl_pixel_format = gdk_x11_display_validate_gl_pixel_format;
display_class->create_gl_context = gdk_x11_display_create_gl_context;
display_class->destroy_gl_context = gdk_x11_display_destroy_gl_context;
display_class->make_gl_context_current = gdk_x11_display_make_gl_context_current;
_gdk_x11_windowing_init ();
}

View File

@@ -124,6 +124,20 @@ struct _GdkX11Display
GSList *error_traps;
gint wm_moveresize_button;
/* GLX information */
guint have_glx : 1;
gint glx_version;
gint glx_error_base;
gint glx_event_base;
/* GLX extensions we check */
guint has_glx_swap_interval : 1;
guint has_glx_create_context : 1;
guint has_glx_texture_from_pixmap : 1;
guint has_glx_video_sync : 1;
guint has_glx_buffer_age : 1;
guint has_glx_sync_control : 1;
};
struct _GdkX11DisplayClass

920
gdk/x11/gdkglcontext-x11.c Normal file
View File

@@ -0,0 +1,920 @@
/* GDK - The GIMP Drawing Kit
*
* gdkglcontext-x11.c: X11 specific OpenGL wrappers
*
* Copyright © 2014 Emmanuele Bassi
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
*/
#include "config.h"
#include "gdkglcontext-x11.h"
#include "gdkdisplay-x11.h"
#include "gdkscreen-x11.h"
#include "gdkx11display.h"
#include "gdkx11glcontext.h"
#include "gdkx11screen.h"
#include "gdkx11window.h"
#include "gdkx11visual.h"
#include "gdkinternals.h"
#include "gdkintl.h"
#include <GL/glx.h>
G_DEFINE_TYPE (GdkX11GLContext, gdk_x11_gl_context, GDK_TYPE_GL_CONTEXT)
typedef struct {
GLXDrawable drawable;
GdkDisplay *display;
GdkGLContext *context;
GdkWindow *window;
guint32 last_frame_counter;
} DrawableInfo;
static void
drawable_info_free (gpointer data_)
{
DrawableInfo *data = data_;
gdk_x11_display_error_trap_push (data->display);
if (data->drawable)
glXDestroyWindow (gdk_x11_display_get_xdisplay (data->display), data->drawable);
gdk_x11_display_error_trap_pop_ignored (data->display);
g_slice_free (DrawableInfo, data);
}
static DrawableInfo *
get_glx_drawable_info (GdkWindow *window)
{
return g_object_get_data (G_OBJECT (window), "-gdk-x11-window-glx-info");
}
static void
set_glx_drawable_info (GdkWindow *window,
DrawableInfo *info)
{
g_object_set_data_full (G_OBJECT (window), "-gdk-x11-window-glx-info",
info,
drawable_info_free);
}
static void
gdk_x11_gl_context_set_window (GdkGLContext *context,
GdkWindow *window)
{
GdkX11GLContext *context_x11 = GDK_X11_GL_CONTEXT (context);
GdkDisplay *display = gdk_gl_context_get_display (context);
DrawableInfo *info;
if (window == NULL)
{
gdk_x11_display_make_gl_context_current (display, context, NULL);
return;
}
/* we need to make sure that the GdkWindow is backed by
* an actual native surface
*/
gdk_window_ensure_native (window);
/* GLX < 1.3 accepts X11 drawables, so there's no need to
* go through the creation of a GLX drawable
*/
if (GDK_X11_DISPLAY (display)->glx_version < 13)
return;
info = get_glx_drawable_info (window);
if (info != NULL)
return;
gdk_x11_display_error_trap_push (display);
info = g_slice_new (DrawableInfo);
info->window = window;
info->context = context;
info->display = display;
info->drawable = glXCreateWindow (gdk_x11_display_get_xdisplay (display),
context_x11->glx_config,
gdk_x11_window_get_xid (window),
NULL);
info->last_frame_counter = 0;
gdk_x11_display_error_trap_pop_ignored (display);
set_glx_drawable_info (window, info);
}
static void
gdk_x11_gl_context_update (GdkGLContext *context,
GdkWindow *window)
{
GdkDisplay *display = gdk_gl_context_get_display (context);
int width, height;
if (!gdk_x11_display_make_gl_context_current (display, context, window))
return;
width = gdk_window_get_width (window);
height = gdk_window_get_height (window);
GDK_NOTE (OPENGL, g_print ("Updating GL viewport size to { %d, %d } for window %lu (context: %p)\n",
width, height,
(unsigned long) gdk_x11_window_get_xid (window),
context));
glViewport (0, 0, width, height);
}
static void
maybe_wait_for_vblank (GdkDisplay *display,
GLXDrawable drawable)
{
GdkX11Display *display_x11 = GDK_X11_DISPLAY (display);
Display *dpy = gdk_x11_display_get_xdisplay (display);
if (display_x11->has_glx_sync_control)
{
gint64 ust, msc, sbc;
glXGetSyncValuesOML (dpy, drawable, &ust, &msc, &sbc);
glXWaitForMscOML (dpy, drawable,
0, 2, (msc + 1) % 2,
&ust, &msc, &sbc);
}
else if (display_x11->has_glx_video_sync)
{
guint32 current_count;
glXGetVideoSyncSGI (&current_count);
glXWaitVideoSyncSGI (2, (current_count + 1) % 2, &current_count);
}
}
static void
gdk_x11_gl_context_flush_buffer (GdkGLContext *context)
{
GdkDisplay *display = gdk_gl_context_get_display (context);
GdkWindow *window = gdk_gl_context_get_window (context);
Display *dpy = gdk_x11_display_get_xdisplay (display);
GdkX11Display *display_x11 = GDK_X11_DISPLAY (display);
DrawableInfo *info;
GLXDrawable drawable;
if (window == NULL)
return;
gdk_x11_display_make_gl_context_current (display, context, window);
info = get_glx_drawable_info (window);
if (info != NULL && info->drawable != None)
drawable = info->drawable;
else
drawable = gdk_x11_window_get_xid (window);
GDK_NOTE (OPENGL,
g_print ("Flushing GLX buffers for %s drawable %lu (window: %lu)\n",
drawable == info->drawable ? "GLX" : "X11",
(unsigned long) drawable,
(unsigned long) gdk_x11_window_get_xid (window)));
/* if we are going to wait for the vertical refresh manually
* we need to flush pending redraws, and we also need to wait
* for that to finish, otherwise we are going to tear.
*
* obviously, this condition should not be hit if we have
* GLX_SGI_swap_control, and we ask the driver to do the right
* thing.
*/
{
guint32 end_frame_counter = 0;
gboolean has_counter = display_x11->has_glx_video_sync;
gboolean can_wait = display_x11->has_glx_video_sync ||
display_x11->has_glx_sync_control;
if (display_x11->has_glx_video_sync)
glXGetVideoSyncSGI (&end_frame_counter);
if (!display_x11->has_glx_swap_interval)
{
glFinish ();
if (has_counter && can_wait)
{
guint32 last_counter = info != NULL ? info->last_frame_counter : 0;
if (last_counter == end_frame_counter)
maybe_wait_for_vblank (display, drawable);
}
else if (can_wait)
maybe_wait_for_vblank (display, drawable);
}
}
glXSwapBuffers (dpy, drawable);
if (info != NULL && display_x11->has_glx_video_sync)
glXGetVideoSyncSGI (&info->last_frame_counter);
}
static void
gdk_x11_gl_context_class_init (GdkX11GLContextClass *klass)
{
GdkGLContextClass *context_class = GDK_GL_CONTEXT_CLASS (klass);
context_class->set_window = gdk_x11_gl_context_set_window;
context_class->update = gdk_x11_gl_context_update;
context_class->flush_buffer = gdk_x11_gl_context_flush_buffer;
}
static void
gdk_x11_gl_context_init (GdkX11GLContext *self)
{
}
gboolean
gdk_x11_display_init_gl (GdkDisplay *display)
{
GdkX11Display *display_x11 = GDK_X11_DISPLAY (display);
GdkScreen *screen;
Display *dpy;
int error_base, event_base;
int screen_num;
if (display_x11->have_glx)
return TRUE;
dpy = gdk_x11_display_get_xdisplay (display);
if (!glXQueryExtension (dpy, &error_base, &event_base))
return FALSE;
screen = gdk_display_get_default_screen (display);
screen_num = GDK_X11_SCREEN (screen)->screen_num;
display_x11->have_glx = TRUE;
display_x11->glx_version = epoxy_glx_version (dpy, screen_num);
display_x11->glx_error_base = error_base;
display_x11->glx_event_base = event_base;
display_x11->has_glx_create_context =
epoxy_has_glx_extension (dpy, screen_num, "GLX_ARB_create_context_profile");
display_x11->has_glx_swap_interval =
epoxy_has_glx_extension (dpy, screen_num, "GLX_SGI_swap_control");
display_x11->has_glx_texture_from_pixmap =
epoxy_has_glx_extension (dpy, screen_num, "GLX_EXT_texture_from_pixmap");
display_x11->has_glx_video_sync =
epoxy_has_glx_extension (dpy, screen_num, "GLX_SGI_video_sync");
display_x11->has_glx_buffer_age =
epoxy_has_glx_extension (dpy, screen_num, "GLX_EXT_buffer_age");
display_x11->has_glx_sync_control =
epoxy_has_glx_extension (dpy, screen_num, "GLX_OML_sync_control");
GDK_NOTE (OPENGL,
g_print ("GLX version %d.%d found\n"
" - Vendor: %s\n"
" - Checked extensions:\n"
"\t* GLX_ARB_create_context_profile: %s\n"
"\t* GLX_SGI_swap_control: %s\n"
"\t* GLX_EXT_texture_from_pixmap: %s\n"
"\t* GLX_SGI_video_sync: %s\n"
"\t* GLX_EXT_buffer_age: %s\n"
"\t* GLX_OML_sync_control: %s\n",
display_x11->glx_version / 10,
display_x11->glx_version % 10,
glXGetClientString (dpy, GLX_VENDOR),
display_x11->has_glx_create_context ? "yes" : "no",
display_x11->has_glx_swap_interval ? "yes" : "no",
display_x11->has_glx_texture_from_pixmap ? "yes" : "no",
display_x11->has_glx_video_sync ? "yes" : "no",
display_x11->has_glx_buffer_age ? "yes" : "no",
display_x11->has_glx_sync_control ? "yes" : "no"));
return TRUE;
}
#define MAX_GLX_ATTRS 30
static void
get_glx_attributes_for_pixel_format (GdkDisplay *display,
GdkGLPixelFormat *format,
int *attrs)
{
GdkX11Display *display_x11;
int i = 0;
attrs[i++] = GLX_DRAWABLE_TYPE;
attrs[i++] = GLX_WINDOW_BIT;
attrs[i++] = GLX_RENDER_TYPE;
attrs[i++] = GLX_RGBA_BIT;
if (format->double_buffer)
{
attrs[i++] = GLX_DOUBLEBUFFER;
attrs[i++] = GL_TRUE;
}
if (format->stereo)
{
attrs[i++] = GLX_STEREO;
attrs[i++] = GL_TRUE;
}
if (format->color_size < 0)
{
attrs[i++] = GLX_RED_SIZE;
attrs[i++] = 1;
attrs[i++] = GLX_GREEN_SIZE;
attrs[i++] = 1;
attrs[i++] = GLX_BLUE_SIZE;
attrs[i++] = 1;
}
else
{
attrs[i++] = GLX_RED_SIZE;
attrs[i++] = format->color_size;
attrs[i++] = GLX_GREEN_SIZE;
attrs[i++] = format->color_size;
attrs[i++] = GLX_BLUE_SIZE;
attrs[i++] = format->color_size;
}
if (format->alpha_size < 0)
{
attrs[i++] = GLX_ALPHA_SIZE;
attrs[i++] = 1;
}
else if (format->alpha_size == 0)
{
attrs[i++] = GLX_ALPHA_SIZE;
attrs[i++] = GLX_DONT_CARE;
}
else
{
attrs[i++] = GLX_ALPHA_SIZE;
attrs[i++] = format->alpha_size;
}
if (format->depth_size < 0)
{
attrs[i++] = GLX_DEPTH_SIZE;
attrs[i++] = 1;
}
else
{
attrs[i++] = GLX_DEPTH_SIZE;
attrs[i++] = format->depth_size;
}
if (format->stencil_size < 0)
{
attrs[i++] = GLX_STENCIL_SIZE;
attrs[i++] = GLX_DONT_CARE;
}
else
{
attrs[i++] = GLX_STENCIL_SIZE;
attrs[i++] = format->stencil_size;
}
if (format->accum_size > 0)
{
attrs[i++] = GLX_ACCUM_RED_SIZE;
attrs[i++] = format->accum_size;
attrs[i++] = GLX_ACCUM_GREEN_SIZE;
attrs[i++] = format->accum_size;
attrs[i++] = GLX_ACCUM_BLUE_SIZE;
attrs[i++] = format->accum_size;
attrs[i++] = GLX_ACCUM_ALPHA_SIZE;
attrs[i++] = format->accum_size;
}
display_x11 = GDK_X11_DISPLAY (display);
if (display_x11->glx_version >= 14 && format->multi_sample)
{
attrs[i++] = GLX_SAMPLE_BUFFERS;
attrs[i++] = format->sample_buffers > 0 ? format->sample_buffers : 1;
attrs[i++] = GLX_SAMPLES;
attrs[i++] = format->samples > 0 ? format->samples : 1;
}
attrs[i++] = None;
g_assert (i < MAX_GLX_ATTRS);
}
static gboolean
find_fbconfig_for_pixel_format (GdkDisplay *display,
GdkGLPixelFormat *format,
GLXFBConfig *fb_config_out,
XVisualInfo **visinfo_out,
GError **error)
{
static int attrs[MAX_GLX_ATTRS];
Display *dpy = gdk_x11_display_get_xdisplay (display);
GLXFBConfig *configs;
int n_configs, i;
gboolean use_rgba;
gboolean retval = FALSE;
get_glx_attributes_for_pixel_format (display, format, attrs);
use_rgba = format->alpha_size != 0;
configs = glXChooseFBConfig (dpy, DefaultScreen (dpy), attrs, &n_configs);
if (configs == NULL || n_configs == 0)
{
g_set_error_literal (error, GDK_GL_PIXEL_FORMAT_ERROR,
GDK_GL_PIXEL_FORMAT_ERROR_NOT_AVAILABLE,
_("No available configurations for the given pixel format"));
return FALSE;
}
/* if we don't care about an alpha channel, then the first
* valid configuration is the one we give back
*/
if (!use_rgba)
{
if (fb_config_out != NULL)
*fb_config_out = configs[0];
if (visinfo_out != NULL)
*visinfo_out = glXGetVisualFromFBConfig (dpy, configs[0]);
retval = TRUE;
goto out;
}
for (i = 0; i < n_configs; i++)
{
XVisualInfo *visinfo;
unsigned long mask;
visinfo = glXGetVisualFromFBConfig (dpy, configs[i]);
if (visinfo == NULL)
continue;
mask = visinfo->red_mask | visinfo->green_mask | visinfo->blue_mask;
if (visinfo->depth == 32 && mask != 0xffffffff)
{
if (fb_config_out != NULL)
*fb_config_out = configs[i];
if (visinfo_out != NULL)
*visinfo_out = visinfo;
retval = TRUE;
goto out;
}
XFree (visinfo);
}
g_set_error (error, GDK_GL_PIXEL_FORMAT_ERROR,
GDK_GL_PIXEL_FORMAT_ERROR_NOT_AVAILABLE,
_("No available configurations for the given RGBA pixel format"));
out:
XFree (configs);
return retval;
}
static void
update_pixel_format (GdkDisplay *display,
GdkGLPixelFormat *format,
GLXFBConfig config)
{
Display *dpy = gdk_x11_display_get_xdisplay (display);
int value = 0;
glXGetFBConfigAttrib (dpy, config, GLX_DOUBLEBUFFER, &format->double_buffer);
glXGetFBConfigAttrib (dpy, config, GLX_RED_SIZE, &value);
format->color_size = value;
glXGetFBConfigAttrib (dpy, config, GLX_GREEN_SIZE, &value);
format->color_size = MIN (format->color_size, value);
glXGetFBConfigAttrib (dpy, config, GLX_BLUE_SIZE, &value);
format->color_size = MIN (format->color_size, value);
glXGetFBConfigAttrib (dpy, config, GLX_ALPHA_SIZE, &format->alpha_size);
glXGetFBConfigAttrib (dpy, config, GLX_AUX_BUFFERS, &format->aux_buffers);
glXGetFBConfigAttrib (dpy, config, GLX_DEPTH_SIZE, &format->depth_size);
glXGetFBConfigAttrib (dpy, config, GLX_STENCIL_SIZE, &format->stencil_size);
glXGetFBConfigAttrib (dpy, config, GLX_ACCUM_RED_SIZE, &value);
format->accum_size = value;
glXGetFBConfigAttrib (dpy, config, GLX_ACCUM_GREEN_SIZE, &value);
format->accum_size = MIN (format->accum_size, value);
glXGetFBConfigAttrib (dpy, config, GLX_ACCUM_BLUE_SIZE, &value);
format->accum_size = MIN (format->accum_size, value);
glXGetFBConfigAttrib (dpy, config, GLX_ACCUM_ALPHA_SIZE, &value);
format->accum_size = MIN (format->accum_size, value);
}
static GLXContext
create_gl3_context (GdkDisplay *display,
GLXFBConfig config,
GdkGLContext *share)
{
static const int attrib_list[] = {
GLX_CONTEXT_PROFILE_MASK_ARB, GLX_CONTEXT_CORE_PROFILE_BIT_ARB,
GLX_CONTEXT_MAJOR_VERSION_ARB, 3,
GLX_CONTEXT_MAJOR_VERSION_ARB, 2,
None,
};
GdkX11GLContext *context_x11 = NULL;
if (share != NULL)
context_x11 = GDK_X11_GL_CONTEXT (share);
return glXCreateContextAttribsARB (gdk_x11_display_get_xdisplay (display),
config,
context_x11 != NULL ? context_x11->glx_context : NULL,
True,
attrib_list);
}
static GLXContext
create_gl_context (GdkDisplay *display,
GLXFBConfig config,
GdkGLContext *share)
{
GdkX11GLContext *context_x11 = NULL;
if (share != NULL)
context_x11 = GDK_X11_GL_CONTEXT (share);
return glXCreateNewContext (gdk_x11_display_get_xdisplay (display),
config,
GLX_RGBA_TYPE,
context_x11 != NULL ? context_x11->glx_context : NULL,
True);
}
GdkGLContext *
gdk_x11_display_create_gl_context (GdkDisplay *display,
GdkGLPixelFormat *format,
GdkGLContext *share,
GError **error)
{
GdkGLPixelFormat *valid_format;
GdkX11GLContext *context;
GdkVisual *gdk_visual;
GLXFBConfig config;
GLXContext glx_context;
Window dummy_xwin;
GLXWindow dummy_glx;
GLXWindow dummy_drawable;
gboolean is_direct;
XVisualInfo *xvisinfo;
XSetWindowAttributes attrs;
unsigned long mask;
Display *dpy;
if (!gdk_x11_display_validate_gl_pixel_format (display, format, NULL, error))
return NULL;
/* if validation succeeded, then we don't need to check for the
* result here: we know the pixel format has a valid GLXFBConfig
*/
find_fbconfig_for_pixel_format (display, format, &config, &xvisinfo, NULL);
dpy = gdk_x11_display_get_xdisplay (display);
/* we check for the GLX_ARB_create_context_profile extension
* while validating the PixelFormat.
*/
if (format->profile == GDK_GL_PIXEL_FORMAT_PROFILE_3_2_CORE)
glx_context = create_gl3_context (display, config, share);
else
{
/* GDK_GL_PIXEL_FORMAT_PROFILE_DEFAULT is currently
* equivalent to the LEGACY profile
*/
glx_context = create_gl_context (display, config, share);
}
if (glx_context == NULL)
{
g_set_error_literal (error, GDK_GL_PIXEL_FORMAT_ERROR,
GDK_GL_PIXEL_FORMAT_ERROR_NOT_AVAILABLE,
_("Unable to create a GL context"));
return NULL;
}
is_direct = glXIsDirect (dpy, glx_context);
gdk_x11_display_error_trap_push (display);
/* create a dummy window; this is needed because GLX does not allow
* us to query the context until it's bound to a drawable; we simply
* create a small OR window, put it off screen, and never map it. in
* order to keep the GL machinery in a sane state, we always make
* the dummy window the current drawable if the user unsets the
* GdkWindow bound to the GdkGLContext.
*/
attrs.override_redirect = True;
attrs.colormap = XCreateColormap (dpy, DefaultRootWindow (dpy), xvisinfo->visual, AllocNone);
attrs.border_pixel = 0;
mask = CWOverrideRedirect | CWColormap | CWBorderPixel;
dummy_xwin = XCreateWindow (dpy, DefaultRootWindow (dpy),
-100, -100, 1, 1,
0,
xvisinfo->depth,
CopyFromParent,
xvisinfo->visual,
mask,
&attrs);
/* GLX API introduced in 1.3 expects GLX drawables */
if (GDK_X11_DISPLAY (display)->glx_version >= 13)
dummy_glx = glXCreateWindow (dpy, config, dummy_xwin, NULL);
else
dummy_glx = None;
dummy_drawable = dummy_glx != None
? dummy_glx
: dummy_xwin;
glXMakeContextCurrent (dpy, dummy_drawable, dummy_drawable, glx_context);
gdk_visual = gdk_x11_screen_lookup_visual (gdk_display_get_default_screen (display),
xvisinfo->visualid);
XFree (xvisinfo);
if (gdk_x11_display_error_trap_pop (display))
{
g_set_error_literal (error, GDK_GL_PIXEL_FORMAT_ERROR,
GDK_GL_PIXEL_FORMAT_ERROR_NOT_AVAILABLE,
_("Unable to create a GL context"));
glXDestroyContext (dpy, glx_context);
if (dummy_xwin)
XDestroyWindow (dpy, dummy_xwin);
if (dummy_glx)
glXDestroyWindow (dpy, dummy_glx);
return NULL;
}
GDK_NOTE (OPENGL,
g_print ("Created GLX context[%p], %s, dummy drawable: %lu\n",
glx_context,
is_direct ? "direct" : "indirect",
(unsigned long) dummy_xwin));
/* the GdkGLContext holds a reference on the pixel format
* that is used to create it, not the one that the user
* passed; this allows the user to query the pixel format
* attributes
*/
valid_format = g_object_new (GDK_TYPE_GL_PIXEL_FORMAT, NULL);
update_pixel_format (display, valid_format, config);
context = g_object_new (GDK_X11_TYPE_GL_CONTEXT,
"display", display,
"pixel-format", valid_format,
"visual", gdk_visual,
NULL);
context->glx_config = config;
context->glx_context = glx_context;
context->dummy_drawable = dummy_xwin;
context->dummy_glx_drawable = dummy_glx;
context->current_drawable = dummy_drawable;
context->is_direct = is_direct;
g_object_unref (valid_format);
return GDK_GL_CONTEXT (context);
}
void
gdk_x11_display_destroy_gl_context (GdkDisplay *display,
GdkGLContext *context)
{
GdkX11GLContext *context_x11 = GDK_X11_GL_CONTEXT (context);
Display *dpy = gdk_x11_display_get_xdisplay (display);
if (context_x11->glx_context != NULL)
{
if (glXGetCurrentContext () == context_x11->glx_context)
glXMakeContextCurrent (dpy, None, None, NULL);
GDK_NOTE (OPENGL, g_print ("Destroying GLX context\n"));
glXDestroyContext (dpy, context_x11->glx_context);
context_x11->glx_context = NULL;
}
if (context_x11->dummy_glx_drawable)
{
GDK_NOTE (OPENGL, g_print ("Destroying dummy GLX drawable\n"));
glXDestroyWindow (dpy, context_x11->dummy_glx_drawable);
context_x11->dummy_glx_drawable = None;
}
if (context_x11->dummy_drawable)
{
GDK_NOTE (OPENGL, g_print ("Destroying dummy drawable\n"));
XDestroyWindow (dpy, context_x11->dummy_drawable);
context_x11->dummy_drawable = None;
}
}
gboolean
gdk_x11_display_make_gl_context_current (GdkDisplay *display,
GdkGLContext *context,
GdkWindow *window)
{
GdkX11GLContext *context_x11 = GDK_X11_GL_CONTEXT (context);
GLXDrawable drawable = None;
if (context_x11->glx_context == NULL)
return FALSE;
if (window == NULL)
{
/* we re-bind our dummy drawable, so that the context
* can still be used for queries
*/
drawable = context_x11->dummy_glx_drawable != None
? context_x11->dummy_glx_drawable
: context_x11->dummy_drawable;
}
else
{
DrawableInfo *info = get_glx_drawable_info (window);
if (info != NULL && info->drawable != None)
drawable = info->drawable;
else
drawable = gdk_x11_window_get_xid (window);
}
if (G_UNLIKELY (drawable == None))
return FALSE;
if (drawable == context_x11->current_drawable)
return TRUE;
GDK_NOTE (OPENGL,
g_print ("Making GLX context current to drawable %lu (dummy: %s)\n",
(unsigned long) drawable,
drawable == context_x11->dummy_drawable ? "yes" : "no"));
gdk_x11_display_error_trap_push (display);
glXMakeContextCurrent (gdk_x11_display_get_xdisplay (display),
drawable, drawable,
context_x11->glx_context);
if (GDK_X11_DISPLAY (display)->has_glx_swap_interval)
{
if (gdk_gl_context_get_swap_interval (context))
glXSwapIntervalSGI (1);
else
glXSwapIntervalSGI (0);
}
XSync (gdk_x11_display_get_xdisplay (display), False);
if (gdk_x11_display_error_trap_pop (display))
{
g_critical ("X Error received while calling glXMakeContextCurrent()");
return FALSE;
}
context_x11->current_drawable = drawable;
return TRUE;
}
gboolean
gdk_x11_display_validate_gl_pixel_format (GdkDisplay *display,
GdkGLPixelFormat *format,
GdkGLPixelFormat **validated_format,
GError **error)
{
GLXFBConfig config;
if (!gdk_x11_display_init_gl (display))
{
g_set_error_literal (error, GDK_GL_PIXEL_FORMAT_ERROR,
GDK_GL_PIXEL_FORMAT_ERROR_NOT_AVAILABLE,
_("No GL implementation is available"));
return FALSE;
}
if (format->profile == GDK_GL_PIXEL_FORMAT_PROFILE_3_2_CORE)
{
if (!GDK_X11_DISPLAY (display)->has_glx_create_context)
{
g_set_error_literal (error, GDK_GL_PIXEL_FORMAT_ERROR,
GDK_GL_PIXEL_FORMAT_ERROR_NOT_AVAILABLE,
_("The GLX_ARB_create_context_profile extension "
"needed to create 3.2 core profiles is not "
"available"));
return FALSE;
}
}
if (!find_fbconfig_for_pixel_format (display, format, &config, NULL, error))
return FALSE;
GDK_NOTE (OPENGL,
g_print ("Found GLX config for requested pixel format:\n"
" - double-buffer: %s\n"
" - multi-sample: %s\n"
" - stereo: %s\n"
" - color-size: %d, alpha-size: %d\n"
" - depth-size: %d\n"
" - stencil-size: %d\n"
" - aux-buffers: %d\n"
" - accum-size: %d\n"
" - sample-buffers: %d, samples: %d\n",
format->double_buffer ? "yes" : "no",
format->multi_sample ? "yes" : "no",
format->stereo ? "yes" : "no",
format->color_size, format->alpha_size,
format->depth_size,
format->stencil_size,
format->aux_buffers,
format->accum_size,
format->sample_buffers, format->samples));
if (validated_format != NULL)
{
GdkGLPixelFormat *valid = g_object_new (GDK_TYPE_GL_PIXEL_FORMAT, NULL);
/* update the pixel format with the values of the
* configuration we found
*/
update_pixel_format (display, valid, config);
*validated_format = valid;
}
return TRUE;
}
/**
* gdk_x11_display_get_glx_version:
* @display: a #GdkDisplay
* @major: (out): return location for the GLX major version
* @minor: (out): return location for the GLX minor version
*
* Retrieves the version of the GLX implementation.
*
* Returns: %TRUE if GLX is available
*
* Since: 3.14
*/
gboolean
gdk_x11_display_get_glx_version (GdkDisplay *display,
int *major,
int *minor)
{
g_return_val_if_fail (GDK_IS_DISPLAY (display), FALSE);
if (!GDK_IS_X11_DISPLAY (display))
return FALSE;
if (!gdk_x11_display_init_gl (display))
return FALSE;
if (major != NULL)
*major = GDK_X11_DISPLAY (display)->glx_version / 10;
if (minor != NULL)
*minor = GDK_X11_DISPLAY (display)->glx_version % 10;
return TRUE;
}

View File

@@ -0,0 +1,78 @@
/* GDK - The GIMP Drawing Kit
*
* gdkglcontext-x11.h: Private X11 specific OpenGL wrappers
*
* Copyright © 2014 Emmanuele Bassi
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef __GDK_X11_GL_CONTEXT__
#define __GDK_X11_GL_CONTEXT__
#include <X11/X.h>
#include <X11/Xlib.h>
#include <epoxy/gl.h>
#include <epoxy/glx.h>
#include "gdkglcontextprivate.h"
#include "gdkglpixelformatprivate.h"
#include "gdkdisplayprivate.h"
#include "gdkglpixelformat.h"
#include "gdkvisual.h"
#include "gdkwindow.h"
#include "gdkinternals.h"
#include "gdkmain.h"
G_BEGIN_DECLS
struct _GdkX11GLContext
{
GdkGLContext parent_instance;
GLXContext glx_context;
GLXFBConfig glx_config;
GLXDrawable current_drawable;
Window dummy_drawable;
GLXWindow dummy_glx_drawable;
guint is_direct : 1;
};
struct _GdkX11GLContextClass
{
GdkGLContextClass parent_class;
};
gboolean gdk_x11_display_init_gl (GdkDisplay *display);
gboolean gdk_x11_display_validate_gl_pixel_format (GdkDisplay *display,
GdkGLPixelFormat *format,
GdkGLPixelFormat **validated_format,
GError **error);
GdkGLContext * gdk_x11_display_create_gl_context (GdkDisplay *display,
GdkGLPixelFormat *format,
GdkGLContext *share,
GError **error);
void gdk_x11_display_destroy_gl_context (GdkDisplay *display,
GdkGLContext *context);
gboolean gdk_x11_display_make_gl_context_current (GdkDisplay *display,
GdkGLContext *context,
GdkWindow *window);
G_END_DECLS
#endif /* __GDK_X11_GL_CONTEXT__ */

View File

@@ -43,6 +43,7 @@
#include <gdk/x11/gdkx11display.h>
#include <gdk/x11/gdkx11displaymanager.h>
#include <gdk/x11/gdkx11dnd.h>
#include <gdk/x11/gdkx11glcontext.h>
#include <gdk/x11/gdkx11keys.h>
#include <gdk/x11/gdkx11property.h>
#include <gdk/x11/gdkx11screen.h>

49
gdk/x11/gdkx11glcontext.h Normal file
View File

@@ -0,0 +1,49 @@
/* GDK - The GIMP Drawing Kit
*
* gdkglcontext-x11.c: X11 specific OpenGL wrappers
*
* Copyright © 2014 Emmanuele Bassi
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef __GDK_X11_GL_CONTEXT_H__
#define __GDK_X11_GL_CONTEXT_H__
#if !defined (__GDKX_H_INSIDE__) && !defined (GDK_COMPILATION)
#error "Only <gdk/gdkx.h> can be included directly."
#endif
#include <gdk/gdk.h>
G_BEGIN_DECLS
#define GDK_X11_TYPE_GL_CONTEXT (gdk_x11_gl_context_get_type ())
#define GDK_X11_GL_CONTEXT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GDK_X11_TYPE_GL_CONTEXT, GdkX11GLContext))
#define GDK_X11_IS_GL_CONTEXT(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GDK_X11_TYPE_GL_CONTEXT))
typedef struct _GdkX11GLContext GdkX11GLContext;
typedef struct _GdkX11GLContextClass GdkX11GLContextClass;
GDK_AVAILABLE_IN_3_14
GType gdk_x11_gl_context_get_type (void) G_GNUC_CONST;
GDK_AVAILABLE_IN_3_14
gboolean gdk_x11_display_get_glx_version (GdkDisplay *display,
int *major,
int *minor);
G_END_DECLS
#endif /* __GDK_X11_GL_CONTEXT_H__ */

View File

@@ -365,6 +365,7 @@ gtk_public_h_sources = \
gtkgesturesingle.h \
gtkgestureswipe.h \
gtkgesturezoom.h \
gtkglarea.h \
gtkgrid.h \
gtkheaderbar.h \
gtkicontheme.h \
@@ -937,6 +938,7 @@ gtk_base_c_sources = \
gtkgesturesingle.c \
gtkgestureswipe.c \
gtkgesturezoom.c \
gtkglarea.c \
gtkgrid.c \
gtkheaderbar.c \
gtkhsla.c \

View File

@@ -114,6 +114,7 @@
#include <gtk/gtkgesturesingle.h>
#include <gtk/gtkgestureswipe.h>
#include <gtk/gtkgesturezoom.h>
#include <gtk/gtkglarea.h>
#include <gtk/gtkgrid.h>
#include <gtk/gtkheaderbar.h>
#include <gtk/gtkicontheme.h>

667
gtk/gtkglarea.c Normal file
View File

@@ -0,0 +1,667 @@
/* GTK - The GIMP Toolkit
*
* gtkglarea.c: A GL drawing area
*
* Copyright © 2014 Emmanuele Bassi
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
*/
#include "config.h"
#include "config.h"
#include "gtkglarea.h"
#include "gtkintl.h"
#include "gtkstylecontext.h"
#include "gtkmarshalers.h"
#include "gtkprivate.h"
/**
* SECTION:gtkglarea
* @Title: GtkGLArea
* @Short_description: A widget for custom drawing with OpenGL
*
* #GtkGLArea is a widget that allows drawing with OpenGL.
*
* #GtkGLArea can set up its own #GdkGLContext using a provided
* #GdkGLPixelFormat, or can use a given #GdkGLContext.
*
* In order to draw, you have to connect to the #GtkGLArea::render signal,
* or subclass #GtkGLArea and override the @GtkGLAreaClass.render() virtual
* function.
*
* The #GtkGLArea widget ensures that the #GdkGLContext is associated with
* the widget's drawing area, and it is kept updated when the size and
* position of the drawing area changes.
*
* ## Drawing with GtkGLArea ##
*
* The simplest way to draw using OpenGL commands in a #GtkGLArea is to
* create a widget instance and connect to the #GtkGLArea::render signal:
*
* |[<!-- language="C" -->
* // create a double buffered pixel format
* GdkGLPixelFormat *format =
* gdk_gl_pixel_format_new ("double-buffer", TRUE);
*
* // create a GtkGLArea instance
* GtkWidget *gl_area = gtk_gl_area_new (format);
*
* // the GtkGLArea now owns the GdkGLPixelFormat
* g_object_unref (format);
*
* // connect to the "render" signal
* g_signal_connect (gl_area, "render", G_CALLBACK (render), NULL);
* ]|
*
* The `render()` function will be called when the #GtkGLArea is ready
* for you to draw its content:
*
* |[<!-- language="C" -->
* static gboolean
* render (GtkGLArea *area, GdkGLContext *context)
* {
* // inside this function it's safe to use GL; the given
* // #GdkGLContext has been made current to the drawable
* // surface used by the #GtkGLArea and the viewport has
* // already been set to be the size of the allocation
*
* // we can start by clearing the buffer
* glClearColor (0, 0, 0, 0);
* glClear (GL_COLOR_BUFFER_BIT);
*
* // draw your object
* draw_an_object ();
*
* // we completed our drawing; the draw commands will be
* // flushed at the end of the signal emission chain, and
* // the buffers swapped if needed
* return TRUE;
* }
* ]|
*
* The `draw_an_object()` function draws a 2D, gold-colored
* triangle:
*
* |[<!-- language="C" -->
* static void
* draw_an_object (void)
* {
* // set the color
* glColor3f (1.0f, 0.85f, 0.35f);
*
* // draw our triangle
* glBegin (GL_TRIANGLES);
* {
* glVertex3f ( 0.0f, 0.6f, 0.0f);
* glVertex3f (-0.2f, -0.3f, 0.0f);
* glVertex3f ( 0.2f, -0.3f, 0.0f);
* }
* glEnd ();
* }
* ]|
*
* This is an extremely simple example; in a real-world application you
* would probably replace the immediate mode drawing with persistent
* geometry primitives, like a Vertex Buffer Object, and only redraw what
* changed in your scene.
*
* ## Using different OpenGL contexts with GtkGLArea ##
*
* The #GtkGLArea widget will create a #GdkGLContext for the given
* pixel format passed on creation. It is possible, however, to change
* this default behavior by connecting to the #GtkGLArea::create-context
* signal, or by overriding the #GtkGLAreaClass.create_context() virtual
* function on a #GtkGLArea subclass.
*
* If you need to let a #GtkGLArea create a #GdkGLContext with shared
* data with another context you can use the #GtkGLArea::create-context
* to override the creation of the widget-specific OpenGL context:
*
* |[<!-- language="C" -->
* static GdkGLContext *
* create_shared_context (GtkGLArea *area,
* GdkGLPixelFormat *format,
* GdkGLContext *shared_context)
* {
* GdkDisplay *display;
*
* display = gtk_widget_get_display (GTK_WIDGET (area));
* if (display == NULL)
* display = gdk_display_get_default ();
*
* // create a GdkGLContext that has shared texture namespace
* // and display lists with a given context
* return gdk_display_create_shared_gl_context (display, format,
* shared_context,
* NULL);
* }
* ]|
*
* The #GtkGLArea will take ownership of the #GdkGLContext returned
* by the #GtkGLArea::create-context signal.
*/
typedef struct {
GdkGLPixelFormat *pixel_format;
GdkGLContext *context;
} GtkGLAreaPrivate;
enum {
PROP_0,
PROP_PIXEL_FORMAT,
PROP_CONTEXT,
LAST_PROP
};
static GParamSpec *obj_props[LAST_PROP] = { NULL, };
enum {
RENDER,
CREATE_CONTEXT,
LAST_SIGNAL
};
static guint area_signals[LAST_SIGNAL] = { 0, };
G_DEFINE_TYPE_WITH_PRIVATE (GtkGLArea, gtk_gl_area, GTK_TYPE_WIDGET)
static void
gtk_gl_area_dispose (GObject *gobject)
{
GtkGLArea *self = GTK_GL_AREA (gobject);
GtkGLAreaPrivate *priv = gtk_gl_area_get_instance_private (self);
g_clear_object (&priv->pixel_format);
g_clear_object (&priv->context);
G_OBJECT_CLASS (gtk_gl_area_parent_class)->dispose (gobject);
}
static void
gtk_gl_area_set_property (GObject *gobject,
guint prop_id,
const GValue *value,
GParamSpec *pspec)
{
GtkGLArea *self = GTK_GL_AREA (gobject);
GtkGLAreaPrivate *priv = gtk_gl_area_get_instance_private (self);
switch (prop_id)
{
case PROP_PIXEL_FORMAT:
priv->pixel_format = g_value_dup_object (value);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
}
}
static void
gtk_gl_area_get_property (GObject *gobject,
guint prop_id,
GValue *value,
GParamSpec *pspec)
{
GtkGLAreaPrivate *priv = gtk_gl_area_get_instance_private (GTK_GL_AREA (gobject));
switch (prop_id)
{
case PROP_PIXEL_FORMAT:
g_value_set_object (value, priv->pixel_format);
break;
case PROP_CONTEXT:
g_value_set_object (value, priv->context);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
}
}
static GdkGLContext *
gtk_gl_area_create_context (GtkGLArea *area)
{
GtkGLAreaPrivate *priv = gtk_gl_area_get_instance_private (area);
GdkGLContext *context;
if (priv->context != NULL)
return priv->context;
context = NULL;
g_signal_emit (area, area_signals[CREATE_CONTEXT], 0,
priv->pixel_format,
&context);
if (context == NULL)
{
g_critical ("No GL context was created for the widget");
return NULL;
}
gtk_widget_set_visual (GTK_WIDGET (area), gdk_gl_context_get_visual (context));
priv->context = context;
return priv->context;
}
static void
gtk_gl_area_realize (GtkWidget *widget)
{
GdkGLContext *context;
GtkAllocation allocation;
GdkWindow *window;
GdkWindowAttr attributes;
gint attributes_mask;
context = gtk_gl_area_create_context (GTK_GL_AREA (widget));
gtk_widget_set_realized (widget, TRUE);
gtk_widget_get_allocation (widget, &allocation);
attributes.window_type = GDK_WINDOW_CHILD;
attributes.x = allocation.x;
attributes.y = allocation.y;
attributes.width = allocation.width;
attributes.height = allocation.height;
attributes.wclass = GDK_INPUT_OUTPUT;
attributes.visual = gtk_widget_get_visual (widget);
attributes.event_mask = gtk_widget_get_events (widget) |
GDK_EXPOSURE_MASK |
GDK_STRUCTURE_MASK;
attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL;
window = gdk_window_new (gtk_widget_get_parent_window (widget),
&attributes,
attributes_mask);
gtk_widget_register_window (widget, window);
gtk_widget_set_window (widget, window);
if (context != NULL)
{
gdk_gl_context_set_window (context, window);
gdk_gl_context_update (context);
}
}
static void
gtk_gl_area_unrealize (GtkWidget *widget)
{
GtkGLAreaPrivate *priv = gtk_gl_area_get_instance_private ((GtkGLArea *) widget);
if (priv->context != NULL)
gdk_gl_context_set_window (priv->context, NULL);
GTK_WIDGET_CLASS (gtk_gl_area_parent_class)->unrealize (widget);
}
static void
gtk_gl_area_size_allocate (GtkWidget *widget,
GtkAllocation *allocation)
{
GtkGLAreaPrivate *priv = gtk_gl_area_get_instance_private ((GtkGLArea *) widget);
gtk_widget_set_allocation (widget, allocation);
if (!gtk_widget_get_realized (widget))
return;
gdk_window_move_resize (gtk_widget_get_window (widget),
allocation->x,
allocation->y,
allocation->width,
allocation->height);
if (priv->context != NULL)
gdk_gl_context_update (priv->context);
}
static gboolean
gtk_gl_area_draw (GtkWidget *widget,
cairo_t *cr)
{
GtkGLArea *self = GTK_GL_AREA (widget);
GtkGLAreaPrivate *priv = gtk_gl_area_get_instance_private (self);
gboolean unused;
if (priv->context == NULL)
return FALSE;
if (!gtk_gl_area_make_current (self))
return FALSE;
g_signal_emit (self, area_signals[RENDER], 0, priv->context, &unused);
/* XXX: this will go away once gdk_window_end_paint() knows about
* GdkGLContext and calls it implicitly when needed
*/
gtk_gl_area_flush_buffer (self);
return TRUE;
}
static void
gtk_gl_area_screen_changed (GtkWidget *widget,
GdkScreen *old_screen)
{
GtkGLArea *self = GTK_GL_AREA (widget);
GtkGLAreaPrivate *priv = gtk_gl_area_get_instance_private (self);
/* this will cause the context to be recreated on realize */
g_clear_object (&priv->context);
}
static GdkGLContext *
gtk_gl_area_real_create_context (GtkGLArea *area,
GdkGLPixelFormat *format)
{
GdkDisplay *display = gtk_widget_get_display (GTK_WIDGET (area));
GdkGLContext *retval;
GError *error = NULL;
if (display == NULL)
display = gdk_display_get_default ();
retval = gdk_display_create_gl_context (display, format, &error);
if (error != NULL)
{
g_critical ("Unable to create a GdkGLContext: %s", error->message);
g_error_free (error);
return NULL;
}
return retval;
}
static gboolean
create_context_accumulator (GSignalInvocationHint *ihint,
GValue *return_accu,
const GValue *handler_return,
gpointer data)
{
g_value_copy (handler_return, return_accu);
/* stop after the first handler returning a valid object */
return g_value_get_object (handler_return) == NULL;
}
static void
gtk_gl_area_class_init (GtkGLAreaClass *klass)
{
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
klass->create_context = gtk_gl_area_real_create_context;
widget_class->screen_changed = gtk_gl_area_screen_changed;
widget_class->realize = gtk_gl_area_realize;
widget_class->unrealize = gtk_gl_area_unrealize;
widget_class->size_allocate = gtk_gl_area_size_allocate;
widget_class->draw = gtk_gl_area_draw;
gtk_widget_class_set_accessible_role (widget_class, ATK_ROLE_DRAWING_AREA);
/**
* GtkGLArea:pixel-format:
*
* The #GdkGLPixelFormat used for creating the #GdkGLContext
* to be used by the #GtkGLArea widget.
*
* If you want to query the effective pixel format used by
* the #GdkGLContext, you should get the #GtkGLArea:context and
* call gdk_gl_context_get_pixel_format().
*
* Since: 3.14
*/
obj_props[PROP_PIXEL_FORMAT] =
g_param_spec_object ("pixel-format",
P_("Pixel Format"),
P_("The GDK pixel format for creating the GL context"),
GDK_TYPE_GL_PIXEL_FORMAT,
G_PARAM_READWRITE |
G_PARAM_CONSTRUCT_ONLY |
G_PARAM_STATIC_STRINGS);
/**
* GtkGLArea:context:
*
* The #GdkGLContext used by the #GtkGLArea widget.
*
* The #GtkGLArea widget is responsible for creating the #GdkGLContext
* instance. See the #GtkGLArea::create-context signal on how to
* override the default behavior.
*
* Since: 3.14
*/
obj_props[PROP_CONTEXT] =
g_param_spec_object ("context",
P_("Context"),
P_("The GL context"),
GDK_TYPE_GL_CONTEXT,
G_PARAM_READABLE |
G_PARAM_STATIC_STRINGS);
gobject_class->set_property = gtk_gl_area_set_property;
gobject_class->get_property = gtk_gl_area_get_property;
gobject_class->dispose = gtk_gl_area_dispose;
g_object_class_install_properties (gobject_class, LAST_PROP, obj_props);
/**
* GtkGLArea::create-context:
* @area: the #GtkGLArea that emitted the signal
* @format: the #GdkGLPixelFormat for the OpenGL context
*
* The ::create-context signal is emitted each time a #GtkGLArea needs
* to create a #GdkGLContext for the given pixel format.
*
* Widgets can change #GdkDisplay, #GdkScreen, or #GdkVisual; this
* implies that a valid pixel format for a specific #GdkDisplay may
* not be valid any more after a change in those objects.
*
* The #GtkGLArea widget will presently emit the ::create-context
* signal when:
*
* - the #GtkWidget::screen-changed signal is emitted
* - the #GtkWidget::realize signal is emitted
*
* Returns: (transfer full): a newly created #GdkGLContext; the
* #GtkGLArea widget will take ownership of the returned value.
*
* Since: 3.14
*/
area_signals[CREATE_CONTEXT] =
g_signal_new (I_("create-context"),
G_TYPE_FROM_CLASS (gobject_class),
G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET (GtkGLAreaClass, create_context),
create_context_accumulator, NULL,
_gtk_marshal_OBJECT__OBJECT,
GDK_TYPE_GL_CONTEXT, 1,
GDK_TYPE_GL_PIXEL_FORMAT);
/**
* GtkGLArea::render:
* @area: the #GtkGLArea that emitted the signal
* @context: the #GdkGLContext used by @area
*
* The ::render signal is emitted every time the contents
* of the #GtkGLArea should be redrawn.
*
* The @context is bound to the @area prior to emitting this function,
* and the buffers are flushed once the emission terminates.
*
* Returns: %TRUE to stop other handlers from being invoked for the event.
* %FALSE to propagate the event further.
*
* Since: 3.14
*/
area_signals[RENDER] =
g_signal_new (I_("render"),
G_TYPE_FROM_CLASS (gobject_class),
G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET (GtkGLAreaClass, render),
_gtk_boolean_handled_accumulator, NULL,
NULL,
G_TYPE_BOOLEAN, 1,
GDK_TYPE_GL_CONTEXT);
}
static void
gtk_gl_area_init (GtkGLArea *self)
{
gtk_widget_set_has_window (GTK_WIDGET (self), TRUE);
gtk_widget_set_app_paintable (GTK_WIDGET (self), TRUE);
G_GNUC_BEGIN_IGNORE_DEPRECATIONS
/* FIXME: we need this because double buffering inside GDK will
* clear the GL drawable we use, which means flickering. the
* proper way to fix this is to make GDK understand that a GDK
* window backed by a native window with a GL context will draw
* on the window itself, and that all the other drawing should
* happen on a seperate surface, which will then get blended via
* GL.
*/
gtk_widget_set_double_buffered (GTK_WIDGET (self), FALSE);
G_GNUC_END_IGNORE_DEPRECATIONS
}
/**
* gtk_gl_area_new:
* @pixel_format: a #GdkGLPixelFormat
*
* Creates a new #GtkGLArea widget using the given @pixel_format to
* configure a #GdkGLContext.
*
* Returns: (transfer full): the newly created #GtkGLArea
*
* Since: 3.14
*/
GtkWidget *
gtk_gl_area_new (GdkGLPixelFormat *pixel_format)
{
g_return_val_if_fail (GDK_IS_GL_PIXEL_FORMAT (pixel_format), NULL);
return g_object_new (GTK_TYPE_GL_AREA,
"pixel-format", pixel_format,
NULL);
}
/**
* gtk_gl_area_get_pixel_format:
* @area: a #GtkGLArea
*
* Retrieves the #GdkGLPixelFormat used by @area.
*
* Returns: (transfer none): the #GdkGLPixelFormat
*
* Since: 3.14
*/
GdkGLPixelFormat *
gtk_gl_area_get_pixel_format (GtkGLArea *area)
{
GtkGLAreaPrivate *priv = gtk_gl_area_get_instance_private (area);
g_return_val_if_fail (GTK_IS_GL_AREA (area), NULL);
return priv->pixel_format;
}
/**
* gtk_gl_area_get_context:
* @area: a #GtkGLArea
*
* Retrieves the #GdkGLContext used by @area.
*
* Returns: (transfer none): the #GdkGLContext
*
* Since: 3.14
*/
GdkGLContext *
gtk_gl_area_get_context (GtkGLArea *area)
{
GtkGLAreaPrivate *priv = gtk_gl_area_get_instance_private (area);
g_return_val_if_fail (GTK_IS_GL_AREA (area), NULL);
return priv->context;
}
/**
* gtk_gl_area_make_current:
* @area: a #GtkGLArea
*
* Ensures that the #GdkGLContext used by @area is associated with
* the #GtkGLArea.
*
* This function is automatically called before emitting the
* #GtkGLArea::render signal, and should not be called by
* application code.
*
* Returns: %TRUE if the context was associated successfully with
* the widget
*
* Since: 3.14
*/
gboolean
gtk_gl_area_make_current (GtkGLArea *area)
{
GtkGLAreaPrivate *priv = gtk_gl_area_get_instance_private (area);
GtkWidget *widget;
g_return_val_if_fail (GTK_IS_GL_AREA (area), FALSE);
widget = GTK_WIDGET (area);
g_return_val_if_fail (gtk_widget_get_realized (widget), FALSE);
if (priv->context == NULL)
return FALSE;
gdk_gl_context_set_window (priv->context, gtk_widget_get_window (widget));
return gdk_gl_context_make_current (priv->context);
}
/**
* gtk_gl_area_flush_buffer:
* @area: a #GtkGLArea
*
* Flushes the buffer associated with @area.
*
* This function is automatically called after emitting
* the #GtkGLArea::render signal, and should not be called
* by application code.
*
* Since: 3.14
*/
void
gtk_gl_area_flush_buffer (GtkGLArea *area)
{
GtkGLAreaPrivate *priv = gtk_gl_area_get_instance_private (area);
g_return_if_fail (GTK_IS_GL_AREA (area));
g_return_if_fail (gtk_widget_get_realized (GTK_WIDGET (area)));
if (priv->context == NULL)
return;
gdk_gl_context_flush_buffer (priv->context);
}

96
gtk/gtkglarea.h Normal file
View File

@@ -0,0 +1,96 @@
/* GTK - The GIMP Toolkit
*
* gtkglarea.h: A GL drawing area
*
* Copyright © 2014 Emmanuele Bassi
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef __GTK_GL_AREA_H__
#define __GTK_GL_AREA_H__
#if !defined (__GTK_H_INSIDE__) && !defined (GTK_COMPILATION)
#error "Only <gtk/gtk.h> can be included directly."
#endif
#include <gtk/gtkwidget.h>
G_BEGIN_DECLS
#define GTK_TYPE_GL_AREA (gtk_gl_area_get_type ())
#define GTK_GL_AREA(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GTK_TYPE_GL_AREA, GtkGLArea))
#define GTK_IS_GL_AREA(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GTK_TYPE_GL_AREA))
#define GTK_GL_AREA_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GTK_TYPE_GL_AREA, GtkGLAreaClass))
#define GTK_IS_GL_AREA_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GTK_TYPE_GL_AREA))
#define GTK_GL_AREA_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GTK_TYPE_GL_AREA, GtkGLAreaClass))
typedef struct _GtkGLArea GtkGLArea;
typedef struct _GtkGLAreaClass GtkGLAreaClass;
/**
* GtkGLArea:
*
* A #GtkWidget used for drawing with OpenGL.
*
* Since: 3.14
*/
struct _GtkGLArea
{
/*< private >*/
GtkWidget parent_instance;
};
/**
* GtkGLAreaClass:
* @create_context: class closure for the #GtkGLArea::create-context signal
* @render: class closure for the #GtkGLArea::render signal
*
* The `GtkGLAreaClass` structure contains only private data.
*
* Since: 3.14
*/
struct _GtkGLAreaClass
{
/*< private >*/
GtkWidgetClass parent_class;
/*< public >*/
GdkGLContext * (* create_context) (GtkGLArea *area,
GdkGLPixelFormat *format);
gboolean (* render) (GtkGLArea *area,
GdkGLContext *context);
/*< private >*/
gpointer _padding[6];
};
GDK_AVAILABLE_IN_3_14
GType gtk_gl_area_get_type (void) G_GNUC_CONST;
GDK_AVAILABLE_IN_3_14
GtkWidget * gtk_gl_area_new (GdkGLPixelFormat *format);
GDK_AVAILABLE_IN_3_14
GdkGLContext * gtk_gl_area_get_context (GtkGLArea *area);
GDK_AVAILABLE_IN_3_14
gboolean gtk_gl_area_make_current (GtkGLArea *area);
GDK_AVAILABLE_IN_3_14
void gtk_gl_area_flush_buffer (GtkGLArea *area);
G_END_DECLS
#endif /* __GTK_GL_AREA_H__ */

View File

@@ -124,6 +124,7 @@ VOID:UINT,STRING,UINT
VOID:UINT,UINT
VOID:VOID
OBJECT:OBJECT,INT,INT
OBJECT:OBJECT
VOID:POINTER,POINTER,POINTER,POINTER,STRING
VOID:OBJECT,STRING,POINTER,POINTER
INT:INT

View File

@@ -66,6 +66,7 @@ noinst_PROGRAMS = $(TEST_PROGS) \
testfullscreen \
testgeometry \
testgiconpixbuf \
testglarea \
testgrid \
testgtk \
testheaderbar \

193
tests/testglarea.c Normal file
View File

@@ -0,0 +1,193 @@
#include <stdlib.h>
#include <gtk/gtk.h>
#include <epoxy/gl.h>
enum {
X_AXIS,
Y_AXIS,
Z_AXIS,
N_AXIS
};
static float rotation_angles[N_AXIS] = { 0.0 };
static GtkWidget *gl_area;
static void
draw_triangle (void)
{
glColor3f (1.0f, 0.85f, 0.35f);
glBegin (GL_TRIANGLES);
{
glVertex3f ( 0.0, 0.6, 0.0);
glVertex3f (-0.2, -0.3, 0.0);
glVertex3f ( 0.2, -0.3, 0.0);
}
glEnd ();
}
static gboolean
render (GtkGLArea *area,
GdkGLContext *context)
{
glClearColor (0.5, 0.5, 0.5, 1.0);
glClear (GL_COLOR_BUFFER_BIT);
glMatrixMode (GL_MODELVIEW);
glLoadIdentity ();
glRotatef (rotation_angles[X_AXIS], 1, 0, 0);
glRotatef (rotation_angles[Y_AXIS], 0, 1, 0);
glRotatef (rotation_angles[Z_AXIS], 0, 0, 1);
draw_triangle ();
glFlush ();
return TRUE;
}
static void
init (GtkWidget *widget)
{
GdkGLContext *context = gtk_gl_area_get_context (GTK_GL_AREA (widget));
GdkGLPixelFormat *format = gdk_gl_context_get_pixel_format (context);
g_print ("GL Pixel format:\n"
" - double-buffer: %s\n"
" - multi-sample: %s\n"
" - stereo: %s\n"
" - color-size: %d, alpha-size: %d\n"
" - depth-size: %d\n"
" - stencil-size: %d\n"
" - aux-buffers: %d\n"
" - accum-size: %d\n"
" - sample-buffers: %d\n"
" - samples: %d\n\n",
gdk_gl_pixel_format_get_double_buffer (format) ? "yes" : "no",
gdk_gl_pixel_format_get_multi_sample (format) ? "yes" : "no",
gdk_gl_pixel_format_get_stereo (format) ? "yes" : "no",
gdk_gl_pixel_format_get_color_size (format),
gdk_gl_pixel_format_get_alpha_size (format),
gdk_gl_pixel_format_get_depth_size (format),
gdk_gl_pixel_format_get_stencil_size (format),
gdk_gl_pixel_format_get_aux_buffers (format),
gdk_gl_pixel_format_get_accum_size (format),
gdk_gl_pixel_format_get_sample_buffers (format),
gdk_gl_pixel_format_get_samples (format));
}
static void
on_axis_value_change (GtkAdjustment *adjustment,
gpointer data)
{
int axis = GPOINTER_TO_INT (data);
if (axis < 0 || axis >= N_AXIS)
return;
rotation_angles[axis] = gtk_adjustment_get_value (adjustment);
gtk_widget_queue_draw (gl_area);
}
static GtkWidget *
create_axis_slider (int axis)
{
GtkWidget *box, *label, *slider;
GtkAdjustment *adj;
const char *text;
box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, FALSE);
switch (axis)
{
case X_AXIS:
text = "X axis";
break;
case Y_AXIS:
text = "Y axis";
break;
case Z_AXIS:
text = "Z axis";
break;
default:
g_assert_not_reached ();
}
label = gtk_label_new (text);
gtk_container_add (GTK_CONTAINER (box), label);
gtk_widget_show (label);
adj = gtk_adjustment_new (0.0, 0.0, 360.0, 1.0, 12.0, 0.0);
g_signal_connect (adj, "value-changed",
G_CALLBACK (on_axis_value_change),
GINT_TO_POINTER (axis));
slider = gtk_scale_new (GTK_ORIENTATION_HORIZONTAL, adj);
gtk_container_add (GTK_CONTAINER (box), slider);
gtk_widget_set_hexpand (slider, TRUE);
gtk_widget_show (slider);
gtk_widget_show (box);
return box;
}
int
main (int argc, char *argv[])
{
GtkWidget *window, *box, *button, *controls;
GdkGLPixelFormat *pixel_format;
int i;
gtk_init (&argc, &argv);
/* create a new pixel format; we use this to configure the
* GL context, and to check for features
*/
pixel_format = gdk_gl_pixel_format_new ("double-buffer", TRUE,
NULL);
window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
gtk_window_set_title (GTK_WINDOW (window), "GtkGLArea - Golden Triangle");
gtk_window_set_default_size (GTK_WINDOW (window), 400, 400);
gtk_container_set_border_width (GTK_CONTAINER (window), 12);
g_signal_connect (window, "destroy", G_CALLBACK (gtk_main_quit), NULL);
gtk_widget_show (window);
box = gtk_box_new (GTK_ORIENTATION_VERTICAL, FALSE);
gtk_box_set_spacing (GTK_BOX (box), 6);
gtk_container_add (GTK_CONTAINER (window), box);
gtk_widget_show (box);
gl_area = gtk_gl_area_new (pixel_format);
gtk_widget_set_hexpand (gl_area, TRUE);
gtk_widget_set_vexpand (gl_area, TRUE);
gtk_container_add (GTK_CONTAINER (box), gl_area);
g_signal_connect (gl_area, "realize", G_CALLBACK (init), NULL);
g_signal_connect (gl_area, "render", G_CALLBACK (render), NULL);
gtk_widget_show (gl_area);
g_object_unref (pixel_format);
controls = gtk_box_new (GTK_ORIENTATION_VERTICAL, FALSE);
gtk_container_add (GTK_CONTAINER (box), controls);
gtk_widget_set_hexpand (controls, TRUE);
gtk_widget_show (controls);
for (i = 0; i < N_AXIS; i++)
gtk_container_add (GTK_CONTAINER (controls), create_axis_slider (i));
button = gtk_button_new_with_label ("Quit");
gtk_widget_set_hexpand (button, TRUE);
gtk_container_add (GTK_CONTAINER (box), button);
g_signal_connect_swapped (button, "clicked", G_CALLBACK (gtk_widget_destroy), window);
gtk_widget_show (button);
gtk_main ();
return EXIT_SUCCESS;
}