Misc mnemonics fixes.
[gliv.git] / src / gl_widget.c
blob14e8b0762e3d4cff41fe6f377f15fb4689d533f4
1 /*
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 ***********************************************/
25 #include <GL/glu.h>
26 #include <gtk/gtkgl.h>
28 #include "gliv.h"
29 #include "gl_widget.h"
30 #include "options.h"
31 #include "rendering.h"
32 #include "messages.h"
33 #include "windows.h"
34 #include "matrix.h"
35 #include "files_list.h"
36 #include "main.h"
37 #include "next_image.h"
38 #include "images_menus.h"
39 #include "cursors.h"
40 #include "opengl.h"
42 extern GtkWidget *gl_widget;
43 extern rt_struct *rt;
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);
59 return FALSE;
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);
70 glLoadIdentity();
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;
97 refresh(what);
99 old_width = event->width;
100 old_height = event->height;
102 return FALSE;
105 static void drag_data_received(GtkWidget * unused1, GdkDragContext * context,
106 gint unused2, gint unused3,
107 GtkSelectionData * data, guint unused4,
108 guint time)
110 gchar *uri_list;
111 gchar **uris;
112 gint i, nb_inserted;
113 GError *err = NULL;
115 /* Based on code from gnome-terminal. */
117 if (data->format != 8) {
118 DIALOG_MSG(_("Wrong URI format: %d (instead of 8)"), data->format);
119 return;
122 if (data->length <= 0) {
123 DIALOG_MSG(_("Wrong URI length: %d"), data->length);
124 return;
127 uri_list = g_strndup(data->data, data->length);
128 uris = g_strsplit(uri_list, "\r\n", -1);
130 if (uris == NULL)
131 return;
133 for (i = 0; uris[i] != NULL; i++) {
134 gchar *old = uris[i];
136 if (old[0] == '\0')
137 break;
139 uris[i] = g_filename_from_uri(old, NULL, &err);
140 if (err != NULL) {
141 DIALOG_MSG("%s", err->message);
142 g_error_free(err);
143 err = NULL;
146 if (uris[i] == NULL)
147 uris[i] = old;
148 else
149 g_free(old);
152 nb_inserted = insert_after_current(uris, i, FALSE, FALSE, TRUE, TRUE);
153 if (nb_inserted > 0)
154 open_next_image(nb_inserted == 1);
156 g_strfreev(uris);
157 g_free(uri_list);
159 gtk_drag_finish(context, TRUE, TRUE, time);
162 static void dnd_init(void)
164 GtkTargetEntry targets[] = {
165 /* *INDENT-OFF* */
166 {"text/uri-list", 0, 0}
167 /* *INDENT-ON* */
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)
183 gint width;
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)
196 #if 0
197 glGetIntegerv(GL_MAX_TEXTURE_SIZE, &rt->max_texture_size);
198 #else
199 rt->max_texture_size = 128;
200 #endif
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);
213 DIALOG_MSG(message);
215 g_free(texture);
216 g_free(message);
217 quit(1);
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),
236 NULL);
238 if (make_current() == FALSE) {
239 DIALOG_MSG(_("glXMakeCurrent() failed"));
240 quit(1);
243 glDisable(GL_DEPTH_TEST);
245 glEnable(GL_TEXTURE_2D);
246 glDisable(GL_BLEND);
247 glShadeModel(GL_FLAT);
248 glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
249 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
251 update_bg_color();
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);
275 dnd_init();
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);
289 load_second_image();
291 if (options->build_menus)
292 rebuild_images_menus();
294 return FALSE;
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"));
306 quit(1);
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)"));
317 quit(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"));
324 quit(1);
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),
336 NULL);
339 gint report_opengl_errors(const gchar * function_name,
340 const gchar * source_name, gint line,
341 gint glBegin_inc)
343 static gint glBegin_counter = 0;
344 gint nb_errors = 0;
345 gchar *errors = NULL;
346 GLenum error;
348 /* We cannot call glGetError() between a glBegin()/glEnd() pair. */
349 glBegin_counter += glBegin_inc;
350 if (glBegin_counter != 0)
351 return 0;
353 /* Get all errors. */
354 while ((error = glGetError()) != GL_NO_ERROR) {
355 gchar *error_msg;
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);
364 if (errors == NULL)
365 errors = g_strdup_printf(_("OpenGL error in %s() at %s:%d:\n%s"),
366 function_name, source_name, line,
367 error_msg);
368 else {
369 gchar *old_msg = errors;
370 errors = g_strconcat(errors, "\n", error_msg, NULL);
371 g_free(old_msg);
374 if (unknown_error)
375 g_free(error_msg);
377 nb_errors++;
380 if (errors != NULL) {
381 ERROR_MSG("%s\n", errors);
382 g_free(errors);
385 return nb_errors;