rev |
line source |
nuclear@4
|
1 #include <stdlib.h>
|
nuclear@3
|
2 #include <X11/Xlib.h>
|
nuclear@3
|
3 #include <GL/glx.h>
|
nuclear@4
|
4 #include "sgl.h"
|
nuclear@3
|
5 #include "wsys.h"
|
nuclear@4
|
6 #include "log.h"
|
nuclear@3
|
7
|
nuclear@3
|
8 struct window {
|
nuclear@3
|
9 Window win;
|
nuclear@3
|
10 GLXContext ctx;
|
nuclear@4
|
11 long evmask;
|
nuclear@3
|
12 struct window *next;
|
nuclear@3
|
13 };
|
nuclear@3
|
14
|
nuclear@3
|
15 static int init(void);
|
nuclear@3
|
16 static void shutdown(void);
|
nuclear@4
|
17
|
nuclear@4
|
18 /* video mode switching */
|
nuclear@4
|
19 static int set_vidmode(int xsz, int ysz);
|
nuclear@4
|
20 static int get_vidmode(int *xsz, int *ysz);
|
nuclear@4
|
21
|
nuclear@4
|
22 /* create/destroy windows */
|
nuclear@3
|
23 static int create_window(int xsz, int ysz, unsigned int flags);
|
nuclear@4
|
24 static void fill_attr(int *attr, unsigned int flags);
|
nuclear@4
|
25 static void print_visual_info(XVisualInfo *vis);
|
nuclear@3
|
26 static void close_window(int id);
|
nuclear@3
|
27
|
nuclear@4
|
28 /* window management */
|
nuclear@4
|
29 static int set_active(int id);
|
nuclear@4
|
30 static int set_title(const char *str);
|
nuclear@4
|
31 static void redisplay(void);
|
nuclear@4
|
32 static void swap_buffers(void);
|
nuclear@4
|
33
|
nuclear@4
|
34 /* event handling and friends */
|
nuclear@4
|
35 static void set_bits(long *mask, long bits);
|
nuclear@4
|
36 static void clear_bits(long *mask, long bits);
|
nuclear@4
|
37 static void set_event(int idx, int enable);
|
nuclear@4
|
38 static int process_events(void);
|
nuclear@4
|
39
|
nuclear@4
|
40
|
nuclear@3
|
41 static struct wsys_module ws = {
|
nuclear@3
|
42 "x11-glx", 0,
|
nuclear@3
|
43 init,
|
nuclear@3
|
44 shutdown,
|
nuclear@3
|
45 set_vidmode,
|
nuclear@3
|
46 get_vidmode,
|
nuclear@3
|
47 create_window,
|
nuclear@3
|
48 close_window,
|
nuclear@4
|
49 set_active,
|
nuclear@4
|
50 set_title,
|
nuclear@4
|
51 redisplay,
|
nuclear@4
|
52 swap_buffers,
|
nuclear@4
|
53 set_event,
|
nuclear@4
|
54 process_events,
|
nuclear@3
|
55 0
|
nuclear@3
|
56 };
|
nuclear@3
|
57
|
nuclear@3
|
58 static Display *dpy;
|
nuclear@4
|
59 static Window root;
|
nuclear@3
|
60 static int scr;
|
nuclear@4
|
61 static Atom xa_wm_prot, xa_wm_del_win;
|
nuclear@3
|
62 static struct window *winlist;
|
nuclear@4
|
63 static struct window *active_win;
|
nuclear@3
|
64
|
nuclear@4
|
65 /* this is the only exported function, everything else should be static */
|
nuclear@3
|
66 void sgl_register_x11(void)
|
nuclear@3
|
67 {
|
nuclear@3
|
68 sgl_register_module(&ws);
|
nuclear@3
|
69 }
|
nuclear@3
|
70
|
nuclear@3
|
71 static int init(void)
|
nuclear@3
|
72 {
|
nuclear@3
|
73 winlist = 0;
|
nuclear@3
|
74
|
nuclear@3
|
75 if(!(dpy = XOpenDisplay(0))) {
|
nuclear@4
|
76 sgl_log("failed to open X display: %s\n", XDisplayName(0));
|
nuclear@3
|
77 return -1;
|
nuclear@3
|
78 }
|
nuclear@3
|
79 scr = DefaultScreen(dpy);
|
nuclear@4
|
80 root = RootWindow(dpy, scr);
|
nuclear@4
|
81
|
nuclear@4
|
82 xa_wm_prot = XInternAtom(dpy, "WM_PROTOCOLS", False);
|
nuclear@4
|
83 xa_wm_del_win = XInternAtom(dpy, "WM_DELETE_WINDOW", False);
|
nuclear@3
|
84
|
nuclear@3
|
85 return 0;
|
nuclear@3
|
86 }
|
nuclear@3
|
87
|
nuclear@3
|
88 static void shutdown(void)
|
nuclear@3
|
89 {
|
nuclear@3
|
90 while(winlist) {
|
nuclear@3
|
91 struct window *win = winlist;
|
nuclear@3
|
92 winlist = winlist->next;
|
nuclear@3
|
93
|
nuclear@3
|
94 glXDestroyContext(dpy, win->ctx);
|
nuclear@4
|
95 XDestroyWindow(dpy, win->win);
|
nuclear@3
|
96 free(win);
|
nuclear@3
|
97 }
|
nuclear@3
|
98 XCloseDisplay(dpy);
|
nuclear@3
|
99 dpy = 0;
|
nuclear@3
|
100 }
|
nuclear@3
|
101
|
nuclear@4
|
102 static int set_vidmode(int xsz, int ysz)
|
nuclear@3
|
103 {
|
nuclear@3
|
104 /* TODO */
|
nuclear@4
|
105 return 0;
|
nuclear@3
|
106 }
|
nuclear@3
|
107
|
nuclear@4
|
108 static int get_vidmode(int *xsz, int *ysz)
|
nuclear@3
|
109 {
|
nuclear@3
|
110 /* TODO */
|
nuclear@4
|
111 return 0;
|
nuclear@3
|
112 }
|
nuclear@3
|
113
|
nuclear@3
|
114 static int create_window(int xsz, int ysz, unsigned int flags)
|
nuclear@3
|
115 {
|
nuclear@4
|
116 int attr[32];
|
nuclear@4
|
117 Window win;
|
nuclear@4
|
118 GLXContext ctx;
|
nuclear@4
|
119 XVisualInfo *vis;
|
nuclear@4
|
120 XClassHint chint;
|
nuclear@4
|
121 XSetWindowAttributes xattr;
|
nuclear@4
|
122 unsigned int attr_valid;
|
nuclear@4
|
123 long evmask;
|
nuclear@4
|
124 struct window *wnode;
|
nuclear@4
|
125
|
nuclear@4
|
126 if(!(wnode = malloc(sizeof *wnode))) {
|
nuclear@4
|
127 return -1;
|
nuclear@4
|
128 }
|
nuclear@4
|
129
|
nuclear@4
|
130 fill_attr(attr, flags);
|
nuclear@4
|
131
|
nuclear@4
|
132 if(!(vis = glXChooseVisual(dpy, scr, attr))) {
|
nuclear@4
|
133 sgl_log("failed to find a suitable visual\n");
|
nuclear@4
|
134 free(wnode);
|
nuclear@4
|
135 return -1;
|
nuclear@4
|
136 }
|
nuclear@4
|
137 print_visual_info(vis);
|
nuclear@4
|
138
|
nuclear@4
|
139 if(!(ctx = glXCreateContext(dpy, vis, 0, True))) {
|
nuclear@4
|
140 sgl_log("failed to create OpenGL context\n");
|
nuclear@4
|
141 XFree(vis);
|
nuclear@4
|
142 free(wnode);
|
nuclear@4
|
143 return -1;
|
nuclear@4
|
144 }
|
nuclear@4
|
145
|
nuclear@4
|
146 xattr.background_pixel = xattr.border_pixel = BlackPixel(dpy, scr);
|
nuclear@4
|
147 xattr.colormap = XCreateColormap(dpy, root, vis->visual, AllocNone);
|
nuclear@4
|
148 attr_valid = CWColormap | CWBackPixel | CWBorderPixel;
|
nuclear@4
|
149
|
nuclear@4
|
150 if(!(win = XCreateWindow(dpy, root, 0, 0, xsz, ysz, 0, vis->depth, InputOutput,
|
nuclear@4
|
151 vis->visual, attr_valid, &xattr))) {
|
nuclear@4
|
152 sgl_log("failed to create window\n");
|
nuclear@4
|
153 glXDestroyContext(dpy, ctx);
|
nuclear@4
|
154 XFree(vis);
|
nuclear@4
|
155 free(wnode);
|
nuclear@4
|
156 return -1;
|
nuclear@4
|
157 }
|
nuclear@4
|
158 XFree(vis);
|
nuclear@4
|
159
|
nuclear@4
|
160 evmask = StructureNotifyMask | VisibilityChangeMask;
|
nuclear@4
|
161 XSelectInput(dpy, win, evmask);
|
nuclear@4
|
162
|
nuclear@4
|
163 XSetWMProtocols(dpy, win, &xa_wm_del_win, 1);
|
nuclear@4
|
164
|
nuclear@4
|
165 set_title("OpenGL/X11");
|
nuclear@4
|
166
|
nuclear@4
|
167 chint.res_name = chint.res_class = "simplygl";
|
nuclear@4
|
168 XSetClassHint(dpy, win, &chint);
|
nuclear@4
|
169
|
nuclear@4
|
170 XMapWindow(dpy, win);
|
nuclear@4
|
171
|
nuclear@4
|
172 wnode->win = win;
|
nuclear@4
|
173 wnode->ctx = ctx;
|
nuclear@4
|
174 wnode->evmask = evmask;
|
nuclear@4
|
175 wnode->next = winlist;
|
nuclear@4
|
176 winlist->next = wnode;
|
nuclear@4
|
177
|
nuclear@4
|
178 if(!active_win) {
|
nuclear@4
|
179 set_active(win);
|
nuclear@4
|
180 }
|
nuclear@4
|
181 return win;
|
nuclear@4
|
182 }
|
nuclear@4
|
183
|
nuclear@4
|
184 static void fill_attr(int *attr, unsigned int flags)
|
nuclear@4
|
185 {
|
nuclear@4
|
186 int i = 0;
|
nuclear@4
|
187
|
nuclear@4
|
188 attr[i++] = GLX_RGBA;
|
nuclear@4
|
189 attr[i++] = GLX_RED_SIZE; attr[i++] = 1;
|
nuclear@4
|
190 attr[i++] = GLX_GREEN_SIZE; attr[i++] = 1;
|
nuclear@4
|
191 attr[i++] = GLX_BLUE_SIZE; attr[i++] = 1;
|
nuclear@4
|
192
|
nuclear@4
|
193 if(flags & SGL_DOUBLE) {
|
nuclear@4
|
194 attr[i++] = GLX_DOUBLEBUFFER;
|
nuclear@4
|
195 }
|
nuclear@4
|
196 if(flags & SGL_DEPTH) {
|
nuclear@4
|
197 attr[i++] = GLX_DEPTH_SIZE;
|
nuclear@4
|
198 attr[i++] = 1;
|
nuclear@4
|
199 }
|
nuclear@4
|
200 if(flags & SGL_STENCIL) {
|
nuclear@4
|
201 attr[i++] = GLX_STENCIL_SIZE;
|
nuclear@4
|
202 attr[i++] = 1;
|
nuclear@4
|
203 }
|
nuclear@4
|
204 if(flags & SGL_STEREO) {
|
nuclear@4
|
205 attr[i++] = GLX_STEREO;
|
nuclear@4
|
206 }
|
nuclear@4
|
207 #if defined(GLX_VERSION_1_4) || defined(GLX_ARB_multisample)
|
nuclear@4
|
208 if(flags & SGL_MULTISAMPLE) {
|
nuclear@4
|
209 attr[i++] = GLX_SAMPLE_BUFFERS_ARB;
|
nuclear@4
|
210 attr[i++] = 1;
|
nuclear@4
|
211 attr[i++] = GLX_SAMPLES_ARB;
|
nuclear@4
|
212 attr[i++] = 1;
|
nuclear@4
|
213 }
|
nuclear@4
|
214 #endif /* defined GLX_VERSION_1_4 || GLX_ARB_multisample */
|
nuclear@4
|
215 attr[i] = None;
|
nuclear@4
|
216 }
|
nuclear@4
|
217
|
nuclear@4
|
218 static void print_visual_info(XVisualInfo *vis)
|
nuclear@4
|
219 {
|
nuclear@4
|
220 int rbits, gbits, bbits, zbits, sbits, stereo, aa, samples;
|
nuclear@4
|
221
|
nuclear@4
|
222 glXGetConfig(dpy, vis, GLX_RED_SIZE, &rbits);
|
nuclear@4
|
223 glXGetConfig(dpy, vis, GLX_GREEN_SIZE, &gbits);
|
nuclear@4
|
224 glXGetConfig(dpy, vis, GLX_BLUE_SIZE, &bbits);
|
nuclear@4
|
225 glXGetConfig(dpy, vis, GLX_DEPTH_SIZE, &zbits);
|
nuclear@4
|
226 glXGetConfig(dpy, vis, GLX_STENCIL_SIZE, &sbits);
|
nuclear@4
|
227 glXGetConfig(dpy, vis, GLX_STEREO, &stereo);
|
nuclear@4
|
228 #if defined(GLX_VERSION_1_4) || defined(GLX_ARB_multisample)
|
nuclear@4
|
229 glXGetConfig(dpy, vis, GLX_SAMPLE_BUFFERS_ARB, &aa);
|
nuclear@4
|
230 if(aa) {
|
nuclear@4
|
231 glXGetConfig(dpy, vis, GLX_SAMPLES_ARB, &samples);
|
nuclear@4
|
232 } else {
|
nuclear@4
|
233 samples = 1;
|
nuclear@4
|
234 }
|
nuclear@4
|
235 #endif /* defined GLX_VERSION_1_4 || GLX_ARB_multisample */
|
nuclear@4
|
236
|
nuclear@4
|
237 sgl_log("got visual: %d%d%d d:%d s:%d", rbits, gbits, bbits, zbits, sbits);
|
nuclear@4
|
238 sgl_log(" %s", stereo ? "stereo" : "mono");
|
nuclear@4
|
239 sgl_log(" samples/pixel: %d\n", samples);
|
nuclear@3
|
240 }
|
nuclear@3
|
241
|
nuclear@3
|
242 static void close_window(int id)
|
nuclear@3
|
243 {
|
nuclear@3
|
244 struct window dummy, *win, *prev;
|
nuclear@3
|
245 dummy.next = winlist;
|
nuclear@3
|
246
|
nuclear@3
|
247 prev = &dummy;
|
nuclear@3
|
248 win = prev->next;
|
nuclear@3
|
249
|
nuclear@3
|
250 while(win) {
|
nuclear@3
|
251 if(win->win == id) {
|
nuclear@3
|
252 glXDestroyContext(dpy, win->ctx);
|
nuclear@4
|
253 XDestroyWindow(dpy, win->win);
|
nuclear@3
|
254 prev->next = win->next;
|
nuclear@3
|
255 free(win);
|
nuclear@3
|
256 return;
|
nuclear@3
|
257 }
|
nuclear@3
|
258 win = win->next;
|
nuclear@3
|
259 }
|
nuclear@3
|
260 }
|
nuclear@4
|
261
|
nuclear@4
|
262 static int set_active(int id)
|
nuclear@4
|
263 {
|
nuclear@4
|
264 struct window *win = winlist;
|
nuclear@4
|
265
|
nuclear@4
|
266 while(win) {
|
nuclear@4
|
267 if(win->win == id) {
|
nuclear@4
|
268 if(glXMakeCurrent(dpy, win->win, win->ctx) == False) {
|
nuclear@4
|
269 sgl_log("failed to activate window %d\n", id);
|
nuclear@4
|
270 return -1;
|
nuclear@4
|
271 }
|
nuclear@4
|
272 active_win = win;
|
nuclear@4
|
273 return 0;
|
nuclear@4
|
274 }
|
nuclear@4
|
275 win = win->next;
|
nuclear@4
|
276 }
|
nuclear@4
|
277
|
nuclear@4
|
278 sgl_log("no such window: %d\n", id);
|
nuclear@4
|
279 return -1;
|
nuclear@4
|
280 }
|
nuclear@4
|
281
|
nuclear@4
|
282 static int set_title(const char *str)
|
nuclear@4
|
283 {
|
nuclear@4
|
284 XTextProperty wm_name;
|
nuclear@4
|
285
|
nuclear@4
|
286 if(!str || !active_win) {
|
nuclear@4
|
287 return -1;
|
nuclear@4
|
288 }
|
nuclear@4
|
289 XStringListToTextProperty((char**)&str, 1, &wm_name);
|
nuclear@4
|
290 XSetWMName(dpy, active_win->win, &wm_name);
|
nuclear@4
|
291 XSetWMIconName(dpy, active_win->win, &wm_name);
|
nuclear@4
|
292 XFree(wm_name.value);
|
nuclear@4
|
293 return 0;
|
nuclear@4
|
294 }
|
nuclear@4
|
295
|
nuclear@4
|
296 static void redisplay(void)
|
nuclear@4
|
297 {
|
nuclear@4
|
298 active_win->redisp_pending = 1;
|
nuclear@4
|
299 }
|
nuclear@4
|
300
|
nuclear@4
|
301 static void swap_buffers(void)
|
nuclear@4
|
302 {
|
nuclear@4
|
303 glXSwapBuffers(dpy, active_win->ctx);
|
nuclear@4
|
304 }
|
nuclear@4
|
305
|
nuclear@4
|
306 static void set_bits(long *mask, long bits)
|
nuclear@4
|
307 {
|
nuclear@4
|
308 *mask |= bits;
|
nuclear@4
|
309 }
|
nuclear@4
|
310
|
nuclear@4
|
311 static void clear_bits(long *mask, long bits)
|
nuclear@4
|
312 {
|
nuclear@4
|
313 *mask &= ~bits;
|
nuclear@4
|
314 }
|
nuclear@4
|
315
|
nuclear@4
|
316 static void set_event(int idx, int enable)
|
nuclear@4
|
317 {
|
nuclear@4
|
318 void (*op)(long*, long);
|
nuclear@4
|
319 op = enable ? set_bits : clear_bits;
|
nuclear@4
|
320
|
nuclear@4
|
321 switch(idx) {
|
nuclear@4
|
322 case SGL_DISPLAY:
|
nuclear@4
|
323 op(&active_win->evmask, ExposureMask);
|
nuclear@4
|
324 break;
|
nuclear@4
|
325
|
nuclear@4
|
326 case SGL_KEYBOARD:
|
nuclear@4
|
327 op(&active_win->evmask, KeyPressMask | KeyReleaseMask);
|
nuclear@4
|
328 break;
|
nuclear@4
|
329
|
nuclear@4
|
330 case SGL_MOUSE:
|
nuclear@4
|
331 op(&active_win->evmask, ButtonPressMask | ButtonReleaseMask);
|
nuclear@4
|
332 break;
|
nuclear@4
|
333
|
nuclear@4
|
334 case SGL_MOTION:
|
nuclear@4
|
335 op(&active_win->evmask, ButtonMotionMask);
|
nuclear@4
|
336 break;
|
nuclear@4
|
337
|
nuclear@4
|
338 case SGL_PASSIVE:
|
nuclear@4
|
339 op(&active_win->evmask, PointerMotionMask);
|
nuclear@4
|
340 break;
|
nuclear@4
|
341
|
nuclear@4
|
342 default:
|
nuclear@4
|
343 return;
|
nuclear@4
|
344 }
|
nuclear@4
|
345
|
nuclear@4
|
346 XSelectInput(dpy, active_win->win, active_win->evmask);
|
nuclear@4
|
347 }
|
nuclear@4
|
348
|
nuclear@4
|
349 static int process_events(void)
|
nuclear@4
|
350 {
|
nuclear@4
|
351 XEvent xev;
|
nuclear@4
|
352 void (*func)();
|
nuclear@4
|
353 int state;
|
nuclear@4
|
354
|
nuclear@4
|
355 while(XPending(dpy)) {
|
nuclear@4
|
356 XNextEvent(dpy, &xev);
|
nuclear@4
|
357
|
nuclear@4
|
358 switch(xev.type) {
|
nuclear@4
|
359 case Expose:
|
nuclear@4
|
360 if(xev.xexpose.count == 0) {
|
nuclear@4
|
361 if((func = sgl_get_callback(SGL_DISPLAY))) {
|
nuclear@4
|
362 func();
|
nuclear@4
|
363 active_win->redisp_pending = 0;
|
nuclear@4
|
364 }
|
nuclear@4
|
365 }
|
nuclear@4
|
366 break;
|
nuclear@4
|
367
|
nuclear@4
|
368 case MotionNotify:
|
nuclear@4
|
369 if(xev.xmotion.state) {
|
nuclear@4
|
370 func = sgl_get_callback(SGL_MOTION);
|
nuclear@4
|
371 } else {
|
nuclear@4
|
372 func = sgl_get_callback(SGL_PASSIVE);
|
nuclear@4
|
373 }
|
nuclear@4
|
374 if(func) {
|
nuclear@4
|
375 func(xev.xmotion.x, xev.xmotion.y);
|
nuclear@4
|
376 }
|
nuclear@4
|
377 break;
|
nuclear@4
|
378
|
nuclear@4
|
379 case ButtonPress:
|
nuclear@4
|
380 if(1) {
|
nuclear@4
|
381 state = 1;
|
nuclear@4
|
382 } else {
|
nuclear@4
|
383 case ButtonRelease:
|
nuclear@4
|
384 state = 0;
|
nuclear@4
|
385 }
|
nuclear@4
|
386 if((func = sgl_get_callback(SGL_MOUSE))) {
|
nuclear@4
|
387 int bn = xev.xbutton.button - 1;
|
nuclear@4
|
388 func(bn, state, xev.xbutton.x, xev.xbutton.y);
|
nuclear@4
|
389 }
|
nuclear@4
|
390 break;
|
nuclear@4
|
391
|
nuclear@4
|
392 case KeyPress:
|
nuclear@4
|
393 if(1) {
|
nuclear@4
|
394 state = 1;
|
nuclear@4
|
395 } else {
|
nuclear@4
|
396 case KeyRelease:
|
nuclear@4
|
397 state = 0;
|
nuclear@4
|
398 }
|
nuclear@4
|
399 if((func = sgl_get_callback(SGL_KEYBOARD))) {
|
nuclear@4
|
400 KeySym sym = XLookupKeysym(xev.xkey, 0);
|
nuclear@4
|
401 func(sym, state);
|
nuclear@4
|
402 /* XXX */
|
nuclear@4
|
403 }
|
nuclear@4
|
404 break;
|
nuclear@4
|
405
|
nuclear@4
|
406 case ConfigureNotify:
|
nuclear@4
|
407 if((func = sgl_get_callback(SGL_RESHAPE))) {
|
nuclear@4
|
408 func(xev.xconfigure.width, xev.xconfigure.height);
|
nuclear@4
|
409 }
|
nuclear@4
|
410 break;
|
nuclear@4
|
411 }
|
nuclear@4
|
412
|
nuclear@4
|
413 case ClientMessage:
|
nuclear@4
|
414 if(xev.xclient.message_type == xa_wm_prot) {
|
nuclear@4
|
415 if(xev.xclient.data.l[0] == xa_wm_del_win) {
|
nuclear@4
|
416 close_window(active_win->win);
|
nuclear@4
|
417 if(!active_win) {
|
nuclear@4
|
418 return 1;
|
nuclear@4
|
419 }
|
nuclear@4
|
420 }
|
nuclear@4
|
421 }
|
nuclear@4
|
422 break;
|
nuclear@4
|
423 }
|
nuclear@4
|
424 return 0;
|
nuclear@4
|
425 }
|