scrolling XOR benchmark
[retrobench] / src / x11 / main.c
1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <string.h>
4 #include <sys/time.h>
5 #include <sys/ipc.h>
6 #include <sys/shm.h>
7 #include <X11/Xlib.h>
8 #include <X11/Xutil.h>
9 #include <X11/keysym.h>
10 #include <X11/extensions/XShm.h>
11 #include "rbench.h"
12
13 enum { QUIT = 1, REDRAW = 2 };
14
15 static Window create_win(int width, int height, int bpp);
16 static void handle_event(XEvent *ev);
17 static int translate_keysym(KeySym sym);
18 static int parse_args(int argc, char **argv);
19
20 static int win_width, win_height;
21 static int mapped;
22 static unsigned int pending;
23 static Display *dpy;
24 static Window win, root;
25 static GC gc;
26 static Visual *vis;
27 static Atom xa_wm_proto, xa_wm_delwin;
28
29 static XImage *ximg;
30 static XShmSegmentInfo shm;
31 static int wait_putimg;
32 static int xshm_ev_completion;
33
34 int main(int argc, char **argv)
35 {
36         int num_frames;
37         XEvent ev;
38         struct timeval tv, tv0;
39
40         shm.shmid = -1;
41         shm.shmaddr = (void*)-1;
42
43         read_config("rbench.cfg");
44
45         if(parse_args(argc, argv) == -1) {
46                 return 1;
47         }
48
49         if(!(dpy = XOpenDisplay(0))) {
50                 fprintf(stderr, "failed to connect to the X server\n");
51                 return 1;
52         }
53         root = DefaultRootWindow(dpy);
54         xa_wm_proto = XInternAtom(dpy, "WM_PROTOCOLS", 0);
55         xa_wm_delwin = XInternAtom(dpy, "WM_DELETE_WINDOW", 0);
56
57         if(!XShmQueryExtension(dpy)) {
58                 fprintf(stderr, "X shared memory extension is not available\n");
59                 XCloseDisplay(dpy);
60                 return 1;
61         }
62         xshm_ev_completion = XShmGetEventBase(dpy) + ShmCompletion;
63
64         if(!(win = create_win(opt.width, opt.height, opt.bpp))) {
65                 return 1;
66         }
67         gc = XCreateGC(dpy, win, 0, 0);
68
69         if(!(ximg = XShmCreateImage(dpy, vis, opt.bpp, ZPixmap, 0, &shm, opt.width, opt.height))) {
70                 fprintf(stderr, "failed to create shared memory image\n");
71                 goto end;
72         }
73         if((shm.shmid = shmget(IPC_PRIVATE, ximg->bytes_per_line * ximg->height, IPC_CREAT | 0777)) == -1) {
74                 fprintf(stderr, "failed to create shared memory block\n");
75                 goto end;
76         }
77         if((shm.shmaddr = ximg->data = shmat(shm.shmid, 0, 0)) == (void*)-1) {
78                 fprintf(stderr, "failed to attach shared memory block\n");
79                 goto end;
80         }
81         shm.readOnly = True;
82         if(!XShmAttach(dpy, &shm)) {
83                 fprintf(stderr, "XShmAttach failed");
84                 goto end;
85         }
86
87         fb_width = opt.width;
88         fb_height = opt.height;
89         fb_bpp = opt.bpp >= 24 ? 32 : opt.bpp;
90         framebuf = ximg->data;
91         fb_pitch = ximg->bytes_per_line;
92
93         if(init() == -1) {
94                 goto end;
95         }
96
97         gettimeofday(&tv0, 0);
98         num_frames = 0;
99
100         for(;;) {
101                 if(mapped) {/* && !wait_putimg) { */
102                         while(XPending(dpy)) {
103                                 XNextEvent(dpy, &ev);
104                                 handle_event(&ev);
105                                 if(pending & QUIT) goto end;
106                         }
107
108                         gettimeofday(&tv, 0);
109                         time_msec = (tv.tv_sec - tv0.tv_sec) * 1000 + (tv.tv_usec - tv0.tv_usec) / 1000;
110                         num_frames++;
111
112                         redraw();
113
114                         XShmPutImage(dpy, win, gc, ximg, 0, 0, 0, 0, ximg->width, ximg->height, False);
115                         /*wait_putimg = 1;*/
116                 } else {
117                         XNextEvent(dpy, &ev);
118                         handle_event(&ev);
119                         if(pending & QUIT) goto end;
120                 }
121         }
122
123 end:
124         cleanup();
125         if(ximg) {
126                 XShmDetach(dpy, &shm);
127                 XDestroyImage(ximg);
128                 if(shm.shmaddr != (void*)-1) {
129                         shmdt(shm.shmaddr);
130                 }
131                 if(shm.shmid != -1) {
132                         shmctl(shm.shmid, IPC_RMID, 0);
133                 }
134         }
135         if(win) {
136                 XFreeGC(dpy, gc);
137                 XDestroyWindow(dpy, win);
138         }
139         XCloseDisplay(dpy);
140
141         if(num_frames) {
142                 printf("avg framerate: %.1f fps\n", (10000 * num_frames / time_msec) / 10.0f);
143         }
144         return 0;
145 }
146
147 static Window create_win(int width, int height, int bpp)
148 {
149         int scr, num_vis;
150         Window win;
151         XVisualInfo *vinf, vtmpl;
152         unsigned int vinf_mask;
153         XSetWindowAttributes xattr;
154         XTextProperty txname;
155         Colormap cmap;
156         const char *name = "retrobench X11";
157
158         scr = DefaultScreen(dpy);
159
160         vtmpl.screen = scr;
161         vtmpl.depth = bpp;
162         vtmpl.class = bpp <= 8 ? PseudoColor : TrueColor;
163         vinf_mask = VisualScreenMask | VisualDepthMask | VisualClassMask;
164         if(!(vinf = XGetVisualInfo(dpy, vinf_mask, &vtmpl, &num_vis))) {
165                 fprintf(stderr, "failed to find appropriate visual for %d bpp\n", bpp);
166                 return 0;
167         }
168         vis = vinf->visual;
169
170         if(!(cmap = XCreateColormap(dpy, root, vis, bpp <= 8 ? AllocAll : AllocNone))) {
171                 fprintf(stderr, "failed to allocate colormap\n");
172                 return 0;
173         }
174
175         xattr.background_pixel = BlackPixel(dpy, scr);
176         xattr.colormap = cmap;
177         win = XCreateWindow(dpy, root, 0, 0, width, height, 0, vinf->depth,
178                         InputOutput, vis, CWColormap | CWBackPixel, &xattr);
179         if(!win) return 0;
180
181         XSelectInput(dpy, win, StructureNotifyMask | ExposureMask | KeyPressMask |
182                         KeyReleaseMask);
183
184         XStringListToTextProperty((char**)&name, 1, &txname);
185         XSetWMName(dpy, win, &txname);
186         XSetWMIconName(dpy, win, &txname);
187         XFree(txname.value);
188
189         XSetWMProtocols(dpy, win, &xa_wm_delwin, 1);
190
191         XMapWindow(dpy, win);
192         return win;
193 }
194
195 static void handle_event(XEvent *ev)
196 {
197         int key;
198         KeySym sym;
199
200         switch(ev->type) {
201         case MapNotify:
202                 mapped = 1;
203                 break;
204
205         case UnmapNotify:
206                 mapped = 0;
207                 break;
208
209         case Expose:
210                 pending |= REDRAW;
211                 break;
212
213         case ConfigureNotify:
214                 if(ev->xconfigure.width != win_width || ev->xconfigure.height != win_height) {
215                         win_width = ev->xconfigure.width;
216                         win_height = ev->xconfigure.height;
217                         /* TODO */
218                 }
219                 break;
220
221         case KeyPress:
222         case KeyRelease:
223                 if((sym = XKeycodeToKeysym(dpy, ev->xkey.keycode, 0))) {
224                         if(sym == XK_Escape) {
225                                 pending |= QUIT;
226                                 break;
227                         }
228                         if((key = translate_keysym(sym))) {
229                                 key_event(key, ev->xkey.type == KeyPress);
230                         }
231                 }
232                 break;
233
234         case ClientMessage:
235                 if(ev->xclient.message_type == xa_wm_proto) {
236                         if(ev->xclient.data.l[0] == xa_wm_delwin) {
237                                 pending |= QUIT;
238                         }
239                 }
240                 break;
241
242         default:
243                 if(ev->type == xshm_ev_completion) {
244                         wait_putimg = 0;
245                 }
246                 break;
247         }
248 }
249
250 static int translate_keysym(KeySym sym)
251 {
252         if(sym < 128) return sym;
253
254         switch(sym) {
255         case XK_Escape:
256                 return 27;
257
258         default:
259                 break;
260         }
261         return 0;
262 }
263
264 static const char *usage_str =
265         "Usage: %s [options]\n"
266         "Options:\n"
267         "  -s <WxH>: resolution\n"
268         "  -b <bpp>: color depth\n"
269         "  -h: print usage and exit\n";
270
271 static int parse_args(int argc, char **argv)
272 {
273         int i;
274
275         for(i=1; i<argc; i++) {
276                 if(argv[i][0] == '-') {
277                         if(argv[i][2]) {
278                                 goto inval_arg;
279                         }
280                         switch(argv[i][1]) {
281                         case 's':
282                                 if(!argv[++i] || sscanf(argv[i], "%dx%d", &opt.width, &opt.height) != 2) {
283                                         fprintf(stderr, "-s must be followed by WxH\n");
284                                         return -1;
285                                 }
286                                 break;
287
288                         case 'b':
289                                 if(!argv[++i] || !(opt.bpp = atoi(argv[i]))) {
290                                         fprintf(stderr, "-b must be followed by the color depth\n");
291                                         return -1;
292                                 }
293                                 break;
294
295                         case 'h':
296                                 printf(usage_str, argv[0]);
297                                 exit(0);
298
299                         default:
300                                 goto inval_arg;
301                         }
302                 } else {
303 inval_arg:      fprintf(stderr, "invalid argument: %s\n", argv[i]);
304                         return -1;
305                 }
306         }
307         return 0;
308 }