Compare commits
26 Commits
matthiasc/
...
wip/gdk-gl
Author | SHA1 | Date | |
---|---|---|---|
|
b12148ef62 | ||
|
26ad6184a4 | ||
|
909b2fe589 | ||
|
29d23aaa96 | ||
|
ba1fe1689b | ||
|
d0fe5ce023 | ||
|
35afc38e29 | ||
|
258060f158 | ||
|
1dd4989bbc | ||
|
e500a156e0 | ||
|
c080e25dd6 | ||
|
519bea5185 | ||
|
b24d58a5f6 | ||
|
d4979fb82a | ||
|
46c6bca189 | ||
|
a3a4384047 | ||
|
6f3d00c0fd | ||
|
ea50477aa7 | ||
|
8bc17f1e5e | ||
|
224404cb2d | ||
|
5b649ea4fe | ||
|
1cc57bd8af | ||
|
946ab24cf7 | ||
|
91f99ed807 | ||
|
76ab606cef | ||
|
5881b5e453 |
@@ -48,6 +48,7 @@ m4_define([cairo_required_version], [1.12.0])
|
||||
m4_define([gdk_pixbuf_required_version], [2.30.0])
|
||||
m4_define([introspection_required_version], [1.39.0])
|
||||
m4_define([wayland_required_version], [1.5.91])
|
||||
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
|
||||
@@ -426,7 +427,7 @@ fi
|
||||
|
||||
PKG_PROG_PKG_CONFIG
|
||||
|
||||
WAYLAND_DEPENDENCIES="wayland-client >= wayland_required_version xkbcommon >= 0.2.0 wayland-cursor >= wayland_required_version"
|
||||
WAYLAND_DEPENDENCIES="wayland-client >= wayland_required_version xkbcommon >= 0.2.0 wayland-cursor >= wayland_required_version wayland-egl >= wayland_required_version"
|
||||
if test "$enable_wayland_backend" = "maybe" ; then
|
||||
AC_PATH_PROG([WAYLAND_SCANNER],[wayland-scanner],[no])
|
||||
PKG_CHECK_EXISTS($WAYLAND_DEPENDENCIES, [have_wayland_deps=yes], [have_wayland_deps=no])
|
||||
@@ -1326,7 +1327,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
|
||||
|
@@ -25,6 +25,7 @@ demos = \
|
||||
event_axes.c \
|
||||
expander.c \
|
||||
gestures.c \
|
||||
glarea.c \
|
||||
headerbar.c \
|
||||
hypertext.c \
|
||||
iconview.c \
|
||||
|
@@ -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>
|
||||
|
184
demos/gtk-demo/glarea.c
Normal file
184
demos/gtk-demo/glarea.c
Normal file
@@ -0,0 +1,184 @@
|
||||
/* 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;
|
||||
}
|
||||
|
||||
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;
|
||||
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);
|
||||
|
||||
gl_area = gtk_gl_area_new ();
|
||||
gtk_widget_set_hexpand (gl_area, TRUE);
|
||||
gtk_widget_set_vexpand (gl_area, TRUE);
|
||||
gtk_container_add (GTK_CONTAINER (box), gl_area);
|
||||
|
||||
/* the main "draw" call for GtkGLArea */
|
||||
g_signal_connect (gl_area, "render", G_CALLBACK (render), NULL);
|
||||
|
||||
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;
|
||||
}
|
@@ -32,6 +32,7 @@
|
||||
<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/events.xml" />
|
||||
<xi:include href="xml/event_structs.xml" />
|
||||
<xi:include href="xml/keys.xml" />
|
||||
|
@@ -394,6 +394,8 @@ gdk_window_constrain_size
|
||||
gdk_window_beep
|
||||
gdk_window_get_scale_factor
|
||||
gdk_window_set_opaque_region
|
||||
gdk_window_create_gl_context
|
||||
gdk_window_mark_paint_from_clip
|
||||
|
||||
<SUBSECTION>
|
||||
gdk_window_get_clip_region
|
||||
@@ -617,6 +619,7 @@ gdk_cairo_rectangle
|
||||
gdk_cairo_region
|
||||
gdk_cairo_region_create_from_surface
|
||||
gdk_cairo_surface_create_from_pixbuf
|
||||
gdk_cairo_draw_from_gl
|
||||
</SECTION>
|
||||
|
||||
<SECTION>
|
||||
@@ -1004,6 +1007,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 +1127,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 +1282,28 @@ gdk_frame_timings_get_predicted_presentation_time
|
||||
<SUBSECTION Private>
|
||||
gdk_frame_get_type
|
||||
</SECTION>
|
||||
|
||||
<SECTION>
|
||||
<FILE>gdkglcontext</FILE>
|
||||
GdkGLContext
|
||||
gdk_gl_context_get_visual
|
||||
gdk_gl_context_get_window
|
||||
gdk_gl_context_make_current
|
||||
gdk_gl_context_clear_current
|
||||
gdk_gl_context_get_current
|
||||
GDK_GL_ERROR
|
||||
GDK_TYPE_GL_ERROR
|
||||
GDK_TYPE_GL_PROFILE
|
||||
GdkGLProfile
|
||||
GdkGLError
|
||||
|
||||
<SUBSECTION Standard>
|
||||
GDK_GL_CONTEXT
|
||||
GDK_IS_GL_CONTEXT
|
||||
GDK_TYPE_GL_CONTEXT
|
||||
GDK_GL_CONTEXT_CLASS
|
||||
GDK_GL_CONTEXT_GET_CLASS
|
||||
GDK_IS_GL_CONTEXT_CLASS
|
||||
gdk_gl_context_get_type
|
||||
gdk_gl_error_quark
|
||||
</SECTION>
|
||||
|
@@ -8,6 +8,7 @@ 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_keymap_get_type
|
||||
gdk_screen_get_type
|
||||
gdk_visual_get_type
|
||||
|
@@ -242,6 +242,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" />
|
||||
|
@@ -8166,7 +8166,6 @@ GTK_GESTURE_ZOOM_GET_CLASS
|
||||
gtk_gesture_zoom_get_type
|
||||
</SECTION>
|
||||
|
||||
|
||||
<SECTION>
|
||||
<FILE>gtksidebar</FILE>
|
||||
GtkSidebar
|
||||
@@ -8185,3 +8184,25 @@ GTK_SIDEBAR_GET_CLASS
|
||||
GtkSidebarPrivate
|
||||
gtk_sidebar_get_type
|
||||
</SECTION>
|
||||
|
||||
<SECTION>
|
||||
<FILE>gtkglarea</FILE>
|
||||
GtkGLArea
|
||||
GtkGLAreaClass
|
||||
gtk_gl_area_new
|
||||
gtk_gl_area_get_context
|
||||
<SUBSECTION>
|
||||
gtk_gl_area_set_has_alpha
|
||||
gtk_gl_area_get_has_alpha
|
||||
gtk_gl_area_set_has_depth_buffer
|
||||
gtk_gl_area_get_has_depth_buffer
|
||||
gtk_gl_area_make_current
|
||||
<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>
|
||||
|
@@ -71,6 +71,7 @@ gdk_public_h_sources = \
|
||||
gdkdnd.h \
|
||||
gdkevents.h \
|
||||
gdkframetimings.h \
|
||||
gdkglcontext.h \
|
||||
gdkkeys.h \
|
||||
gdkkeysyms.h \
|
||||
gdkkeysyms-compat.h \
|
||||
@@ -107,6 +108,7 @@ gdk_private_headers = \
|
||||
gdkdndprivate.h \
|
||||
gdkframeclockidle.h \
|
||||
gdkframeclockprivate.h \
|
||||
gdkglcontextprivate.h \
|
||||
gdkscreenprivate.h \
|
||||
gdkinternals.h \
|
||||
gdkintl.h \
|
||||
@@ -131,6 +133,8 @@ gdk_c_sources = \
|
||||
gdkdnd.c \
|
||||
gdkevents.c \
|
||||
gdkframetimings.c \
|
||||
gdkgl.c \
|
||||
gdkglcontext.c \
|
||||
gdkglobals.c \
|
||||
gdkkeys.c \
|
||||
gdkkeyuni.c \
|
||||
|
@@ -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
|
||||
|
@@ -41,6 +41,7 @@
|
||||
#include <gdk/gdkevents.h>
|
||||
#include <gdk/gdkframeclock.h>
|
||||
#include <gdk/gdkframetimings.h>
|
||||
#include <gdk/gdkglcontext.h>
|
||||
#include <gdk/gdkkeys.h>
|
||||
#include <gdk/gdkkeysyms.h>
|
||||
#include <gdk/gdkmain.h>
|
||||
|
@@ -525,3 +525,38 @@ gdk_cairo_region_create_from_surface (cairo_surface_t *surface)
|
||||
|
||||
return region;
|
||||
}
|
||||
|
||||
cairo_region_t *
|
||||
gdk_cairo_region_from_clip (cairo_t *cr)
|
||||
{
|
||||
cairo_rectangle_list_t *rectangles;
|
||||
cairo_region_t *region;
|
||||
int i;
|
||||
|
||||
rectangles = cairo_copy_clip_rectangle_list (cr);
|
||||
|
||||
if (rectangles->status != CAIRO_STATUS_SUCCESS)
|
||||
return NULL;
|
||||
|
||||
region = cairo_region_create ();
|
||||
for (i = 0; i < rectangles->num_rectangles; i++)
|
||||
{
|
||||
cairo_rectangle_int_t clip_rect;
|
||||
cairo_rectangle_t *rect;
|
||||
|
||||
rect = &rectangles->rectangles[i];
|
||||
|
||||
/* Here we assume clip rects are ints for direct targets, which
|
||||
is true for cairo */
|
||||
clip_rect.x = (int)rect->x;
|
||||
clip_rect.y = (int)rect->y;
|
||||
clip_rect.width = (int)rect->width;
|
||||
clip_rect.height = (int)rect->height;
|
||||
|
||||
cairo_region_union_rectangle (region, &clip_rect);
|
||||
}
|
||||
|
||||
cairo_rectangle_list_destroy (rectangles);
|
||||
|
||||
return region;
|
||||
}
|
||||
|
@@ -70,6 +70,17 @@ GDK_AVAILABLE_IN_3_10
|
||||
cairo_surface_t * gdk_cairo_surface_create_from_pixbuf (const GdkPixbuf *pixbuf,
|
||||
int scale,
|
||||
GdkWindow *for_window);
|
||||
GDK_AVAILABLE_IN_3_16
|
||||
void gdk_cairo_draw_from_gl (cairo_t *cr,
|
||||
GdkWindow *window,
|
||||
int source,
|
||||
int source_type,
|
||||
int buffer_scale,
|
||||
int x,
|
||||
int y,
|
||||
int width,
|
||||
int height);
|
||||
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
|
@@ -2225,3 +2225,66 @@ 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)
|
||||
{
|
||||
GdkGLContext *current = gdk_display_get_current_gl_context (display);
|
||||
|
||||
if (current == context)
|
||||
g_object_set_data (G_OBJECT (display), "-gdk-gl-current-context", NULL);
|
||||
|
||||
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
|
||||
*
|
||||
* Makes the given @context the current GL context, or unsets
|
||||
* the current GL context if @context is %NULL.
|
||||
*/
|
||||
void
|
||||
gdk_display_make_gl_context_current (GdkDisplay *display,
|
||||
GdkGLContext *context)
|
||||
{
|
||||
GdkGLContext *current = gdk_display_get_current_gl_context (display);
|
||||
|
||||
if (current == context)
|
||||
return;
|
||||
|
||||
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);
|
||||
|
||||
GDK_DISPLAY_GET_CLASS (display)->make_gl_context_current (display, context);
|
||||
}
|
||||
|
||||
/*< 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");
|
||||
}
|
||||
|
@@ -225,6 +225,11 @@ struct _GdkDisplayClass
|
||||
gchar * (*utf8_to_string_target) (GdkDisplay *display,
|
||||
const gchar *text);
|
||||
|
||||
void (*make_gl_context_current) (GdkDisplay *display,
|
||||
GdkGLContext *context);
|
||||
void (*destroy_gl_context) (GdkDisplay *display,
|
||||
GdkGLContext *context);
|
||||
|
||||
/* Signals */
|
||||
void (*opened) (GdkDisplay *display);
|
||||
void (*closed) (GdkDisplay *display,
|
||||
@@ -303,6 +308,12 @@ void _gdk_display_create_window_impl (GdkDisplay *display
|
||||
gint attributes_mask);
|
||||
GdkWindow * _gdk_display_create_window (GdkDisplay *display);
|
||||
|
||||
void gdk_display_destroy_gl_context (GdkDisplay *display,
|
||||
GdkGLContext *context);
|
||||
void gdk_display_make_gl_context_current (GdkDisplay *display,
|
||||
GdkGLContext *context);
|
||||
GdkGLContext * gdk_display_get_current_gl_context (GdkDisplay *display);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __GDK_DISPLAY_PRIVATE_H__ */
|
||||
|
461
gdk/gdkgl.c
Normal file
461
gdk/gdkgl.c
Normal file
@@ -0,0 +1,461 @@
|
||||
/* GDK - The GIMP Drawing Kit
|
||||
* Copyright (C) 2014 Red Hat, Inc.
|
||||
*
|
||||
* 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 "gdkcairo.h"
|
||||
#include "gdkglcontextprivate.h"
|
||||
|
||||
#include "gdkinternals.h"
|
||||
|
||||
#include <epoxy/gl.h>
|
||||
#include <math.h>
|
||||
|
||||
static cairo_user_data_key_t direct_key;
|
||||
|
||||
void
|
||||
gdk_cairo_surface_mark_as_direct (cairo_surface_t *surface,
|
||||
GdkWindow *window)
|
||||
{
|
||||
cairo_surface_set_user_data (surface, &direct_key,
|
||||
g_object_ref (window), g_object_unref);
|
||||
}
|
||||
|
||||
/* x,y,width,height describes a rectangle in the gl render buffer
|
||||
coordinate space, and its top left corner is drawn at the current
|
||||
position according to the cairo translation. */
|
||||
|
||||
/**
|
||||
* gdk_cairo_draw_from_gl:
|
||||
* @cr: a cairo context
|
||||
* @window: The window we're rendering for (not necessarily into)
|
||||
* @source: The GL ID of the source buffer
|
||||
* @source_type: The type of the @source
|
||||
* @buffer_scale: The scale-factor that the @source buffer is allocated for
|
||||
* @x: The source x position in @source to start copying from in GL coordinates
|
||||
* @y: The source y position in @source to start copying from in GL coordinates
|
||||
* @width: The width of the region to draw
|
||||
* @height: The height of the region to draw
|
||||
*
|
||||
* This is the main way to draw GL content in GTK+. It takes a render buffer ID
|
||||
* (@source_type == #GL_RENDERBUFFER) or a texture id (@source_type == #GL_TEXTURE)
|
||||
* and draws it onto @cr with an OVER operation, respecting the current clip.
|
||||
*
|
||||
* This will work for *all* cairo_t, as long as @window is realized, but the
|
||||
* fallback implementation that reads back the pixels from the buffer may be
|
||||
* used in the general case. In the case of direct drawing to a window with
|
||||
* no special effects applied to @cr it will however use a more efficient
|
||||
* approach.
|
||||
*
|
||||
* For #GL_RENDERBUFFER the code will always fall back to software for buffers
|
||||
* with alpha components, so make sure you use #GL_TEXTURE if using alpha.
|
||||
*
|
||||
* Since: 3.16
|
||||
*/
|
||||
void
|
||||
gdk_cairo_draw_from_gl (cairo_t *cr,
|
||||
GdkWindow *window,
|
||||
int source,
|
||||
int source_type,
|
||||
int buffer_scale,
|
||||
int x,
|
||||
int y,
|
||||
int width,
|
||||
int height)
|
||||
{
|
||||
GdkGLContext *context;
|
||||
cairo_surface_t *image;
|
||||
cairo_matrix_t matrix;
|
||||
int dx, dy, window_scale;
|
||||
gboolean trivial_transform;
|
||||
cairo_surface_t *group_target;
|
||||
GdkWindow *direct_window, *impl_window;
|
||||
GLuint framebuffer;
|
||||
GLint alpha_size = 0;
|
||||
cairo_region_t *clip_region;
|
||||
|
||||
impl_window = window->impl_window;
|
||||
|
||||
window_scale = gdk_window_get_scale_factor (impl_window);
|
||||
|
||||
context = gdk_window_get_paint_gl_context (window, NULL);
|
||||
if (context == NULL)
|
||||
{
|
||||
g_warning ("gdk_cairo_draw_gl_render_buffer failed - no paint context");
|
||||
return;
|
||||
}
|
||||
|
||||
clip_region = gdk_cairo_region_from_clip (cr);
|
||||
|
||||
gdk_gl_context_make_current (context);
|
||||
|
||||
glGenFramebuffersEXT (1, &framebuffer);
|
||||
glBindFramebufferEXT (GL_FRAMEBUFFER_EXT, framebuffer);
|
||||
|
||||
if (source_type == GL_RENDERBUFFER)
|
||||
{
|
||||
glGetRenderbufferParameteriv (GL_RENDERBUFFER, GL_RENDERBUFFER_ALPHA_SIZE, &alpha_size);
|
||||
|
||||
glBindRenderbufferEXT (GL_RENDERBUFFER_EXT, source);
|
||||
glFramebufferRenderbufferEXT (GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT,
|
||||
GL_RENDERBUFFER_EXT, source);
|
||||
glBindFramebufferEXT(GL_DRAW_FRAMEBUFFER_EXT, 0);
|
||||
}
|
||||
else if (source_type == GL_TEXTURE)
|
||||
{
|
||||
glGetTexLevelParameteriv (GL_TEXTURE_2D, 0, GL_TEXTURE_ALPHA_SIZE, &alpha_size);
|
||||
|
||||
glBindTexture (GL_TEXTURE_2D, source);
|
||||
glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
|
||||
glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
|
||||
glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
|
||||
glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
||||
glFramebufferTexture2DEXT (GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT,
|
||||
GL_TEXTURE_2D, source, 0);
|
||||
glBindFramebufferEXT(GL_DRAW_FRAMEBUFFER_EXT, 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
g_warning ("Unsupported gl source type %d\n", source_type);
|
||||
return;
|
||||
}
|
||||
|
||||
group_target = cairo_get_group_target (cr);
|
||||
direct_window = cairo_surface_get_user_data (group_target, &direct_key);
|
||||
|
||||
cairo_get_matrix (cr, &matrix);
|
||||
|
||||
dx = matrix.x0;
|
||||
dy = matrix.y0;
|
||||
|
||||
/* Trivial == integer-only translation */
|
||||
trivial_transform =
|
||||
(double)dx == matrix.x0 && (double)dy == matrix.y0 &&
|
||||
matrix.xx == 1.0 && matrix.xy == 0.0 &&
|
||||
matrix.yx == 0.0 && matrix.yy == 1.0;
|
||||
|
||||
/* For direct paint of non-alpha renderbuffer, we can
|
||||
just do a bitblit */
|
||||
if (source_type == GL_RENDERBUFFER &&
|
||||
alpha_size == 0 &&
|
||||
direct_window != NULL &&
|
||||
direct_window->current_paint.use_gl &&
|
||||
trivial_transform &&
|
||||
clip_region != NULL)
|
||||
{
|
||||
int window_height;
|
||||
int i;
|
||||
|
||||
/* Translate to impl coords */
|
||||
cairo_region_translate (clip_region, dx, dy);
|
||||
|
||||
glEnable (GL_SCISSOR_TEST);
|
||||
|
||||
window_height = gdk_window_get_height (impl_window);
|
||||
glDrawBuffer (GL_BACK);
|
||||
|
||||
#define FLIP_Y(_y) (window_height*window_scale - (_y))
|
||||
|
||||
for (i = 0; i < cairo_region_num_rectangles (clip_region); i++)
|
||||
{
|
||||
cairo_rectangle_int_t clip_rect, dest;
|
||||
|
||||
cairo_region_get_rectangle (clip_region, i, &clip_rect);
|
||||
clip_rect.x *= window_scale;
|
||||
clip_rect.y *= window_scale;
|
||||
clip_rect.width *= window_scale;
|
||||
clip_rect.height *= window_scale;
|
||||
|
||||
glScissor (clip_rect.x, FLIP_Y (clip_rect.y + clip_rect.height),
|
||||
clip_rect.width, clip_rect.height);
|
||||
|
||||
dest.x = dx * window_scale;
|
||||
dest.y = dy * window_scale;
|
||||
dest.width = width * window_scale / buffer_scale;
|
||||
dest.height = height * window_scale / buffer_scale;
|
||||
|
||||
if (gdk_rectangle_intersect (&clip_rect, &dest, &dest))
|
||||
{
|
||||
int clipped_src_x = x + (dest.x - dx * window_scale);
|
||||
int clipped_src_y = y + (height - dest.height - (dest.y - dy * window_scale));
|
||||
glBlitFramebufferEXT(clipped_src_x, clipped_src_y,
|
||||
(clipped_src_x + dest.width), (clipped_src_y + dest.height),
|
||||
dest.x, FLIP_Y(dest.y + dest.height),
|
||||
dest.x + dest.width, FLIP_Y(dest.y),
|
||||
GL_COLOR_BUFFER_BIT, GL_NEAREST);
|
||||
if (impl_window->current_paint.flushed_region)
|
||||
{
|
||||
cairo_rectangle_int_t flushed_rect;
|
||||
|
||||
flushed_rect.x = dest.x / window_scale;
|
||||
flushed_rect.y = dest.y / window_scale;
|
||||
flushed_rect.width = (dest.x + dest.width + window_scale - 1) / window_scale - flushed_rect.x;
|
||||
flushed_rect.height = (dest.y + dest.height + window_scale - 1) / window_scale - flushed_rect.y;
|
||||
|
||||
cairo_region_union_rectangle (impl_window->current_paint.flushed_region,
|
||||
&flushed_rect);
|
||||
cairo_region_subtract_rectangle (impl_window->current_paint.need_blend_region,
|
||||
&flushed_rect);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
glDisable (GL_SCISSOR_TEST);
|
||||
|
||||
#undef FLIP_Y
|
||||
|
||||
}
|
||||
/* For direct paint of alpha or non-alpha textures we can use texturing */
|
||||
else if (source_type == GL_TEXTURE &&
|
||||
direct_window != NULL &&
|
||||
direct_window->current_paint.use_gl &&
|
||||
trivial_transform &&
|
||||
clip_region != NULL)
|
||||
{
|
||||
int window_height;
|
||||
GLint texture_width;
|
||||
GLint texture_height;
|
||||
int i;
|
||||
|
||||
/* Translate to impl coords */
|
||||
cairo_region_translate (clip_region, dx, dy);
|
||||
|
||||
glGetTexLevelParameteriv (GL_TEXTURE_2D, 0, GL_TEXTURE_WIDTH, &texture_width);
|
||||
glGetTexLevelParameteriv (GL_TEXTURE_2D, 0, GL_TEXTURE_HEIGHT, &texture_height);
|
||||
|
||||
if (alpha_size != 0)
|
||||
{
|
||||
cairo_region_t *opaque_region, *blend_region;
|
||||
|
||||
opaque_region = cairo_region_copy (clip_region);
|
||||
cairo_region_subtract (opaque_region, impl_window->current_paint.flushed_region);
|
||||
cairo_region_subtract (opaque_region, impl_window->current_paint.need_blend_region);
|
||||
|
||||
if (!cairo_region_is_empty (opaque_region))
|
||||
gdk_gl_texture_from_surface (impl_window->current_paint.surface,
|
||||
opaque_region);
|
||||
|
||||
blend_region = cairo_region_copy (clip_region);
|
||||
cairo_region_intersect (blend_region, impl_window->current_paint.need_blend_region);
|
||||
|
||||
glEnable (GL_BLEND);
|
||||
if (!cairo_region_is_empty (blend_region))
|
||||
gdk_gl_texture_from_surface (impl_window->current_paint.surface,
|
||||
blend_region);
|
||||
|
||||
cairo_region_destroy (opaque_region);
|
||||
cairo_region_destroy (blend_region);
|
||||
}
|
||||
glEnable (GL_SCISSOR_TEST);
|
||||
glEnable (GL_TEXTURE_2D);
|
||||
|
||||
window_height = gdk_window_get_height (impl_window);
|
||||
|
||||
#define FLIP_Y(_y) (window_height*window_scale - (_y))
|
||||
|
||||
for (i = 0; i < cairo_region_num_rectangles (clip_region); i++)
|
||||
{
|
||||
cairo_rectangle_int_t clip_rect, dest;
|
||||
|
||||
cairo_region_get_rectangle (clip_region, i, &clip_rect);
|
||||
|
||||
clip_rect.x *= window_scale;
|
||||
clip_rect.y *= window_scale;
|
||||
clip_rect.width *= window_scale;
|
||||
clip_rect.height *= window_scale;
|
||||
|
||||
glScissor (clip_rect.x, FLIP_Y (clip_rect.y + clip_rect.height),
|
||||
clip_rect.width, clip_rect.height);
|
||||
|
||||
dest.x = dx * window_scale;
|
||||
dest.y = dy * window_scale;
|
||||
dest.width = width * window_scale / buffer_scale;
|
||||
dest.height = height * window_scale / buffer_scale;
|
||||
|
||||
if (gdk_rectangle_intersect (&clip_rect, &dest, &dest))
|
||||
{
|
||||
int clipped_src_x = x + (dest.x - dx * window_scale);
|
||||
int clipped_src_y = y + (height - dest.height - (dest.y - dy * window_scale));
|
||||
|
||||
glBegin (GL_QUADS);
|
||||
glTexCoord2f (clipped_src_x / (float)texture_width, clipped_src_y / (float)texture_height);
|
||||
glVertex2f (dest.x, FLIP_Y(dest.y + dest.height));
|
||||
|
||||
glTexCoord2f ((clipped_src_x + dest.width) / (float)texture_width, clipped_src_y / (float)texture_height);
|
||||
glVertex2f (dest.x + dest.width, FLIP_Y(dest.y + dest.height));
|
||||
|
||||
glTexCoord2f ((clipped_src_x + dest.width) / (float)texture_width, (clipped_src_y + dest.height) / (float)texture_height);
|
||||
glVertex2f (dest.x + dest.width, FLIP_Y(dest.y));
|
||||
|
||||
glTexCoord2f (clipped_src_x / (float)texture_width, (clipped_src_y + dest.height) / (float)texture_height);
|
||||
glVertex2f (dest.x, FLIP_Y(dest.y));
|
||||
glEnd();
|
||||
|
||||
if (impl_window->current_paint.flushed_region)
|
||||
{
|
||||
cairo_rectangle_int_t flushed_rect;
|
||||
|
||||
flushed_rect.x = dest.x / window_scale;
|
||||
flushed_rect.y = dest.y / window_scale;
|
||||
flushed_rect.width = (dest.x + dest.width + window_scale - 1) / window_scale - flushed_rect.x;
|
||||
flushed_rect.height = (dest.y + dest.height + window_scale - 1) / window_scale - flushed_rect.y;
|
||||
|
||||
cairo_region_union_rectangle (impl_window->current_paint.flushed_region,
|
||||
&flushed_rect);
|
||||
cairo_region_subtract_rectangle (impl_window->current_paint.need_blend_region,
|
||||
&flushed_rect);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (alpha_size != 0)
|
||||
glDisable (GL_BLEND);
|
||||
|
||||
glDisable (GL_TEXTURE_2D);
|
||||
glDisable (GL_SCISSOR_TEST);
|
||||
|
||||
#undef FLIP_Y
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Software fallback */
|
||||
|
||||
/* TODO: avoid reading back non-required data due to dest clip */
|
||||
image = cairo_surface_create_similar_image (cairo_get_target (cr),
|
||||
(alpha_size == 0) ? CAIRO_FORMAT_RGB24 : CAIRO_FORMAT_ARGB32,
|
||||
width, height);
|
||||
|
||||
#ifdef HAVE_CAIRO_SURFACE_SET_DEVICE_SCALE
|
||||
cairo_surface_set_device_scale (image, buffer_scale, buffer_scale);
|
||||
#endif
|
||||
|
||||
glPixelStorei (GL_PACK_ALIGNMENT, 4);
|
||||
glPixelStorei (GL_PACK_ROW_LENGTH, cairo_image_surface_get_stride (image) / 4);
|
||||
|
||||
glReadPixels (x, y, width, height, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV,
|
||||
cairo_image_surface_get_data (image));
|
||||
|
||||
glPixelStorei (GL_PACK_ROW_LENGTH, 0);
|
||||
|
||||
cairo_surface_mark_dirty (image);
|
||||
|
||||
/* Invert due to opengl having different origin */
|
||||
cairo_scale (cr, 1, -1);
|
||||
cairo_translate (cr, 0, -height / buffer_scale);
|
||||
|
||||
cairo_set_source_surface (cr, image, 0, 0);
|
||||
cairo_set_operator (cr, CAIRO_OPERATOR_OVER);
|
||||
cairo_paint (cr);
|
||||
|
||||
cairo_surface_destroy (image);
|
||||
}
|
||||
|
||||
glDrawBuffer (GL_BACK);
|
||||
glReadBuffer(GL_BACK);
|
||||
|
||||
glBindFramebufferEXT (GL_FRAMEBUFFER_EXT, 0);
|
||||
glDeleteFramebuffersEXT (1, &framebuffer);
|
||||
|
||||
if (clip_region)
|
||||
cairo_region_destroy (clip_region);
|
||||
}
|
||||
|
||||
void
|
||||
gdk_gl_texture_from_surface (cairo_surface_t *surface,
|
||||
cairo_region_t *region)
|
||||
{
|
||||
GdkGLContext *current;
|
||||
cairo_surface_t *image;
|
||||
double device_x_offset, device_y_offset;
|
||||
cairo_rectangle_int_t rect, e;
|
||||
int n_rects, i;
|
||||
GdkWindow *window;
|
||||
int window_height;
|
||||
unsigned int texture_id;
|
||||
int window_scale;
|
||||
double sx, sy;
|
||||
|
||||
current = gdk_gl_context_get_current ();
|
||||
if (current &&
|
||||
GDK_GL_CONTEXT_GET_CLASS (current)->texture_from_surface &&
|
||||
GDK_GL_CONTEXT_GET_CLASS (current)->texture_from_surface (current, surface, region))
|
||||
return;
|
||||
|
||||
/* Software fallback */
|
||||
|
||||
window = gdk_gl_context_get_window (gdk_gl_context_get_current ());
|
||||
window_scale = gdk_window_get_scale_factor (window);
|
||||
window_height = gdk_window_get_height (window);
|
||||
|
||||
sx = sy = 1;
|
||||
#ifdef HAVE_CAIRO_SURFACE_SET_DEVICE_SCALE
|
||||
cairo_surface_get_device_scale (window->current_paint.surface, &sx, &sy);
|
||||
#endif
|
||||
|
||||
cairo_surface_get_device_offset (surface,
|
||||
&device_x_offset, &device_y_offset);
|
||||
|
||||
glGenTextures (1, &texture_id);
|
||||
glBindTexture (GL_TEXTURE_RECTANGLE_ARB, texture_id);
|
||||
glEnable (GL_TEXTURE_RECTANGLE_ARB);
|
||||
|
||||
n_rects = cairo_region_num_rectangles (region);
|
||||
for (i = 0; i < n_rects; i++)
|
||||
{
|
||||
cairo_region_get_rectangle (region, i, &rect);
|
||||
|
||||
glScissor (rect.x * window_scale, (window_height - rect.y - rect.height) * window_scale,
|
||||
rect.width * window_scale, rect.height * window_scale);
|
||||
|
||||
e = rect;
|
||||
e.x *= sx;
|
||||
e.y *= sy;
|
||||
e.x += (int)device_x_offset;
|
||||
e.y += (int)device_y_offset;
|
||||
e.width *= sx;
|
||||
e.height *= sy;
|
||||
image = cairo_surface_map_to_image (surface, &e);
|
||||
|
||||
glPixelStorei (GL_UNPACK_ALIGNMENT, 4);
|
||||
glPixelStorei (GL_UNPACK_ROW_LENGTH, cairo_image_surface_get_stride (image)/4);
|
||||
glTexImage2D (GL_TEXTURE_RECTANGLE_ARB, 0, 4, e.width, e.height, 0, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV,
|
||||
cairo_image_surface_get_data (image));
|
||||
glPixelStorei (GL_UNPACK_ROW_LENGTH, 0);
|
||||
|
||||
cairo_surface_unmap_image (surface, image);
|
||||
|
||||
#define FLIP_Y(_y) (window_height - (_y))
|
||||
|
||||
glBegin (GL_QUADS);
|
||||
glTexCoord2f (0.0f * sx, rect.height * sy);
|
||||
glVertex2f (rect.x * window_scale, FLIP_Y(rect.y + rect.height) * window_scale);
|
||||
|
||||
glTexCoord2f (rect.width * sx, rect.height * sy);
|
||||
glVertex2f ((rect.x + rect.width) * window_scale, FLIP_Y(rect.y + rect.height) * window_scale);
|
||||
|
||||
glTexCoord2f (rect.width * sx, 0.0f * sy);
|
||||
glVertex2f ((rect.x + rect.width) * window_scale, FLIP_Y(rect.y) * window_scale);
|
||||
|
||||
glTexCoord2f (0.0f * sx, 0.0f * sy);
|
||||
glVertex2f (rect.x * window_scale, FLIP_Y(rect.y) * window_scale);
|
||||
glEnd();
|
||||
}
|
||||
|
||||
glDisable (GL_TEXTURE_RECTANGLE_ARB);
|
||||
glDeleteTextures (1, &texture_id);
|
||||
}
|
339
gdk/gdkglcontext.c
Normal file
339
gdk/gdkglcontext.c
Normal file
@@ -0,0 +1,339 @@
|
||||
/* 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 for a #GdkWindow using
|
||||
* gdk_window_create_gl_context(), and the context will match
|
||||
* the #GdkVisual of the window.
|
||||
*
|
||||
* A #GdkGLContext is not tied to any particular normal framebuffer.
|
||||
* For instance, it cannot draw to the #GdkWindow back buffer. The GDK
|
||||
* repaint system is in full control of the painting to that. Instead,
|
||||
* you can create render buffers or textures and use gdk_cairo_draw_from_gl()
|
||||
* in the draw function of your widget to draw them. Then GDK will handle
|
||||
* the integration of your rendering with that of other widgets.
|
||||
*
|
||||
* Support for #GdkGLContext is platform-specific, context creation
|
||||
* can fail, returning %NULL context.
|
||||
*
|
||||
* A #GdkGLContext has to be made "current" in order to start using
|
||||
* it, otherwise any OpenGL call will be ignored.
|
||||
*
|
||||
* ## Creating a new OpenGL context ##
|
||||
*
|
||||
* In order to create a new #GdkGLContext instance you need a
|
||||
* #GdkWindow, which you typically get during the realize call
|
||||
* of a widget.
|
||||
*
|
||||
* ## Using a GdkGLContext ##
|
||||
*
|
||||
* You will 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.
|
||||
*
|
||||
* 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 "gdkvisual.h"
|
||||
#include "gdkinternals.h"
|
||||
|
||||
#include "gdkintl.h"
|
||||
|
||||
typedef struct {
|
||||
GdkWindow *window;
|
||||
GdkVisual *visual;
|
||||
} GdkGLContextPrivate;
|
||||
|
||||
enum {
|
||||
PROP_0,
|
||||
|
||||
PROP_WINDOW,
|
||||
PROP_VISUAL,
|
||||
|
||||
LAST_PROP
|
||||
};
|
||||
|
||||
static GParamSpec *obj_pspecs[LAST_PROP] = { NULL, };
|
||||
|
||||
G_DEFINE_QUARK (gdk-gl-error-quark, gdk_gl_error)
|
||||
|
||||
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 (gdk_window_get_display (priv->window), context);
|
||||
|
||||
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_WINDOW:
|
||||
{
|
||||
GdkWindow *window = g_value_get_object (value);
|
||||
|
||||
if (window)
|
||||
g_object_ref (window);
|
||||
|
||||
if (priv->window)
|
||||
g_object_unref (priv->window);
|
||||
|
||||
priv->window = window;
|
||||
}
|
||||
break;
|
||||
|
||||
case PROP_VISUAL:
|
||||
{
|
||||
GdkVisual *visual = g_value_get_object (value);
|
||||
|
||||
if (visual != NULL)
|
||||
priv->visual = g_object_ref (visual);
|
||||
}
|
||||
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_WINDOW:
|
||||
g_value_set_object (value, priv->window);
|
||||
break;
|
||||
|
||||
case PROP_VISUAL:
|
||||
g_value_set_object (value, priv->visual);
|
||||
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:window:
|
||||
*
|
||||
* The #GdkWindow the gl context is bound to.
|
||||
*
|
||||
* Since: 3.16
|
||||
*/
|
||||
obj_pspecs[PROP_WINDOW] =
|
||||
g_param_spec_object ("window",
|
||||
P_("Window"),
|
||||
P_("The GDK window bound to the GL context"),
|
||||
GDK_TYPE_WINDOW,
|
||||
G_PARAM_READWRITE |
|
||||
G_PARAM_CONSTRUCT_ONLY |
|
||||
G_PARAM_STATIC_STRINGS);
|
||||
|
||||
/**
|
||||
* GdkGLContext:visual:
|
||||
*
|
||||
* The #GdkVisual matching the pixel format used by the context.
|
||||
*
|
||||
* Since: 3.16
|
||||
*/
|
||||
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);
|
||||
|
||||
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)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* gdk_gl_context_get_visual:
|
||||
* @context: a #GdkGLContext
|
||||
*
|
||||
* Retrieves the #GdkVisual associated with the @context.
|
||||
*
|
||||
* Returns: (transfer none): the #GdkVisual
|
||||
*
|
||||
* Since: 3.16
|
||||
*/
|
||||
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;
|
||||
}
|
||||
|
||||
/*< private >
|
||||
* gdk_gl_context_flush_buffer:
|
||||
* @context: a #GdkGLContext
|
||||
* @painted: The area that has been redrawn this frame
|
||||
* @damage: The area that we know is actually different from the last frame
|
||||
*
|
||||
* Copies the back buffer to the front buffer.
|
||||
*
|
||||
* This function may call `glFlush()` implicitly before returning; it
|
||||
* is not recommended to call `glFlush()` explicitly before calling
|
||||
* this function.
|
||||
*
|
||||
* Since: 3.16
|
||||
*/
|
||||
void
|
||||
gdk_gl_context_flush_buffer (GdkGLContext *context,
|
||||
cairo_region_t *painted,
|
||||
cairo_region_t *damage)
|
||||
{
|
||||
g_return_if_fail (GDK_IS_GL_CONTEXT (context));
|
||||
|
||||
GDK_GL_CONTEXT_GET_CLASS (context)->flush_buffer (context, painted, damage);
|
||||
}
|
||||
|
||||
/**
|
||||
* gdk_gl_context_make_current:
|
||||
* @context: a #GdkGLContext
|
||||
*
|
||||
* Makes the @context the current one.
|
||||
*
|
||||
* Since: 3.16
|
||||
*/
|
||||
void
|
||||
gdk_gl_context_make_current (GdkGLContext *context)
|
||||
{
|
||||
GdkGLContextPrivate *priv = gdk_gl_context_get_instance_private (context);
|
||||
|
||||
g_return_if_fail (GDK_IS_GL_CONTEXT (context));
|
||||
|
||||
gdk_display_make_gl_context_current (gdk_window_get_display (priv->window), context);
|
||||
}
|
||||
|
||||
/**
|
||||
* gdk_gl_context_get_window:
|
||||
* @context: a #GdkGLContext
|
||||
*
|
||||
* Retrieves the #GdkWindow used by the @context.
|
||||
*
|
||||
* Returns: (transfer none): a #GdkWindow or %NULL
|
||||
*
|
||||
* Since: 3.16
|
||||
*/
|
||||
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_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.16
|
||||
*/
|
||||
void
|
||||
gdk_gl_context_clear_current (void)
|
||||
{
|
||||
GdkDisplay *display = gdk_display_get_default ();
|
||||
|
||||
gdk_display_make_gl_context_current (display, NULL);
|
||||
}
|
||||
|
||||
/**
|
||||
* gdk_gl_context_get_current:
|
||||
*
|
||||
* Retrieves the current #GdkGLContext.
|
||||
*
|
||||
* Returns: (transfer none): the current #GdkGLContext, or %NULL
|
||||
*
|
||||
* Since: 3.16
|
||||
*/
|
||||
GdkGLContext *
|
||||
gdk_gl_context_get_current (void)
|
||||
{
|
||||
GdkDisplay *display = gdk_display_get_default ();
|
||||
|
||||
return gdk_display_get_current_gl_context (display);
|
||||
}
|
59
gdk/gdkglcontext.h
Normal file
59
gdk/gdkglcontext.h
Normal file
@@ -0,0 +1,59 @@
|
||||
/* 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))
|
||||
|
||||
#define GDK_GL_ERROR (gdk_gl_error_quark ())
|
||||
|
||||
GDK_AVAILABLE_IN_3_16
|
||||
GQuark gdk_gl_error_quark (void);
|
||||
|
||||
GDK_AVAILABLE_IN_3_16
|
||||
GType gdk_gl_context_get_type (void) G_GNUC_CONST;
|
||||
|
||||
GDK_AVAILABLE_IN_3_16
|
||||
GdkVisual * gdk_gl_context_get_visual (GdkGLContext *context);
|
||||
GDK_AVAILABLE_IN_3_16
|
||||
GdkWindow * gdk_gl_context_get_window (GdkGLContext *context);
|
||||
|
||||
GDK_AVAILABLE_IN_3_16
|
||||
void gdk_gl_context_make_current (GdkGLContext *context);
|
||||
GDK_AVAILABLE_IN_3_16
|
||||
GdkGLContext * gdk_gl_context_get_current (void);
|
||||
GDK_AVAILABLE_IN_3_16
|
||||
void gdk_gl_context_clear_current (void);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __GDK_GL_CONTEXT_H__ */
|
58
gdk/gdkglcontextprivate.h
Normal file
58
gdk/gdkglcontextprivate.h
Normal file
@@ -0,0 +1,58 @@
|
||||
/* 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 (* update) (GdkGLContext *context);
|
||||
void (* flush_buffer) (GdkGLContext *context,
|
||||
cairo_region_t *painted,
|
||||
cairo_region_t *damage);
|
||||
gboolean (* texture_from_surface) (GdkGLContext *context,
|
||||
cairo_surface_t *surface,
|
||||
cairo_region_t *region);
|
||||
};
|
||||
|
||||
void gdk_gl_context_flush_buffer (GdkGLContext *context,
|
||||
cairo_region_t *painted,
|
||||
cairo_region_t *damage);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __GDK_GL_CONTEXT_PRIVATE_H__ */
|
@@ -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 {
|
||||
@@ -208,11 +209,27 @@ struct _GdkWindow
|
||||
struct {
|
||||
cairo_region_t *region;
|
||||
cairo_surface_t *surface;
|
||||
|
||||
/* Areas of region that have been copied to the back buffer already */
|
||||
cairo_region_t *flushed_region;
|
||||
/* Areas of region that have been copied to the back buffer but
|
||||
needs furter blending of surface data. These two regions are
|
||||
always non-intersecting. */
|
||||
cairo_region_t *need_blend_region;
|
||||
|
||||
gboolean surface_needs_composite;
|
||||
gboolean use_gl;
|
||||
} current_paint;
|
||||
GdkGLContext *gl_paint_context;
|
||||
|
||||
cairo_region_t *update_area;
|
||||
guint update_freeze_count;
|
||||
/* This is the update_area that was in effect when the current expose
|
||||
started. It may be smaller than the expose area if we'e painting
|
||||
more than we have to, but it represents the "true" damage. */
|
||||
cairo_region_t *active_update_area;
|
||||
/* We store the old expose areas to support buffer-age optimizations */
|
||||
cairo_region_t *old_updated_area[2];
|
||||
|
||||
GdkWindowState state;
|
||||
|
||||
@@ -319,8 +336,14 @@ void _gdk_windowing_event_data_free (GdkEvent *event);
|
||||
void _gdk_set_window_state (GdkWindow *window,
|
||||
GdkWindowState new_state);
|
||||
|
||||
gboolean _gdk_cairo_surface_extents (cairo_surface_t *surface,
|
||||
GdkRectangle *extents);
|
||||
gboolean _gdk_cairo_surface_extents (cairo_surface_t *surface,
|
||||
GdkRectangle *extents);
|
||||
void gdk_gl_texture_from_surface (cairo_surface_t *surface,
|
||||
cairo_region_t *region);
|
||||
void gdk_cairo_surface_mark_as_direct (cairo_surface_t *surface,
|
||||
GdkWindow *window);
|
||||
cairo_region_t *gdk_cairo_region_from_clip (cairo_t *cr);
|
||||
|
||||
|
||||
/*************************************
|
||||
* Interfaces used by windowing code *
|
||||
@@ -334,6 +357,9 @@ void _gdk_window_destroy (GdkWindow *window,
|
||||
void _gdk_window_clear_update_area (GdkWindow *window);
|
||||
void _gdk_window_update_size (GdkWindow *window);
|
||||
gboolean _gdk_window_update_viewable (GdkWindow *window);
|
||||
GdkGLContext * gdk_window_get_paint_gl_context (GdkWindow *window,
|
||||
GError **error);
|
||||
|
||||
|
||||
void _gdk_window_process_updates_recurse (GdkWindow *window,
|
||||
cairo_region_t *expose_region);
|
||||
|
@@ -128,6 +128,8 @@ typedef struct _GdkWindow GdkWindow;
|
||||
typedef struct _GdkKeymap GdkKeymap;
|
||||
typedef struct _GdkAppLaunchContext GdkAppLaunchContext;
|
||||
|
||||
typedef struct _GdkGLContext GdkGLContext;
|
||||
|
||||
/**
|
||||
* GdkByteOrder:
|
||||
* @GDK_LSB_FIRST: The values are stored with the least-significant byte
|
||||
@@ -429,8 +431,26 @@ struct _GdkPoint
|
||||
gint y;
|
||||
};
|
||||
|
||||
/**
|
||||
* GdkGLProfile:
|
||||
* @GDK_GL_PROFILE_DEFAULT: ...
|
||||
* @GDK_GL_PROFILE_LEGACY: ...
|
||||
* @GDK_GL_PROFILE_3_2_CORE: ...
|
||||
*
|
||||
* ...
|
||||
*/
|
||||
typedef enum {
|
||||
GDK_GL_PROFILE_DEFAULT,
|
||||
GDK_GL_PROFILE_LEGACY,
|
||||
GDK_GL_PROFILE_3_2_CORE
|
||||
} GdkGLProfile;
|
||||
|
||||
typedef enum {
|
||||
GDK_GL_ERROR_NOT_AVAILABLE,
|
||||
GDK_GL_ERROR_UNSUPPORTED_FORMAT,
|
||||
GDK_GL_ERROR_UNSUPPORTED_PROFILE
|
||||
} GdkGLError;
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
|
||||
#endif /* __GDK_TYPES_H__ */
|
||||
|
345
gdk/gdkwindow.c
345
gdk/gdkwindow.c
@@ -39,9 +39,12 @@
|
||||
#include "gdkmarshalers.h"
|
||||
#include "gdkframeclockidle.h"
|
||||
#include "gdkwindowimpl.h"
|
||||
#include "gdkglcontextprivate.h"
|
||||
|
||||
#include <math.h>
|
||||
|
||||
#include <epoxy/gl.h>
|
||||
|
||||
/* for the use of round() */
|
||||
#include "fallback-c89.c"
|
||||
|
||||
@@ -185,6 +188,11 @@ static cairo_surface_t *gdk_window_ref_impl_surface (GdkWindow *window);
|
||||
static void gdk_window_set_frame_clock (GdkWindow *window,
|
||||
GdkFrameClock *clock);
|
||||
|
||||
static void draw_ugly_color (GdkWindow *window,
|
||||
const cairo_region_t *region,
|
||||
int color);
|
||||
|
||||
|
||||
static guint signals[LAST_SIGNAL] = { 0 };
|
||||
|
||||
static gpointer parent_class = NULL;
|
||||
@@ -279,11 +287,16 @@ create_surface_accumulator (GSignalInvocationHint *ihint,
|
||||
|
||||
static GQuark quark_pointer_window = 0;
|
||||
|
||||
static gboolean always_use_gl = FALSE;
|
||||
|
||||
static void
|
||||
gdk_window_class_init (GdkWindowClass *klass)
|
||||
{
|
||||
GObjectClass *object_class = G_OBJECT_CLASS (klass);
|
||||
|
||||
if (g_getenv ("GDK_ALWAYS_USE_GL"))
|
||||
always_use_gl = TRUE;
|
||||
|
||||
parent_class = g_type_class_peek_parent (klass);
|
||||
|
||||
object_class->finalize = gdk_window_finalize;
|
||||
@@ -1024,9 +1037,35 @@ recompute_visible_regions (GdkWindow *private,
|
||||
recalculate_children);
|
||||
}
|
||||
|
||||
static void
|
||||
gdk_window_clear_old_updated_area (GdkWindow *window)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < 2; i++)
|
||||
{
|
||||
if (window->old_updated_area[i])
|
||||
{
|
||||
cairo_region_destroy (window->old_updated_area[i]);
|
||||
window->old_updated_area[i] = NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
gdk_window_append_old_updated_area (GdkWindow *window,
|
||||
cairo_region_t *region)
|
||||
{
|
||||
if (window->old_updated_area[1])
|
||||
cairo_region_destroy (window->old_updated_area[1]);
|
||||
window->old_updated_area[1] = window->old_updated_area[0];
|
||||
window->old_updated_area[0] = cairo_region_reference (region);
|
||||
}
|
||||
|
||||
void
|
||||
_gdk_window_update_size (GdkWindow *window)
|
||||
{
|
||||
gdk_window_clear_old_updated_area (window);
|
||||
recompute_visible_regions (window, FALSE);
|
||||
}
|
||||
|
||||
@@ -1384,6 +1423,18 @@ gdk_window_new (GdkWindow *parent,
|
||||
g_signal_connect (device_manager, "device-removed",
|
||||
G_CALLBACK (device_removed_cb), window);
|
||||
|
||||
|
||||
if (always_use_gl)
|
||||
{
|
||||
GError *error = NULL;
|
||||
|
||||
if (gdk_window_get_paint_gl_context (window, &error) == NULL)
|
||||
{
|
||||
g_warning ("Unable to force GL enabled: %s\n", error->message);
|
||||
g_error_free (error);
|
||||
}
|
||||
}
|
||||
|
||||
return window;
|
||||
}
|
||||
|
||||
@@ -1835,6 +1886,12 @@ gdk_window_free_current_paint (GdkWindow *window)
|
||||
cairo_region_destroy (window->current_paint.region);
|
||||
window->current_paint.region = NULL;
|
||||
|
||||
cairo_region_destroy (window->current_paint.flushed_region);
|
||||
window->current_paint.flushed_region = NULL;
|
||||
|
||||
cairo_region_destroy (window->current_paint.need_blend_region);
|
||||
window->current_paint.need_blend_region = NULL;
|
||||
|
||||
window->current_paint.surface_needs_composite = FALSE;
|
||||
}
|
||||
|
||||
@@ -2662,6 +2719,58 @@ gdk_window_ref_impl_surface (GdkWindow *window)
|
||||
return GDK_WINDOW_IMPL_GET_CLASS (window->impl)->ref_cairo_surface (gdk_window_get_impl_window (window));
|
||||
}
|
||||
|
||||
GdkGLContext *
|
||||
gdk_window_get_paint_gl_context (GdkWindow *window, GError **error)
|
||||
{
|
||||
if (window->impl_window->gl_paint_context == NULL)
|
||||
window->impl_window->gl_paint_context =
|
||||
GDK_WINDOW_IMPL_GET_CLASS (window->impl)->create_gl_context (window,
|
||||
TRUE,
|
||||
GDK_GL_PROFILE_DEFAULT,
|
||||
NULL,
|
||||
error);
|
||||
|
||||
return window->impl_window->gl_paint_context;
|
||||
}
|
||||
|
||||
/**
|
||||
* gdk_window_create_gl_context:
|
||||
* @window: a #GdkWindow
|
||||
* @profile: the GL profile the context should target
|
||||
* @error: return location for an error
|
||||
*
|
||||
* Creates a new #GdkGLContext matching the
|
||||
* framebuffer format to the visual of the #GdkWindow. The context
|
||||
* is disconnected from any particular window or surface.
|
||||
*
|
||||
* If the creation of the #GdkGLContext failed, @error will be set.
|
||||
*
|
||||
* Returns: (transfer full): the newly created #GdkGLContext, or
|
||||
* %NULL on error
|
||||
*
|
||||
* Since: 3.16
|
||||
**/
|
||||
GdkGLContext *
|
||||
gdk_window_create_gl_context (GdkWindow *window,
|
||||
GdkGLProfile profile,
|
||||
GError **error)
|
||||
{
|
||||
GdkGLContext *paint_context;
|
||||
|
||||
g_return_val_if_fail (GDK_IS_WINDOW (window), NULL);
|
||||
g_return_val_if_fail (error == NULL || *error == NULL, NULL);
|
||||
|
||||
paint_context = gdk_window_get_paint_gl_context (window, error);
|
||||
if (paint_context == NULL)
|
||||
return NULL;
|
||||
|
||||
return GDK_WINDOW_IMPL_GET_CLASS (window->impl)->create_gl_context (window,
|
||||
FALSE,
|
||||
profile,
|
||||
paint_context,
|
||||
error);
|
||||
}
|
||||
|
||||
/**
|
||||
* gdk_window_begin_paint_rect:
|
||||
* @window: a #GdkWindow
|
||||
@@ -2738,6 +2847,7 @@ gdk_window_begin_paint_region (GdkWindow *window,
|
||||
GdkWindowImplClass *impl_class;
|
||||
double sx, sy;
|
||||
gboolean needs_surface;
|
||||
cairo_content_t surface_content;
|
||||
|
||||
g_return_if_fail (GDK_IS_WINDOW (window));
|
||||
|
||||
@@ -2759,14 +2869,59 @@ gdk_window_begin_paint_region (GdkWindow *window,
|
||||
needs_surface = impl_class->begin_paint_region (window, region);
|
||||
|
||||
window->current_paint.region = cairo_region_copy (region);
|
||||
|
||||
cairo_region_intersect (window->current_paint.region, window->clip_region);
|
||||
cairo_region_get_extents (window->current_paint.region, &clip_box);
|
||||
|
||||
window->current_paint.flushed_region = cairo_region_create ();
|
||||
window->current_paint.need_blend_region = cairo_region_create ();
|
||||
|
||||
surface_content = gdk_window_get_content (window);
|
||||
|
||||
window->current_paint.use_gl = window->impl_window->gl_paint_context != NULL;
|
||||
|
||||
if (window->current_paint.use_gl)
|
||||
{
|
||||
GdkGLContext *context;
|
||||
|
||||
int ww = gdk_window_get_width (window) * gdk_window_get_scale_factor (window);
|
||||
int wh = gdk_window_get_height (window) * gdk_window_get_scale_factor (window);
|
||||
|
||||
context = gdk_window_get_paint_gl_context (window, NULL);
|
||||
if (context == NULL)
|
||||
{
|
||||
g_warning ("gl rendering failed, context: %p", context);
|
||||
window->current_paint.use_gl = FALSE;
|
||||
}
|
||||
else
|
||||
{
|
||||
gdk_gl_context_make_current (context);
|
||||
/* With gl we always need a surface to combine the gl
|
||||
drawing with the native drawing. */
|
||||
needs_surface = TRUE;
|
||||
/* Also, we need the surface to include alpha */
|
||||
surface_content = CAIRO_CONTENT_COLOR_ALPHA;
|
||||
|
||||
/* Initial setup */
|
||||
glClearColor (0.0f, 0.0f, 0.0f, 0.0f);
|
||||
glDisable (GL_DEPTH_TEST);
|
||||
glDisable(GL_BLEND);
|
||||
glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
||||
glTexEnvi (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
|
||||
glViewport (0, 0, ww, wh);
|
||||
|
||||
glMatrixMode (GL_PROJECTION);
|
||||
glLoadIdentity ();
|
||||
glOrtho (0.0f, ww, 0.0f, wh, -1.0f, 1.0f);
|
||||
|
||||
glMatrixMode (GL_MODELVIEW);
|
||||
glLoadIdentity ();
|
||||
}
|
||||
}
|
||||
|
||||
if (needs_surface)
|
||||
{
|
||||
window->current_paint.surface = gdk_window_create_similar_surface (window,
|
||||
gdk_window_get_content (window),
|
||||
surface_content,
|
||||
MAX (clip_box.width, 1),
|
||||
MAX (clip_box.height, 1));
|
||||
sx = sy = 1;
|
||||
@@ -2774,6 +2929,7 @@ gdk_window_begin_paint_region (GdkWindow *window,
|
||||
cairo_surface_get_device_scale (window->current_paint.surface, &sx, &sy);
|
||||
#endif
|
||||
cairo_surface_set_device_offset (window->current_paint.surface, -clip_box.x*sx, -clip_box.y*sy);
|
||||
gdk_cairo_surface_mark_as_direct (window->current_paint.surface, window);
|
||||
|
||||
window->current_paint.surface_needs_composite = TRUE;
|
||||
}
|
||||
@@ -2787,18 +2943,87 @@ gdk_window_begin_paint_region (GdkWindow *window,
|
||||
gdk_window_clear_backing_region (window);
|
||||
}
|
||||
|
||||
/**
|
||||
* gdk_window_mark_paint_from_clip:
|
||||
* @window: a #GdkWindow
|
||||
* @cr: a #cairo_t
|
||||
*
|
||||
* If you call this during a paint (e.g. between gdk_window_begin_paint_region()
|
||||
* and gdk_window_end_paint() then GDK will mark the current clip region of the
|
||||
* window as being drawn. This is required when mixing GL rendering via
|
||||
* gdk_cairo_draw_from_gl() and cairo rendering, as otherwise GDK has no way
|
||||
* of knowing when something paints over the GL-drawn regions.
|
||||
*
|
||||
* This is typically called automatically by GTK+ and you don't need
|
||||
* to care about this.
|
||||
*
|
||||
* Since: 3.16
|
||||
**/
|
||||
void
|
||||
gdk_window_mark_paint_from_clip (GdkWindow *window,
|
||||
cairo_t *cr)
|
||||
{
|
||||
cairo_region_t *clip_region;
|
||||
GdkWindow *impl_window = window->impl_window;
|
||||
|
||||
if (impl_window->current_paint.surface == NULL ||
|
||||
cairo_get_target (cr) != impl_window->current_paint.surface)
|
||||
return;
|
||||
|
||||
if (cairo_region_is_empty (impl_window->current_paint.flushed_region))
|
||||
return;
|
||||
|
||||
/* This here seems a bit weird, but basically, we're taking the current
|
||||
clip and applying also the flushed region, and the result is that the
|
||||
new clip is the intersection of these. This is the area where the newly
|
||||
drawn region overlaps a previosly flushed area, which is an area of the
|
||||
double buffer surface that need to be blended OVER the back buffer rather
|
||||
than SRCed. */
|
||||
cairo_save (cr);
|
||||
/* We set the identity matrix here so we get and apply regions in native
|
||||
window coordinates. */
|
||||
cairo_identity_matrix (cr);
|
||||
gdk_cairo_region (cr, impl_window->current_paint.flushed_region);
|
||||
cairo_clip (cr);
|
||||
|
||||
clip_region = gdk_cairo_region_from_clip (cr);
|
||||
if (clip_region == NULL)
|
||||
{
|
||||
/* Failed to represent clip as region, mark all as requiring
|
||||
blend */
|
||||
cairo_region_union (impl_window->current_paint.need_blend_region,
|
||||
impl_window->current_paint.flushed_region);
|
||||
cairo_region_destroy (impl_window->current_paint.flushed_region);
|
||||
impl_window->current_paint.flushed_region = cairo_region_create ();
|
||||
}
|
||||
else
|
||||
{
|
||||
cairo_region_subtract (impl_window->current_paint.flushed_region, clip_region);
|
||||
cairo_region_union (impl_window->current_paint.need_blend_region, clip_region);
|
||||
}
|
||||
|
||||
/* Clear the area on the double buffer surface to transparent so we
|
||||
can start drawing from scratch the area "above" the flushed
|
||||
region */
|
||||
cairo_set_source_rgba (cr, 0, 0, 0, 0);
|
||||
cairo_set_operator (cr, CAIRO_OPERATOR_SOURCE);
|
||||
cairo_paint (cr);
|
||||
|
||||
cairo_restore (cr);
|
||||
}
|
||||
|
||||
/**
|
||||
* gdk_window_end_paint:
|
||||
* @window: a #GdkWindow
|
||||
*
|
||||
* Indicates that the backing store created by the most recent call to
|
||||
* gdk_window_begin_paint_region() should be copied onscreen and
|
||||
* Indicates that the backing store created by the most recent call
|
||||
* to gdk_window_begin_paint_region() should be copied onscreen and
|
||||
* deleted, leaving the next-most-recent backing store or no backing
|
||||
* store at all as the active paint region. See
|
||||
* gdk_window_begin_paint_region() for full details. It is an error to
|
||||
* call this function without a matching
|
||||
* gdk_window_begin_paint_region() first.
|
||||
* gdk_window_begin_paint_region() for full details.
|
||||
*
|
||||
* It is an error to call this function without a matching
|
||||
* gdk_window_begin_paint_region() first.
|
||||
**/
|
||||
void
|
||||
gdk_window_end_paint (GdkWindow *window)
|
||||
@@ -2806,7 +3031,6 @@ gdk_window_end_paint (GdkWindow *window)
|
||||
GdkWindow *composited;
|
||||
GdkWindowImplClass *impl_class;
|
||||
GdkRectangle clip_box = { 0, };
|
||||
cairo_region_t *full_clip;
|
||||
cairo_t *cr;
|
||||
|
||||
g_return_if_fail (GDK_IS_WINDOW (window));
|
||||
@@ -2826,41 +3050,56 @@ gdk_window_end_paint (GdkWindow *window)
|
||||
if (impl_class->end_paint)
|
||||
impl_class->end_paint (window);
|
||||
|
||||
|
||||
if (window->current_paint.surface_needs_composite)
|
||||
{
|
||||
cairo_surface_t *surface;
|
||||
gboolean skip_alpha_blending;
|
||||
|
||||
cairo_region_get_extents (window->current_paint.region, &clip_box);
|
||||
full_clip = cairo_region_copy (window->clip_region);
|
||||
cairo_region_intersect (full_clip, window->current_paint.region);
|
||||
|
||||
surface = gdk_window_ref_impl_surface (window);
|
||||
cr = cairo_create (surface);
|
||||
cairo_surface_destroy (surface);
|
||||
|
||||
cairo_set_source_surface (cr, window->current_paint.surface, 0, 0);
|
||||
gdk_cairo_region (cr, full_clip);
|
||||
cairo_clip (cr);
|
||||
|
||||
/* We can skip alpha blending for a fast composite case
|
||||
* if we have an impl window or we're a fully opaque window. */
|
||||
skip_alpha_blending = (gdk_window_has_impl (window) ||
|
||||
window->alpha == 255);
|
||||
|
||||
if (skip_alpha_blending)
|
||||
if (window->current_paint.use_gl)
|
||||
{
|
||||
cairo_set_operator (cr, CAIRO_OPERATOR_SOURCE);
|
||||
cairo_paint (cr);
|
||||
cairo_region_t *opaque_region = cairo_region_copy (window->current_paint.region);
|
||||
cairo_region_subtract (opaque_region, window->current_paint.flushed_region);
|
||||
cairo_region_subtract (opaque_region, window->current_paint.need_blend_region);
|
||||
|
||||
gdk_gl_context_make_current (window->gl_paint_context);
|
||||
|
||||
if (!cairo_region_is_empty (opaque_region))
|
||||
gdk_gl_texture_from_surface (window->current_paint.surface,
|
||||
opaque_region);
|
||||
if (!cairo_region_is_empty (window->current_paint.need_blend_region))
|
||||
{
|
||||
glEnable(GL_BLEND);
|
||||
gdk_gl_texture_from_surface (window->current_paint.surface,
|
||||
window->current_paint.need_blend_region);
|
||||
glDisable(GL_BLEND);
|
||||
}
|
||||
|
||||
cairo_region_destroy (opaque_region);
|
||||
|
||||
gdk_gl_context_flush_buffer (window->gl_paint_context,
|
||||
window->current_paint.region,
|
||||
window->active_update_area);
|
||||
|
||||
if (epoxy_has_gl_extension ("GL_GREMEDY_frame_terminator"))
|
||||
glFrameTerminatorGREMEDY();
|
||||
}
|
||||
else
|
||||
{
|
||||
cairo_set_operator (cr, CAIRO_OPERATOR_OVER);
|
||||
cairo_paint_with_alpha (cr, window->alpha / 255.0);
|
||||
}
|
||||
surface = gdk_window_ref_impl_surface (window);
|
||||
cr = cairo_create (surface);
|
||||
cairo_surface_destroy (surface);
|
||||
|
||||
cairo_destroy (cr);
|
||||
cairo_region_destroy (full_clip);
|
||||
cairo_set_source_surface (cr, window->current_paint.surface, 0, 0);
|
||||
gdk_cairo_region (cr, window->current_paint.region);
|
||||
cairo_clip (cr);
|
||||
|
||||
cairo_set_operator (cr, CAIRO_OPERATOR_SOURCE);
|
||||
cairo_paint (cr);
|
||||
|
||||
cairo_destroy (cr);
|
||||
}
|
||||
}
|
||||
|
||||
gdk_window_free_current_paint (window);
|
||||
@@ -3375,18 +3614,33 @@ gdk_window_process_updates_internal (GdkWindow *window)
|
||||
*/
|
||||
if (window->update_area)
|
||||
{
|
||||
cairo_region_t *update_area = window->update_area;
|
||||
g_assert (window->active_update_area == NULL); /* No reentrancy */
|
||||
|
||||
window->active_update_area = window->update_area;
|
||||
window->update_area = NULL;
|
||||
|
||||
if (gdk_window_is_viewable (window))
|
||||
{
|
||||
cairo_region_t *expose_region;
|
||||
|
||||
expose_region = cairo_region_copy (window->active_update_area);
|
||||
|
||||
/* Sometimes we can't just paint only the new area, as the windowing system
|
||||
requires more to be repainted. For instance, with opengl you typically
|
||||
repaint all of each frame each time and then swap the buffer, although
|
||||
there are extensions that allow us to reuse part of an old frame */
|
||||
if (GDK_WINDOW_IMPL_GET_CLASS (window->impl)->invalidate_for_new_frame)
|
||||
GDK_WINDOW_IMPL_GET_CLASS (window->impl)->invalidate_for_new_frame (window, expose_region);
|
||||
|
||||
/* Clip to part visible in impl window */
|
||||
cairo_region_intersect (update_area, window->clip_region);
|
||||
cairo_region_intersect (expose_region, window->clip_region);
|
||||
|
||||
if (debug_updates)
|
||||
{
|
||||
cairo_region_t *swap_region = cairo_region_copy (expose_region);
|
||||
cairo_region_subtract (swap_region, window->active_update_area);
|
||||
draw_ugly_color (window, swap_region, 1);
|
||||
|
||||
/* Make sure we see the red invalid area before redrawing. */
|
||||
gdk_display_sync (gdk_window_get_display (window));
|
||||
g_usleep (70000);
|
||||
@@ -3395,14 +3649,15 @@ gdk_window_process_updates_internal (GdkWindow *window)
|
||||
impl_class = GDK_WINDOW_IMPL_GET_CLASS (window->impl);
|
||||
|
||||
if (impl_class->queue_antiexpose)
|
||||
impl_class->queue_antiexpose (window, update_area);
|
||||
impl_class->queue_antiexpose (window, expose_region);
|
||||
|
||||
expose_region = cairo_region_copy (update_area);
|
||||
impl_class->process_updates_recurse (window, expose_region);
|
||||
cairo_region_destroy (expose_region);
|
||||
}
|
||||
|
||||
cairo_region_destroy (update_area);
|
||||
gdk_window_append_old_updated_area (window, window->active_update_area);
|
||||
}
|
||||
|
||||
cairo_region_destroy (window->active_update_area);
|
||||
window->active_update_area = NULL;
|
||||
}
|
||||
|
||||
window->in_update = FALSE;
|
||||
@@ -3705,13 +3960,17 @@ gdk_window_set_invalidate_handler (GdkWindow *window,
|
||||
|
||||
static void
|
||||
draw_ugly_color (GdkWindow *window,
|
||||
const cairo_region_t *region)
|
||||
const cairo_region_t *region,
|
||||
int color)
|
||||
{
|
||||
cairo_t *cr;
|
||||
|
||||
cr = gdk_cairo_create (window);
|
||||
/* Draw ugly color all over the newly-invalid region */
|
||||
cairo_set_source_rgb (cr, 50000/65535., 10000/65535., 10000/65535.);
|
||||
if (color == 0)
|
||||
cairo_set_source_rgb (cr, 50000/65535., 10000/65535., 10000/65535.);
|
||||
else
|
||||
cairo_set_source_rgb (cr, 10000/65535., 50000/65535., 10000/65535.);
|
||||
gdk_cairo_region (cr, region);
|
||||
cairo_fill (cr);
|
||||
|
||||
@@ -3807,7 +4066,7 @@ gdk_window_invalidate_maybe_recurse_full (GdkWindow *window,
|
||||
invalidate_impl_subwindows (window, region, child_func, user_data, 0, 0);
|
||||
|
||||
if (debug_updates)
|
||||
draw_ugly_color (window, visible_region);
|
||||
draw_ugly_color (window, visible_region, 0);
|
||||
|
||||
while (window != NULL &&
|
||||
!cairo_region_is_empty (visible_region))
|
||||
@@ -5046,6 +5305,7 @@ gdk_window_hide (GdkWindow *window)
|
||||
impl_class->hide (window);
|
||||
}
|
||||
|
||||
gdk_window_clear_old_updated_area (window);
|
||||
recompute_visible_regions (window, FALSE);
|
||||
|
||||
/* all decendants became non-visible, we need to send visibility notify */
|
||||
@@ -5105,6 +5365,7 @@ gdk_window_withdraw (GdkWindow *window)
|
||||
}
|
||||
|
||||
recompute_visible_regions (window, FALSE);
|
||||
gdk_window_clear_old_updated_area (window);
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -739,6 +739,9 @@ cairo_region_t *gdk_window_get_visible_region(GdkWindow *window);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
void gdk_window_begin_paint_rect (GdkWindow *window,
|
||||
const GdkRectangle *rectangle);
|
||||
GDK_AVAILABLE_IN_3_16
|
||||
void gdk_window_mark_paint_from_clip (GdkWindow *window,
|
||||
cairo_t *cr);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
void gdk_window_begin_paint_region (GdkWindow *window,
|
||||
const cairo_region_t *region);
|
||||
@@ -1108,6 +1111,12 @@ GDK_AVAILABLE_IN_3_14
|
||||
gboolean gdk_window_show_window_menu (GdkWindow *window,
|
||||
GdkEvent *event);
|
||||
|
||||
GDK_AVAILABLE_IN_3_16
|
||||
GdkGLContext * gdk_window_create_gl_context (GdkWindow *window,
|
||||
GdkGLProfile profile,
|
||||
GError **error);
|
||||
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __GDK_WINDOW_H__ */
|
||||
|
@@ -289,6 +289,13 @@ struct _GdkWindowImplClass
|
||||
gint bottom);
|
||||
gboolean (* show_window_menu) (GdkWindow *window,
|
||||
GdkEvent *event);
|
||||
GdkGLContext *(*create_gl_context) (GdkWindow *window,
|
||||
gboolean attached,
|
||||
GdkGLProfile profile,
|
||||
GdkGLContext *share,
|
||||
GError **error);
|
||||
void (*invalidate_for_new_frame)(GdkWindow *window,
|
||||
cairo_region_t *update_area);
|
||||
};
|
||||
|
||||
/* Interface Functions */
|
||||
|
@@ -39,7 +39,9 @@ libgdk_wayland_la_SOURCES = \
|
||||
gdkdisplay-wayland.h \
|
||||
gdkdnd-wayland.c \
|
||||
gdkeventsource.c \
|
||||
gdkkeys-wayland.c \
|
||||
gdkkeys-wayland.c \
|
||||
gdkglcontext-wayland.c \
|
||||
gdkglcontext-wayland.h \
|
||||
gdkscreen-wayland.c \
|
||||
gdkselection-wayland.c \
|
||||
gdkwindow-wayland.c \
|
||||
@@ -54,6 +56,7 @@ libgdkinclude_HEADERS = \
|
||||
libgdkwaylandinclude_HEADERS = \
|
||||
gdkwaylanddevice.h \
|
||||
gdkwaylanddisplay.h \
|
||||
gdkwaylandglcontext.h \
|
||||
gdkwaylandselection.h \
|
||||
gdkwaylandwindow.h
|
||||
|
||||
|
@@ -34,6 +34,7 @@
|
||||
#include "gdkdevicemanager.h"
|
||||
#include "gdkkeysprivate.h"
|
||||
#include "gdkprivate-wayland.h"
|
||||
#include "gdkglcontext-wayland.h"
|
||||
|
||||
/**
|
||||
* SECTION:wayland_interaction
|
||||
@@ -540,6 +541,9 @@ gdk_wayland_display_class_init (GdkWaylandDisplayClass * class)
|
||||
display_class->convert_selection = _gdk_wayland_display_convert_selection;
|
||||
display_class->text_property_to_utf8_list = _gdk_wayland_display_text_property_to_utf8_list;
|
||||
display_class->utf8_to_string_target = _gdk_wayland_display_utf8_to_string_target;
|
||||
|
||||
display_class->destroy_gl_context = gdk_wayland_display_destroy_gl_context;
|
||||
display_class->make_gl_context_current = gdk_wayland_display_make_gl_context_current;
|
||||
}
|
||||
|
||||
static void
|
||||
|
@@ -26,6 +26,7 @@
|
||||
#include <stdint.h>
|
||||
#include <wayland-client.h>
|
||||
#include <wayland-cursor.h>
|
||||
#include <wayland-egl.h>
|
||||
#include <gdk/wayland/gtk-shell-client-protocol.h>
|
||||
#include <gdk/wayland/xdg-shell-client-protocol.h>
|
||||
|
||||
@@ -37,6 +38,8 @@
|
||||
|
||||
#include "gdkdisplayprivate.h"
|
||||
|
||||
#include <epoxy/egl.h>
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
typedef struct _GdkWaylandSelection GdkWaylandSelection;
|
||||
@@ -76,6 +79,17 @@ struct _GdkWaylandDisplay
|
||||
struct xkb_context *xkb_context;
|
||||
|
||||
GdkWaylandSelection *selection;
|
||||
|
||||
/* egl info */
|
||||
EGLDisplay egl_display;
|
||||
int egl_major_version;
|
||||
int egl_minor_version;
|
||||
|
||||
guint have_egl : 1;
|
||||
guint have_egl_khr_create_context : 1;
|
||||
guint have_egl_buffer_age : 1;
|
||||
guint have_egl_swap_buffers_with_damage : 1;
|
||||
guint have_egl_surfaceless_context : 1;
|
||||
};
|
||||
|
||||
struct _GdkWaylandDisplayClass
|
||||
|
427
gdk/wayland/gdkglcontext-wayland.c
Normal file
427
gdk/wayland/gdkglcontext-wayland.c
Normal file
@@ -0,0 +1,427 @@
|
||||
/* GDK - The GIMP Drawing Kit
|
||||
*
|
||||
* gdkglcontext-wayland.c: Wayland specific OpenGL wrappers
|
||||
*
|
||||
* Copyright © 2014 Emmanuele Bassi
|
||||
* Copyright © 2014 Alexander Larsson
|
||||
*
|
||||
* 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-wayland.h"
|
||||
#include "gdkdisplay-wayland.h"
|
||||
|
||||
#include "gdkwaylanddisplay.h"
|
||||
#include "gdkwaylandglcontext.h"
|
||||
#include "gdkwaylandwindow.h"
|
||||
#include "gdkprivate-wayland.h"
|
||||
|
||||
#include "gdkinternals.h"
|
||||
|
||||
#include "gdkintl.h"
|
||||
|
||||
G_DEFINE_TYPE (GdkWaylandGLContext, gdk_wayland_gl_context, GDK_TYPE_GL_CONTEXT)
|
||||
|
||||
static void
|
||||
gdk_wayland_gl_context_update (GdkGLContext *context)
|
||||
{
|
||||
GdkWindow *window = gdk_gl_context_get_window (context);
|
||||
int width, height;
|
||||
|
||||
gdk_gl_context_make_current (context);
|
||||
|
||||
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 %p (context: %p)\n",
|
||||
width, height,
|
||||
window, context));
|
||||
|
||||
glViewport (0, 0, width, height);
|
||||
}
|
||||
|
||||
void
|
||||
gdk_wayland_window_invalidate_for_new_frame (GdkWindow *window,
|
||||
cairo_region_t *update_area)
|
||||
{
|
||||
cairo_rectangle_int_t window_rect;
|
||||
GdkDisplay *display = gdk_window_get_display (window);
|
||||
GdkWaylandDisplay *display_wayland = GDK_WAYLAND_DISPLAY (display);
|
||||
GdkWaylandGLContext *context_wayland;
|
||||
int buffer_age;
|
||||
gboolean invalidate_all;
|
||||
EGLSurface egl_surface;
|
||||
|
||||
/* Minimal update is ok if we're not drawing with gl */
|
||||
if (window->gl_paint_context == NULL)
|
||||
return;
|
||||
|
||||
context_wayland = GDK_WAYLAND_GL_CONTEXT (window->gl_paint_context);
|
||||
buffer_age = 0;
|
||||
|
||||
egl_surface = gdk_wayland_window_get_egl_surface (window->impl_window,
|
||||
context_wayland->egl_config);
|
||||
|
||||
if (display_wayland->have_egl_buffer_age)
|
||||
{
|
||||
gdk_gl_context_make_current (window->gl_paint_context);
|
||||
eglQuerySurface (display_wayland->egl_display, egl_surface,
|
||||
EGL_BUFFER_AGE_EXT, &buffer_age);
|
||||
}
|
||||
|
||||
invalidate_all = FALSE;
|
||||
if (buffer_age == 0 || buffer_age >= 4)
|
||||
invalidate_all = TRUE;
|
||||
else
|
||||
{
|
||||
if (buffer_age >= 2)
|
||||
{
|
||||
if (window->old_updated_area[0])
|
||||
cairo_region_union (update_area, window->old_updated_area[0]);
|
||||
else
|
||||
invalidate_all = TRUE;
|
||||
}
|
||||
if (buffer_age >= 3)
|
||||
{
|
||||
if (window->old_updated_area[1])
|
||||
cairo_region_union (update_area, window->old_updated_area[1]);
|
||||
else
|
||||
invalidate_all = TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
if (invalidate_all)
|
||||
{
|
||||
window_rect.x = 0;
|
||||
window_rect.y = 0;
|
||||
window_rect.width = gdk_window_get_width (window);
|
||||
window_rect.height = gdk_window_get_height (window);
|
||||
|
||||
/* If nothing else is known, repaint everything so that the back
|
||||
buffer is fully up-to-date for the swapbuffer */
|
||||
cairo_region_union_rectangle (update_area, &window_rect);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
gdk_wayland_gl_context_flush_buffer (GdkGLContext *context,
|
||||
cairo_region_t *painted,
|
||||
cairo_region_t *damage)
|
||||
{
|
||||
GdkWindow *window = gdk_gl_context_get_window (context);
|
||||
GdkDisplay *display = gdk_window_get_display (window);
|
||||
GdkWaylandDisplay *display_wayland = GDK_WAYLAND_DISPLAY (display);
|
||||
GdkWaylandGLContext *context_wayland = GDK_WAYLAND_GL_CONTEXT (context);
|
||||
EGLSurface egl_surface;
|
||||
|
||||
gdk_gl_context_make_current (context);
|
||||
|
||||
egl_surface = gdk_wayland_window_get_egl_surface (window->impl_window,
|
||||
context_wayland->egl_config);
|
||||
|
||||
// TODO: Use eglSwapBuffersWithDamageEXT if available
|
||||
if (display_wayland->have_egl_swap_buffers_with_damage)
|
||||
{
|
||||
int i, j, n_rects = cairo_region_num_rectangles (damage);
|
||||
EGLint *rects = g_new (EGLint, n_rects * 4);
|
||||
cairo_rectangle_int_t rect;
|
||||
int window_height = gdk_window_get_height (window);
|
||||
|
||||
for (i = 0, j = 0; i < n_rects; i++)
|
||||
{
|
||||
cairo_region_get_rectangle (damage, i, &rect);
|
||||
rects[j++] = rect.x;
|
||||
rects[j++] = window_height - rect.height - rect.y;
|
||||
rects[j++] = rect.width;
|
||||
rects[j++] = rect.height;
|
||||
}
|
||||
eglSwapBuffersWithDamageEXT (display_wayland->egl_display, egl_surface, rects, n_rects);
|
||||
g_free (rects);
|
||||
}
|
||||
else
|
||||
eglSwapBuffers (display_wayland->egl_display, egl_surface);
|
||||
}
|
||||
|
||||
static void
|
||||
gdk_wayland_gl_context_class_init (GdkWaylandGLContextClass *klass)
|
||||
{
|
||||
GdkGLContextClass *context_class = GDK_GL_CONTEXT_CLASS (klass);
|
||||
|
||||
context_class->update = gdk_wayland_gl_context_update;
|
||||
context_class->flush_buffer = gdk_wayland_gl_context_flush_buffer;
|
||||
}
|
||||
|
||||
static void
|
||||
gdk_wayland_gl_context_init (GdkWaylandGLContext *self)
|
||||
{
|
||||
}
|
||||
|
||||
gboolean
|
||||
gdk_wayland_display_init_gl (GdkDisplay *display)
|
||||
{
|
||||
GdkWaylandDisplay *display_wayland = GDK_WAYLAND_DISPLAY (display);
|
||||
EGLint major, minor;
|
||||
EGLDisplay *dpy;
|
||||
|
||||
if (display_wayland->have_egl)
|
||||
return TRUE;
|
||||
|
||||
dpy = eglGetDisplay ((EGLNativeDisplayType)display_wayland->wl_display);
|
||||
if (dpy == NULL)
|
||||
return FALSE;
|
||||
|
||||
if (!eglInitialize (dpy, &major, &minor))
|
||||
return FALSE;
|
||||
|
||||
if (!eglBindAPI (EGL_OPENGL_API))
|
||||
return FALSE;
|
||||
|
||||
display_wayland->egl_display = dpy;
|
||||
display_wayland->egl_major_version = major;
|
||||
display_wayland->egl_minor_version = minor;
|
||||
|
||||
display_wayland->have_egl = TRUE;
|
||||
|
||||
display_wayland->have_egl_khr_create_context =
|
||||
epoxy_has_egl_extension (dpy, "EGL_KHR_create_context");
|
||||
|
||||
display_wayland->have_egl_buffer_age =
|
||||
epoxy_has_egl_extension (dpy, "EGL_EXT_buffer_age");
|
||||
|
||||
display_wayland->have_egl_swap_buffers_with_damage =
|
||||
epoxy_has_egl_extension (dpy, "EGL_EXT_swap_buffers_with_damage");
|
||||
|
||||
display_wayland->have_egl_surfaceless_context =
|
||||
epoxy_has_egl_extension (dpy, "EGL_KHR_surfaceless_context");
|
||||
|
||||
GDK_NOTE (OPENGL,
|
||||
g_print ("EGL API version %d.%d found\n"
|
||||
" - Vendor: %s\n"
|
||||
" - Version: %s\n"
|
||||
" - Client APIs: %s\n"
|
||||
" - Extensions:\n"
|
||||
"\t%s\n"
|
||||
,
|
||||
display_wayland->egl_major_version,
|
||||
display_wayland->egl_minor_version,
|
||||
eglQueryString (dpy, EGL_VENDOR),
|
||||
eglQueryString(dpy, EGL_VERSION),
|
||||
eglQueryString(dpy, EGL_CLIENT_APIS),
|
||||
eglQueryString(dpy, EGL_EXTENSIONS)));
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
#define MAX_EGL_ATTRS 30
|
||||
|
||||
static gboolean
|
||||
find_eglconfig_for_window (GdkWindow *window,
|
||||
EGLConfig *egl_config_out,
|
||||
GError **error)
|
||||
{
|
||||
GdkDisplay *display = gdk_window_get_display (window);
|
||||
GdkWaylandDisplay *display_wayland = GDK_WAYLAND_DISPLAY (display);
|
||||
GdkVisual *visual = gdk_window_get_visual (window);
|
||||
EGLint attrs[MAX_EGL_ATTRS];
|
||||
EGLint count;
|
||||
EGLConfig *configs;
|
||||
gboolean use_rgba;
|
||||
|
||||
int i = 0;
|
||||
|
||||
attrs[i++] = EGL_SURFACE_TYPE;
|
||||
attrs[i++] = EGL_WINDOW_BIT;
|
||||
|
||||
attrs[i++] = EGL_COLOR_BUFFER_TYPE;
|
||||
attrs[i++] = EGL_RGB_BUFFER;
|
||||
|
||||
attrs[i++] = EGL_RED_SIZE;
|
||||
attrs[i++] = gdk_visual_get_bits_per_rgb (visual);
|
||||
attrs[i++] = EGL_GREEN_SIZE;
|
||||
attrs[i++] = gdk_visual_get_bits_per_rgb (visual);
|
||||
attrs[i++] = EGL_BLUE_SIZE;
|
||||
attrs[i++] = gdk_visual_get_bits_per_rgb (visual);
|
||||
|
||||
use_rgba = (visual == gdk_screen_get_rgba_visual (gdk_display_get_default_screen (display)));
|
||||
|
||||
if (use_rgba)
|
||||
{
|
||||
attrs[i++] = EGL_ALPHA_SIZE;
|
||||
attrs[i++] = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
attrs[i++] = EGL_ALPHA_SIZE;
|
||||
attrs[i++] = 0;
|
||||
}
|
||||
|
||||
attrs[i++] = EGL_NONE;
|
||||
g_assert (i < MAX_EGL_ATTRS);
|
||||
|
||||
if (!eglChooseConfig (display_wayland->egl_display, attrs, NULL, 0, &count) || count < 1)
|
||||
{
|
||||
g_set_error_literal (error, GDK_GL_ERROR,
|
||||
GDK_GL_ERROR_UNSUPPORTED_FORMAT,
|
||||
_("No available configurations for the given pixel format"));
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
configs = g_new (EGLConfig, count);
|
||||
|
||||
if (!eglChooseConfig (display_wayland->egl_display, attrs, configs, count, &count) || count < 1)
|
||||
{
|
||||
g_set_error_literal (error, GDK_GL_ERROR,
|
||||
GDK_GL_ERROR_UNSUPPORTED_FORMAT,
|
||||
_("No available configurations for the given pixel format"));
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/* Pick first valid configuration i guess? */
|
||||
|
||||
if (egl_config_out != NULL)
|
||||
*egl_config_out = configs[0];
|
||||
|
||||
g_free (configs);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
GdkGLContext *
|
||||
gdk_wayland_window_create_gl_context (GdkWindow *window,
|
||||
gboolean attached,
|
||||
GdkGLProfile profile,
|
||||
GdkGLContext *share,
|
||||
GError **error)
|
||||
{
|
||||
GdkDisplay *display = gdk_window_get_display (window);
|
||||
GdkWaylandDisplay *display_wayland = GDK_WAYLAND_DISPLAY (display);
|
||||
GdkWaylandGLContext *context;
|
||||
EGLContext ctx;
|
||||
EGLConfig config;
|
||||
int i;
|
||||
EGLint context_attribs[3];
|
||||
|
||||
if (!gdk_wayland_display_init_gl (display))
|
||||
{
|
||||
g_set_error_literal (error, GDK_GL_ERROR,
|
||||
GDK_GL_ERROR_NOT_AVAILABLE,
|
||||
_("No GL implementation is available"));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (profile == GDK_GL_PROFILE_3_2_CORE &&
|
||||
!display_wayland->have_egl_khr_create_context)
|
||||
{
|
||||
g_set_error_literal (error, GDK_GL_ERROR,
|
||||
GDK_GL_ERROR_UNSUPPORTED_PROFILE,
|
||||
_("3.2 core GL profile is not available on EGL implementation"));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (!find_eglconfig_for_window (window, &config, error))
|
||||
return NULL;
|
||||
|
||||
i = 0;
|
||||
if (profile == GDK_GL_PROFILE_3_2_CORE)
|
||||
{
|
||||
context_attribs[i++] = EGL_CONTEXT_OPENGL_PROFILE_MASK_KHR;
|
||||
context_attribs[i++] = EGL_CONTEXT_OPENGL_CORE_PROFILE_BIT_KHR;
|
||||
}
|
||||
context_attribs[i++] = EGL_NONE;
|
||||
|
||||
ctx = eglCreateContext (display_wayland->egl_display,
|
||||
config,
|
||||
share ? GDK_WAYLAND_GL_CONTEXT (share)->egl_context : EGL_NO_CONTEXT,
|
||||
context_attribs);
|
||||
if (ctx == NULL)
|
||||
{
|
||||
g_set_error_literal (error, GDK_GL_ERROR,
|
||||
GDK_GL_ERROR_NOT_AVAILABLE,
|
||||
_("Unable to create a GL context"));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
GDK_NOTE (OPENGL,
|
||||
g_print ("Created EGL context[%p]\n", ctx));
|
||||
|
||||
context = g_object_new (GDK_WAYLAND_TYPE_GL_CONTEXT,
|
||||
"window", window,
|
||||
"visual", gdk_window_get_visual (window),
|
||||
NULL);
|
||||
|
||||
context->egl_config = config;
|
||||
context->egl_context = ctx;
|
||||
context->is_attached = attached;
|
||||
|
||||
return GDK_GL_CONTEXT (context);
|
||||
}
|
||||
|
||||
void
|
||||
gdk_wayland_display_destroy_gl_context (GdkDisplay *display,
|
||||
GdkGLContext *context)
|
||||
{
|
||||
GdkWaylandDisplay *display_wayland = GDK_WAYLAND_DISPLAY (display);
|
||||
GdkWaylandGLContext *context_wayland = GDK_WAYLAND_GL_CONTEXT (context);
|
||||
|
||||
/* TODO: Unset as current if current? */
|
||||
if (context_wayland->egl_context != NULL)
|
||||
{
|
||||
if (eglGetCurrentContext () == context_wayland->egl_context)
|
||||
eglMakeCurrent(display_wayland->egl_display, EGL_NO_SURFACE, EGL_NO_SURFACE,
|
||||
EGL_NO_CONTEXT);
|
||||
|
||||
GDK_NOTE (OPENGL, g_print ("Destroying EGL context\n"));
|
||||
|
||||
eglDestroyContext (display_wayland->egl_display,
|
||||
context_wayland->egl_context);
|
||||
context_wayland->egl_context = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
gdk_wayland_display_make_gl_context_current (GdkDisplay *display,
|
||||
GdkGLContext *context)
|
||||
{
|
||||
GdkWaylandDisplay *display_wayland = GDK_WAYLAND_DISPLAY (display);
|
||||
GdkWaylandGLContext *context_wayland;
|
||||
GdkWindow *window;
|
||||
EGLSurface egl_surface;
|
||||
|
||||
if (context == NULL)
|
||||
{
|
||||
eglMakeCurrent(display_wayland->egl_display, EGL_NO_SURFACE, EGL_NO_SURFACE,
|
||||
EGL_NO_CONTEXT);
|
||||
return;
|
||||
}
|
||||
|
||||
context_wayland = GDK_WAYLAND_GL_CONTEXT (context);
|
||||
window = gdk_gl_context_get_window (context);
|
||||
|
||||
if (context_wayland->is_attached)
|
||||
egl_surface = gdk_wayland_window_get_egl_surface (window->impl_window, context_wayland->egl_config);
|
||||
else
|
||||
{
|
||||
if (display_wayland->have_egl_surfaceless_context)
|
||||
egl_surface = EGL_NO_SURFACE;
|
||||
else
|
||||
egl_surface = gdk_wayland_window_get_dummy_egl_surface (window->impl_window,
|
||||
context_wayland->egl_config);
|
||||
}
|
||||
|
||||
if (!eglMakeCurrent (display_wayland->egl_display, egl_surface,
|
||||
egl_surface, context_wayland->egl_context))
|
||||
g_critical ("eglMakeCurrent failed");
|
||||
}
|
65
gdk/wayland/gdkglcontext-wayland.h
Normal file
65
gdk/wayland/gdkglcontext-wayland.h
Normal file
@@ -0,0 +1,65 @@
|
||||
/* GDK - The GIMP Drawing Kit
|
||||
*
|
||||
* gdkglcontext-wayland.h: Private Wayland specific OpenGL wrappers
|
||||
*
|
||||
* Copyright © 2014 Emmanuele Bassi
|
||||
* Copyright © 2014 Red Hat, Int
|
||||
*
|
||||
* 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_WAYLAND_GL_CONTEXT__
|
||||
#define __GDK_WAYLAND_GL_CONTEXT__
|
||||
|
||||
#include "gdkglcontextprivate.h"
|
||||
#include "gdkdisplayprivate.h"
|
||||
#include "gdkvisual.h"
|
||||
#include "gdkwindow.h"
|
||||
#include "gdkinternals.h"
|
||||
#include "gdkmain.h"
|
||||
|
||||
#include <epoxy/egl.h>
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
struct _GdkWaylandGLContext
|
||||
{
|
||||
GdkGLContext parent_instance;
|
||||
|
||||
EGLContext egl_context;
|
||||
EGLConfig egl_config;
|
||||
gboolean is_attached;
|
||||
};
|
||||
|
||||
struct _GdkWaylandGLContextClass
|
||||
{
|
||||
GdkGLContextClass parent_class;
|
||||
};
|
||||
|
||||
gboolean gdk_wayland_display_init_gl (GdkDisplay *display);
|
||||
GdkGLContext * gdk_wayland_window_create_gl_context (GdkWindow *window,
|
||||
gboolean attach,
|
||||
GdkGLProfile profile,
|
||||
GdkGLContext *share,
|
||||
GError **error);
|
||||
void gdk_wayland_window_invalidate_for_new_frame (GdkWindow *window,
|
||||
cairo_region_t *update_area);
|
||||
void gdk_wayland_display_destroy_gl_context (GdkDisplay *display,
|
||||
GdkGLContext *context);
|
||||
void gdk_wayland_display_make_gl_context_current (GdkDisplay *display,
|
||||
GdkGLContext *context);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __GDK_WAYLAND_GL_CONTEXT__ */
|
@@ -237,5 +237,9 @@ struct wl_data_source * gdk_wayland_selection_get_data_source (GdkWindow *owner,
|
||||
GdkAtom selection);
|
||||
void gdk_wayland_selection_unset_data_source (GdkAtom selection);
|
||||
|
||||
EGLSurface gdk_wayland_window_get_egl_surface (GdkWindow *window,
|
||||
EGLConfig config);
|
||||
EGLSurface gdk_wayland_window_get_dummy_egl_surface (GdkWindow *window,
|
||||
EGLConfig config);
|
||||
|
||||
#endif /* __GDK_PRIVATE_WAYLAND_H__ */
|
||||
|
@@ -911,6 +911,7 @@ gdk_wayland_visual_new (GdkScreen *screen)
|
||||
visual->screen = GDK_SCREEN (screen);
|
||||
visual->type = GDK_VISUAL_TRUE_COLOR;
|
||||
visual->depth = 32;
|
||||
visual->bits_per_rgb = 8;
|
||||
|
||||
return visual;
|
||||
}
|
||||
|
@@ -33,6 +33,7 @@
|
||||
#include <gdk/wayland/gdkwaylanddisplay.h>
|
||||
#include <gdk/wayland/gdkwaylandselection.h>
|
||||
#include <gdk/wayland/gdkwaylandwindow.h>
|
||||
#include <gdk/wayland/gdkwaylandglcontext.h>
|
||||
|
||||
#undef __GDKWAYLAND_H_INSIDE__
|
||||
|
||||
|
45
gdk/wayland/gdkwaylandglcontext.h
Normal file
45
gdk/wayland/gdkwaylandglcontext.h
Normal file
@@ -0,0 +1,45 @@
|
||||
/* GDK - The GIMP Drawing Kit
|
||||
*
|
||||
* gdkglcontext-wayland.c: Wayland specific OpenGL wrappers
|
||||
*
|
||||
* Copyright © 2014 Emmanuele Bassi
|
||||
* Copyright © 2014 Red Hat, Inc
|
||||
*
|
||||
* 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_WAYLAND_GL_CONTEXT_H__
|
||||
#define __GDK_WAYLAND_GL_CONTEXT_H__
|
||||
|
||||
#if !defined (__GDKWAYLAND_H_INSIDE__) && !defined (GDK_COMPILATION)
|
||||
#error "Only <gdk/gdkwayland.h> can be included directly."
|
||||
#endif
|
||||
|
||||
#include <gdk/gdk.h>
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
#define GDK_WAYLAND_TYPE_GL_CONTEXT (gdk_wayland_gl_context_get_type ())
|
||||
#define GDK_WAYLAND_GL_CONTEXT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GDK_WAYLAND_TYPE_GL_CONTEXT, GdkWaylandGLContext))
|
||||
#define GDK_WAYLAND_IS_GL_CONTEXT(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GDK_WAYLAND_TYPE_GL_CONTEXT))
|
||||
|
||||
typedef struct _GdkWaylandGLContext GdkWaylandGLContext;
|
||||
typedef struct _GdkWaylandGLContextClass GdkWaylandGLContextClass;
|
||||
|
||||
GDK_AVAILABLE_IN_3_16
|
||||
GType gdk_wayland_gl_context_get_type (void) G_GNUC_CONST;
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __GDK_WAYLAND_GL_CONTEXT_H__ */
|
@@ -26,6 +26,7 @@
|
||||
#include "gdkwindow.h"
|
||||
#include "gdkwindowimpl.h"
|
||||
#include "gdkdisplay-wayland.h"
|
||||
#include "gdkglcontext-wayland.h"
|
||||
#include "gdkframeclockprivate.h"
|
||||
#include "gdkprivate-wayland.h"
|
||||
#include "gdkinternals.h"
|
||||
@@ -97,6 +98,12 @@ struct _GdkWindowImplWayland
|
||||
|
||||
struct wl_subsurface *subsurface;
|
||||
|
||||
struct wl_egl_window *egl_window;
|
||||
EGLSurface egl_surface;
|
||||
|
||||
struct wl_egl_window *dummy_egl_window;
|
||||
EGLSurface dummy_egl_surface;
|
||||
|
||||
unsigned int mapped : 1;
|
||||
unsigned int use_custom_surface : 1;
|
||||
unsigned int pending_commit : 1;
|
||||
@@ -170,6 +177,9 @@ gdk_wayland_window_update_size (GdkWindow *window,
|
||||
window->width = width;
|
||||
window->height = height;
|
||||
|
||||
if (impl->egl_window)
|
||||
wl_egl_window_resize (impl->egl_window, width, height, 0, 0);
|
||||
|
||||
area.x = 0;
|
||||
area.y = 0;
|
||||
area.width = window->width;
|
||||
@@ -582,15 +592,18 @@ gdk_window_impl_wayland_end_paint (GdkWindow *window)
|
||||
cairo_rectangle_int_t rect;
|
||||
int i, n;
|
||||
|
||||
gdk_wayland_window_attach_image (window);
|
||||
|
||||
n = cairo_region_num_rectangles (window->current_paint.region);
|
||||
for (i = 0; i < n; i++)
|
||||
if (!window->current_paint.use_gl)
|
||||
{
|
||||
cairo_region_get_rectangle (window->current_paint.region, i, &rect);
|
||||
wl_surface_damage (impl->surface,
|
||||
rect.x, rect.y, rect.width, rect.height);
|
||||
impl->pending_commit = TRUE;
|
||||
gdk_wayland_window_attach_image (window);
|
||||
|
||||
n = cairo_region_num_rectangles (window->current_paint.region);
|
||||
for (i = 0; i < n; i++)
|
||||
{
|
||||
cairo_region_get_rectangle (window->current_paint.region, i, &rect);
|
||||
wl_surface_damage (impl->surface,
|
||||
rect.x, rect.y, rect.width, rect.height);
|
||||
impl->pending_commit = TRUE;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1156,10 +1169,35 @@ gdk_wayland_window_show (GdkWindow *window,
|
||||
static void
|
||||
gdk_wayland_window_hide_surface (GdkWindow *window)
|
||||
{
|
||||
GdkWaylandDisplay *display_wayland = GDK_WAYLAND_DISPLAY (gdk_window_get_display (window));
|
||||
GdkWindowImplWayland *impl = GDK_WINDOW_IMPL_WAYLAND (window->impl);
|
||||
|
||||
if (impl->surface)
|
||||
{
|
||||
if (impl->dummy_egl_surface)
|
||||
{
|
||||
eglDestroySurface(display_wayland->egl_display, impl->dummy_egl_surface);
|
||||
impl->dummy_egl_surface = NULL;
|
||||
}
|
||||
|
||||
if (impl->dummy_egl_window)
|
||||
{
|
||||
wl_egl_window_destroy (impl->dummy_egl_window);
|
||||
impl->dummy_egl_window = NULL;
|
||||
}
|
||||
|
||||
if (impl->egl_surface)
|
||||
{
|
||||
eglDestroySurface(display_wayland->egl_display, impl->egl_surface);
|
||||
impl->egl_surface = NULL;
|
||||
}
|
||||
|
||||
if (impl->egl_window)
|
||||
{
|
||||
wl_egl_window_destroy (impl->egl_window);
|
||||
impl->egl_window = NULL;
|
||||
}
|
||||
|
||||
if (impl->xdg_surface)
|
||||
{
|
||||
xdg_surface_destroy (impl->xdg_surface);
|
||||
@@ -2128,6 +2166,8 @@ _gdk_window_impl_wayland_class_init (GdkWindowImplWaylandClass *klass)
|
||||
impl_class->set_opaque_region = gdk_wayland_window_set_opaque_region;
|
||||
impl_class->set_shadow_width = gdk_wayland_window_set_shadow_width;
|
||||
impl_class->show_window_menu = gdk_wayland_window_show_window_menu;
|
||||
impl_class->create_gl_context = gdk_wayland_window_create_gl_context;
|
||||
impl_class->invalidate_for_new_frame = gdk_wayland_window_invalidate_for_new_frame;
|
||||
}
|
||||
|
||||
void
|
||||
@@ -2169,6 +2209,75 @@ gdk_wayland_window_get_wl_surface (GdkWindow *window)
|
||||
return impl->surface;
|
||||
}
|
||||
|
||||
static struct wl_egl_window *
|
||||
gdk_wayland_window_get_wl_egl_window (GdkWindow *window)
|
||||
{
|
||||
GdkWindowImplWayland *impl;
|
||||
|
||||
g_return_val_if_fail (GDK_IS_WAYLAND_WINDOW (window), NULL);
|
||||
|
||||
impl = GDK_WINDOW_IMPL_WAYLAND (window->impl);
|
||||
|
||||
if (impl->egl_window == NULL)
|
||||
{
|
||||
impl->egl_window =
|
||||
wl_egl_window_create(impl->surface,
|
||||
impl->wrapper->width,
|
||||
impl->wrapper->height);
|
||||
}
|
||||
|
||||
return impl->egl_window;
|
||||
}
|
||||
|
||||
EGLSurface
|
||||
gdk_wayland_window_get_egl_surface (GdkWindow *window,
|
||||
EGLConfig config)
|
||||
{
|
||||
GdkWaylandDisplay *display_wayland = GDK_WAYLAND_DISPLAY (gdk_window_get_display (window));
|
||||
GdkWindowImplWayland *impl;
|
||||
struct wl_egl_window *egl_window;
|
||||
|
||||
g_return_val_if_fail (GDK_IS_WAYLAND_WINDOW (window), NULL);
|
||||
|
||||
impl = GDK_WINDOW_IMPL_WAYLAND (window->impl);
|
||||
|
||||
if (impl->egl_surface == NULL)
|
||||
{
|
||||
egl_window = gdk_wayland_window_get_wl_egl_window (window);
|
||||
|
||||
impl->egl_surface =
|
||||
eglCreateWindowSurface (display_wayland->egl_display,
|
||||
config, egl_window, NULL);
|
||||
}
|
||||
|
||||
return impl->egl_surface;
|
||||
}
|
||||
|
||||
EGLSurface
|
||||
gdk_wayland_window_get_dummy_egl_surface (GdkWindow *window,
|
||||
EGLConfig config)
|
||||
{
|
||||
GdkWaylandDisplay *display_wayland = GDK_WAYLAND_DISPLAY (gdk_window_get_display (window));
|
||||
GdkWindowImplWayland *impl;
|
||||
|
||||
g_return_val_if_fail (GDK_IS_WAYLAND_WINDOW (window), NULL);
|
||||
|
||||
impl = GDK_WINDOW_IMPL_WAYLAND (window->impl);
|
||||
|
||||
if (impl->dummy_egl_surface == NULL)
|
||||
{
|
||||
impl->dummy_egl_window =
|
||||
wl_egl_window_create (impl->surface, 1, 1);
|
||||
|
||||
impl->dummy_egl_surface =
|
||||
eglCreateWindowSurface (display_wayland->egl_display,
|
||||
config, impl->dummy_egl_window, NULL);
|
||||
}
|
||||
|
||||
return impl->dummy_egl_surface;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* gdk_wayland_window_set_use_custom_surface:
|
||||
* @window: (type GdkWaylandWindow): a #GdkWindow
|
||||
|
@@ -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 \
|
||||
|
@@ -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>
|
||||
@@ -2903,5 +2904,8 @@ 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->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 ();
|
||||
}
|
||||
|
@@ -124,6 +124,21 @@ struct _GdkX11Display
|
||||
GSList *error_traps;
|
||||
|
||||
gint wm_moveresize_button;
|
||||
|
||||
/* GLX information */
|
||||
gint glx_version;
|
||||
gint glx_error_base;
|
||||
gint glx_event_base;
|
||||
|
||||
guint have_glx : 1;
|
||||
|
||||
/* 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
|
||||
|
906
gdk/x11/gdkglcontext-x11.c
Normal file
906
gdk/x11/gdkglcontext-x11.c
Normal file
@@ -0,0 +1,906 @@
|
||||
/* 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 <cairo/cairo-xlib.h>
|
||||
|
||||
#include <GL/glx.h>
|
||||
|
||||
G_DEFINE_TYPE (GdkX11GLContext, gdk_x11_gl_context, GDK_TYPE_GL_CONTEXT)
|
||||
|
||||
typedef struct {
|
||||
GdkDisplay *display;
|
||||
|
||||
GLXDrawable glx_drawable;
|
||||
|
||||
Window dummy_xwin;
|
||||
GLXWindow dummy_glx;
|
||||
|
||||
guint32 last_frame_counter;
|
||||
} DrawableInfo;
|
||||
|
||||
static void
|
||||
drawable_info_free (gpointer data_)
|
||||
{
|
||||
DrawableInfo *data = data_;
|
||||
Display *dpy;
|
||||
|
||||
gdk_x11_display_error_trap_push (data->display);
|
||||
|
||||
dpy = gdk_x11_display_get_xdisplay (data->display);
|
||||
|
||||
if (data->glx_drawable)
|
||||
glXDestroyWindow (dpy, data->glx_drawable);
|
||||
|
||||
if (data->dummy_glx)
|
||||
glXDestroyWindow (dpy, data->dummy_glx);
|
||||
|
||||
if (data->dummy_xwin)
|
||||
XDestroyWindow (dpy, data->dummy_xwin);
|
||||
|
||||
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_update (GdkGLContext *context)
|
||||
{
|
||||
GdkWindow *window = gdk_gl_context_get_window (context);
|
||||
int width, height;
|
||||
|
||||
gdk_gl_context_make_current (context);
|
||||
|
||||
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 (¤t_count);
|
||||
glXWaitVideoSyncSGI (2, (current_count + 1) % 2, ¤t_count);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
gdk_x11_window_invalidate_for_new_frame (GdkWindow *window,
|
||||
cairo_region_t *update_area)
|
||||
{
|
||||
cairo_rectangle_int_t window_rect;
|
||||
GdkDisplay *display = gdk_window_get_display (window);
|
||||
GdkX11Display *display_x11 = GDK_X11_DISPLAY (display);
|
||||
Display *dpy = gdk_x11_display_get_xdisplay (display);
|
||||
GdkX11GLContext *context_x11;
|
||||
unsigned int buffer_age;
|
||||
gboolean invalidate_all;
|
||||
|
||||
/* Minimal update is ok if we're not drawing with gl */
|
||||
if (window->gl_paint_context == NULL)
|
||||
return;
|
||||
|
||||
context_x11 = GDK_X11_GL_CONTEXT (window->gl_paint_context);
|
||||
|
||||
buffer_age = 0;
|
||||
|
||||
if (display_x11->has_glx_buffer_age)
|
||||
{
|
||||
gdk_gl_context_make_current (window->gl_paint_context);
|
||||
glXQueryDrawable(dpy, context_x11->drawable,
|
||||
GLX_BACK_BUFFER_AGE_EXT, &buffer_age);
|
||||
}
|
||||
|
||||
invalidate_all = FALSE;
|
||||
if (buffer_age == 0 || buffer_age >= 4)
|
||||
invalidate_all = TRUE;
|
||||
else
|
||||
{
|
||||
if (buffer_age >= 2)
|
||||
{
|
||||
if (window->old_updated_area[0])
|
||||
cairo_region_union (update_area, window->old_updated_area[0]);
|
||||
else
|
||||
invalidate_all = TRUE;
|
||||
}
|
||||
if (buffer_age >= 3)
|
||||
{
|
||||
if (window->old_updated_area[1])
|
||||
cairo_region_union (update_area, window->old_updated_area[1]);
|
||||
else
|
||||
invalidate_all = TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
if (invalidate_all)
|
||||
{
|
||||
window_rect.x = 0;
|
||||
window_rect.y = 0;
|
||||
window_rect.width = gdk_window_get_width (window);
|
||||
window_rect.height = gdk_window_get_height (window);
|
||||
|
||||
/* If nothing else is known, repaint everything so that the back
|
||||
buffer is fully up-to-date for the swapbuffer */
|
||||
cairo_region_union_rectangle (update_area, &window_rect);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static void
|
||||
gdk_x11_gl_context_flush_buffer (GdkGLContext *context,
|
||||
cairo_region_t *painted,
|
||||
cairo_region_t *damage)
|
||||
{
|
||||
GdkX11GLContext *context_x11 = GDK_X11_GL_CONTEXT (context);
|
||||
GdkWindow *window = gdk_gl_context_get_window (context);
|
||||
GdkDisplay *display = gdk_window_get_display (window);
|
||||
Display *dpy = gdk_x11_display_get_xdisplay (display);
|
||||
GdkX11Display *display_x11 = GDK_X11_DISPLAY (display);
|
||||
DrawableInfo *info;
|
||||
GLXDrawable drawable;
|
||||
|
||||
gdk_gl_context_make_current (context);
|
||||
|
||||
info = get_glx_drawable_info (window);
|
||||
|
||||
drawable = context_x11->drawable;
|
||||
|
||||
GDK_NOTE (OPENGL,
|
||||
g_print ("Flushing GLX buffers for drawable %lu (window: %lu), frame sync: %s\n",
|
||||
(unsigned long) drawable,
|
||||
(unsigned long) gdk_x11_window_get_xid (window),
|
||||
context_x11->do_frame_sync ? "yes" : "no"));
|
||||
|
||||
/* 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.
|
||||
*/
|
||||
if (context_x11->do_frame_sync)
|
||||
{
|
||||
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 (context_x11->do_frame_sync && !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 (context_x11->do_frame_sync && info != NULL && display_x11->has_glx_video_sync)
|
||||
glXGetVideoSyncSGI (&info->last_frame_counter);
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
Display *display;
|
||||
GLXDrawable drawable;
|
||||
gboolean y_inverted;
|
||||
} GdkGLXPixmap;
|
||||
|
||||
static void
|
||||
glx_pixmap_destroy (void *data)
|
||||
{
|
||||
GdkGLXPixmap *glx_pixmap = data;
|
||||
|
||||
glXDestroyPixmap (glx_pixmap->display, glx_pixmap->drawable);
|
||||
|
||||
g_slice_free (GdkGLXPixmap, glx_pixmap);
|
||||
}
|
||||
|
||||
static GdkGLXPixmap *
|
||||
glx_pixmap_get (cairo_surface_t *surface)
|
||||
{
|
||||
Display *display = cairo_xlib_surface_get_display (surface);
|
||||
Screen *screen = cairo_xlib_surface_get_screen (surface);
|
||||
Visual *visual = cairo_xlib_surface_get_visual (surface);;
|
||||
GdkGLXPixmap *glx_pixmap;
|
||||
GLXFBConfig *fbconfigs;
|
||||
int nfbconfigs;
|
||||
XVisualInfo *visinfo;
|
||||
int i, value;
|
||||
gboolean y_inverted;
|
||||
const int pixmap_attributes[] = {
|
||||
GLX_TEXTURE_TARGET_EXT, GLX_TEXTURE_RECTANGLE_EXT,
|
||||
GLX_TEXTURE_FORMAT_EXT, GLX_TEXTURE_FORMAT_RGBA_EXT,
|
||||
None
|
||||
};
|
||||
|
||||
y_inverted = FALSE;
|
||||
fbconfigs = glXGetFBConfigs (display, XScreenNumberOfScreen (screen), &nfbconfigs);
|
||||
for (i = 0; i < nfbconfigs; i++)
|
||||
{
|
||||
visinfo = glXGetVisualFromFBConfig (display, fbconfigs[i]);
|
||||
if (!visinfo || visinfo->visualid != XVisualIDFromVisual (visual))
|
||||
continue;
|
||||
|
||||
glXGetFBConfigAttrib (display, fbconfigs[i], GLX_DRAWABLE_TYPE, &value);
|
||||
if (!(value & GLX_PIXMAP_BIT))
|
||||
continue;
|
||||
|
||||
glXGetFBConfigAttrib (display, fbconfigs[i],
|
||||
GLX_BIND_TO_TEXTURE_TARGETS_EXT,
|
||||
&value);
|
||||
if (!(value & GLX_TEXTURE_RECTANGLE_BIT_EXT))
|
||||
continue;
|
||||
|
||||
glXGetFBConfigAttrib (display, fbconfigs[i],
|
||||
GLX_BIND_TO_TEXTURE_RGBA_EXT,
|
||||
&value);
|
||||
if (value == FALSE)
|
||||
{
|
||||
glXGetFBConfigAttrib (display, fbconfigs[i],
|
||||
GLX_BIND_TO_TEXTURE_RGB_EXT,
|
||||
&value);
|
||||
if (value == FALSE)
|
||||
continue;
|
||||
}
|
||||
|
||||
glXGetFBConfigAttrib (display, fbconfigs[i],
|
||||
GLX_Y_INVERTED_EXT,
|
||||
&value);
|
||||
if (value == TRUE)
|
||||
y_inverted = TRUE;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
if (i == nfbconfigs)
|
||||
return NULL;
|
||||
|
||||
glx_pixmap = g_slice_new0 (GdkGLXPixmap);
|
||||
glx_pixmap->y_inverted = y_inverted;
|
||||
glx_pixmap->display = display;
|
||||
glx_pixmap->drawable = glXCreatePixmap (display, fbconfigs[i],
|
||||
cairo_xlib_surface_get_drawable (surface),
|
||||
pixmap_attributes);
|
||||
|
||||
return glx_pixmap;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
gdk_x11_gl_context_texture_from_surface (GdkGLContext *context,
|
||||
cairo_surface_t *surface,
|
||||
cairo_region_t *region)
|
||||
{
|
||||
GdkGLXPixmap *glx_pixmap;
|
||||
double device_x_offset, device_y_offset;
|
||||
cairo_rectangle_int_t rect;
|
||||
int n_rects, i;
|
||||
GdkWindow *window;
|
||||
int window_height;
|
||||
int window_scale;
|
||||
unsigned int texture_id;
|
||||
double sx, sy;
|
||||
|
||||
if (cairo_surface_get_type (surface) != CAIRO_SURFACE_TYPE_XLIB)
|
||||
return FALSE;
|
||||
|
||||
glx_pixmap = glx_pixmap_get (surface);
|
||||
if (glx_pixmap == NULL)
|
||||
return FALSE;
|
||||
|
||||
window = gdk_gl_context_get_window (gdk_gl_context_get_current ());
|
||||
window_scale = gdk_window_get_scale_factor (window);
|
||||
window_height = gdk_window_get_height (window);
|
||||
|
||||
sx = sy = 1;
|
||||
#ifdef HAVE_CAIRO_SURFACE_SET_DEVICE_SCALE
|
||||
cairo_surface_get_device_scale (window->current_paint.surface, &sx, &sy);
|
||||
#endif
|
||||
|
||||
cairo_surface_get_device_offset (surface,
|
||||
&device_x_offset, &device_y_offset);
|
||||
|
||||
/* Ensure all the X stuff are synced before we read it back via texture-from-pixmap */
|
||||
glXWaitX();
|
||||
|
||||
glGenTextures (1, &texture_id);
|
||||
glBindTexture (GL_TEXTURE_RECTANGLE_ARB, texture_id);
|
||||
glEnable (GL_TEXTURE_RECTANGLE_ARB);
|
||||
|
||||
glXBindTexImageEXT (glx_pixmap->display, glx_pixmap->drawable,
|
||||
GLX_FRONT_LEFT_EXT, NULL);
|
||||
|
||||
n_rects = cairo_region_num_rectangles (region);
|
||||
for (i = 0; i < n_rects; i++)
|
||||
{
|
||||
int src_x, src_y, src_height, src_width;
|
||||
|
||||
cairo_region_get_rectangle (region, i, &rect);
|
||||
|
||||
glScissor (rect.x * window_scale, (window_height - rect.y - rect.height) * window_scale,
|
||||
rect.width * window_scale, rect.height * window_scale);
|
||||
|
||||
src_x = rect.x * sx + device_x_offset;
|
||||
src_y = rect.y * sy + device_y_offset;
|
||||
src_width = rect.width * sx;
|
||||
src_height = rect.height * sy;
|
||||
|
||||
#define FLIP_Y(_y) (window_height - (_y))
|
||||
|
||||
glBegin (GL_QUADS);
|
||||
glTexCoord2f (src_x, src_y + src_height);
|
||||
glVertex2f (rect.x * window_scale, FLIP_Y(rect.y + rect.height) * window_scale);
|
||||
|
||||
glTexCoord2f (src_x + src_width, src_y + src_height);
|
||||
glVertex2f ((rect.x + rect.width) * window_scale, FLIP_Y(rect.y + rect.height) * window_scale);
|
||||
|
||||
glTexCoord2f (src_x + src_width, src_y);
|
||||
glVertex2f ((rect.x + rect.width) * window_scale, FLIP_Y(rect.y) * window_scale);
|
||||
|
||||
glTexCoord2f (src_x, src_y);
|
||||
glVertex2f (rect.x * window_scale, FLIP_Y(rect.y) * window_scale);
|
||||
glEnd();
|
||||
}
|
||||
|
||||
glXReleaseTexImageEXT (glx_pixmap->display, glx_pixmap->drawable,
|
||||
GLX_FRONT_LEFT_EXT);
|
||||
|
||||
glDisable (GL_TEXTURE_RECTANGLE_ARB);
|
||||
glDeleteTextures (1, &texture_id);
|
||||
|
||||
glx_pixmap_destroy(glx_pixmap);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static void
|
||||
gdk_x11_gl_context_class_init (GdkX11GLContextClass *klass)
|
||||
{
|
||||
GdkGLContextClass *context_class = GDK_GL_CONTEXT_CLASS (klass);
|
||||
|
||||
context_class->update = gdk_x11_gl_context_update;
|
||||
context_class->flush_buffer = gdk_x11_gl_context_flush_buffer;
|
||||
context_class->texture_from_surface = gdk_x11_gl_context_texture_from_surface;
|
||||
}
|
||||
|
||||
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 gboolean
|
||||
find_fbconfig_for_visual (GdkDisplay *display,
|
||||
GdkVisual *visual,
|
||||
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;
|
||||
|
||||
i = 0;
|
||||
attrs[i++] = GLX_DRAWABLE_TYPE;
|
||||
attrs[i++] = GLX_WINDOW_BIT;
|
||||
|
||||
attrs[i++] = GLX_RENDER_TYPE;
|
||||
attrs[i++] = GLX_RGBA_BIT;
|
||||
|
||||
attrs[i++] = GLX_DOUBLEBUFFER;
|
||||
attrs[i++] = GL_TRUE;
|
||||
|
||||
attrs[i++] = GLX_RED_SIZE;
|
||||
attrs[i++] = gdk_visual_get_bits_per_rgb (visual);
|
||||
attrs[i++] = GLX_GREEN_SIZE;
|
||||
attrs[i++] = gdk_visual_get_bits_per_rgb (visual);;
|
||||
attrs[i++] = GLX_BLUE_SIZE;
|
||||
attrs[i++] = gdk_visual_get_bits_per_rgb (visual);;
|
||||
|
||||
use_rgba = (visual == gdk_screen_get_rgba_visual (gdk_display_get_default_screen (display)));
|
||||
|
||||
if (use_rgba)
|
||||
{
|
||||
attrs[i++] = GLX_ALPHA_SIZE;
|
||||
attrs[i++] = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
attrs[i++] = GLX_ALPHA_SIZE;
|
||||
attrs[i++] = GLX_DONT_CARE;
|
||||
}
|
||||
|
||||
attrs[i++] = None;
|
||||
|
||||
g_assert (i < MAX_GLX_ATTRS);
|
||||
|
||||
configs = glXChooseFBConfig (dpy, DefaultScreen (dpy), attrs, &n_configs);
|
||||
if (configs == NULL || n_configs == 0)
|
||||
{
|
||||
g_set_error_literal (error, GDK_GL_ERROR,
|
||||
GDK_GL_ERROR_UNSUPPORTED_FORMAT,
|
||||
_("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_ERROR,
|
||||
GDK_GL_ERROR_UNSUPPORTED_FORMAT,
|
||||
_("No available configurations for the given RGBA pixel format"));
|
||||
|
||||
out:
|
||||
XFree (configs);
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
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_window_create_gl_context (GdkWindow *window,
|
||||
gboolean attached,
|
||||
GdkGLProfile profile,
|
||||
GdkGLContext *share,
|
||||
GError **error)
|
||||
{
|
||||
GdkDisplay *display;
|
||||
GdkX11GLContext *context;
|
||||
GdkVisual *visual;
|
||||
GdkVisual *gdk_visual;
|
||||
GLXFBConfig config;
|
||||
GLXContext glx_context;
|
||||
GLXWindow drawable;
|
||||
gboolean is_direct;
|
||||
XVisualInfo *xvisinfo;
|
||||
Display *dpy;
|
||||
DrawableInfo *info;
|
||||
|
||||
display = gdk_window_get_display (window);
|
||||
|
||||
if (!gdk_x11_display_init_gl (display))
|
||||
{
|
||||
g_set_error_literal (error, GDK_GL_ERROR,
|
||||
GDK_GL_ERROR_NOT_AVAILABLE,
|
||||
_("No GL implementation is available"));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (profile == GDK_GL_PROFILE_3_2_CORE &&
|
||||
!GDK_X11_DISPLAY (display)->has_glx_create_context)
|
||||
{
|
||||
g_set_error_literal (error, GDK_GL_ERROR,
|
||||
GDK_GL_ERROR_UNSUPPORTED_PROFILE,
|
||||
_("The GLX_ARB_create_context_profile extension "
|
||||
"needed to create 3.2 core profiles is not "
|
||||
"available"));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
visual = gdk_window_get_visual (window);
|
||||
|
||||
if (!find_fbconfig_for_visual (display, visual, &config, &xvisinfo, error))
|
||||
return NULL;
|
||||
|
||||
dpy = gdk_x11_display_get_xdisplay (display);
|
||||
|
||||
/* we check for the GLX_ARB_create_context_profile extension
|
||||
* while validating the PixelFormat.
|
||||
*/
|
||||
if (profile == GDK_GL_PROFILE_3_2_CORE)
|
||||
glx_context = create_gl3_context (display, config, share);
|
||||
else
|
||||
{
|
||||
/* GDK_GL_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_ERROR,
|
||||
GDK_GL_ERROR_NOT_AVAILABLE,
|
||||
_("Unable to create a GL context"));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
is_direct = glXIsDirect (dpy, glx_context);
|
||||
|
||||
info = get_glx_drawable_info (window->impl_window);
|
||||
if (info == NULL)
|
||||
{
|
||||
XSetWindowAttributes attrs;
|
||||
unsigned long mask;
|
||||
|
||||
gdk_x11_display_error_trap_push (display);
|
||||
|
||||
info = g_slice_new0 (DrawableInfo);
|
||||
info->display = display;
|
||||
info->last_frame_counter = 0;
|
||||
|
||||
attrs.override_redirect = True;
|
||||
attrs.colormap = XCreateColormap (dpy, DefaultRootWindow (dpy), xvisinfo->visual, AllocNone);
|
||||
attrs.border_pixel = 0;
|
||||
mask = CWOverrideRedirect | CWColormap | CWBorderPixel;
|
||||
info->dummy_xwin = XCreateWindow (dpy, DefaultRootWindow (dpy),
|
||||
-100, -100, 1, 1,
|
||||
0,
|
||||
xvisinfo->depth,
|
||||
CopyFromParent,
|
||||
xvisinfo->visual,
|
||||
mask,
|
||||
&attrs);
|
||||
XMapWindow(dpy, info->dummy_xwin);
|
||||
|
||||
if (GDK_X11_DISPLAY (display)->glx_version >= 13)
|
||||
{
|
||||
info->glx_drawable = glXCreateWindow (dpy, config,
|
||||
gdk_x11_window_get_xid (window->impl_window),
|
||||
NULL);
|
||||
info->dummy_glx = glXCreateWindow (dpy, config, info->dummy_xwin, NULL);
|
||||
}
|
||||
|
||||
if (gdk_x11_display_error_trap_pop (display))
|
||||
{
|
||||
g_set_error_literal (error, GDK_GL_ERROR,
|
||||
GDK_GL_ERROR_NOT_AVAILABLE,
|
||||
_("Unable to create a GL context"));
|
||||
|
||||
drawable_info_free (info);
|
||||
glXDestroyContext (dpy, glx_context);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
set_glx_drawable_info (window->impl_window, info);
|
||||
}
|
||||
|
||||
gdk_visual = gdk_x11_screen_lookup_visual (gdk_display_get_default_screen (display),
|
||||
xvisinfo->visualid);
|
||||
|
||||
XFree (xvisinfo);
|
||||
|
||||
if (attached)
|
||||
drawable = info->glx_drawable ? info->glx_drawable : gdk_x11_window_get_xid (window->impl_window);
|
||||
else
|
||||
drawable = info->dummy_glx ? info->dummy_glx : info->dummy_xwin;
|
||||
|
||||
GDK_NOTE (OPENGL,
|
||||
g_print ("Created GLX context[%p], %s\n",
|
||||
glx_context,
|
||||
is_direct ? "direct" : "indirect"));
|
||||
|
||||
context = g_object_new (GDK_X11_TYPE_GL_CONTEXT,
|
||||
"window", window,
|
||||
"visual", gdk_visual,
|
||||
NULL);
|
||||
|
||||
context->profile = profile;
|
||||
context->glx_config = config;
|
||||
context->glx_context = glx_context;
|
||||
context->drawable = drawable;
|
||||
context->is_attached = attached;
|
||||
context->is_direct = is_direct;
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
gdk_x11_display_make_gl_context_current (GdkDisplay *display,
|
||||
GdkGLContext *context)
|
||||
{
|
||||
GdkX11GLContext *context_x11;
|
||||
Display *dpy = gdk_x11_display_get_xdisplay (display);
|
||||
GdkWindow *window;
|
||||
GdkScreen *screen;
|
||||
gboolean do_frame_sync = FALSE;
|
||||
|
||||
if (context == NULL)
|
||||
{
|
||||
glXMakeContextCurrent (dpy, None, None, NULL);
|
||||
return;
|
||||
}
|
||||
|
||||
context_x11 = GDK_X11_GL_CONTEXT (context);
|
||||
|
||||
window = gdk_gl_context_get_window (context);
|
||||
|
||||
// If the WM is compositing there is no particular need to delay
|
||||
// the swap when drawing on the offscreen, rendering to the screen
|
||||
// happens later anyway, and its up to the compositor to sync that
|
||||
// to the vblank.
|
||||
screen = gdk_window_get_screen (window);
|
||||
do_frame_sync = ! gdk_screen_is_composited (screen);
|
||||
|
||||
context_x11->do_frame_sync = do_frame_sync;
|
||||
|
||||
GDK_NOTE (OPENGL,
|
||||
g_print ("Making GLX context current to drawable %lu\n",
|
||||
(unsigned long) context_x11->drawable));
|
||||
|
||||
glXMakeContextCurrent (dpy, context_x11->drawable, context_x11->drawable,
|
||||
context_x11->glx_context);
|
||||
|
||||
if (context_x11->is_attached && GDK_X11_DISPLAY (display)->has_glx_swap_interval)
|
||||
{
|
||||
if (context_x11->do_frame_sync)
|
||||
glXSwapIntervalSGI (1);
|
||||
else
|
||||
glXSwapIntervalSGI (0);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.16
|
||||
*/
|
||||
gboolean
|
||||
gdk_x11_display_get_glx_version (GdkDisplay *display,
|
||||
gint *major,
|
||||
gint *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;
|
||||
}
|
74
gdk/x11/gdkglcontext-x11.h
Normal file
74
gdk/x11/gdkglcontext-x11.h
Normal file
@@ -0,0 +1,74 @@
|
||||
/* 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 "gdkdisplayprivate.h"
|
||||
#include "gdkvisual.h"
|
||||
#include "gdkwindow.h"
|
||||
#include "gdkinternals.h"
|
||||
#include "gdkmain.h"
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
struct _GdkX11GLContext
|
||||
{
|
||||
GdkGLContext parent_instance;
|
||||
|
||||
GdkGLProfile profile;
|
||||
GLXContext glx_context;
|
||||
GLXFBConfig glx_config;
|
||||
GLXDrawable drawable;
|
||||
|
||||
guint is_attached : 1;
|
||||
guint is_direct : 1;
|
||||
guint do_frame_sync : 1;
|
||||
|
||||
};
|
||||
|
||||
struct _GdkX11GLContextClass
|
||||
{
|
||||
GdkGLContextClass parent_class;
|
||||
};
|
||||
|
||||
gboolean gdk_x11_display_init_gl (GdkDisplay *display);
|
||||
GdkGLContext * gdk_x11_window_create_gl_context (GdkWindow *window,
|
||||
gboolean attached,
|
||||
GdkGLProfile profile,
|
||||
GdkGLContext *share,
|
||||
GError **error);
|
||||
void gdk_x11_window_invalidate_for_new_frame (GdkWindow *window,
|
||||
cairo_region_t *update_area);
|
||||
void gdk_x11_display_destroy_gl_context (GdkDisplay *display,
|
||||
GdkGLContext *context);
|
||||
void gdk_x11_display_make_gl_context_current (GdkDisplay *display,
|
||||
GdkGLContext *context);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __GDK_X11_GL_CONTEXT__ */
|
@@ -36,6 +36,7 @@
|
||||
#include "gdkasync.h"
|
||||
#include "gdkeventsource.h"
|
||||
#include "gdkdisplay-x11.h"
|
||||
#include "gdkglcontext-x11.h"
|
||||
#include "gdkprivate-x11.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
@@ -5730,4 +5731,6 @@ gdk_window_impl_x11_class_init (GdkWindowImplX11Class *klass)
|
||||
impl_class->set_opaque_region = gdk_x11_window_set_opaque_region;
|
||||
impl_class->set_shadow_width = gdk_x11_window_set_shadow_width;
|
||||
impl_class->show_window_menu = gdk_x11_window_show_window_menu;
|
||||
impl_class->create_gl_context = gdk_x11_window_create_gl_context;
|
||||
impl_class->invalidate_for_new_frame = gdk_x11_window_invalidate_for_new_frame;
|
||||
}
|
||||
|
@@ -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
49
gdk/x11/gdkx11glcontext.h
Normal 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_16
|
||||
GType gdk_x11_gl_context_get_type (void) G_GNUC_CONST;
|
||||
|
||||
GDK_AVAILABLE_IN_3_16
|
||||
gboolean gdk_x11_display_get_glx_version (GdkDisplay *display,
|
||||
gint *major,
|
||||
gint *minor);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __GDK_X11_GL_CONTEXT_H__ */
|
@@ -363,6 +363,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 \
|
||||
|
@@ -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>
|
||||
|
629
gtk/gtkglarea.c
Normal file
629
gtk/gtkglarea.c
Normal file
@@ -0,0 +1,629 @@
|
||||
/* 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"
|
||||
#include <epoxy/gl.h>
|
||||
|
||||
/**
|
||||
* SECTION:gtkglarea
|
||||
* @Title: GtkGLArea
|
||||
* @Short_description: A widget for custom drawing with OpenGL
|
||||
*
|
||||
* #GtkGLArea is a widget that allows drawing with OpenGL.
|
||||
*
|
||||
* #GtkGLArea sets up its own #GdkGLContext for the window it creates, and
|
||||
* creates a custom GL framebuffer that the widget will do GL rendering onto.
|
||||
* It also ensures that this framebuffer is the default GL rendering target
|
||||
* when rendering.
|
||||
*
|
||||
* 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 GtkGLArea instance
|
||||
* GtkWidget *gl_area = gtk_gl_area_new ();
|
||||
*
|
||||
* // 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 will be drawn on the window
|
||||
* 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.
|
||||
*
|
||||
*/
|
||||
|
||||
typedef struct {
|
||||
GdkGLContext *context;
|
||||
GLuint framebuffer;
|
||||
gboolean has_alpha;
|
||||
gboolean has_depth_buffer;
|
||||
} GtkGLAreaPrivate;
|
||||
|
||||
enum {
|
||||
PROP_0,
|
||||
|
||||
PROP_CONTEXT,
|
||||
PROP_HAS_ALPHA,
|
||||
PROP_HAS_DEPTH_BUFFER,
|
||||
|
||||
LAST_PROP
|
||||
};
|
||||
|
||||
static GParamSpec *obj_props[LAST_PROP] = { NULL, };
|
||||
|
||||
enum {
|
||||
RENDER,
|
||||
|
||||
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->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)
|
||||
{
|
||||
switch (prop_id)
|
||||
{
|
||||
case PROP_HAS_ALPHA:
|
||||
gtk_gl_area_set_has_alpha (GTK_GL_AREA(gobject),
|
||||
g_value_get_boolean (value));
|
||||
break;
|
||||
|
||||
case PROP_HAS_DEPTH_BUFFER:
|
||||
gtk_gl_area_set_has_depth_buffer (GTK_GL_AREA(gobject),
|
||||
g_value_get_boolean (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_HAS_ALPHA:
|
||||
g_value_set_boolean (value, priv->has_alpha);
|
||||
break;
|
||||
|
||||
case PROP_HAS_DEPTH_BUFFER:
|
||||
g_value_set_boolean (value, priv->has_depth_buffer);
|
||||
break;
|
||||
|
||||
case PROP_CONTEXT:
|
||||
g_value_set_object (value, priv->context);
|
||||
break;
|
||||
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_gl_area_realize (GtkWidget *widget)
|
||||
{
|
||||
GtkGLAreaPrivate *priv = gtk_gl_area_get_instance_private ((GtkGLArea *) widget);
|
||||
GdkWindow *window;
|
||||
|
||||
GTK_WIDGET_CLASS (gtk_gl_area_parent_class)->realize (widget);
|
||||
|
||||
window = gtk_widget_get_window (widget);
|
||||
priv->context = gdk_window_create_gl_context (window,
|
||||
GDK_GL_PROFILE_DEFAULT,
|
||||
NULL);
|
||||
if (priv->context != NULL)
|
||||
{
|
||||
gdk_gl_context_make_current (priv->context);
|
||||
glGenFramebuffersEXT (1, &priv->framebuffer);
|
||||
glBindFramebufferEXT (GL_FRAMEBUFFER_EXT, priv->framebuffer);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_gl_area_unrealize (GtkWidget *widget)
|
||||
{
|
||||
GtkGLArea *self = GTK_GL_AREA (widget);
|
||||
GtkGLAreaPrivate *priv = gtk_gl_area_get_instance_private (self);
|
||||
|
||||
if (priv->context != NULL)
|
||||
{
|
||||
if (priv->framebuffer != 0)
|
||||
{
|
||||
gtk_gl_area_make_current (self);
|
||||
/* Bind 0, which means render to back buffer, as a result, fb is unbound */
|
||||
glBindFramebufferEXT (GL_FRAMEBUFFER_EXT, 0);
|
||||
glDeleteFramebuffersEXT (1, &priv->framebuffer);
|
||||
priv->framebuffer = 0;
|
||||
}
|
||||
else
|
||||
g_warning ("can't free framebuffer");
|
||||
|
||||
gdk_gl_context_clear_current ();
|
||||
}
|
||||
|
||||
GTK_WIDGET_CLASS (gtk_gl_area_parent_class)->unrealize (widget);
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_gl_area_size_allocate (GtkWidget *widget,
|
||||
GtkAllocation *allocation)
|
||||
{
|
||||
GTK_WIDGET_CLASS (gtk_gl_area_parent_class)->size_allocate (widget, allocation);
|
||||
}
|
||||
|
||||
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;
|
||||
int w, h, scale;
|
||||
GLuint color_rb = 0, depth_rb = 0, color_tex = 0;
|
||||
GLenum status;
|
||||
|
||||
if (priv->context == NULL)
|
||||
return FALSE;
|
||||
|
||||
gtk_gl_area_make_current (self);
|
||||
|
||||
scale = gtk_widget_get_scale_factor (widget);
|
||||
w = gtk_widget_get_allocated_width (widget) * scale;
|
||||
h = gtk_widget_get_allocated_height (widget) * scale;
|
||||
|
||||
if (priv->has_alpha)
|
||||
{
|
||||
/* For alpha we use textures as that is required for blending to work */
|
||||
glGenTextures (1, &color_tex);
|
||||
glBindTexture (GL_TEXTURE_2D, color_tex);
|
||||
glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
|
||||
glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
|
||||
glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
|
||||
glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
||||
glTexImage2D (GL_TEXTURE_2D, 0, GL_RGBA8, w, h, 0, GL_BGRA, GL_UNSIGNED_BYTE, NULL);
|
||||
glFramebufferTexture2DEXT (GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT,
|
||||
GL_TEXTURE_2D, color_tex, 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* For non-alpha we use render buffers so we can blit instead of texture the result */
|
||||
glGenRenderbuffersEXT (1, &color_rb);
|
||||
glBindRenderbufferEXT (GL_RENDERBUFFER_EXT, color_rb);
|
||||
glRenderbufferStorageEXT (GL_RENDERBUFFER_EXT, GL_RGB8, w, h);
|
||||
glFramebufferRenderbufferEXT (GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT,
|
||||
GL_RENDERBUFFER_EXT, color_rb);
|
||||
}
|
||||
|
||||
if (priv->has_depth_buffer)
|
||||
{
|
||||
glGenRenderbuffersEXT (1, &depth_rb);
|
||||
glBindRenderbufferEXT (GL_RENDERBUFFER_EXT, depth_rb);
|
||||
/* TODO: Pick actual requested depth */
|
||||
glRenderbufferStorageEXT (GL_RENDERBUFFER_EXT, GL_DEPTH_COMPONENT24, w, h);
|
||||
glFramebufferRenderbufferEXT (GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT,
|
||||
GL_RENDERBUFFER_EXT, depth_rb);
|
||||
glEnable (GL_DEPTH_TEST);
|
||||
}
|
||||
else
|
||||
glDisable (GL_DEPTH_TEST);
|
||||
|
||||
status = glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT);
|
||||
if (status == GL_FRAMEBUFFER_COMPLETE_EXT)
|
||||
{
|
||||
glViewport(0, 0, w, h);
|
||||
|
||||
g_signal_emit (self, area_signals[RENDER], 0, priv->context, &unused);
|
||||
|
||||
gdk_cairo_draw_from_gl (cr,
|
||||
gtk_widget_get_window (widget),
|
||||
color_tex ? color_tex : color_rb,
|
||||
color_tex ? GL_TEXTURE : GL_RENDERBUFFER,
|
||||
scale, 0, 0, w, h);
|
||||
|
||||
gtk_gl_area_make_current (self);
|
||||
}
|
||||
else
|
||||
{
|
||||
g_print ("fb setup not supported\n");
|
||||
}
|
||||
|
||||
if (color_tex != 0)
|
||||
glDeleteTextures(1, &color_tex);
|
||||
|
||||
if (color_rb != 0)
|
||||
glDeleteRenderbuffersEXT(1, &color_rb);
|
||||
|
||||
if (depth_rb != 0)
|
||||
glDeleteRenderbuffersEXT(1, &depth_rb);
|
||||
|
||||
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 void
|
||||
gtk_gl_area_class_init (GtkGLAreaClass *klass)
|
||||
{
|
||||
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
|
||||
GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
|
||||
|
||||
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:context:
|
||||
*
|
||||
* The #GdkGLContext used by the #GtkGLArea widget.
|
||||
*
|
||||
* The #GtkGLArea widget is responsible for creating the #GdkGLContext
|
||||
* instance. If you need to render with other kinds of buffers (stencil,
|
||||
* depth, etc), use render buffers.
|
||||
*
|
||||
* Since: 3.16
|
||||
*/
|
||||
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);
|
||||
|
||||
/**
|
||||
* GtkGLArea:has-alpha:
|
||||
*
|
||||
* If set to %TRUE the buffer allocated by the widget will have an alpha channel component,
|
||||
* and when rendering to the window the result will be composited over whatever is below
|
||||
* the widget.
|
||||
*
|
||||
* If set to %FALSE there will be no alpha channel, and the buffer will fully replace anything
|
||||
* below the widget.
|
||||
*
|
||||
* Since: 3.16
|
||||
*/
|
||||
obj_props[PROP_HAS_ALPHA] =
|
||||
g_param_spec_boolean ("has-alpha",
|
||||
P_("Has alpha"),
|
||||
P_("Whether the gl area color buffer has an alpha component"),
|
||||
FALSE,
|
||||
GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY);
|
||||
|
||||
/**
|
||||
* GtkGLArea:has-depth-buffer:
|
||||
*
|
||||
* If set to %TRUE the widget will allocate and enable a depth buffer for the target
|
||||
* framebuffer.
|
||||
*
|
||||
* Since: 3.16
|
||||
*/
|
||||
obj_props[PROP_HAS_DEPTH_BUFFER] =
|
||||
g_param_spec_boolean ("has-depth-buffer",
|
||||
P_("Has depth buffer"),
|
||||
P_("Whether a depth buffer is allocated"),
|
||||
FALSE,
|
||||
GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY);
|
||||
|
||||
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::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 painted to the window once the emission terminates.
|
||||
*
|
||||
* Returns: %TRUE to stop other handlers from being invoked for the event.
|
||||
* %FALSE to propagate the event further.
|
||||
*
|
||||
* Since: 3.16
|
||||
*/
|
||||
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), FALSE);
|
||||
gtk_widget_set_app_paintable (GTK_WIDGET (self), TRUE);
|
||||
}
|
||||
|
||||
/**
|
||||
* gtk_gl_area_new:
|
||||
*
|
||||
* Creates a new #GtkGLArea widget.
|
||||
*
|
||||
* Returns: (transfer full): the newly created #GtkGLArea
|
||||
*
|
||||
* Since: 3.16
|
||||
*/
|
||||
GtkWidget *
|
||||
gtk_gl_area_new (void)
|
||||
{
|
||||
return g_object_new (GTK_TYPE_GL_AREA, NULL);
|
||||
}
|
||||
|
||||
/**
|
||||
* gtk_gl_area_get_has_alpha:
|
||||
* @area: a #GtkGLArea
|
||||
*
|
||||
* Returns whether the area has an alpha component.
|
||||
*
|
||||
* Returns: %TRUE if the @area has an alpha component, %FALSE otherwise
|
||||
*
|
||||
* Since: 3.16
|
||||
*/
|
||||
gboolean
|
||||
gtk_gl_area_get_has_alpha (GtkGLArea *area)
|
||||
{
|
||||
GtkGLAreaPrivate *priv = gtk_gl_area_get_instance_private (area);
|
||||
|
||||
g_return_val_if_fail (GTK_IS_GL_AREA (area), FALSE);
|
||||
|
||||
return priv->has_alpha;
|
||||
}
|
||||
|
||||
/**
|
||||
* gtk_gl_area_set_has_alpha:
|
||||
* @area: a #GtkGLArea
|
||||
* @has_alpha: %TRUE to add an alpha component
|
||||
*
|
||||
* If @has_alpha is %TRUE the buffer allocated by the widget will have
|
||||
* an alpha channel component, and when rendering to the window the
|
||||
* result will be composited over whatever is below the widget.
|
||||
*
|
||||
* If @has_alpha is %FALSE there will be no alpha channel, and the
|
||||
* buffer will fully replace anything below the widget.
|
||||
*
|
||||
* Since: 3.16
|
||||
*/
|
||||
void
|
||||
gtk_gl_area_set_has_alpha (GtkGLArea *area,
|
||||
gboolean has_alpha)
|
||||
{
|
||||
GtkGLAreaPrivate *priv = gtk_gl_area_get_instance_private (area);
|
||||
|
||||
g_return_if_fail (GTK_IS_GL_AREA (area));
|
||||
|
||||
has_alpha = !!has_alpha;
|
||||
|
||||
if (priv->has_alpha != has_alpha)
|
||||
{
|
||||
priv->has_alpha = has_alpha;
|
||||
|
||||
g_object_notify (G_OBJECT (area), "has-alpha");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* gtk_gl_area_get_has_depth_buffer:
|
||||
* @area: a #GtkGLArea
|
||||
*
|
||||
* Returns whether the area has a depth buffer.
|
||||
*
|
||||
* Returns: %TRUE if the @area has a depth buffer, %FALSE otherwise
|
||||
*
|
||||
* Since: 3.16
|
||||
*/
|
||||
gboolean
|
||||
gtk_gl_area_get_has_depth_buffer (GtkGLArea *area)
|
||||
{
|
||||
GtkGLAreaPrivate *priv = gtk_gl_area_get_instance_private (area);
|
||||
|
||||
g_return_val_if_fail (GTK_IS_GL_AREA (area), FALSE);
|
||||
|
||||
return priv->has_depth_buffer;
|
||||
}
|
||||
|
||||
/**
|
||||
* gtk_gl_area_set_has_depth_buffer:
|
||||
* @area: a #GtkGLArea
|
||||
* @has_depth_buffer: %TRUE to add a depth buffer
|
||||
*
|
||||
* If @has_depth_buffer is %TRUE the widget will allocate and
|
||||
* enable a depth buffer for the target framebuffer. Otherwise
|
||||
* there will be none.
|
||||
*
|
||||
* Since: 3.16
|
||||
*/
|
||||
void
|
||||
gtk_gl_area_set_has_depth_buffer (GtkGLArea *area,
|
||||
gboolean has_depth_buffer)
|
||||
{
|
||||
GtkGLAreaPrivate *priv = gtk_gl_area_get_instance_private (area);
|
||||
|
||||
g_return_if_fail (GTK_IS_GL_AREA (area));
|
||||
|
||||
has_depth_buffer = !!has_depth_buffer;
|
||||
|
||||
if (priv->has_depth_buffer != has_depth_buffer)
|
||||
{
|
||||
priv->has_depth_buffer = has_depth_buffer;
|
||||
|
||||
g_object_notify (G_OBJECT (area), "has-depth-buffer");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* gtk_gl_area_get_context:
|
||||
* @area: a #GtkGLArea
|
||||
*
|
||||
* Retrieves the #GdkGLContext used by @area.
|
||||
*
|
||||
* Returns: (transfer none): the #GdkGLContext
|
||||
*
|
||||
* Since: 3.16
|
||||
*/
|
||||
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.
|
||||
*
|
||||
* Since: 3.16
|
||||
*/
|
||||
void
|
||||
gtk_gl_area_make_current (GtkGLArea *area)
|
||||
{
|
||||
GtkGLAreaPrivate *priv = gtk_gl_area_get_instance_private (area);
|
||||
GtkWidget *widget;
|
||||
|
||||
g_return_if_fail (GTK_IS_GL_AREA (area));
|
||||
|
||||
widget = GTK_WIDGET (area);
|
||||
|
||||
g_return_if_fail (gtk_widget_get_realized (widget));
|
||||
|
||||
if (priv->context)
|
||||
gdk_gl_context_make_current (priv->context);
|
||||
}
|
105
gtk/gtkglarea.h
Normal file
105
gtk/gtkglarea.h
Normal file
@@ -0,0 +1,105 @@
|
||||
/* 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.16
|
||||
*/
|
||||
struct _GtkGLArea
|
||||
{
|
||||
/*< private >*/
|
||||
GtkWidget parent_instance;
|
||||
};
|
||||
|
||||
/**
|
||||
* GtkGLAreaClass:
|
||||
* @render: class closure for the #GtkGLArea::render signal
|
||||
*
|
||||
* The `GtkGLAreaClass` structure contains only private data.
|
||||
*
|
||||
* Since: 3.16
|
||||
*/
|
||||
struct _GtkGLAreaClass
|
||||
{
|
||||
/*< private >*/
|
||||
GtkWidgetClass parent_class;
|
||||
|
||||
/*< public >*/
|
||||
gboolean (* render) (GtkGLArea *area,
|
||||
GdkGLContext *context);
|
||||
|
||||
/*< private >*/
|
||||
gpointer _padding[6];
|
||||
};
|
||||
|
||||
GDK_AVAILABLE_IN_3_16
|
||||
GType gtk_gl_area_get_type (void) G_GNUC_CONST;
|
||||
|
||||
GDK_AVAILABLE_IN_3_16
|
||||
GtkWidget * gtk_gl_area_new (void);
|
||||
|
||||
|
||||
GDK_AVAILABLE_IN_3_16
|
||||
gboolean gtk_gl_area_get_has_alpha (GtkGLArea *area);
|
||||
|
||||
GDK_AVAILABLE_IN_3_16
|
||||
void gtk_gl_area_set_has_alpha (GtkGLArea *area,
|
||||
gboolean has_alpha);
|
||||
|
||||
GDK_AVAILABLE_IN_3_16
|
||||
gboolean gtk_gl_area_get_has_depth_buffer (GtkGLArea *area);
|
||||
|
||||
GDK_AVAILABLE_IN_3_16
|
||||
void gtk_gl_area_set_has_depth_buffer (GtkGLArea *area,
|
||||
gboolean has_depth_buffer);
|
||||
|
||||
GDK_AVAILABLE_IN_3_16
|
||||
GdkGLContext * gtk_gl_area_get_context (GtkGLArea *area);
|
||||
|
||||
GDK_AVAILABLE_IN_3_16
|
||||
void gtk_gl_area_make_current (GtkGLArea *area);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __GTK_GL_AREA_H__ */
|
@@ -6948,6 +6948,8 @@ _gtk_widget_draw_internal (GtkWidget *widget,
|
||||
{
|
||||
gboolean result;
|
||||
|
||||
gdk_window_mark_paint_from_clip (window, cr);
|
||||
|
||||
g_signal_emit (widget, widget_signals[DRAW],
|
||||
0, cr,
|
||||
&result);
|
||||
|
@@ -68,6 +68,8 @@ noinst_PROGRAMS = $(TEST_PROGS) \
|
||||
testfullscreen \
|
||||
testgeometry \
|
||||
testgiconpixbuf \
|
||||
testglarea \
|
||||
testglblending \
|
||||
testgrid \
|
||||
testgtk \
|
||||
testheaderbar \
|
||||
@@ -152,6 +154,7 @@ noinst_PROGRAMS = $(TEST_PROGS) \
|
||||
testactionbar \
|
||||
testwindowsize \
|
||||
testpopover \
|
||||
gdkgears \
|
||||
$(NULL)
|
||||
|
||||
if USE_X11
|
||||
@@ -499,6 +502,16 @@ testtitlebar_SOURCES = testtitlebar.c
|
||||
|
||||
testwindowsize_SOURCES = testwindowsize.c
|
||||
|
||||
gdkgears_SOURCES = \
|
||||
gdkgears.c \
|
||||
gtkgears.c \
|
||||
gtkgears.h
|
||||
|
||||
testglblending_SOURCES = \
|
||||
testglblending.c \
|
||||
gtkgears.c \
|
||||
gtkgears.h
|
||||
|
||||
EXTRA_DIST += \
|
||||
gradient1.png \
|
||||
testgtk.1 \
|
||||
|
238
tests/gdkgears.c
Normal file
238
tests/gdkgears.c
Normal file
@@ -0,0 +1,238 @@
|
||||
#include <stdlib.h>
|
||||
#include <gtk/gtk.h>
|
||||
|
||||
#include "gtkgears.h"
|
||||
|
||||
/************************************************************************
|
||||
* DEMO CODE *
|
||||
************************************************************************/
|
||||
|
||||
static void
|
||||
toggle_alpha (GtkWidget *checkbutton,
|
||||
GtkWidget *gears)
|
||||
{
|
||||
gtk_gl_area_set_has_alpha (GTK_GL_AREA (gears),
|
||||
gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON(checkbutton)));
|
||||
}
|
||||
|
||||
static void
|
||||
toggle_overlay (GtkWidget *checkbutton,
|
||||
GtkWidget *revealer)
|
||||
{
|
||||
gtk_revealer_set_reveal_child (GTK_REVEALER (revealer),
|
||||
gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON(checkbutton)));
|
||||
}
|
||||
|
||||
static void
|
||||
toggle_spin (GtkWidget *checkbutton,
|
||||
GtkWidget *spinner)
|
||||
{
|
||||
if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON(checkbutton)))
|
||||
gtk_spinner_start (GTK_SPINNER (spinner));
|
||||
else
|
||||
gtk_spinner_stop (GTK_SPINNER (spinner));
|
||||
}
|
||||
|
||||
static void
|
||||
on_axis_value_change (GtkAdjustment *adjustment,
|
||||
gpointer data)
|
||||
{
|
||||
GtkGears *gears = GTK_GEARS (data);
|
||||
int axis = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (adjustment), "axis"));
|
||||
|
||||
gtk_gears_set_axis (gears, axis, gtk_adjustment_get_value (adjustment));
|
||||
}
|
||||
|
||||
|
||||
static GtkWidget *
|
||||
create_axis_slider (GtkGears *gears,
|
||||
int axis)
|
||||
{
|
||||
GtkWidget *box, *label, *slider;
|
||||
GtkAdjustment *adj;
|
||||
const char *text;
|
||||
|
||||
box = gtk_box_new (GTK_ORIENTATION_VERTICAL, FALSE);
|
||||
|
||||
switch (axis)
|
||||
{
|
||||
case GTK_GEARS_X_AXIS:
|
||||
text = "X";
|
||||
break;
|
||||
|
||||
case GTK_GEARS_Y_AXIS:
|
||||
text = "Y";
|
||||
break;
|
||||
|
||||
case GTK_GEARS_Z_AXIS:
|
||||
text = "Z";
|
||||
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 (gtk_gears_get_axis (gears, axis), 0.0, 360.0, 1.0, 12.0, 0.0);
|
||||
g_object_set_data (G_OBJECT (adj), "axis", GINT_TO_POINTER (axis));
|
||||
g_signal_connect (adj, "value-changed",
|
||||
G_CALLBACK (on_axis_value_change),
|
||||
gears);
|
||||
slider = gtk_scale_new (GTK_ORIENTATION_VERTICAL, adj);
|
||||
gtk_scale_set_draw_value (GTK_SCALE (slider), FALSE);
|
||||
gtk_container_add (GTK_CONTAINER (box), slider);
|
||||
gtk_widget_set_vexpand (slider, TRUE);
|
||||
gtk_widget_show (slider);
|
||||
|
||||
gtk_widget_show (box);
|
||||
|
||||
return box;
|
||||
}
|
||||
|
||||
static void
|
||||
moar_gears (GtkButton *button, gpointer data)
|
||||
{
|
||||
GtkContainer *container = GTK_CONTAINER (data);
|
||||
GtkWidget *gears;
|
||||
|
||||
gears = gtk_gears_new ();
|
||||
gtk_widget_set_size_request (gears, 100, 100);
|
||||
gtk_container_add (container, gears);
|
||||
gtk_widget_show (gears);
|
||||
}
|
||||
|
||||
int
|
||||
main (int argc, char *argv[])
|
||||
{
|
||||
GtkWidget *window, *box, *hbox, *button, *spinner, *check,
|
||||
*fps_label, *gears, *extra_hbox, *bbox, *overlay,
|
||||
*revealer, *frame, *label, *scrolled;
|
||||
int i;
|
||||
|
||||
gtk_init (&argc, &argv);
|
||||
|
||||
window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
|
||||
gtk_window_set_title (GTK_WINDOW (window), "GdkGears");
|
||||
gtk_window_set_default_size (GTK_WINDOW (window), 640, 640);
|
||||
gtk_container_set_border_width (GTK_CONTAINER (window), 12);
|
||||
g_signal_connect (window, "destroy", G_CALLBACK (gtk_main_quit), NULL);
|
||||
|
||||
overlay = gtk_overlay_new ();
|
||||
gtk_container_add (GTK_CONTAINER (window), overlay);
|
||||
gtk_widget_show (overlay);
|
||||
|
||||
revealer = gtk_revealer_new ();
|
||||
gtk_widget_set_halign (revealer, GTK_ALIGN_END);
|
||||
gtk_widget_set_valign (revealer, GTK_ALIGN_START);
|
||||
gtk_overlay_add_overlay (GTK_OVERLAY (overlay),
|
||||
revealer);
|
||||
gtk_widget_show (revealer);
|
||||
|
||||
frame = gtk_frame_new (NULL);
|
||||
gtk_style_context_add_class (gtk_widget_get_style_context (frame),
|
||||
"app-notification");
|
||||
gtk_container_add (GTK_CONTAINER (revealer), frame);
|
||||
gtk_widget_show (frame);
|
||||
|
||||
hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, FALSE);
|
||||
gtk_box_set_spacing (GTK_BOX (hbox), 6);
|
||||
gtk_container_add (GTK_CONTAINER (frame), hbox);
|
||||
gtk_widget_show (hbox);
|
||||
|
||||
label = gtk_label_new ("This is a transparent overlay widget!!!!\nAmazing, eh?");
|
||||
gtk_container_add (GTK_CONTAINER (hbox), label);
|
||||
gtk_widget_show (label);
|
||||
|
||||
box = gtk_box_new (GTK_ORIENTATION_VERTICAL, FALSE);
|
||||
gtk_box_set_spacing (GTK_BOX (box), 6);
|
||||
gtk_container_add (GTK_CONTAINER (overlay), box);
|
||||
gtk_widget_show (box);
|
||||
|
||||
hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, FALSE);
|
||||
gtk_box_set_spacing (GTK_BOX (box), 6);
|
||||
gtk_container_add (GTK_CONTAINER (box), hbox);
|
||||
gtk_widget_show (hbox);
|
||||
|
||||
gears = gtk_gears_new ();
|
||||
gtk_widget_set_hexpand (gears, TRUE);
|
||||
gtk_widget_set_vexpand (gears, TRUE);
|
||||
gtk_container_add (GTK_CONTAINER (hbox), gears);
|
||||
gtk_widget_show (gears);
|
||||
|
||||
for (i = 0; i < GTK_GEARS_N_AXIS; i++)
|
||||
gtk_container_add (GTK_CONTAINER (hbox), create_axis_slider (GTK_GEARS (gears), i));
|
||||
|
||||
hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, FALSE);
|
||||
gtk_box_set_spacing (GTK_BOX (hbox), 6);
|
||||
gtk_container_add (GTK_CONTAINER (box), hbox);
|
||||
gtk_widget_show (hbox);
|
||||
|
||||
fps_label = gtk_label_new ("");
|
||||
gtk_container_add (GTK_CONTAINER (hbox), fps_label);
|
||||
gtk_widget_show (fps_label);
|
||||
gtk_gears_set_fps_label (GTK_GEARS (gears), GTK_LABEL (fps_label));
|
||||
|
||||
spinner = gtk_spinner_new ();
|
||||
gtk_box_pack_end (GTK_BOX (hbox), spinner, FALSE, FALSE, 0);
|
||||
gtk_widget_show (spinner);
|
||||
gtk_spinner_start (GTK_SPINNER (spinner));
|
||||
|
||||
check = gtk_check_button_new_with_label ("Animate spinner");
|
||||
gtk_box_pack_end (GTK_BOX (hbox), check, FALSE, FALSE, 0);
|
||||
gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (check), TRUE);
|
||||
gtk_widget_show (check);
|
||||
g_signal_connect (check, "toggled",
|
||||
G_CALLBACK (toggle_spin), spinner);
|
||||
|
||||
check = gtk_check_button_new_with_label ("Alpha");
|
||||
gtk_box_pack_end (GTK_BOX (hbox), check, FALSE, FALSE, 0);
|
||||
gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (check), FALSE);
|
||||
gtk_widget_show (check);
|
||||
g_signal_connect (check, "toggled",
|
||||
G_CALLBACK (toggle_alpha), gears);
|
||||
|
||||
check = gtk_check_button_new_with_label ("Overlay");
|
||||
gtk_box_pack_end (GTK_BOX (hbox), check, FALSE, FALSE, 0);
|
||||
gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (check), FALSE);
|
||||
gtk_widget_show (check);
|
||||
g_signal_connect (check, "toggled",
|
||||
G_CALLBACK (toggle_overlay), revealer);
|
||||
|
||||
scrolled = gtk_scrolled_window_new (NULL, NULL);
|
||||
gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled),
|
||||
GTK_POLICY_AUTOMATIC,
|
||||
GTK_POLICY_NEVER);
|
||||
gtk_container_add (GTK_CONTAINER (box), scrolled);
|
||||
gtk_widget_show (scrolled);
|
||||
|
||||
extra_hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, FALSE);
|
||||
gtk_box_set_spacing (GTK_BOX (extra_hbox), 6);
|
||||
gtk_container_add (GTK_CONTAINER (scrolled), extra_hbox);
|
||||
gtk_widget_show (extra_hbox);
|
||||
|
||||
bbox = gtk_button_box_new (GTK_ORIENTATION_HORIZONTAL);
|
||||
gtk_box_set_spacing (GTK_BOX (bbox), 6);
|
||||
gtk_container_add (GTK_CONTAINER (box), bbox);
|
||||
gtk_widget_show (bbox);
|
||||
|
||||
button = gtk_button_new_with_label ("Moar gears!");
|
||||
gtk_widget_set_hexpand (button, TRUE);
|
||||
gtk_container_add (GTK_CONTAINER (bbox), button);
|
||||
g_signal_connect (button, "clicked", G_CALLBACK (moar_gears), extra_hbox);
|
||||
gtk_widget_show (button);
|
||||
|
||||
button = gtk_button_new_with_label ("Quit");
|
||||
gtk_widget_set_hexpand (button, TRUE);
|
||||
gtk_container_add (GTK_CONTAINER (bbox), button);
|
||||
g_signal_connect_swapped (button, "clicked", G_CALLBACK (gtk_widget_destroy), window);
|
||||
gtk_widget_show (button);
|
||||
|
||||
gtk_widget_show (window);
|
||||
|
||||
gtk_main ();
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
439
tests/gtkgears.c
Normal file
439
tests/gtkgears.c
Normal file
@@ -0,0 +1,439 @@
|
||||
/* The rendering code in here is taken from glxgears, which has the
|
||||
* following copyright notice:
|
||||
*
|
||||
* Copyright (C) 1999-2001 Brian Paul All Rights Reserved.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||
* copy of this software and associated documentation files (the "Software"),
|
||||
* to deal in the Software without restriction, including without limitation
|
||||
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
* and/or sell copies of the Software, and to permit persons to whom the
|
||||
* Software is furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included
|
||||
* in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* BRIAN PAUL BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
|
||||
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <math.h>
|
||||
#include <epoxy/gl.h>
|
||||
|
||||
#include "gtkgears.h"
|
||||
|
||||
typedef struct {
|
||||
GLfloat view_rot[GTK_GEARS_N_AXIS];
|
||||
GLint gear1, gear2, gear3;
|
||||
GLfloat angle;
|
||||
gint64 first_frame_time;
|
||||
guint tick;
|
||||
GtkLabel *fps_label;
|
||||
} GtkGearsPrivate;
|
||||
|
||||
|
||||
G_DEFINE_TYPE_WITH_PRIVATE (GtkGears, gtk_gears, GTK_TYPE_GL_AREA);
|
||||
|
||||
static gboolean gtk_gears_render (GtkGLArea *area,
|
||||
GdkGLContext *context);
|
||||
static void gtk_gears_size_allocate (GtkWidget *widget,
|
||||
GtkAllocation *allocation);
|
||||
static void gtk_gears_realize (GtkWidget *widget);
|
||||
static gboolean gtk_gears_tick (GtkWidget *widget,
|
||||
GdkFrameClock *frame_clock,
|
||||
gpointer user_data);
|
||||
|
||||
GtkWidget *
|
||||
gtk_gears_new ()
|
||||
{
|
||||
GtkWidget *gears;
|
||||
|
||||
gears = g_object_new (gtk_gears_get_type (),
|
||||
"has-depth-buffer", TRUE,
|
||||
NULL);
|
||||
|
||||
return gears;
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_gears_init (GtkGears *gears)
|
||||
{
|
||||
GtkGearsPrivate *priv = gtk_gears_get_instance_private (gears);
|
||||
|
||||
priv->view_rot[GTK_GEARS_X_AXIS] = 20.0;
|
||||
priv->view_rot[GTK_GEARS_Y_AXIS] = 30.0;
|
||||
priv->view_rot[GTK_GEARS_Z_AXIS] = 20.0;
|
||||
|
||||
priv->tick = gtk_widget_add_tick_callback (GTK_WIDGET (gears), gtk_gears_tick, gears, NULL);
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_gears_finalize (GObject *obj)
|
||||
{
|
||||
GtkGears *gears = GTK_GEARS (obj);
|
||||
GtkGearsPrivate *priv = gtk_gears_get_instance_private (gears);
|
||||
|
||||
gtk_widget_remove_tick_callback (GTK_WIDGET (gears), priv->tick);
|
||||
|
||||
g_clear_object (&priv->fps_label);
|
||||
|
||||
G_OBJECT_CLASS (gtk_gears_parent_class)->finalize (obj);
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_gears_class_init (GtkGearsClass *klass)
|
||||
{
|
||||
GTK_GL_AREA_CLASS (klass)->render = gtk_gears_render;
|
||||
GTK_WIDGET_CLASS (klass)->realize = gtk_gears_realize;
|
||||
GTK_WIDGET_CLASS (klass)->size_allocate = gtk_gears_size_allocate;
|
||||
G_OBJECT_CLASS (klass)->finalize = gtk_gears_finalize;
|
||||
}
|
||||
|
||||
/*
|
||||
*
|
||||
* Draw a gear wheel. You'll probably want to call this function when
|
||||
* building a display list since we do a lot of trig here.
|
||||
*
|
||||
* Input: inner_radius - radius of hole at center
|
||||
* outer_radius - radius at center of teeth
|
||||
* width - width of gear
|
||||
* teeth - number of teeth
|
||||
* tooth_depth - depth of tooth
|
||||
*/
|
||||
static void
|
||||
gear(GLfloat inner_radius, GLfloat outer_radius, GLfloat width,
|
||||
GLint teeth, GLfloat tooth_depth)
|
||||
{
|
||||
GLint i;
|
||||
GLfloat r0, r1, r2;
|
||||
GLfloat angle, da;
|
||||
GLfloat u, v, len;
|
||||
|
||||
r0 = inner_radius;
|
||||
r1 = outer_radius - tooth_depth / 2.0;
|
||||
r2 = outer_radius + tooth_depth / 2.0;
|
||||
|
||||
da = 2.0 * G_PI / teeth / 4.0;
|
||||
|
||||
glShadeModel(GL_FLAT);
|
||||
|
||||
glNormal3f(0.0, 0.0, 1.0);
|
||||
|
||||
/* draw front face */
|
||||
glBegin(GL_QUAD_STRIP);
|
||||
for (i = 0; i <= teeth; i++) {
|
||||
angle = i * 2.0 * M_PI / teeth;
|
||||
glVertex3f(r0 * cos(angle), r0 * sin(angle), width * 0.5);
|
||||
glVertex3f(r1 * cos(angle), r1 * sin(angle), width * 0.5);
|
||||
if (i < teeth) {
|
||||
glVertex3f(r0 * cos(angle), r0 * sin(angle), width * 0.5);
|
||||
glVertex3f(r1 * cos(angle + 3 * da), r1 * sin(angle + 3 * da),
|
||||
width * 0.5);
|
||||
}
|
||||
}
|
||||
glEnd();
|
||||
|
||||
/* draw front sides of teeth */
|
||||
glBegin(GL_QUADS);
|
||||
da = 2.0 * M_PI / teeth / 4.0;
|
||||
for (i = 0; i < teeth; i++) {
|
||||
angle = i * 2.0 * M_PI / teeth;
|
||||
|
||||
glVertex3f(r1 * cos(angle), r1 * sin(angle), width * 0.5);
|
||||
glVertex3f(r2 * cos(angle + da), r2 * sin(angle + da), width * 0.5);
|
||||
glVertex3f(r2 * cos(angle + 2 * da), r2 * sin(angle + 2 * da),
|
||||
width * 0.5);
|
||||
glVertex3f(r1 * cos(angle + 3 * da), r1 * sin(angle + 3 * da),
|
||||
width * 0.5);
|
||||
}
|
||||
glEnd();
|
||||
|
||||
glNormal3f(0.0, 0.0, -1.0);
|
||||
|
||||
/* draw back face */
|
||||
glBegin(GL_QUAD_STRIP);
|
||||
for (i = 0; i <= teeth; i++) {
|
||||
angle = i * 2.0 * M_PI / teeth;
|
||||
glVertex3f(r1 * cos(angle), r1 * sin(angle), -width * 0.5);
|
||||
glVertex3f(r0 * cos(angle), r0 * sin(angle), -width * 0.5);
|
||||
if (i < teeth) {
|
||||
glVertex3f(r1 * cos(angle + 3 * da), r1 * sin(angle + 3 * da),
|
||||
-width * 0.5);
|
||||
glVertex3f(r0 * cos(angle), r0 * sin(angle), -width * 0.5);
|
||||
}
|
||||
}
|
||||
glEnd();
|
||||
|
||||
/* draw back sides of teeth */
|
||||
glBegin(GL_QUADS);
|
||||
da = 2.0 * M_PI / teeth / 4.0;
|
||||
for (i = 0; i < teeth; i++) {
|
||||
angle = i * 2.0 * M_PI / teeth;
|
||||
|
||||
glVertex3f(r1 * cos(angle + 3 * da), r1 * sin(angle + 3 * da),
|
||||
-width * 0.5);
|
||||
glVertex3f(r2 * cos(angle + 2 * da), r2 * sin(angle + 2 * da),
|
||||
-width * 0.5);
|
||||
glVertex3f(r2 * cos(angle + da), r2 * sin(angle + da), -width * 0.5);
|
||||
glVertex3f(r1 * cos(angle), r1 * sin(angle), -width * 0.5);
|
||||
}
|
||||
glEnd();
|
||||
|
||||
/* draw outward faces of teeth */
|
||||
glBegin(GL_QUAD_STRIP);
|
||||
for (i = 0; i < teeth; i++) {
|
||||
angle = i * 2.0 * M_PI / teeth;
|
||||
|
||||
glVertex3f(r1 * cos(angle), r1 * sin(angle), width * 0.5);
|
||||
glVertex3f(r1 * cos(angle), r1 * sin(angle), -width * 0.5);
|
||||
u = r2 * cos(angle + da) - r1 * cos(angle);
|
||||
v = r2 * sin(angle + da) - r1 * sin(angle);
|
||||
len = sqrt(u * u + v * v);
|
||||
u /= len;
|
||||
v /= len;
|
||||
glNormal3f(v, -u, 0.0);
|
||||
glVertex3f(r2 * cos(angle + da), r2 * sin(angle + da), width * 0.5);
|
||||
glVertex3f(r2 * cos(angle + da), r2 * sin(angle + da), -width * 0.5);
|
||||
glNormal3f(cos(angle), sin(angle), 0.0);
|
||||
glVertex3f(r2 * cos(angle + 2 * da), r2 * sin(angle + 2 * da),
|
||||
width * 0.5);
|
||||
glVertex3f(r2 * cos(angle + 2 * da), r2 * sin(angle + 2 * da),
|
||||
-width * 0.5);
|
||||
u = r1 * cos(angle + 3 * da) - r2 * cos(angle + 2 * da);
|
||||
v = r1 * sin(angle + 3 * da) - r2 * sin(angle + 2 * da);
|
||||
glNormal3f(v, -u, 0.0);
|
||||
glVertex3f(r1 * cos(angle + 3 * da), r1 * sin(angle + 3 * da),
|
||||
width * 0.5);
|
||||
glVertex3f(r1 * cos(angle + 3 * da), r1 * sin(angle + 3 * da),
|
||||
-width * 0.5);
|
||||
glNormal3f(cos(angle), sin(angle), 0.0);
|
||||
}
|
||||
|
||||
glVertex3f(r1 * cos(0), r1 * sin(0), width * 0.5);
|
||||
glVertex3f(r1 * cos(0), r1 * sin(0), -width * 0.5);
|
||||
|
||||
glEnd();
|
||||
|
||||
glShadeModel(GL_SMOOTH);
|
||||
|
||||
/* draw inside radius cylinder */
|
||||
glBegin(GL_QUAD_STRIP);
|
||||
for (i = 0; i <= teeth; i++) {
|
||||
angle = i * 2.0 * M_PI / teeth;
|
||||
glNormal3f(-cos(angle), -sin(angle), 0.0);
|
||||
glVertex3f(r0 * cos(angle), r0 * sin(angle), -width * 0.5);
|
||||
glVertex3f(r0 * cos(angle), r0 * sin(angle), width * 0.5);
|
||||
}
|
||||
glEnd();
|
||||
}
|
||||
|
||||
/* new window size or exposure */
|
||||
static void
|
||||
reshape(int width, int height)
|
||||
{
|
||||
GLfloat h = (GLfloat) height / (GLfloat) width;
|
||||
|
||||
glMatrixMode(GL_PROJECTION);
|
||||
glLoadIdentity();
|
||||
glFrustum(-1.0, 1.0, -h, h, 5.0, 60.0);
|
||||
|
||||
glMatrixMode(GL_MODELVIEW);
|
||||
glLoadIdentity();
|
||||
glTranslatef(0.0, 0.0, -40.0);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
gtk_gears_render (GtkGLArea *area,
|
||||
GdkGLContext *context)
|
||||
{
|
||||
GtkGearsPrivate *priv = gtk_gears_get_instance_private (GTK_GEARS (area));
|
||||
|
||||
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
|
||||
|
||||
glPushMatrix();
|
||||
glRotatef(priv->view_rot[GTK_GEARS_X_AXIS], 1.0, 0.0, 0.0);
|
||||
glRotatef(priv->view_rot[GTK_GEARS_Y_AXIS], 0.0, 1.0, 0.0);
|
||||
glRotatef(priv->view_rot[GTK_GEARS_Z_AXIS], 0.0, 0.0, 1.0);
|
||||
|
||||
glPushMatrix();
|
||||
glTranslatef(-3.0, -2.0, 0.0);
|
||||
glRotatef(priv->angle, 0.0, 0.0, 1.0);
|
||||
glCallList(priv->gear1);
|
||||
glPopMatrix();
|
||||
|
||||
glPushMatrix();
|
||||
glTranslatef(3.1, -2.0, 0.0);
|
||||
glRotatef(-2.0 * priv->angle - 9.0, 0.0, 0.0, 1.0);
|
||||
glCallList(priv->gear2);
|
||||
glPopMatrix();
|
||||
|
||||
glPushMatrix();
|
||||
glTranslatef(-3.1, 4.2, 0.0);
|
||||
glRotatef(-2.0 * priv->angle - 25.0, 0.0, 0.0, 1.0);
|
||||
glCallList(priv->gear3);
|
||||
glPopMatrix();
|
||||
|
||||
glPopMatrix();
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_gears_size_allocate (GtkWidget *widget,
|
||||
GtkAllocation *allocation)
|
||||
{
|
||||
GtkGLArea *glarea = GTK_GL_AREA (widget);
|
||||
|
||||
GTK_WIDGET_CLASS (gtk_gears_parent_class)->size_allocate (widget, allocation);
|
||||
|
||||
if (gtk_widget_get_realized (widget))
|
||||
{
|
||||
gtk_gl_area_make_current (glarea);
|
||||
reshape (allocation->width, allocation->height);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_gears_realize (GtkWidget *widget)
|
||||
{
|
||||
GtkGLArea *glarea = GTK_GL_AREA (widget);
|
||||
GtkGears *gears = GTK_GEARS(widget);
|
||||
GtkGearsPrivate *priv = gtk_gears_get_instance_private (gears);
|
||||
GtkAllocation allocation;
|
||||
static GLfloat pos[4] = { 5.0, 5.0, 10.0, 0.0 };
|
||||
static GLfloat red[4] = { 0.8, 0.1, 0.0, 1.0 };
|
||||
static GLfloat green[4] = { 0.0, 0.8, 0.2, 1.0 };
|
||||
static GLfloat blue[4] = { 0.2, 0.2, 1.0, 1.0 };
|
||||
|
||||
GTK_WIDGET_CLASS (gtk_gears_parent_class)->realize (widget);
|
||||
|
||||
gtk_gl_area_make_current (glarea);
|
||||
|
||||
glLightfv(GL_LIGHT0, GL_POSITION, pos);
|
||||
glEnable(GL_CULL_FACE);
|
||||
glEnable(GL_LIGHTING);
|
||||
glEnable(GL_LIGHT0);
|
||||
glEnable(GL_DEPTH_TEST);
|
||||
|
||||
/* make the gears */
|
||||
priv->gear1 = glGenLists(1);
|
||||
glNewList(priv->gear1, GL_COMPILE);
|
||||
glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, red);
|
||||
gear(1.0, 4.0, 1.0, 20, 0.7);
|
||||
glEndList();
|
||||
|
||||
priv->gear2 = glGenLists(1);
|
||||
glNewList(priv->gear2, GL_COMPILE);
|
||||
glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, green);
|
||||
gear(0.5, 2.0, 2.0, 10, 0.7);
|
||||
glEndList();
|
||||
|
||||
priv->gear3 = glGenLists(1);
|
||||
glNewList(priv->gear3, GL_COMPILE);
|
||||
glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, blue);
|
||||
gear(1.3, 2.0, 0.5, 10, 0.7);
|
||||
glEndList();
|
||||
|
||||
glEnable(GL_NORMALIZE);
|
||||
|
||||
gtk_widget_get_allocation (widget, &allocation);
|
||||
reshape (allocation.width, allocation.height);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
gtk_gears_tick (GtkWidget *widget,
|
||||
GdkFrameClock *frame_clock,
|
||||
gpointer user_data)
|
||||
{
|
||||
GtkGears *gears = GTK_GEARS (widget);
|
||||
GtkGearsPrivate *priv = gtk_gears_get_instance_private (gears);
|
||||
GdkFrameTimings *timings, *previous_timings;
|
||||
gint64 previous_frame_time = 0;
|
||||
gint64 frame_time;
|
||||
gint64 history_start, history_len;
|
||||
gint64 frame;
|
||||
char *s;
|
||||
|
||||
frame = gdk_frame_clock_get_frame_counter (frame_clock);
|
||||
frame_time = gdk_frame_clock_get_frame_time (frame_clock);
|
||||
|
||||
if (priv->first_frame_time == 0)
|
||||
{
|
||||
/* No need for changes on first frame */
|
||||
priv->first_frame_time = frame_time;
|
||||
if (priv->fps_label)
|
||||
gtk_label_set_label (priv->fps_label, "FPS: ---");
|
||||
return G_SOURCE_CONTINUE;
|
||||
}
|
||||
|
||||
/* glxgears advances 70 degrees per second, so do the same */
|
||||
|
||||
priv->angle = fmod ((frame_time - priv->first_frame_time) / (double)G_USEC_PER_SEC * 70.0, 360.0);
|
||||
|
||||
gtk_widget_queue_draw (widget);
|
||||
|
||||
history_start = gdk_frame_clock_get_history_start (frame_clock);
|
||||
|
||||
if (priv->fps_label && frame % 60 == 0)
|
||||
{
|
||||
history_len = frame - history_start;
|
||||
if (history_len > 0)
|
||||
{
|
||||
previous_timings = gdk_frame_clock_get_timings (frame_clock, frame - history_len);
|
||||
previous_frame_time = gdk_frame_timings_get_frame_time (previous_timings);
|
||||
|
||||
s = g_strdup_printf ("FPS: %-4.1f", (G_USEC_PER_SEC * history_len) / (double)(frame_time - previous_frame_time));
|
||||
gtk_label_set_label (priv->fps_label, s);
|
||||
g_free (s);
|
||||
}
|
||||
}
|
||||
|
||||
timings = gdk_frame_clock_get_current_timings (frame_clock);
|
||||
previous_timings = gdk_frame_clock_get_timings (frame_clock,
|
||||
gdk_frame_timings_get_frame_counter (timings) - 1);
|
||||
if (previous_timings != NULL)
|
||||
previous_frame_time = gdk_frame_timings_get_frame_time (previous_timings);
|
||||
|
||||
return G_SOURCE_CONTINUE;
|
||||
}
|
||||
|
||||
void
|
||||
gtk_gears_set_axis (GtkGears *gears, int axis, double value)
|
||||
{
|
||||
GtkGearsPrivate *priv = gtk_gears_get_instance_private (gears);
|
||||
|
||||
if (axis < 0 || axis >= GTK_GEARS_N_AXIS)
|
||||
return;
|
||||
|
||||
priv->view_rot[axis] = value;
|
||||
|
||||
gtk_widget_queue_draw (GTK_WIDGET (gears));
|
||||
}
|
||||
|
||||
double
|
||||
gtk_gears_get_axis (GtkGears *gears, int axis)
|
||||
{
|
||||
GtkGearsPrivate *priv = gtk_gears_get_instance_private (gears);
|
||||
|
||||
if (axis < 0 || axis >= GTK_GEARS_N_AXIS)
|
||||
return 0.0;
|
||||
|
||||
return priv->view_rot[axis];
|
||||
}
|
||||
|
||||
void
|
||||
gtk_gears_set_fps_label (GtkGears *gears, GtkLabel *label)
|
||||
{
|
||||
GtkGearsPrivate *priv = gtk_gears_get_instance_private (gears);
|
||||
|
||||
if (label)
|
||||
g_object_ref (label);
|
||||
|
||||
g_clear_object (&priv->fps_label);
|
||||
|
||||
priv->fps_label = label;
|
||||
}
|
44
tests/gtkgears.h
Normal file
44
tests/gtkgears.h
Normal file
@@ -0,0 +1,44 @@
|
||||
#ifndef __GTK_GEARS_H__
|
||||
#define __GTK_GEARS_H__
|
||||
|
||||
#include <gtk/gtk.h>
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
enum {
|
||||
GTK_GEARS_X_AXIS,
|
||||
GTK_GEARS_Y_AXIS,
|
||||
GTK_GEARS_Z_AXIS,
|
||||
|
||||
GTK_GEARS_N_AXIS
|
||||
};
|
||||
|
||||
#define GTK_TYPE_GEARS (gtk_gears_get_type ())
|
||||
#define GTK_GEARS(inst) (G_TYPE_CHECK_INSTANCE_CAST ((inst), \
|
||||
GTK_TYPE_GEARS, \
|
||||
GtkGears))
|
||||
#define GTK_IS_GEARS(inst) (G_TYPE_CHECK_INSTANCE_TYPE ((inst), \
|
||||
GTK_TYPE_GEARS))
|
||||
|
||||
typedef struct {
|
||||
GtkGLArea parent;
|
||||
} GtkGears;
|
||||
|
||||
typedef struct {
|
||||
GtkGLAreaClass parent_class;
|
||||
} GtkGearsClass;
|
||||
|
||||
|
||||
GtkWidget *gtk_gears_new ();
|
||||
void gtk_gears_set_axis (GtkGears *gears,
|
||||
int axis,
|
||||
double value);
|
||||
double gtk_gears_get_axis (GtkGears *gears,
|
||||
int axis);
|
||||
void gtk_gears_set_fps_label (GtkGears *gears,
|
||||
GtkLabel *label);
|
||||
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __GTK_GEARS_H__ */
|
159
tests/testglarea.c
Normal file
159
tests/testglarea.c
Normal file
@@ -0,0 +1,159 @@
|
||||
#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
|
||||
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;
|
||||
int i;
|
||||
|
||||
gtk_init (&argc, &argv);
|
||||
|
||||
/* create a new pixel format; we use this to configure the
|
||||
* GL context, and to check for features
|
||||
*/
|
||||
|
||||
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);
|
||||
|
||||
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 ();
|
||||
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, "render", G_CALLBACK (render), NULL);
|
||||
gtk_widget_show (gl_area);
|
||||
|
||||
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_widget_show (window);
|
||||
|
||||
gtk_main ();
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
94
tests/testglblending.c
Normal file
94
tests/testglblending.c
Normal file
@@ -0,0 +1,94 @@
|
||||
#include <stdlib.h>
|
||||
#include <gtk/gtk.h>
|
||||
|
||||
#include "gtkgears.h"
|
||||
|
||||
int
|
||||
main (int argc, char *argv[])
|
||||
{
|
||||
GtkWidget *window, *fixed, *gears, *spinner;
|
||||
|
||||
gtk_init (&argc, &argv);
|
||||
|
||||
window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
|
||||
gtk_window_set_title (GTK_WINDOW (window), "Test GL/gtk inter-blending");
|
||||
gtk_window_set_default_size (GTK_WINDOW (window), 250, 250);
|
||||
gtk_container_set_border_width (GTK_CONTAINER (window), 12);
|
||||
g_signal_connect (window, "destroy", G_CALLBACK (gtk_main_quit), NULL);
|
||||
|
||||
fixed = gtk_fixed_new ();
|
||||
gtk_container_add (GTK_CONTAINER (window), fixed);
|
||||
|
||||
spinner = gtk_spinner_new ();
|
||||
gtk_spinner_start (GTK_SPINNER (spinner));
|
||||
gtk_widget_set_size_request (spinner, 50, 50);
|
||||
gtk_fixed_put (GTK_FIXED (fixed), spinner, 90, 80);
|
||||
|
||||
spinner = gtk_spinner_new ();
|
||||
gtk_spinner_start (GTK_SPINNER (spinner));
|
||||
gtk_widget_set_size_request (spinner, 50, 50);
|
||||
gtk_fixed_put (GTK_FIXED (fixed), spinner, 100, 80);
|
||||
|
||||
spinner = gtk_spinner_new ();
|
||||
gtk_spinner_start (GTK_SPINNER (spinner));
|
||||
gtk_widget_set_size_request (spinner, 50, 50);
|
||||
gtk_fixed_put (GTK_FIXED (fixed), spinner, 110, 80);
|
||||
|
||||
|
||||
gears = gtk_gears_new ();
|
||||
gtk_widget_set_size_request (gears, 70, 50);
|
||||
gtk_fixed_put (GTK_FIXED (fixed), gears, 60, 100);
|
||||
|
||||
gears = gtk_gears_new ();
|
||||
gtk_gl_area_set_has_alpha (GTK_GL_AREA (gears), TRUE);
|
||||
gtk_widget_set_size_request (gears, 70, 50);
|
||||
gtk_fixed_put (GTK_FIXED (fixed), gears, 120, 100);
|
||||
|
||||
|
||||
spinner = gtk_spinner_new ();
|
||||
gtk_spinner_start (GTK_SPINNER (spinner));
|
||||
gtk_widget_set_size_request (spinner, 50, 50);
|
||||
gtk_fixed_put (GTK_FIXED (fixed), spinner, 90, 110);
|
||||
|
||||
spinner = gtk_spinner_new ();
|
||||
gtk_spinner_start (GTK_SPINNER (spinner));
|
||||
gtk_widget_set_size_request (spinner, 50, 50);
|
||||
gtk_fixed_put (GTK_FIXED (fixed), spinner, 100, 110);
|
||||
|
||||
spinner = gtk_spinner_new ();
|
||||
gtk_spinner_start (GTK_SPINNER (spinner));
|
||||
gtk_widget_set_size_request (spinner, 50, 50);
|
||||
gtk_fixed_put (GTK_FIXED (fixed), spinner, 110, 110);
|
||||
|
||||
|
||||
gears = gtk_gears_new ();
|
||||
gtk_widget_set_size_request (gears, 70, 50);
|
||||
gtk_fixed_put (GTK_FIXED (fixed), gears, 60, 130);
|
||||
|
||||
gears = gtk_gears_new ();
|
||||
gtk_gl_area_set_has_alpha (GTK_GL_AREA (gears), TRUE);
|
||||
gtk_widget_set_size_request (gears, 70, 50);
|
||||
gtk_fixed_put (GTK_FIXED (fixed), gears, 120, 130);
|
||||
|
||||
|
||||
spinner = gtk_spinner_new ();
|
||||
gtk_spinner_start (GTK_SPINNER (spinner));
|
||||
gtk_widget_set_size_request (spinner, 50, 50);
|
||||
gtk_fixed_put (GTK_FIXED (fixed), spinner, 90, 150);
|
||||
|
||||
spinner = gtk_spinner_new ();
|
||||
gtk_spinner_start (GTK_SPINNER (spinner));
|
||||
gtk_widget_set_size_request (spinner, 50, 50);
|
||||
gtk_fixed_put (GTK_FIXED (fixed), spinner, 100, 150);
|
||||
|
||||
spinner = gtk_spinner_new ();
|
||||
gtk_spinner_start (GTK_SPINNER (spinner));
|
||||
gtk_widget_set_size_request (spinner, 50, 50);
|
||||
gtk_fixed_put (GTK_FIXED (fixed), spinner, 110, 150);
|
||||
|
||||
gtk_widget_show_all (window);
|
||||
|
||||
gtk_main ();
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
Reference in New Issue
Block a user