gliv-1.7
[gliv.git] / src / gl_widget.c
blobb8f080d41a731511eb36cf17b9db82ac749f2d01
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 <gfc@altern.org>
21 /************************************************
22 * Creation and callbacks of the OpenGL widget. *
23 ************************************************/
25 #include "gliv.h"
27 #include <GL/gl.h>
28 #include <gtk/gtkgl.h>
30 extern GtkWidget *gl_widget;
31 extern rt_struct *rt;
32 extern options_struct *options;
34 static gint old_width, old_height;
36 void update_bg_color()
38 glClearColor(options->bg_col[0] / 65535.0, options->bg_col[1] / 65535.0,
39 options->bg_col[2] / 65535.0, 0.0);
42 gboolean toggle_cursor(gboolean visible)
44 static GdkCursor *hidden_cursor = NULL;
45 GdkCursor *new_cursor;
47 if (hidden_cursor == NULL) {
48 /* First time, create the invisible cursor. */
49 GdkPixmap *pmap;
50 GdkColor *col;
51 gchar *data;
53 data = g_new0(gchar, 1);
54 col = g_new0(GdkColor, 1);
55 pmap = gdk_bitmap_create_from_data(NULL, data, 1, 1);
56 hidden_cursor = gdk_cursor_new_from_pixmap(pmap, pmap, col, col, 0, 0);
58 g_free(data);
59 g_free(col);
60 gdk_bitmap_unref(pmap);
63 if (visible) {
64 /* Show the cursor. */
65 new_cursor = NULL;
66 schedule_hide_cursor();
67 } else {
68 /* Hide the cursor. */
69 new_cursor = hidden_cursor;
70 gtk_widget_add_events(gl_widget, GDK_POINTER_MOTION_MASK);
73 rt->cursor_hidden = !visible;
74 gdk_window_set_cursor(gl_widget->window, new_cursor);
76 return FALSE;
79 void schedule_hide_cursor(void)
81 static guint id = 0;
83 if (id != 0)
84 /* Remove previous schedule. */
85 g_source_remove(id);
87 if (options->delay == 0)
88 id = 0;
89 else
90 id = g_timeout_add(options->delay, (GSourceFunc) toggle_cursor,
91 GINT_TO_POINTER(FALSE));
94 static gboolean expose_event(GtkWidget * unused, GdkEventExpose * event)
96 if (event->count == 0)
97 refresh(REFRESH_IMAGE);
99 return TRUE;
102 static void resize_widget(gint width, gint height)
104 gdk_gl_drawable_wait_gdk(gtk_widget_get_gl_drawable(gl_widget));
106 glViewport(0, 0, width, height);
108 glMatrixMode(GL_PROJECTION);
109 glLoadIdentity();
111 /* Vertical flip to have coordinates like X but centered like OpenGL. */
112 glOrtho(-width / 2.0, width / 2.0, height / 2.0, -height / 2.0, -1.0, 1.0);
114 glMatrixMode(GL_MODELVIEW);
117 static gboolean configure_event(GtkWidget * unused, GdkEventConfigure * event)
119 gint what = REFRESH_IMAGE;
121 resize_widget(event->width, event->height);
123 if ((options->maximize || options->scaledown) &&
124 matrix_set_max_zoom(old_width, old_height, FALSE) == FALSE) {
126 * When options->maximize or options->scaledown is set we zoom only
127 * if a matrix_set_max_zoom() with previous dimensions would have been
128 * ignored. It happens if the image properties didn't change after the
129 * previous matrix_set_max_zoom().
130 * This is achieved by the test of matrix_set_max_zoom().
132 matrix_set_max_zoom(event->width, event->height, TRUE);
133 what |= REFRESH_STATUS;
136 refresh(what);
138 old_width = event->width;
139 old_height = event->height;
141 return TRUE;
144 static void drag_data_received(GtkWidget * unused1, GdkDragContext * context,
145 gint unused2, gint unused3,
146 GtkSelectionData * data, guint unused4,
147 guint time)
149 gchar *uri_list;
150 gchar **uris;
151 gint i;
152 GError *err = NULL;
154 /* Based on code from gnome-terminal. */
156 if (data->format != 8) {
157 ERROR_MSG(_("Wrong URI format: %d (instead of 8)\n"), data->format);
158 return;
161 if (data->length <= 0) {
162 ERROR_MSG(_("Wrong URI length: %d\n"), data->length);
163 return;
166 uri_list = g_strndup(data->data, data->length);
167 uris = g_strsplit(uri_list, "\r\n", -1);
169 if (uris == NULL)
170 return;
172 for (i = 0; uris[i] != NULL; i++) {
173 gchar *old = uris[i];
175 if (old[0] == '\0')
176 break;
178 uris[i] = g_filename_from_uri(old, NULL, &err);
179 if (err != NULL) {
180 g_printerr("%s\n", err->message);
181 g_error_free(err);
182 err = NULL;
185 if (uris[i] == NULL)
186 uris[i] = old;
187 else
188 g_free(old);
191 if (insert_after_current(uris, i, FALSE))
192 open_next_image();
194 g_strfreev(uris);
195 g_free(uri_list);
197 gtk_drag_finish(context, TRUE, TRUE, time);
200 static void dnd_init(void)
202 GtkTargetEntry targets[] = {
203 /* *INDENT-OFF* */
204 {"text/uri-list", 0, 0}
205 /* *INDENT-ON* */
208 gtk_drag_dest_set(gl_widget, GTK_DEST_DEFAULT_ALL, targets,
209 G_N_ELEMENTS(targets), GDK_ACTION_COPY);
211 g_signal_connect(gl_widget, "drag-data-received",
212 G_CALLBACK(drag_data_received), NULL);
216 * Probe the max_texture_size with a GL_PROXY_TEXTURE_2D.
217 * Used only when initializing the widget.
219 static gboolean check_texture_size(void)
221 gint width;
223 glTexImage2D(GL_PROXY_TEXTURE_2D, 0, GL_RGB,
224 rt->max_texture_size, rt->max_texture_size, 0, GL_RGBA,
225 GL_UNSIGNED_BYTE, NULL);
227 glGetTexLevelParameteriv(GL_PROXY_TEXTURE_2D, 0, GL_TEXTURE_WIDTH, &width);
229 return width == rt->max_texture_size;
232 static void get_max_texture_size(void)
234 glGetIntegerv(GL_MAX_TEXTURE_SIZE, &rt->max_texture_size);
235 while (rt->max_texture_size > 0 && check_texture_size() == FALSE)
236 rt->max_texture_size >>= 1;
238 if (rt->max_texture_size < 64) {
239 ERROR_MSG(_("Fatal error: GL_MAX_TEXTURE_SIZE = %d\n"),
240 rt->max_texture_size);
241 ERROR_MSG(_("GL_MAX_TEXTURE_SIZE must be >= 64\n"));
242 quit(1);
246 static gboolean make_current(void)
248 GdkGLContext *glcontext = gtk_widget_get_gl_context(gl_widget);
249 GdkGLDrawable *gldrawable = gtk_widget_get_gl_drawable(gl_widget);
251 return gdk_gl_drawable_make_current(gldrawable, glcontext);
254 static gboolean first_expose(void)
256 /* We only handle the first one. */
257 g_signal_handlers_disconnect_by_func(gl_widget, G_CALLBACK(first_expose),
258 NULL);
260 if (make_current() == FALSE) {
261 ERROR_MSG(_("glXMakeCurrent() failed\n"));
262 quit(1);
265 glDisable(GL_DEPTH_TEST);
267 glEnable(GL_TEXTURE_2D);
268 glEnable(GL_BLEND);
269 glShadeModel(GL_FLAT);
270 glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
271 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
273 update_bg_color();
275 get_max_texture_size();
277 rt->wid_size = &gl_widget->allocation;
279 g_signal_connect(gl_widget, "configure-event",
280 G_CALLBACK(configure_event), NULL);
282 g_signal_connect(gl_widget, "expose-event", G_CALLBACK(expose_event), 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 refresh_all();
297 load_first_image();
299 refresh_all();
300 load_second_image();
301 refresh_all();
303 if (options->build_menus)
304 rebuild_images_menus();
306 return TRUE;
309 void create_gl_widget(void)
311 GdkGLConfigMode mode = GDK_GL_MODE_RGBA | GDK_GL_MODE_DEPTH |
312 GDK_GL_MODE_DOUBLE | GDK_GL_MODE_ALPHA;
314 GdkGLConfig *glconfig;
316 if (gdk_gl_query_extension() == FALSE) {
317 ERROR_MSG(_("OpenGL not supported\n"));
318 quit(1);
321 glconfig = gdk_gl_config_new_by_mode(mode);
323 if (glconfig == NULL)
324 /* Try without the alpha channel. */
325 glconfig = gdk_gl_config_new_by_mode(mode & ~GDK_GL_MODE_ALPHA);
327 if (glconfig == NULL) {
328 ERROR_MSG(_("Cannot find an appropriate visual, try glxinfo(1)\n"));
329 quit(1);
332 gl_widget = gtk_drawing_area_new();
333 if (gtk_widget_set_gl_capability(gl_widget, glconfig, NULL, TRUE,
334 GDK_GL_RGBA_TYPE) == FALSE) {
335 ERROR_MSG(_("Cannot set the OpenGL capability\n"));
336 quit(1);
339 gtk_widget_set_events(gl_widget,
340 GDK_BUTTON_PRESS_MASK | GDK_BUTTON1_MOTION_MASK |
341 GDK_BUTTON3_MOTION_MASK | GDK_BUTTON_RELEASE_MASK);
343 if (options->delay != 0)
344 gtk_widget_add_events(gl_widget, GDK_POINTER_MOTION_MASK);
346 /* The real initialization is done on the first expose. */
347 g_signal_connect_after(gl_widget, "expose-event", G_CALLBACK(first_expose),
348 NULL);