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@gmail.com>
21 /***********************************************
22 * Creation and callbacks of the OpenGL widget *
23 ***********************************************/
29 #include "gl_widget.h"
31 #include "rendering.h"
35 #include "files_list.h"
37 #include "next_image.h"
38 #include "images_menus.h"
41 extern GtkWidget
*gl_widget
;
43 extern options_struct
*options
;
45 static gint old_width
, old_height
;
52 void update_bg_color()
54 glClearColor(options
->bg_col
.red
/ 65535.0, options
->bg_col
.green
/ 65535.0,
55 options
->bg_col
.blue
/ 65535.0, 1.0);
58 static gboolean
expose_event(GtkWidget
* unused
, GdkEventExpose
* event
)
60 if (event
->count
== 0)
61 refresh(REFRESH_IMAGE
);
66 static void resize_widget(gint width
, gint height
)
76 gdk_gl_drawable_wait_gdk(gtk_widget_get_gl_drawable(gl_widget
));
78 glViewport(0, 0, width
, height
);
79 glMatrixMode(GL_PROJECTION
);
82 /* Vertical flip to have coordinates like X but centered like OpenGL. */
83 glOrtho(-w2
, w2
, h2
, -h2
, -1.0, 1.0);
85 glMatrixMode(GL_MODELVIEW
);
88 static gboolean
configure_event(GtkWidget
* unused
, GdkEventConfigure
* event
)
90 gint what
= REFRESH_IMAGE
;
92 resize_widget(event
->width
, event
->height
);
94 if ((options
->maximize
|| options
->scaledown
) &&
95 matrix_set_max_zoom(old_width
, old_height
, FALSE
) == FALSE
) {
97 * When options->maximize or options->scaledown is set we zoom only
98 * if a matrix_set_max_zoom() with previous dimensions would have been
99 * ignored. It happens if the image properties didn't change after the
100 * previous matrix_set_max_zoom().
101 * This is achieved by the test of matrix_set_max_zoom().
103 matrix_set_max_zoom(event
->width
, event
->height
, TRUE
);
104 what
|= REFRESH_STATUS
;
109 old_width
= event
->width
;
110 old_height
= event
->height
;
115 static gboolean
drag_data_received(GtkWidget
* unused1
,
116 GdkDragContext
* context
,
117 gint unused2
, gint unused3
,
118 GtkSelectionData
* data
, guint info
,
127 /* Based on code from gnome-terminal. */
129 if (data
->format
!= 8) {
130 DIALOG_MSG(_("Wrong URI format: %d (instead of 8)"), data
->format
);
134 if (data
->length
<= 0) {
135 DIALOG_MSG(_("Wrong URI length: %d"), data
->length
);
139 uri_list
= g_strndup((gchar
*) data
->data
, data
->length
);
140 if (info
== TARGET_URI_LIST
)
141 uris
= g_strsplit(uri_list
, "\r\n", -1);
143 uris
= g_strsplit(uri_list
, "\n", -1);
148 for (i
= 0; uris
[i
] != NULL
&& uris
[i
][0] != '\0'; i
++) {
149 gchar
*uri
= uris
[i
];
151 if (info
== TARGET_URI_LIST
) {
152 uri
= g_filename_from_uri(uris
[i
], NULL
, &err
);
154 DIALOG_MSG("%s", err
->message
);
164 nb_inserted
= insert_after_current(uris
, i
, FALSE
, TRUE
);
165 new_images(nb_inserted
);
174 gtk_drag_finish(context
, ok
, FALSE
, time
);
178 static void dnd_init(void)
180 GtkTargetEntry targets
[] = {
182 {"text/uri-list", 0, TARGET_URI_LIST
},
183 {"UTF8_STRING", 0, TARGET_STRING
},
184 {"COMPOUND_TEXT", 0, TARGET_STRING
},
185 {"TEXT", 0, TARGET_STRING
},
186 {"STRING", 0, TARGET_STRING
}
190 gtk_drag_dest_set(gl_widget
, GTK_DEST_DEFAULT_ALL
, targets
,
191 G_N_ELEMENTS(targets
), GDK_ACTION_COPY
);
193 g_signal_connect(gl_widget
, "drag-data-received",
194 G_CALLBACK(drag_data_received
), NULL
);
198 * Probe the max_texture_size with a GL_PROXY_TEXTURE_2D.
199 * Used only when initializing the widget.
201 static gboolean
check_texture_size(void)
205 glTexImage2D(GL_PROXY_TEXTURE_2D
, 0, GL_RGB
,
206 rt
->max_texture_size
, rt
->max_texture_size
, 0, GL_RGBA
,
207 GL_UNSIGNED_BYTE
, NULL
);
209 glGetTexLevelParameteriv(GL_PROXY_TEXTURE_2D
, 0, GL_TEXTURE_WIDTH
, &width
);
211 return width
== rt
->max_texture_size
;
214 static void get_max_texture_size(void)
217 rt
->max_texture_size
= 128;
219 glGetIntegerv(GL_MAX_TEXTURE_SIZE
, &rt
->max_texture_size
);
221 while (rt
->max_texture_size
> 0 && check_texture_size() == FALSE
)
222 rt
->max_texture_size
>>= 1;
224 if (rt
->max_texture_size
< 64) {
225 gchar
*message
, *texture
;
227 texture
= g_strdup_printf(_("Fatal error: GL_MAX_TEXTURE_SIZE = %d\n"),
228 rt
->max_texture_size
);
230 message
= g_strconcat(texture
,
231 _("GL_MAX_TEXTURE_SIZE must be >= 64"), NULL
);
232 DIALOG_MSG("%s", message
);
241 /* Direct all OpenGL calls to the created widget. */
242 static gboolean
make_current(void)
244 GdkGLContext
*glcontext
= gtk_widget_get_gl_context(gl_widget
);
245 GdkGLDrawable
*gldrawable
= gtk_widget_get_gl_drawable(gl_widget
);
247 return gdk_gl_drawable_make_current(gldrawable
, glcontext
);
250 static gboolean
first_expose(void)
252 /* We only handle the first one. */
253 g_signal_handlers_disconnect_by_func(gl_widget
, G_CALLBACK(first_expose
),
256 if (make_current() == FALSE
) {
257 DIALOG_MSG(_("glXMakeCurrent() failed"));
261 glDisable(GL_DEPTH_TEST
);
263 glEnable(GL_TEXTURE_2D
);
265 glShadeModel(GL_FLAT
);
266 glPixelStorei(GL_UNPACK_ALIGNMENT
, 1);
267 glPixelStorei(GL_PACK_ALIGNMENT
, 1);
268 glBlendFunc(GL_SRC_ALPHA
, GL_ONE_MINUS_SRC_ALPHA
);
272 get_max_texture_size();
274 rt
->wid_size
= &gl_widget
->allocation
;
276 g_signal_connect(gl_widget
, "configure-event",
277 G_CALLBACK(configure_event
), NULL
);
279 g_signal_connect(gl_widget
, "expose-event", G_CALLBACK(expose_event
), NULL
);
281 g_signal_connect_after(gl_widget
, "focus-in-event",
282 G_CALLBACK(set_correct_cursor
), NULL
);
284 schedule_hide_cursor();
286 old_width
= rt
->wid_size
->width
;
287 old_height
= rt
->wid_size
->height
;
289 if (options
->fullscreen
)
290 toggle_fullscreen(TRUE
);
292 resize_widget(rt
->wid_size
->width
, rt
->wid_size
->height
);
296 gtk_widget_queue_draw(GTK_WIDGET(get_current_window()));
297 do_later(G_PRIORITY_LOW
, load_first_image
);
302 void create_gl_widget(void)
304 GdkGLConfigMode mode
= GDK_GL_MODE_RGBA
| GDK_GL_MODE_DEPTH
|
305 GDK_GL_MODE_DOUBLE
| GDK_GL_MODE_ALPHA
;
307 GdkGLConfig
*glconfig
;
309 if (gdk_gl_query_extension() == FALSE
) {
310 DIALOG_MSG(_("OpenGL not supported"));
314 glconfig
= gdk_gl_config_new_by_mode(mode
);
316 if (glconfig
== NULL
)
317 /* Try without the alpha channel. */
318 glconfig
= gdk_gl_config_new_by_mode(mode
& ~GDK_GL_MODE_ALPHA
);
320 if (glconfig
== NULL
) {
321 DIALOG_MSG(_("Cannot find an appropriate visual, try glxinfo(1)"));
325 gl_widget
= gtk_drawing_area_new();
326 if (gtk_widget_set_gl_capability(gl_widget
, glconfig
, NULL
, TRUE
,
327 GDK_GL_RGBA_TYPE
) == FALSE
) {
328 DIALOG_MSG(_("Cannot set the OpenGL capability"));
332 gtk_widget_add_events(gl_widget
,
333 GDK_BUTTON_PRESS_MASK
| GDK_POINTER_MOTION_MASK
|
334 GDK_BUTTON_RELEASE_MASK
);
336 if (options
->delay
!= 0)
337 gtk_widget_add_events(gl_widget
, GDK_POINTER_MOTION_MASK
);
339 /* The real initialization is done on the first expose. */
340 g_signal_connect_after(gl_widget
, "expose-event", G_CALLBACK(first_expose
),
344 gint
report_opengl_errors(const gchar
* function_name
,
345 const gchar
* source_name
, gint line
,
348 static gint glBegin_counter
= 0;
350 gchar
*errors
= NULL
;
353 /* We cannot call glGetError() between a glBegin()/glEnd() pair. */
354 glBegin_counter
+= glBegin_inc
;
355 if (glBegin_counter
!= 0)
358 /* Get all errors. */
359 while ((error
= glGetError()) != GL_NO_ERROR
) {
361 gboolean unknown_error
= FALSE
;
363 error_msg
= (gchar
*) gluErrorString(error
);
364 if (error_msg
== NULL
) {
365 unknown_error
= TRUE
;
366 error_msg
= g_strdup_printf(_("Unknown OpenGL error (%d)"), error
);
370 errors
= g_strdup_printf(_("OpenGL error in %s() at %s:%d:\n%s"),
371 function_name
, source_name
, line
,
374 gchar
*old_msg
= errors
;
375 errors
= g_strconcat(errors
, "\n", error_msg
, NULL
);
385 if (errors
!= NULL
) {
386 g_printerr("%s\n", errors
);