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 ************************************************/
28 #include <gtk/gtkgl.h>
30 extern GtkWidget
*gl_widget
;
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. */
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);
60 gdk_bitmap_unref(pmap
);
64 /* Show the cursor. */
66 schedule_hide_cursor();
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
);
79 void schedule_hide_cursor(void)
84 /* Remove previous schedule. */
87 if (options
->delay
== 0)
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
);
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
);
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
;
138 old_width
= event
->width
;
139 old_height
= event
->height
;
144 static void drag_data_received(GtkWidget
* unused1
, GdkDragContext
* context
,
145 gint unused2
, gint unused3
,
146 GtkSelectionData
* data
, guint unused4
,
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
);
161 if (data
->length
<= 0) {
162 ERROR_MSG(_("Wrong URI length: %d\n"), data
->length
);
166 uri_list
= g_strndup(data
->data
, data
->length
);
167 uris
= g_strsplit(uri_list
, "\r\n", -1);
172 for (i
= 0; uris
[i
] != NULL
; i
++) {
173 gchar
*old
= uris
[i
];
178 uris
[i
] = g_filename_from_uri(old
, NULL
, &err
);
180 g_printerr("%s\n", err
->message
);
191 if (insert_after_current(uris
, i
, FALSE
))
197 gtk_drag_finish(context
, TRUE
, TRUE
, time
);
200 static void dnd_init(void)
202 GtkTargetEntry targets
[] = {
204 {"text/uri-list", 0, 0}
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)
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"));
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
),
260 if (make_current() == FALSE
) {
261 ERROR_MSG(_("glXMakeCurrent() failed\n"));
265 glDisable(GL_DEPTH_TEST
);
267 glEnable(GL_TEXTURE_2D
);
269 glShadeModel(GL_FLAT
);
270 glPixelStorei(GL_UNPACK_ALIGNMENT
, 1);
271 glBlendFunc(GL_SRC_ALPHA
, GL_ONE_MINUS_SRC_ALPHA
);
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
);
303 if (options
->build_menus
)
304 rebuild_images_menus();
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"));
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"));
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"));
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
),