Fix some more build warnings
[gliv.git] / src / gl_widget.c
blobd5678082b6810d3feb17bb19453ffc979aff30b9
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@gmail.com>
21 /***********************************************
22 * Creation and callbacks of the OpenGL widget *
23 ***********************************************/
25 #include <GL/glu.h>
26 #include "opengl.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"
41 extern GtkWidget *gl_widget;
42 extern rt_struct *rt;
43 extern options_struct *options;
45 static gint old_width, old_height;
47 enum {
48 TARGET_URI_LIST,
49 TARGET_STRING
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);
63 return FALSE;
66 static void resize_widget(gint width, gint height)
68 gfloat w2, h2;
70 width += width % 2;
71 height += height % 2;
73 w2 = width / 2.0;
74 h2 = height / 2.0;
76 gdk_gl_drawable_wait_gdk(gtk_widget_get_gl_drawable(gl_widget));
78 glViewport(0, 0, width, height);
79 glMatrixMode(GL_PROJECTION);
80 glLoadIdentity();
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;
107 refresh(what);
109 old_width = event->width;
110 old_height = event->height;
112 return FALSE;
115 static gboolean drag_data_received(GtkWidget * unused1,
116 GdkDragContext * context,
117 gint unused2, gint unused3,
118 GtkSelectionData * data, guint info,
119 guint time)
121 gchar *uri_list;
122 gchar **uris;
123 gint i, nb_inserted;
124 gboolean ok = FALSE;
125 GError *err = NULL;
127 /* Based on code from gnome-terminal. */
129 if (data->format != 8) {
130 DIALOG_MSG(_("Wrong URI format: %d (instead of 8)"), data->format);
131 goto out;
134 if (data->length <= 0) {
135 DIALOG_MSG(_("Wrong URI length: %d"), data->length);
136 goto out;
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);
142 else
143 uris = g_strsplit(uri_list, "\n", -1);
145 if (uris == NULL)
146 goto out_free;
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);
153 if (err != NULL) {
154 DIALOG_MSG("%s", err->message);
155 g_error_free(err);
156 goto out_strfreev;
158 g_free(uris[i]);
159 uris[i] = uri;
163 ok = TRUE;
164 nb_inserted = insert_after_current(uris, i, FALSE, TRUE);
165 new_images(nb_inserted);
167 out_strfreev:
168 g_strfreev(uris);
170 out_free:
171 g_free(uri_list);
173 out:
174 gtk_drag_finish(context, ok, FALSE, time);
175 return FALSE;
178 static void dnd_init(void)
180 GtkTargetEntry targets[] = {
181 /* *INDENT-OFF* */
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}
187 /* *INDENT-ON* */
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)
203 gint width;
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)
216 #if NO_OPENGL
217 rt->max_texture_size = 128;
218 #else
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);
234 g_free(texture);
235 g_free(message);
236 quit(1);
238 #endif
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),
254 NULL);
256 if (make_current() == FALSE) {
257 DIALOG_MSG(_("glXMakeCurrent() failed"));
258 quit(1);
261 glDisable(GL_DEPTH_TEST);
263 glEnable(GL_TEXTURE_2D);
264 glDisable(GL_BLEND);
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);
270 update_bg_color();
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);
294 dnd_init();
296 gtk_widget_queue_draw(GTK_WIDGET(get_current_window()));
297 do_later(G_PRIORITY_LOW, load_first_image);
299 return FALSE;
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"));
311 quit(1);
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)"));
322 quit(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"));
329 quit(1);
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),
341 NULL);
344 gint report_opengl_errors(const gchar * function_name,
345 const gchar * source_name, gint line,
346 gint glBegin_inc)
348 static gint glBegin_counter = 0;
349 gint nb_errors = 0;
350 gchar *errors = NULL;
351 GLenum error;
353 /* We cannot call glGetError() between a glBegin()/glEnd() pair. */
354 glBegin_counter += glBegin_inc;
355 if (glBegin_counter != 0)
356 return 0;
358 /* Get all errors. */
359 while ((error = glGetError()) != GL_NO_ERROR) {
360 gchar *error_msg;
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);
369 if (errors == NULL)
370 errors = g_strdup_printf(_("OpenGL error in %s() at %s:%d:\n%s"),
371 function_name, source_name, line,
372 error_msg);
373 else {
374 gchar *old_msg = errors;
375 errors = g_strconcat(errors, "\n", error_msg, NULL);
376 g_free(old_msg);
379 if (unknown_error)
380 g_free(error_msg);
382 nb_errors++;
385 if (errors != NULL) {
386 g_printerr("%s\n", errors);
387 g_free(errors);
390 return nb_errors;