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