11218
|
1 #include <assert.h>
|
|
2 #include <stdio.h>
|
|
3 #include <GL/gl.h>
|
|
4 #include <GL/glu.h>
|
|
5 #include "cc_gtk_gl.h"
|
|
6 #include "util.h"
|
|
7
|
|
8 static GdkGLConfig *glconfig = NULL;
|
|
9
|
|
10 /**
|
|
11 * Resets the OpenGL viewport stuff on widget reconfiguration (resize,
|
|
12 * reposition)
|
|
13 * @param widget widget that got reconfigured
|
|
14 * @param event the configuration event
|
|
15 * @param data unused
|
|
16 * @return FALSE to propagate other handlers
|
|
17 */
|
|
18 static gboolean configure_event(GtkWidget *widget, GdkEventConfigure *event,
|
|
19 void *data);
|
|
20
|
|
21 /**
|
|
22 * Maps the widget to the screen.
|
|
23 * @param widget widget that got mapped
|
|
24 * @param event the map event
|
|
25 * @param data draw info struct
|
|
26 * @return FALSE to propagate other handlers
|
|
27 */
|
|
28 static gboolean map_event(GtkWidget *widget, GdkEventAny *event, void *data);
|
|
29
|
|
30 /**
|
|
31 * Unmaps the widget from the screen.
|
|
32 * @param widget widget that got unmapped
|
|
33 * @param event the configuration event
|
|
34 * @param data draw info struct
|
|
35 * @return FALSE to propagate other handlers
|
|
36 */
|
|
37 static gboolean unmap_event(GtkWidget *widget, GdkEventAny *event, void *data);
|
|
38
|
|
39 /**
|
|
40 * Respond to widget visibility change.
|
|
41 * @param widget widget whose visibility changed
|
|
42 * @param event the visibility event
|
|
43 * @param data draw info struct
|
|
44 * @return FALSE to propagate other handlers
|
|
45 */
|
|
46 static gboolean visibility_notify_event(GtkWidget *widget,
|
|
47 GdkEventVisibility *event, void *data);
|
|
48
|
|
49 /**
|
|
50 * Add a glib timer to periodically draw the widget.
|
|
51 * @param widget widget we're drawing
|
|
52 * @param info draw info struct
|
|
53 */
|
|
54 static void widget_draw_timer_add(GtkWidget *widget, struct draw_info *info);
|
|
55
|
|
56 /**
|
|
57 * Remove glib timer that was drawing this widget.
|
|
58 * @param widget widget we're drawing
|
|
59 * @param info draw info struct
|
|
60 */
|
|
61 static void widget_draw_timer_remove(GtkWidget *widget, struct draw_info *info);
|
|
62
|
|
63 /**
|
|
64 * Periodically invalidates gtk gl widget and tells GTK to redraw
|
|
65 * @param widget widget we're drawing
|
|
66 */
|
|
67 static gboolean widget_draw_timer(GtkWidget *widget);
|
|
68
|
|
69 /**
|
|
70 * Cleanup widget stuff when it's getting destroyed.
|
|
71 * @param widget widget that got destroyed
|
|
72 * @param data draw info struct
|
|
73 */
|
|
74 static void destroy_event(GtkWidget *widget, struct draw_info *data);
|
|
75
|
|
76 int cc_init_gtk_gl()
|
|
77 {
|
|
78 if (glconfig)
|
|
79 return 0;
|
|
80
|
|
81 /* configure OpenGL */
|
|
82
|
|
83 glconfig = gdk_gl_config_new_by_mode(GDK_GL_MODE_RGB |
|
|
84 GDK_GL_MODE_DEPTH |
|
|
85 GDK_GL_MODE_DOUBLE);
|
|
86
|
|
87 if (glconfig == NULL) {
|
|
88 Debug("*** Cannot find the double-buffered visual.\n");
|
|
89 Debug("*** Trying single-buffered visual.\n");
|
|
90
|
|
91 /* Try single-buffered visual */
|
|
92 glconfig = gdk_gl_config_new_by_mode(GDK_GL_MODE_RGB |
|
|
93 GDK_GL_MODE_DEPTH);
|
|
94 if (glconfig == NULL) {
|
|
95 Debug("*** No appropriate OpenGL-capable visual "
|
|
96 "found.\n");
|
|
97 return 1;
|
|
98 }
|
|
99 }
|
|
100
|
|
101 return 0;
|
|
102 }
|
|
103
|
|
104 void cc_new_gl_window(gl_init_func init, gl_config_func config,
|
|
105 gl_draw_func draw, struct draw_info *data,
|
|
106 struct window_box *ret)
|
|
107 {
|
|
108 GtkWidget *window;
|
|
109 GtkWidget *vbox;
|
|
110 GtkWidget *drawing_area;
|
|
111
|
|
112 window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
|
|
113 gtk_container_set_reallocate_redraws(GTK_CONTAINER(window), TRUE);
|
|
114
|
|
115 vbox = gtk_vbox_new(FALSE, 0);
|
|
116 gtk_container_add(GTK_CONTAINER(window), vbox);
|
|
117 gtk_widget_show(vbox);
|
|
118
|
|
119 if (!data) {
|
|
120 data = (struct draw_info*)malloc(sizeof(*data));
|
|
121 assert(data);
|
|
122 memset(data, 0, sizeof(*data));
|
|
123 data->timeout = TRUE;
|
|
124 data->delay_ms = DEFAULT_FRAME_DELAY;
|
|
125 }
|
|
126 drawing_area = cc_new_gl_area(init, config, draw, data);
|
|
127 gtk_box_pack_start(GTK_BOX(vbox), drawing_area, TRUE, TRUE, 0);
|
|
128 gtk_widget_show(drawing_area);
|
|
129 ret->window = window;
|
|
130 ret->vbox = vbox;
|
|
131 ret->draw_area = drawing_area;
|
|
132 }
|
|
133
|
|
134 GtkWidget *cc_new_gl_area(gl_init_func init, gl_config_func config,
|
|
135 gl_draw_func draw, struct draw_info *data)
|
|
136 {
|
|
137 GtkWidget *drawing_area;
|
|
138
|
|
139 assert(data);
|
|
140
|
|
141 drawing_area = gtk_drawing_area_new();
|
|
142 assert(drawing_area);
|
|
143
|
|
144 assert(gtk_widget_set_gl_capability(drawing_area, glconfig, NULL, FALSE,
|
|
145 GDK_GL_RGBA_TYPE));
|
|
146 gtk_widget_add_events (drawing_area, GDK_VISIBILITY_NOTIFY_MASK);
|
|
147 if (init) {
|
|
148 g_signal_connect_after(G_OBJECT(drawing_area), "realize",
|
|
149 G_CALLBACK(init), data->data);
|
|
150 }
|
|
151 if (config) {
|
|
152 g_signal_connect(G_OBJECT(drawing_area), "configure_event",
|
|
153 G_CALLBACK(config), NULL);
|
|
154 } else {
|
|
155 g_signal_connect(G_OBJECT(drawing_area), "configure_event",
|
|
156 G_CALLBACK(configure_event), NULL);
|
|
157 }
|
|
158 if (draw) {
|
|
159 g_signal_connect(G_OBJECT(drawing_area), "expose_event",
|
|
160 G_CALLBACK(draw), data->data);
|
|
161 }
|
|
162 g_signal_connect(G_OBJECT(drawing_area), "map_event",
|
|
163 G_CALLBACK(map_event), data);
|
|
164 g_signal_connect(G_OBJECT(drawing_area), "unmap_event",
|
|
165 G_CALLBACK(unmap_event), data);
|
|
166 g_signal_connect(G_OBJECT(drawing_area), "visibility_notify_event",
|
|
167 G_CALLBACK(visibility_notify_event), data);
|
|
168 g_signal_connect(G_OBJECT(drawing_area), "destroy",
|
|
169 G_CALLBACK(destroy_event), data);
|
|
170
|
|
171 return drawing_area;
|
|
172 }
|
|
173
|
|
174
|
|
175 static gboolean configure_event(GtkWidget *widget,
|
|
176 GdkEventConfigure *event, void *data)
|
|
177 {
|
|
178 GdkGLContext *glcontext = gtk_widget_get_gl_context(widget);
|
|
179 GdkGLDrawable *gldrawable = gtk_widget_get_gl_drawable(widget);
|
|
180
|
|
181 GLfloat w = widget->allocation.width;
|
|
182 GLfloat h = widget->allocation.height;
|
|
183 GLfloat aspect;
|
|
184
|
|
185 // Debug("configuring\n");
|
|
186
|
|
187 /*** OpenGL BEGIN ***/
|
|
188 if (!gdk_gl_drawable_gl_begin(gldrawable, glcontext))
|
|
189 return FALSE;
|
|
190
|
|
191 glViewport(0, 0, w, h);
|
|
192 glMatrixMode(GL_PROJECTION);
|
|
193
|
|
194 glLoadIdentity();
|
|
195 if (w > h) {
|
|
196 aspect = w / h;
|
|
197 glFrustum(-aspect, aspect, -1.0, 1.0, 2.0, 60.0);
|
|
198 } else {
|
|
199 aspect = h / w;
|
|
200 glFrustum(-1.0, 1.0, -aspect, aspect, 2.0, 60.0);
|
|
201 }
|
|
202 glMatrixMode(GL_MODELVIEW);
|
|
203 glLoadIdentity();
|
|
204
|
|
205 gdk_gl_drawable_gl_end(gldrawable);
|
|
206 /*** OpenGL END ***/
|
|
207
|
|
208 return FALSE;
|
|
209 }
|
|
210
|
|
211 static int map_event(GtkWidget *widget, GdkEventAny *event, void *data)
|
|
212 {
|
|
213 struct draw_info *info = (struct draw_info*)data;
|
|
214 Debug("map\n");
|
|
215
|
|
216 if (info->timeout) {
|
|
217 widget_draw_timer_add(widget, info);
|
|
218 }
|
|
219 return FALSE;
|
|
220 }
|
|
221
|
|
222 static int unmap_event(GtkWidget *widget, GdkEventAny *event, void *data)
|
|
223 {
|
|
224 struct draw_info *info = (struct draw_info*)data;
|
|
225 Debug("unmap\n");
|
|
226
|
|
227 if (info->timeout) {
|
|
228 widget_draw_timer_remove(widget, info);
|
|
229 }
|
|
230 return FALSE;
|
|
231 }
|
|
232
|
|
233 static int visibility_notify_event(GtkWidget *widget, GdkEventVisibility *event,
|
|
234 void *data)
|
|
235 {
|
|
236 struct draw_info *info = (struct draw_info*)data;
|
|
237 Debug("visibility\n");
|
|
238
|
|
239 if (event->state == GDK_VISIBILITY_FULLY_OBSCURED) {
|
|
240 Debug("obscured\n");
|
|
241 if (info->timeout) {
|
|
242 widget_draw_timer_remove(widget, info);
|
|
243 }
|
|
244 } else {
|
|
245 Debug("visible\n");
|
|
246 if (info->timeout) {
|
|
247 widget_draw_timer_add(widget, info);
|
|
248 }
|
|
249 }
|
|
250 return FALSE;
|
|
251 }
|
|
252
|
|
253 static void widget_draw_timer_add(GtkWidget *widget, struct draw_info *info)
|
|
254 {
|
|
255 if (!info->timer_id) {
|
|
256 info->timer_id = g_timeout_add(info->delay_ms,
|
|
257 (GSourceFunc)widget_draw_timer, widget);
|
|
258 }
|
|
259 }
|
|
260
|
|
261 static void widget_draw_timer_remove(GtkWidget *widget, struct draw_info *info)
|
|
262 {
|
|
263 if (info->timer_id) {
|
|
264 g_source_remove(info->timer_id);
|
|
265 info->timer_id = 0;
|
|
266 }
|
|
267 }
|
|
268
|
|
269 static gboolean widget_draw_timer(GtkWidget *widget)
|
|
270 {
|
|
271 /* invalidate the window */
|
|
272 gdk_window_invalidate_rect (widget->window, &widget->allocation, FALSE);
|
|
273
|
|
274 /* tell gtk to update it _now_ */
|
|
275 gdk_window_process_updates (widget->window, FALSE);
|
|
276
|
|
277 return TRUE;
|
|
278 }
|
|
279
|
|
280 static void destroy_event(GtkWidget *widget, struct draw_info *data)
|
|
281 {
|
|
282 Debug("destroying widget\n");
|
|
283
|
|
284 if (data) {
|
|
285 widget_draw_timer_remove(widget, data);
|
|
286 free(data);
|
|
287 }
|
|
288 }
|