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