rev |
line source |
nuclear@0
|
1 #include <stdio.h>
|
nuclear@0
|
2 #include <stdlib.h>
|
nuclear@0
|
3 #include <unistd.h>
|
nuclear@0
|
4 #include <sys/select.h>
|
nuclear@0
|
5 #include <X11/Xlib.h>
|
nuclear@1
|
6 #include <X11/Xutil.h>
|
nuclear@0
|
7 #include <X11/Xatom.h>
|
nuclear@0
|
8 #include <GLES2/gl2.h>
|
nuclear@0
|
9 #include <EGL/egl.h>
|
nuclear@0
|
10 #include "spycam.h"
|
nuclear@0
|
11
|
nuclear@0
|
12 static void cleanup();
|
nuclear@0
|
13 static bool create_glwin(int xsz, int ysz);
|
nuclear@0
|
14 static bool handle_event(XEvent *ev);
|
nuclear@0
|
15 static void set_window_title(const char *title);
|
nuclear@0
|
16
|
nuclear@0
|
17 static int win_width, win_height;
|
nuclear@0
|
18 static bool quit, redraw_pending;
|
nuclear@0
|
19 static Display *dpy;
|
nuclear@0
|
20 static Window win;
|
nuclear@0
|
21 static EGLDisplay egl_dpy;
|
nuclear@0
|
22 static EGLContext egl_ctx;
|
nuclear@0
|
23 static EGLSurface egl_win;
|
nuclear@0
|
24 static Atom xa_wm_proto, xa_del_window;
|
nuclear@0
|
25
|
nuclear@0
|
26 int main(int argc, char **argv)
|
nuclear@0
|
27 {
|
nuclear@0
|
28 if(!(dpy = XOpenDisplay(0))) {
|
nuclear@0
|
29 fprintf(stderr, "failed to connect to the X server.\n");
|
nuclear@0
|
30 return 1;
|
nuclear@0
|
31 }
|
nuclear@0
|
32 xa_wm_proto = XInternAtom(dpy, "WM_PROTOCOLS", False);
|
nuclear@0
|
33 xa_del_window = XInternAtom(dpy, "WM_DELETE_WINDOW", False);
|
nuclear@0
|
34
|
nuclear@0
|
35 egl_dpy = eglGetDisplay(dpy);
|
nuclear@0
|
36 int egl_ver_major, egl_ver_minor;
|
nuclear@0
|
37 if(!eglInitialize(egl_dpy, &egl_ver_major, &egl_ver_minor)) {
|
nuclear@0
|
38 fprintf(stderr, "failed to initialize EGL\n");
|
nuclear@0
|
39 return 1;
|
nuclear@0
|
40 }
|
nuclear@0
|
41 printf("initialized EGL %d.%d\n", egl_ver_major, egl_ver_minor);
|
nuclear@0
|
42
|
nuclear@0
|
43 if(!create_glwin(800, 600)) {
|
nuclear@0
|
44 cleanup();
|
nuclear@0
|
45 return 1;
|
nuclear@0
|
46 }
|
nuclear@0
|
47
|
nuclear@0
|
48 if(!app_init()) {
|
nuclear@0
|
49 cleanup();
|
nuclear@0
|
50 return 1;
|
nuclear@0
|
51 }
|
nuclear@0
|
52
|
nuclear@0
|
53 for(;;) {
|
nuclear@0
|
54 while(XPending(dpy) || !redraw_pending) {
|
nuclear@0
|
55 XEvent ev;
|
nuclear@0
|
56 XNextEvent(dpy, &ev);
|
nuclear@0
|
57 if(!handle_event(&ev) || quit) {
|
nuclear@0
|
58 goto break_main_loop;
|
nuclear@0
|
59 }
|
nuclear@0
|
60 }
|
nuclear@0
|
61
|
nuclear@0
|
62 if(redraw_pending) {
|
nuclear@0
|
63 app_draw();
|
nuclear@0
|
64 eglSwapBuffers(egl_dpy, egl_win);
|
nuclear@0
|
65 }
|
nuclear@0
|
66 }
|
nuclear@0
|
67 break_main_loop:
|
nuclear@0
|
68
|
nuclear@0
|
69 cleanup();
|
nuclear@0
|
70 return 0;
|
nuclear@0
|
71 }
|
nuclear@0
|
72
|
nuclear@0
|
73 void app_quit()
|
nuclear@0
|
74 {
|
nuclear@0
|
75 quit = true;
|
nuclear@0
|
76 }
|
nuclear@0
|
77
|
nuclear@0
|
78 void app_redisplay()
|
nuclear@0
|
79 {
|
nuclear@0
|
80 redraw_pending = true;
|
nuclear@0
|
81 }
|
nuclear@0
|
82
|
nuclear@0
|
83 static void cleanup()
|
nuclear@0
|
84 {
|
nuclear@0
|
85 if(egl_dpy) {
|
nuclear@0
|
86 eglMakeCurrent(egl_dpy, 0, 0, 0);
|
nuclear@0
|
87 if(egl_win)
|
nuclear@0
|
88 eglDestroySurface(egl_dpy, egl_win);
|
nuclear@0
|
89 if(egl_ctx)
|
nuclear@0
|
90 eglDestroyContext(egl_dpy, egl_ctx);
|
nuclear@0
|
91 eglTerminate(egl_dpy);
|
nuclear@0
|
92 }
|
nuclear@0
|
93 if(dpy) {
|
nuclear@0
|
94 if(win)
|
nuclear@0
|
95 XDestroyWindow(dpy, win);
|
nuclear@0
|
96 XCloseDisplay(dpy);
|
nuclear@0
|
97 }
|
nuclear@0
|
98 }
|
nuclear@0
|
99
|
nuclear@0
|
100 static bool create_glwin(int xsz, int ysz)
|
nuclear@0
|
101 {
|
nuclear@0
|
102 static const int egl_attr[] = {
|
nuclear@0
|
103 EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
|
nuclear@0
|
104 EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
|
nuclear@0
|
105 EGL_RED_SIZE, 5,
|
nuclear@0
|
106 EGL_GREEN_SIZE, 5,
|
nuclear@0
|
107 EGL_BLUE_SIZE, 5,
|
nuclear@0
|
108 EGL_DEPTH_SIZE, 16,
|
nuclear@0
|
109 EGL_NONE
|
nuclear@0
|
110 };
|
nuclear@0
|
111
|
nuclear@0
|
112 int scr = DefaultScreen(dpy);
|
nuclear@0
|
113 Window root_win = RootWindow(dpy, scr);
|
nuclear@0
|
114
|
nuclear@0
|
115 EGLConfig cfg;
|
nuclear@0
|
116 int num_cfg;
|
nuclear@0
|
117 if(!eglChooseConfig(egl_dpy, egl_attr, &cfg, 1, &num_cfg)) {
|
nuclear@0
|
118 fprintf(stderr, "failed to find matching EGL visual\n");
|
nuclear@0
|
119 return false;
|
nuclear@0
|
120 }
|
nuclear@0
|
121 int rsize, gsize, bsize, zsize, ssize, vis_id;
|
nuclear@0
|
122 eglGetConfigAttrib(egl_dpy, cfg, EGL_RED_SIZE, &rsize);
|
nuclear@0
|
123 eglGetConfigAttrib(egl_dpy, cfg, EGL_GREEN_SIZE, &gsize);
|
nuclear@0
|
124 eglGetConfigAttrib(egl_dpy, cfg, EGL_BLUE_SIZE, &bsize);
|
nuclear@0
|
125 eglGetConfigAttrib(egl_dpy, cfg, EGL_DEPTH_SIZE, &zsize);
|
nuclear@0
|
126 eglGetConfigAttrib(egl_dpy, cfg, EGL_STENCIL_SIZE, &ssize);
|
nuclear@0
|
127 eglGetConfigAttrib(egl_dpy, cfg, EGL_NATIVE_VISUAL_ID, &vis_id);
|
nuclear@0
|
128 printf("got visual %d: %d bpp (%d%d%d), %d zbuffer, %d stencil\n", vis_id, rsize + gsize + bsize,
|
nuclear@0
|
129 rsize, gsize, bsize, zsize, ssize);
|
nuclear@0
|
130
|
nuclear@0
|
131 int num_vis;
|
nuclear@0
|
132 XVisualInfo *vis_info, vis_template;
|
nuclear@0
|
133 vis_template.visualid = vis_id;
|
nuclear@0
|
134 if(!(vis_info = XGetVisualInfo(dpy, VisualIDMask, &vis_template, &num_vis))) {
|
nuclear@0
|
135 fprintf(stderr, "visual id %d is invalid (?!)\n", vis_id);
|
nuclear@0
|
136 return false;
|
nuclear@0
|
137 }
|
nuclear@0
|
138
|
nuclear@0
|
139 XSetWindowAttributes xattr;
|
nuclear@0
|
140 xattr.border_pixel = xattr.backing_pixel = BlackPixel(dpy, scr);
|
nuclear@0
|
141 xattr.colormap = XCreateColormap(dpy, root_win, vis_info->visual, AllocNone);
|
nuclear@0
|
142 unsigned int xattr_mask = CWColormap | CWBorderPixel | CWBackPixel;
|
nuclear@0
|
143
|
nuclear@0
|
144 win = XCreateWindow(dpy, root_win, 0, 0, xsz, ysz, 0, vis_info->depth, InputOutput,
|
nuclear@0
|
145 vis_info->visual, xattr_mask, &xattr);
|
nuclear@0
|
146 if(!win) {
|
nuclear@0
|
147 fprintf(stderr, "failed to create window\n");
|
nuclear@0
|
148 XFree(vis_info);
|
nuclear@0
|
149 return false;
|
nuclear@0
|
150 }
|
nuclear@0
|
151 XFree(vis_info);
|
nuclear@0
|
152 XSelectInput(dpy, win, ExposureMask | StructureNotifyMask | KeyPressMask | KeyReleaseMask);
|
nuclear@0
|
153
|
nuclear@0
|
154 XSetWMProtocols(dpy, win, &xa_del_window, 1);
|
nuclear@0
|
155 set_window_title("spycam");
|
nuclear@0
|
156
|
nuclear@0
|
157 eglBindAPI(EGL_OPENGL_ES_API);
|
nuclear@0
|
158
|
nuclear@0
|
159 if(!(egl_win = eglCreateWindowSurface(egl_dpy, cfg, win, 0))) {
|
nuclear@0
|
160 fprintf(stderr, "failed to create EGL window\n");
|
nuclear@0
|
161 XDestroyWindow(dpy, win);
|
nuclear@0
|
162 return false;
|
nuclear@0
|
163 }
|
nuclear@0
|
164
|
nuclear@0
|
165 static const int ctxattr[] = { EGL_CONTEXT_CLIENT_VERSION, 2, EGL_NONE };
|
nuclear@0
|
166 if(!(egl_ctx = eglCreateContext(egl_dpy, cfg, EGL_NO_CONTEXT, ctxattr))) {
|
nuclear@0
|
167 fprintf(stderr, "failed to create EGL context\n");
|
nuclear@0
|
168 eglDestroySurface(egl_dpy, egl_win);
|
nuclear@0
|
169 XDestroyWindow(dpy, win);
|
nuclear@0
|
170 return false;
|
nuclear@0
|
171 }
|
nuclear@0
|
172 eglMakeCurrent(egl_dpy, egl_win, egl_win, egl_ctx);
|
nuclear@0
|
173
|
nuclear@0
|
174 XMapWindow(dpy, win);
|
nuclear@0
|
175
|
nuclear@0
|
176 eglQuerySurface(dpy, egl_win, EGL_WIDTH, &win_width);
|
nuclear@0
|
177 eglQuerySurface(dpy, egl_win, EGL_HEIGHT, &win_height);
|
nuclear@0
|
178 app_reshape(win_width, win_height);
|
nuclear@0
|
179
|
nuclear@0
|
180 return true;
|
nuclear@0
|
181 }
|
nuclear@0
|
182
|
nuclear@0
|
183 static bool handle_event(XEvent *ev)
|
nuclear@0
|
184 {
|
nuclear@0
|
185 static bool mapped;
|
nuclear@0
|
186
|
nuclear@0
|
187 switch(ev->type) {
|
nuclear@0
|
188 case Expose:
|
nuclear@0
|
189 if(mapped) {
|
nuclear@0
|
190 redraw_pending = true;
|
nuclear@0
|
191 }
|
nuclear@0
|
192 break;
|
nuclear@0
|
193
|
nuclear@0
|
194 case MapNotify:
|
nuclear@0
|
195 mapped = true;
|
nuclear@0
|
196 redraw_pending = true;
|
nuclear@0
|
197 break;
|
nuclear@0
|
198
|
nuclear@0
|
199 case UnmapNotify:
|
nuclear@0
|
200 mapped = false;
|
nuclear@0
|
201 redraw_pending = false;
|
nuclear@0
|
202 break;
|
nuclear@0
|
203
|
nuclear@0
|
204 case ConfigureNotify:
|
nuclear@0
|
205 if(win_width != (int)ev->xconfigure.width || win_height != (int)ev->xconfigure.height) {
|
nuclear@0
|
206 win_width = ev->xconfigure.width;
|
nuclear@0
|
207 win_height = ev->xconfigure.height;
|
nuclear@0
|
208 app_reshape(win_width, win_height);
|
nuclear@0
|
209 }
|
nuclear@0
|
210 break;
|
nuclear@0
|
211
|
nuclear@0
|
212 case KeyPress:
|
nuclear@0
|
213 case KeyRelease:
|
nuclear@0
|
214 app_keyboard(XLookupKeysym(&ev->xkey, 0) & 0xff, ev->type == KeyPress);
|
nuclear@0
|
215 break;
|
nuclear@0
|
216
|
nuclear@0
|
217 case ClientMessage:
|
nuclear@0
|
218 if(ev->xclient.message_type == xa_wm_proto &&
|
nuclear@0
|
219 (Atom)ev->xclient.data.l[0] == xa_del_window) {
|
nuclear@0
|
220 return false;
|
nuclear@0
|
221 }
|
nuclear@0
|
222 break;
|
nuclear@0
|
223
|
nuclear@0
|
224 default:
|
nuclear@0
|
225 break;
|
nuclear@0
|
226 }
|
nuclear@0
|
227 return true;
|
nuclear@0
|
228 }
|
nuclear@0
|
229
|
nuclear@0
|
230 static void set_window_title(const char *title)
|
nuclear@0
|
231 {
|
nuclear@0
|
232 XTextProperty text_prop;
|
nuclear@0
|
233 XStringListToTextProperty((char**)&title, 1, &text_prop);
|
nuclear@0
|
234 XSetWMName(dpy, win, &text_prop);
|
nuclear@0
|
235 XSetWMIconName(dpy, win, &text_prop);
|
nuclear@0
|
236 XFree(text_prop.value);
|
nuclear@0
|
237 }
|