initial commit
[retrobench] / src / x11 / main.c
1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <string.h>
4 #include <X11/Xlib.h>
5 #include <X11/Xutil.h>
6 #include "rbench.h"
7
8 enum { QUIT = 1, REDRAW = 2 };
9
10 static Window create_win(int width, int height, int bpp);
11 static void handle_event(XEvent *ev);
12 static int translate_keysym(KeySym sym);
13 static int parse_args(int argc, char **argv);
14
15 static int win_width, win_height;
16 static int mapped;
17 static unsigned int pending;
18 static Display *dpy;
19 static Window win, root;
20 static Atom xa_wm_proto, xa_wm_delwin;
21
22 int main(int argc, char **argv)
23 {
24         XEvent ev;
25
26         read_config("rbench.cfg");
27
28         if(parse_args(argc, argv) == -1) {
29                 return 1;
30         }
31
32         if(!(dpy = XOpenDisplay(0))) {
33                 fprintf(stderr, "failed to connect to the X server\n");
34                 return 1;
35         }
36         root = DefaultRootWindow(dpy);
37         xa_wm_proto = XInternAtom(dpy, "WM_PROTOCOLS", 0);
38         xa_wm_delwin = XInternAtom(dpy, "WM_DELETE_WINDOW", 0);
39
40         if(!(win = create_win(opt.width, opt.height, opt.bpp))) {
41                 return 1;
42         }
43
44         for(;;) {
45                 if(mapped) {
46                         while(XPending(dpy)) {
47                                 XNextEvent(dpy, &ev);
48                                 handle_event(&ev);
49                                 if(pending & QUIT) goto end;
50                         }
51                         redraw();
52                 } else {
53                         XNextEvent(dpy, &ev);
54                         handle_event(&ev);
55                         if(pending & QUIT) goto end;
56                 }
57         }
58
59 end:
60         XDestroyWindow(dpy, win);
61         XCloseDisplay(dpy);
62         return 0;
63 }
64
65 static Window create_win(int width, int height, int bpp)
66 {
67         int scr, num_vis;
68         Window win;
69         XVisualInfo *vis, vinf;
70         unsigned int vinf_mask;
71         XSetWindowAttributes xattr;
72         XTextProperty txname;
73         Colormap cmap;
74         const char *name = "retrobench X11";
75
76         scr = DefaultScreen(dpy);
77
78         vinf.screen = scr;
79         vinf.depth = bpp;
80         vinf.class = bpp <= 8 ? PseudoColor : TrueColor;
81         vinf_mask = VisualScreenMask | VisualDepthMask | VisualClassMask;
82         if(!(vis = XGetVisualInfo(dpy, vinf_mask, &vinf, &num_vis))) {
83                 fprintf(stderr, "failed to find appropriate visual for %d bpp\n", bpp);
84                 return 0;
85         }
86
87         if(!(cmap = XCreateColormap(dpy, root, vis->visual, bpp <= 8 ? AllocAll : AllocNone))) {
88                 fprintf(stderr, "failed to allocate colormap\n");
89                 return 0;
90         }
91
92         xattr.background_pixel = BlackPixel(dpy, scr);
93         xattr.colormap = cmap;
94         win = XCreateWindow(dpy, root, 0, 0, width, height, 0, vis->depth,
95                         InputOutput, vis->visual, CWColormap | CWBackPixel, &xattr);
96         if(!win) return 0;
97
98         XSelectInput(dpy, win, StructureNotifyMask | ExposureMask | KeyPressMask |
99                         KeyReleaseMask);
100
101         XStringListToTextProperty((char**)&name, 1, &txname);
102         XSetWMName(dpy, win, &txname);
103         XSetWMIconName(dpy, win, &txname);
104         XFree(txname.value);
105
106         XSetWMProtocols(dpy, win, &xa_wm_delwin, 1);
107
108         XMapWindow(dpy, win);
109         return win;
110 }
111
112 static void handle_event(XEvent *ev)
113 {
114         int key;
115         KeySym sym;
116
117         switch(ev->type) {
118         case MapNotify:
119                 mapped = 1;
120                 break;
121
122         case UnmapNotify:
123                 mapped = 0;
124                 break;
125
126         case Expose:
127                 pending |= REDRAW;
128                 break;
129
130         case ConfigureNotify:
131                 if(ev->xconfigure.width != win_width || ev->xconfigure.height != win_height) {
132                         win_width = ev->xconfigure.width;
133                         win_height = ev->xconfigure.height;
134                         /* TODO */
135                 }
136                 break;
137
138         case KeyPress:
139         case KeyRelease:
140                 if((sym = XKeycodeToKeysym(dpy, ev->xkey.keycode, 0))) {
141                         if(sym == XK_Escape) {
142                                 pending |= QUIT;
143                                 break;
144                         }
145                         if((key = translate_keysym(sym))) {
146                                 key_event(key, ev->xkey.type == KeyPress);
147                         }
148                 }
149                 break;
150
151         case ClientMessage:
152                 if(ev->xclient.message_type == xa_wm_proto) {
153                         if(ev->xclient.data.l[0] == xa_wm_delwin) {
154                                 pending |= QUIT;
155                         }
156                 }
157                 break;
158
159         default:
160                 break;
161         }
162 }
163
164 static int translate_keysym(KeySym sym)
165 {
166         if(sym < 128) return sym;
167
168         switch(sym) {
169         case XK_Escape:
170                 return 27;
171
172         default:
173                 break;
174         }
175         return 0;
176 }
177
178 static const char *usage_str =
179         "Usage: %s [options]\n"
180         "Options:\n"
181         "  -s <WxH>: resolution\n"
182         "  -b <bpp>: color depth\n"
183         "  -h: print usage and exit\n";
184
185 static int parse_args(int argc, char **argv)
186 {
187         int i;
188
189         for(i=1; i<argc; i++) {
190                 if(argv[i][0] == '-') {
191                         if(argv[i][2]) {
192                                 goto inval_arg;
193                         }
194                         switch(argv[i][1]) {
195                         case 's':
196                                 if(!argv[++i] || sscanf(argv[i], "%dx%d", &opt.width, &opt.height) != 2) {
197                                         fprintf(stderr, "-s must be followed by WxH\n");
198                                         return -1;
199                                 }
200                                 break;
201
202                         case 'b':
203                                 if(!argv[++i] || !(opt.bpp = atoi(argv[i]))) {
204                                         fprintf(stderr, "-b must be followed by the color depth\n");
205                                         return -1;
206                                 }
207                                 break;
208
209                         case 'h':
210                                 printf(usage_str, argv[0]);
211                                 exit(0);
212
213                         default:
214                                 goto inval_arg;
215                         }
216                 } else {
217 inval_arg:      fprintf(stderr, "invalid argument: %s\n", argv[i]);
218                         return -1;
219                 }
220         }
221         return 0;
222 }