2 * This program is free software; you can redistribute it and/or
3 * modify it under the terms of the GNU General Public License
4 * as published by the Free Software Foundation; either version 2
5 * of the License, or (at your option) any later version.
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
12 * You should have received a copy of the GNU General Public License
13 * along with this program; if not, write to the Free Software
14 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
16 * See the COPYING file for license information.
18 * Guillaume Chazarain <guichaz@yahoo.fr>
21 /***********************************************
22 * Creation and callbacks of the OpenGL widget *
23 ***********************************************/
26 #include <gtk/gtkgl.h>
29 #include "gl_widget.h"
31 #include "rendering.h"
35 #include "files_list.h"
37 #include "next_image.h"
38 #include "images_menus.h"
42 extern GtkWidget
*gl_widget
;
44 extern options_struct
*options
;
46 static gint old_width
, old_height
;
48 void update_bg_color()
50 glClearColor(options
->bg_col
.red
/ 65535.0, options
->bg_col
.green
/ 65535.0,
51 options
->bg_col
.blue
/ 65535.0, 0.0);
54 static gboolean
expose_event(GtkWidget
* unused
, GdkEventExpose
* event
)
56 if (event
->count
== 0)
57 refresh(REFRESH_IMAGE
);
62 static void resize_widget(gint width
, gint height
)
64 gfloat w2
= (width
+ 1) / 2.0, h2
= (height
+ 1) / 2.0;
66 gdk_gl_drawable_wait_gdk(gtk_widget_get_gl_drawable(gl_widget
));
68 glViewport(0, 0, width
, height
);
69 glMatrixMode(GL_PROJECTION
);
72 /* Vertical flip to have coordinates like X but centered like OpenGL. */
73 glOrtho(-w2
, w2
, h2
, -h2
, -1.0, 1.0);
75 glMatrixMode(GL_MODELVIEW
);
78 static gboolean
configure_event(GtkWidget
* unused
, GdkEventConfigure
* event
)
80 gint what
= REFRESH_IMAGE
;
82 resize_widget(event
->width
, event
->height
);
84 if ((options
->maximize
|| options
->scaledown
) &&
85 matrix_set_max_zoom(old_width
, old_height
, FALSE
) == FALSE
) {
87 * When options->maximize or options->scaledown is set we zoom only
88 * if a matrix_set_max_zoom() with previous dimensions would have been
89 * ignored. It happens if the image properties didn't change after the
90 * previous matrix_set_max_zoom().
91 * This is achieved by the test of matrix_set_max_zoom().
93 matrix_set_max_zoom(event
->width
, event
->height
, TRUE
);
94 what
|= REFRESH_STATUS
;
99 old_width
= event
->width
;
100 old_height
= event
->height
;
105 static void drag_data_received(GtkWidget
* unused1
, GdkDragContext
* context
,
106 gint unused2
, gint unused3
,
107 GtkSelectionData
* data
, guint unused4
,
115 /* Based on code from gnome-terminal. */
117 if (data
->format
!= 8) {
118 DIALOG_MSG(_("Wrong URI format: %d (instead of 8)"), data
->format
);
122 if (data
->length
<= 0) {
123 DIALOG_MSG(_("Wrong URI length: %d"), data
->length
);
127 uri_list
= g_strndup(data
->data
, data
->length
);
128 uris
= g_strsplit(uri_list
, "\r\n", -1);
133 for (i
= 0; uris
[i
] != NULL
; i
++) {
134 gchar
*old
= uris
[i
];
139 uris
[i
] = g_filename_from_uri(old
, NULL
, &err
);
141 DIALOG_MSG("%s", err
->message
);
152 nb_inserted
= insert_after_current(uris
, i
, FALSE
, FALSE
, TRUE
, TRUE
);
154 open_next_image(nb_inserted
== 1);
159 gtk_drag_finish(context
, TRUE
, TRUE
, time
);
162 static void dnd_init(void)
164 GtkTargetEntry targets
[] = {
166 {"text/uri-list", 0, 0}
170 gtk_drag_dest_set(gl_widget
, GTK_DEST_DEFAULT_ALL
, targets
,
171 G_N_ELEMENTS(targets
), GDK_ACTION_COPY
);
173 g_signal_connect(gl_widget
, "drag-data-received",
174 G_CALLBACK(drag_data_received
), NULL
);
178 * Probe the max_texture_size with a GL_PROXY_TEXTURE_2D.
179 * Used only when initializing the widget.
181 static gboolean
check_texture_size(void)
185 glTexImage2D(GL_PROXY_TEXTURE_2D
, 0, GL_RGB
,
186 rt
->max_texture_size
, rt
->max_texture_size
, 0, GL_RGBA
,
187 GL_UNSIGNED_BYTE
, NULL
);
189 glGetTexLevelParameteriv(GL_PROXY_TEXTURE_2D
, 0, GL_TEXTURE_WIDTH
, &width
);
191 return width
== rt
->max_texture_size
;
194 static void get_max_texture_size(void)
197 glGetIntegerv(GL_MAX_TEXTURE_SIZE
, &rt
->max_texture_size
);
199 rt
->max_texture_size
= 128;
202 while (rt
->max_texture_size
> 0 && check_texture_size() == FALSE
)
203 rt
->max_texture_size
>>= 1;
205 if (rt
->max_texture_size
< 64) {
206 gchar
*message
, *texture
;
208 texture
= g_strdup_printf(_("Fatal error: GL_MAX_TEXTURE_SIZE = %d\n"),
209 rt
->max_texture_size
);
211 message
= g_strconcat(texture
,
212 _("GL_MAX_TEXTURE_SIZE must be >= 64"), NULL
);
221 /* Direct all OpenGL calls to the created widget. */
222 static gboolean
make_current(void)
224 GdkGLContext
*glcontext
= gtk_widget_get_gl_context(gl_widget
);
225 GdkGLDrawable
*gldrawable
= gtk_widget_get_gl_drawable(gl_widget
);
227 return gdk_gl_drawable_make_current(gldrawable
, glcontext
);
230 static gboolean
first_expose(void)
232 GlivImage
*first_image
;
234 /* We only handle the first one. */
235 g_signal_handlers_disconnect_by_func(gl_widget
, G_CALLBACK(first_expose
),
238 if (make_current() == FALSE
) {
239 DIALOG_MSG(_("glXMakeCurrent() failed"));
243 glDisable(GL_DEPTH_TEST
);
245 glEnable(GL_TEXTURE_2D
);
247 glShadeModel(GL_FLAT
);
248 glPixelStorei(GL_UNPACK_ALIGNMENT
, 1);
249 glBlendFunc(GL_SRC_ALPHA
, GL_ONE_MINUS_SRC_ALPHA
);
253 get_max_texture_size();
255 rt
->wid_size
= &gl_widget
->allocation
;
257 g_signal_connect(gl_widget
, "configure-event",
258 G_CALLBACK(configure_event
), NULL
);
260 g_signal_connect(gl_widget
, "expose-event", G_CALLBACK(expose_event
), NULL
);
262 g_signal_connect_after(gl_widget
, "focus-in-event",
263 G_CALLBACK(set_correct_cursor
), NULL
);
265 schedule_hide_cursor();
267 old_width
= rt
->wid_size
->width
;
268 old_height
= rt
->wid_size
->height
;
270 if (options
->fullscreen
)
271 toggle_fullscreen(TRUE
);
273 resize_widget(rt
->wid_size
->width
, rt
->wid_size
->height
);
277 gtk_widget_queue_draw(GTK_WIDGET(get_current_window()));
279 first_image
= load_first_image();
280 if (first_image
!= NULL
) {
281 if (options
->fullscreen
== FALSE
&& options
->resize_win
== FALSE
)
282 resize_window(first_image
->width
, first_image
->height
, TRUE
);
284 g_object_unref(first_image
);
286 } else if (options
->fullscreen
== FALSE
)
287 resize_window(-1, -1, TRUE
);
291 if (options
->build_menus
)
292 rebuild_images_menus();
297 void create_gl_widget(void)
299 GdkGLConfigMode mode
= GDK_GL_MODE_RGBA
| GDK_GL_MODE_DEPTH
|
300 GDK_GL_MODE_DOUBLE
| GDK_GL_MODE_ALPHA
;
302 GdkGLConfig
*glconfig
;
304 if (gdk_gl_query_extension() == FALSE
) {
305 DIALOG_MSG(_("OpenGL not supported"));
309 glconfig
= gdk_gl_config_new_by_mode(mode
);
311 if (glconfig
== NULL
)
312 /* Try without the alpha channel. */
313 glconfig
= gdk_gl_config_new_by_mode(mode
& ~GDK_GL_MODE_ALPHA
);
315 if (glconfig
== NULL
) {
316 DIALOG_MSG(_("Cannot find an appropriate visual, try glxinfo(1)"));
320 gl_widget
= gtk_drawing_area_new();
321 if (gtk_widget_set_gl_capability(gl_widget
, glconfig
, NULL
, TRUE
,
322 GDK_GL_RGBA_TYPE
) == FALSE
) {
323 DIALOG_MSG(_("Cannot set the OpenGL capability"));
327 gtk_widget_set_events(gl_widget
,
328 GDK_BUTTON_PRESS_MASK
| GDK_BUTTON1_MOTION_MASK
|
329 GDK_BUTTON3_MOTION_MASK
| GDK_BUTTON_RELEASE_MASK
);
331 if (options
->delay
!= 0)
332 gtk_widget_add_events(gl_widget
, GDK_POINTER_MOTION_MASK
);
334 /* The real initialization is done on the first expose. */
335 g_signal_connect_after(gl_widget
, "expose-event", G_CALLBACK(first_expose
),
339 gint
report_opengl_errors(const gchar
* function_name
,
340 const gchar
* source_name
, gint line
,
343 static gint glBegin_counter
= 0;
345 gchar
*errors
= NULL
;
348 /* We cannot call glGetError() between a glBegin()/glEnd() pair. */
349 glBegin_counter
+= glBegin_inc
;
350 if (glBegin_counter
!= 0)
353 /* Get all errors. */
354 while ((error
= glGetError()) != GL_NO_ERROR
) {
356 gboolean unknown_error
= FALSE
;
358 error_msg
= (gchar
*) gluErrorString(error
);
359 if (error_msg
== NULL
) {
360 unknown_error
= TRUE
;
361 error_msg
= g_strdup_printf(_("Unknown OpenGL error (%d)"), error
);
365 errors
= g_strdup_printf(_("OpenGL error in %s() at %s:%d:\n%s"),
366 function_name
, source_name
, line
,
369 gchar
*old_msg
= errors
;
370 errors
= g_strconcat(errors
, "\n", error_msg
, NULL
);
380 if (errors
!= NULL
) {
381 ERROR_MSG("%s\n", errors
);