nuclear@0: #include nuclear@0: #include nuclear@0: #include nuclear@0: #include nuclear@0: #include nuclear@0: #include nuclear@0: #include nuclear@0: #include nuclear@0: #include "spycam.h" nuclear@0: nuclear@0: static void cleanup(); nuclear@0: static bool create_glwin(int xsz, int ysz); nuclear@0: static bool handle_event(XEvent *ev); nuclear@0: static void set_window_title(const char *title); nuclear@0: nuclear@0: static int win_width, win_height; nuclear@0: static bool quit, redraw_pending; nuclear@0: static Display *dpy; nuclear@0: static Window win; nuclear@0: static EGLDisplay egl_dpy; nuclear@0: static EGLContext egl_ctx; nuclear@0: static EGLSurface egl_win; nuclear@0: static Atom xa_wm_proto, xa_del_window; nuclear@0: nuclear@0: int main(int argc, char **argv) nuclear@0: { nuclear@0: if(!(dpy = XOpenDisplay(0))) { nuclear@0: fprintf(stderr, "failed to connect to the X server.\n"); nuclear@0: return 1; nuclear@0: } nuclear@0: xa_wm_proto = XInternAtom(dpy, "WM_PROTOCOLS", False); nuclear@0: xa_del_window = XInternAtom(dpy, "WM_DELETE_WINDOW", False); nuclear@0: nuclear@0: egl_dpy = eglGetDisplay(dpy); nuclear@0: int egl_ver_major, egl_ver_minor; nuclear@0: if(!eglInitialize(egl_dpy, &egl_ver_major, &egl_ver_minor)) { nuclear@0: fprintf(stderr, "failed to initialize EGL\n"); nuclear@0: return 1; nuclear@0: } nuclear@0: printf("initialized EGL %d.%d\n", egl_ver_major, egl_ver_minor); nuclear@0: nuclear@0: if(!create_glwin(800, 600)) { nuclear@0: cleanup(); nuclear@0: return 1; nuclear@0: } nuclear@0: nuclear@0: if(!app_init()) { nuclear@0: cleanup(); nuclear@0: return 1; nuclear@0: } nuclear@0: nuclear@0: for(;;) { nuclear@0: while(XPending(dpy) || !redraw_pending) { nuclear@0: XEvent ev; nuclear@0: XNextEvent(dpy, &ev); nuclear@0: if(!handle_event(&ev) || quit) { nuclear@0: goto break_main_loop; nuclear@0: } nuclear@0: } nuclear@0: nuclear@0: if(redraw_pending) { nuclear@0: app_draw(); nuclear@0: eglSwapBuffers(egl_dpy, egl_win); nuclear@0: } nuclear@0: } nuclear@0: break_main_loop: nuclear@0: nuclear@0: cleanup(); nuclear@0: return 0; nuclear@0: } nuclear@0: nuclear@0: void app_quit() nuclear@0: { nuclear@0: quit = true; nuclear@0: } nuclear@0: nuclear@0: void app_redisplay() nuclear@0: { nuclear@0: redraw_pending = true; nuclear@0: } nuclear@0: nuclear@0: static void cleanup() nuclear@0: { nuclear@0: if(egl_dpy) { nuclear@0: eglMakeCurrent(egl_dpy, 0, 0, 0); nuclear@0: if(egl_win) nuclear@0: eglDestroySurface(egl_dpy, egl_win); nuclear@0: if(egl_ctx) nuclear@0: eglDestroyContext(egl_dpy, egl_ctx); nuclear@0: eglTerminate(egl_dpy); nuclear@0: } nuclear@0: if(dpy) { nuclear@0: if(win) nuclear@0: XDestroyWindow(dpy, win); nuclear@0: XCloseDisplay(dpy); nuclear@0: } nuclear@0: } nuclear@0: nuclear@0: static bool create_glwin(int xsz, int ysz) nuclear@0: { nuclear@0: static const int egl_attr[] = { nuclear@0: EGL_SURFACE_TYPE, EGL_WINDOW_BIT, nuclear@0: EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT, nuclear@0: EGL_RED_SIZE, 5, nuclear@0: EGL_GREEN_SIZE, 5, nuclear@0: EGL_BLUE_SIZE, 5, nuclear@0: EGL_DEPTH_SIZE, 16, nuclear@0: EGL_NONE nuclear@0: }; nuclear@0: nuclear@0: int scr = DefaultScreen(dpy); nuclear@0: Window root_win = RootWindow(dpy, scr); nuclear@0: nuclear@0: EGLConfig cfg; nuclear@0: int num_cfg; nuclear@0: if(!eglChooseConfig(egl_dpy, egl_attr, &cfg, 1, &num_cfg)) { nuclear@0: fprintf(stderr, "failed to find matching EGL visual\n"); nuclear@0: return false; nuclear@0: } nuclear@0: int rsize, gsize, bsize, zsize, ssize, vis_id; nuclear@0: eglGetConfigAttrib(egl_dpy, cfg, EGL_RED_SIZE, &rsize); nuclear@0: eglGetConfigAttrib(egl_dpy, cfg, EGL_GREEN_SIZE, &gsize); nuclear@0: eglGetConfigAttrib(egl_dpy, cfg, EGL_BLUE_SIZE, &bsize); nuclear@0: eglGetConfigAttrib(egl_dpy, cfg, EGL_DEPTH_SIZE, &zsize); nuclear@0: eglGetConfigAttrib(egl_dpy, cfg, EGL_STENCIL_SIZE, &ssize); nuclear@0: eglGetConfigAttrib(egl_dpy, cfg, EGL_NATIVE_VISUAL_ID, &vis_id); nuclear@0: printf("got visual %d: %d bpp (%d%d%d), %d zbuffer, %d stencil\n", vis_id, rsize + gsize + bsize, nuclear@0: rsize, gsize, bsize, zsize, ssize); nuclear@0: nuclear@0: int num_vis; nuclear@0: XVisualInfo *vis_info, vis_template; nuclear@0: vis_template.visualid = vis_id; nuclear@0: if(!(vis_info = XGetVisualInfo(dpy, VisualIDMask, &vis_template, &num_vis))) { nuclear@0: fprintf(stderr, "visual id %d is invalid (?!)\n", vis_id); nuclear@0: return false; nuclear@0: } nuclear@0: nuclear@0: XSetWindowAttributes xattr; nuclear@0: xattr.border_pixel = xattr.backing_pixel = BlackPixel(dpy, scr); nuclear@0: xattr.colormap = XCreateColormap(dpy, root_win, vis_info->visual, AllocNone); nuclear@0: unsigned int xattr_mask = CWColormap | CWBorderPixel | CWBackPixel; nuclear@0: nuclear@0: win = XCreateWindow(dpy, root_win, 0, 0, xsz, ysz, 0, vis_info->depth, InputOutput, nuclear@0: vis_info->visual, xattr_mask, &xattr); nuclear@0: if(!win) { nuclear@0: fprintf(stderr, "failed to create window\n"); nuclear@0: XFree(vis_info); nuclear@0: return false; nuclear@0: } nuclear@0: XFree(vis_info); nuclear@0: XSelectInput(dpy, win, ExposureMask | StructureNotifyMask | KeyPressMask | KeyReleaseMask); nuclear@0: nuclear@0: XSetWMProtocols(dpy, win, &xa_del_window, 1); nuclear@0: set_window_title("spycam"); nuclear@0: nuclear@0: eglBindAPI(EGL_OPENGL_ES_API); nuclear@0: nuclear@0: if(!(egl_win = eglCreateWindowSurface(egl_dpy, cfg, win, 0))) { nuclear@0: fprintf(stderr, "failed to create EGL window\n"); nuclear@0: XDestroyWindow(dpy, win); nuclear@0: return false; nuclear@0: } nuclear@0: nuclear@0: static const int ctxattr[] = { EGL_CONTEXT_CLIENT_VERSION, 2, EGL_NONE }; nuclear@0: if(!(egl_ctx = eglCreateContext(egl_dpy, cfg, EGL_NO_CONTEXT, ctxattr))) { nuclear@0: fprintf(stderr, "failed to create EGL context\n"); nuclear@0: eglDestroySurface(egl_dpy, egl_win); nuclear@0: XDestroyWindow(dpy, win); nuclear@0: return false; nuclear@0: } nuclear@0: eglMakeCurrent(egl_dpy, egl_win, egl_win, egl_ctx); nuclear@0: nuclear@0: XMapWindow(dpy, win); nuclear@0: nuclear@0: eglQuerySurface(dpy, egl_win, EGL_WIDTH, &win_width); nuclear@0: eglQuerySurface(dpy, egl_win, EGL_HEIGHT, &win_height); nuclear@0: app_reshape(win_width, win_height); nuclear@0: nuclear@0: return true; nuclear@0: } nuclear@0: nuclear@0: static bool handle_event(XEvent *ev) nuclear@0: { nuclear@0: static bool mapped; nuclear@0: nuclear@0: switch(ev->type) { nuclear@0: case Expose: nuclear@0: if(mapped) { nuclear@0: redraw_pending = true; nuclear@0: } nuclear@0: break; nuclear@0: nuclear@0: case MapNotify: nuclear@0: mapped = true; nuclear@0: redraw_pending = true; nuclear@0: break; nuclear@0: nuclear@0: case UnmapNotify: nuclear@0: mapped = false; nuclear@0: redraw_pending = false; nuclear@0: break; nuclear@0: nuclear@0: case ConfigureNotify: nuclear@0: if(win_width != (int)ev->xconfigure.width || win_height != (int)ev->xconfigure.height) { nuclear@0: win_width = ev->xconfigure.width; nuclear@0: win_height = ev->xconfigure.height; nuclear@0: app_reshape(win_width, win_height); nuclear@0: } nuclear@0: break; nuclear@0: nuclear@0: case KeyPress: nuclear@0: case KeyRelease: nuclear@0: app_keyboard(XLookupKeysym(&ev->xkey, 0) & 0xff, ev->type == KeyPress); nuclear@0: break; nuclear@0: nuclear@0: case ClientMessage: nuclear@0: if(ev->xclient.message_type == xa_wm_proto && nuclear@0: (Atom)ev->xclient.data.l[0] == xa_del_window) { nuclear@0: return false; nuclear@0: } nuclear@0: break; nuclear@0: nuclear@0: default: nuclear@0: break; nuclear@0: } nuclear@0: return true; nuclear@0: } nuclear@0: nuclear@0: static void set_window_title(const char *title) nuclear@0: { nuclear@0: XTextProperty text_prop; nuclear@0: XStringListToTextProperty((char**)&title, 1, &text_prop); nuclear@0: XSetWMName(dpy, win, &text_prop); nuclear@0: XSetWMIconName(dpy, win, &text_prop); nuclear@0: XFree(text_prop.value); nuclear@0: }