capture feature
[censuslogo] / src / main_x11.c
1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <string.h>
4 #include <assert.h>
5 #include <unistd.h>
6 #include <sys/time.h>
7 #include <X11/Xlib.h>
8 #include <GL/glx.h>
9 #include "app.h"
10 #include "vroot.h"
11
12 #define DEF_WIN_WIDTH   1280
13 #define DEF_WIN_HEIGHT  800
14
15 static Window create_xwindow(void);
16 static int init_gl(void);
17 static void destroy_gl(void);
18 static int proc_xevent(XEvent *ev);
19
20 static Display *dpy;
21 static int scr;
22 static Window win, root;
23 static GLXContext ctx;
24 static XVisualInfo *visinf;
25 static Atom xa_wm_proto, xa_wm_delwin;
26
27 static volatile int quit;
28 static int mapped;
29
30 static struct timeval tv, tv0;
31
32 int main(int argc, char **argv)
33 {
34         int i;
35         unsigned int wid;
36         char *endp;
37
38         if((endp = getenv("XSCREENSAVER_WINDOW"))) {
39                 printf("XSCREENSAVER_WINDOW: %s\n", endp);
40         }
41
42         if(!(dpy = XOpenDisplay(0))) {
43                 fprintf(stderr, "failed to open connection to the X server\n");
44                 return 1;
45         }
46         scr = DefaultScreen(dpy);
47         root = RootWindow(dpy, scr);
48
49         xa_wm_proto = XInternAtom(dpy, "WM_PROTOCOLS", False);
50         xa_wm_delwin = XInternAtom(dpy, "WM_DELETE_WINDOW", False);
51
52         for(i=0; i<argc; i++) {
53                 printf("argv[%d]: %s\n", i, argv[i]);
54         }
55
56         for(i=1; i<argc; i++) {
57                 if(strcmp(argv[i], "-root") == 0) {
58                         argv[i] = argv[--argc];
59                         if(!(win = VirtualRootWindowOfScreen(ScreenOfDisplay(dpy, scr)))) {
60                                 fprintf(stderr, "failed to find root window!\n");
61                                 XCloseDisplay(dpy);
62                                 return 1;
63                         }
64                         i--;
65
66                 } else if(strcmp(argv[i], "-window-id") == 0) {
67                         if((wid = strtol(argv[i+1], &endp, 0)) > 0 && endp != argv[i+1]) {
68                                 printf("using window: %x\n", wid);
69                                 win = (Window)wid;
70                                 memmove(argv + i, argv + i + 2, (argc - i - 2) * sizeof *argv);
71                                 argc -= 2;
72                                 i--;
73                         } else {
74                                 fprintf(stderr, "invalid option: -window-id must be followed by a valid window id\n");
75                                 XCloseDisplay(dpy);
76                                 return 1;
77                         }
78                 }
79         }
80
81         if(app_parse_args(argc, argv) == -1) {
82                 XCloseDisplay(dpy);
83                 return 1;
84         }
85
86         if(!win && !(win = create_xwindow())) {
87                 XCloseDisplay(dpy);
88                 return 1;
89         }
90         if(init_gl() == -1) {
91                 return 1;
92         }
93         if(app_init() == -1) {
94                 destroy_gl();
95                 XCloseDisplay(dpy);
96                 return 1;
97         }
98
99         gettimeofday(&tv0, 0);
100
101         while(!quit) {
102                 while(XPending(dpy)) {
103                         XEvent ev;
104                         XNextEvent(dpy, &ev);
105                         if(proc_xevent(&ev) == -1 || quit) {
106                                 goto done;
107                         }
108                 }
109
110                 gettimeofday(&tv, 0);
111                 msec = (tv.tv_sec - tv0.tv_sec) * 1000 + (tv.tv_usec - tv0.tv_usec) / 1000;
112
113                 app_display();
114
115                 glXSwapBuffers(dpy, win);
116                 assert(glGetError() == GL_NO_ERROR);
117         }
118
119 done:
120         destroy_gl();
121         XCloseDisplay(dpy);
122         return 0;
123 }
124
125 void app_quit(void)
126 {
127         exit(0);
128 }
129
130 void app_fullscreen(void)
131 {
132 }
133
134 void app_windowed(void)
135 {
136 }
137
138 static XVisualInfo *choose_visual(void)
139 {
140         int glxattr[] = {
141                 GLX_RGBA, GLX_DOUBLEBUFFER,
142                 GLX_RED_SIZE, 1,
143                 GLX_GREEN_SIZE, 1,
144                 GLX_BLUE_SIZE, 1,
145                 GLX_DEPTH_SIZE, 1,
146                 GLX_SAMPLE_BUFFERS, 1,
147                 GLX_SAMPLES, 8,
148                 None
149         };
150         int *sample_buffers = glxattr + 10;
151         int *num_samples = glxattr + 13;
152         XVisualInfo *res;
153
154         do {
155                 res = glXChooseVisual(dpy, scr, glxattr);
156                 *num_samples >>= 1;
157                 if(*num_samples <= 1) {
158                         *sample_buffers = None;
159                 }
160         } while(!res && *num_samples > 0);
161
162         return res;
163 }
164
165 static XVisualInfo *match_visual(Window win)
166 {
167         int numvi, i;
168         XVisualInfo vitmp, *vi;
169         XWindowAttributes wattr;
170         unsigned int mask = VisualScreenMask | VisualDepthMask | VisualClassMask;
171
172         XGetWindowAttributes(dpy, win, &wattr);
173
174         vitmp.visualid = XVisualIDFromVisual(wattr.visual);
175         vi = XGetVisualInfo(dpy, VisualIDMask, &vitmp, &numvi);
176         vitmp = *vi;
177         vi = XGetVisualInfo(dpy, mask, &vitmp, &numvi);
178
179         printf("found %d matching visuals\n", numvi);
180         for(i=0; i<numvi; i++) {
181                 int nsamp;
182
183                 glXGetConfig(dpy, vi + i, GLX_SAMPLES, &nsamp);
184
185                 printf("%x (msaa: %d)\n", (unsigned int)vi[i].visualid, nsamp);
186         }
187         return 0;
188 }
189
190 static Window create_xwindow(void)
191 {
192         XSetWindowAttributes xattr;
193         long xattr_mask, evmask;
194
195         if(!(visinf = choose_visual())) {
196                 fprintf(stderr, "failed to find appropriate visual\n");
197                 return 0;
198         }
199
200         xattr.colormap = XCreateColormap(dpy, root, visinf->visual, AllocNone);
201         xattr.background_pixel = BlackPixel(dpy, scr);
202         xattr_mask = CWColormap | CWBackPixel;
203
204         if(!(win = XCreateWindow(dpy, root, 0, 0, DEF_WIN_WIDTH, DEF_WIN_HEIGHT, 0,
205                         visinf->depth, InputOutput, visinf->visual, xattr_mask, &xattr))) {
206                 fprintf(stderr, "failed to create X window\n");
207                 XFree(visinf);
208                 visinf = 0;
209                 return 0;
210         }
211
212         evmask = ExposureMask | StructureNotifyMask | KeyPressMask;
213         XSelectInput(dpy, win, evmask);
214
215         Xutf8SetWMProperties(dpy, win, "census", "census", 0, 0, 0, 0, 0);
216         XSetWMProtocols(dpy, win, &xa_wm_delwin, 1);
217
218         XMapRaised(dpy, win);
219         return win;
220 }
221
222 static int init_gl(void)
223 {
224         XWindowAttributes wattr;
225         int rbits, gbits, bbits, zbits, sbits, nsamp;
226
227         if(!win) return -1;
228
229         XGetWindowAttributes(dpy, win, &wattr);
230
231         if(!visinf && !(visinf = choose_visual())) {
232                 fprintf(stderr, "failed to find appropriate visual\n");
233                 return -1;
234         }
235
236         if(!(ctx = glXCreateContext(dpy, visinf, 0, 1))) {
237                 fprintf(stderr, "failed to create OpenGL context\n");
238                 XFree(visinf);
239                 if(win != root) {
240                         XDestroyWindow(dpy, win);
241                 }
242                 return -1;
243         }
244
245         glXGetConfig(dpy, visinf, GLX_RED_SIZE, &rbits);
246         glXGetConfig(dpy, visinf, GLX_GREEN_SIZE, &gbits);
247         glXGetConfig(dpy, visinf, GLX_BLUE_SIZE, &bbits);
248         glXGetConfig(dpy, visinf, GLX_DEPTH_SIZE, &zbits);
249         glXGetConfig(dpy, visinf, GLX_STENCIL_SIZE, &sbits);
250         glXGetConfig(dpy, visinf, GLX_SAMPLES, &nsamp);
251
252         printf("created GLX visual %d bpp (%d%d%d), %dz, %ds, %d samp/pix\n",
253                         visinf->depth, rbits, gbits, bbits, zbits, sbits, nsamp);
254
255         printf("glXMakeCurrent(%p, %x, %p)\n", (void*)dpy, (unsigned int)win, (void*)ctx);
256         glXMakeCurrent(dpy, win, ctx);
257         app_reshape(wattr.width, wattr.height);
258         win_width = wattr.width;
259         win_height = wattr.height;
260         return 0;
261 }
262
263 static void destroy_gl(void)
264 {
265         if(visinf) {
266                 XFree(visinf);
267         }
268         if(ctx) {
269                 glXMakeCurrent(dpy, 0, 0);
270                 glXDestroyContext(dpy, ctx);
271         }
272         if(win && win != root) {
273                 XDestroyWindow(dpy, win);
274         }
275 }
276
277 static int proc_xevent(XEvent *ev)
278 {
279         KeySym sym;
280
281         switch(ev->type) {
282         case MapNotify:
283                 mapped = 1;
284                 break;
285
286         case UnmapNotify:
287                 mapped = 0;
288                 break;
289
290         case ConfigureNotify:
291                 if(ev->xconfigure.width != win_width || ev->xconfigure.height != win_height) {
292                         int w = ev->xconfigure.width;
293                         int h = ev->xconfigure.height;
294                         app_reshape(w, h);
295                         win_width = w;
296                         win_height = h;
297                 }
298                 break;
299
300         case KeyPress:
301         case KeyRelease:
302                 if((sym = XLookupKeysym(&ev->xkey, 0))) {
303                         if(sym == XK_Escape) {
304                                 sym = 27;
305                         }
306                         app_keyboard(sym, ev->type == KeyPress);
307                 }
308                 break;
309
310         case ClientMessage:
311                 if(ev->xclient.message_type == xa_wm_proto) {
312                         if(ev->xclient.data.l[0] == xa_wm_delwin) {
313                                 app_quit();
314                         }
315                 }
316                 break;
317
318         default:
319                 break;
320         }
321
322         return 0;
323 }