github.com/as/shiny@v0.8.2/driver/gldriver/x11.c (about) 1 // Copyright 2015 The Go Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 // +build linux,!android 6 7 #include "_cgo_export.h" 8 #include <EGL/egl.h> 9 #include <stdio.h> 10 #include <stdlib.h> 11 #include <string.h> 12 13 Atom net_wm_name; 14 Atom utf8_string; 15 Atom wm_delete_window; 16 Atom wm_protocols; 17 Atom wm_take_focus; 18 19 EGLConfig e_config; 20 EGLContext e_ctx; 21 EGLDisplay e_dpy; 22 Colormap x_colormap; 23 Display *x_dpy; 24 XVisualInfo *x_visual_info; 25 Window x_root; 26 27 // TODO: share code with eglErrString 28 char * 29 eglGetErrorStr() { 30 switch (eglGetError()) { 31 case EGL_SUCCESS: 32 return "EGL_SUCCESS"; 33 case EGL_NOT_INITIALIZED: 34 return "EGL_NOT_INITIALIZED"; 35 case EGL_BAD_ACCESS: 36 return "EGL_BAD_ACCESS"; 37 case EGL_BAD_ALLOC: 38 return "EGL_BAD_ALLOC"; 39 case EGL_BAD_ATTRIBUTE: 40 return "EGL_BAD_ATTRIBUTE"; 41 case EGL_BAD_CONFIG: 42 return "EGL_BAD_CONFIG"; 43 case EGL_BAD_CONTEXT: 44 return "EGL_BAD_CONTEXT"; 45 case EGL_BAD_CURRENT_SURFACE: 46 return "EGL_BAD_CURRENT_SURFACE"; 47 case EGL_BAD_DISPLAY: 48 return "EGL_BAD_DISPLAY"; 49 case EGL_BAD_MATCH: 50 return "EGL_BAD_MATCH"; 51 case EGL_BAD_NATIVE_PIXMAP: 52 return "EGL_BAD_NATIVE_PIXMAP"; 53 case EGL_BAD_NATIVE_WINDOW: 54 return "EGL_BAD_NATIVE_WINDOW"; 55 case EGL_BAD_PARAMETER: 56 return "EGL_BAD_PARAMETER"; 57 case EGL_BAD_SURFACE: 58 return "EGL_BAD_SURFACE"; 59 case EGL_CONTEXT_LOST: 60 return "EGL_CONTEXT_LOST"; 61 } 62 return "unknown EGL error"; 63 } 64 65 void 66 startDriver() { 67 x_dpy = XOpenDisplay(NULL); 68 if (!x_dpy) { 69 fprintf(stderr, "XOpenDisplay failed\n"); 70 exit(1); 71 } 72 e_dpy = eglGetDisplay(x_dpy); 73 if (!e_dpy) { 74 fprintf(stderr, "eglGetDisplay failed: %s\n", eglGetErrorStr()); 75 exit(1); 76 } 77 EGLint e_major, e_minor; 78 if (!eglInitialize(e_dpy, &e_major, &e_minor)) { 79 fprintf(stderr, "eglInitialize failed: %s\n", eglGetErrorStr()); 80 exit(1); 81 } 82 if (!eglBindAPI(EGL_OPENGL_ES_API)) { 83 fprintf(stderr, "eglBindAPI failed: %s\n", eglGetErrorStr()); 84 exit(1); 85 } 86 87 static const EGLint attribs[] = { 88 EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT, 89 EGL_SURFACE_TYPE, EGL_WINDOW_BIT, 90 EGL_BLUE_SIZE, 8, 91 EGL_GREEN_SIZE, 8, 92 EGL_RED_SIZE, 8, 93 EGL_DEPTH_SIZE, 16, 94 EGL_CONFIG_CAVEAT, EGL_NONE, 95 EGL_NONE 96 }; 97 EGLint num_configs; 98 if (!eglChooseConfig(e_dpy, attribs, &e_config, 1, &num_configs)) { 99 fprintf(stderr, "eglChooseConfig failed: %s\n", eglGetErrorStr()); 100 exit(1); 101 } 102 EGLint vid; 103 if (!eglGetConfigAttrib(e_dpy, e_config, EGL_NATIVE_VISUAL_ID, &vid)) { 104 fprintf(stderr, "eglGetConfigAttrib failed: %s\n", eglGetErrorStr()); 105 exit(1); 106 } 107 108 XVisualInfo visTemplate; 109 visTemplate.visualid = vid; 110 int num_visuals; 111 x_visual_info = XGetVisualInfo(x_dpy, VisualIDMask, &visTemplate, &num_visuals); 112 if (!x_visual_info) { 113 fprintf(stderr, "XGetVisualInfo failed\n"); 114 exit(1); 115 } 116 117 x_root = RootWindow(x_dpy, DefaultScreen(x_dpy)); 118 x_colormap = XCreateColormap(x_dpy, x_root, x_visual_info->visual, AllocNone); 119 if (!x_colormap) { 120 fprintf(stderr, "XCreateColormap failed\n"); 121 exit(1); 122 } 123 124 static const EGLint ctx_attribs[] = { 125 EGL_CONTEXT_CLIENT_VERSION, 3, 126 EGL_NONE 127 }; 128 e_ctx = eglCreateContext(e_dpy, e_config, EGL_NO_CONTEXT, ctx_attribs); 129 if (!e_ctx) { 130 fprintf(stderr, "eglCreateContext failed: %s\n", eglGetErrorStr()); 131 exit(1); 132 } 133 134 net_wm_name = XInternAtom(x_dpy, "_NET_WM_NAME", False); 135 utf8_string = XInternAtom(x_dpy, "UTF8_STRING", False); 136 wm_delete_window = XInternAtom(x_dpy, "WM_DELETE_WINDOW", False); 137 wm_protocols = XInternAtom(x_dpy, "WM_PROTOCOLS", False); 138 wm_take_focus = XInternAtom(x_dpy, "WM_TAKE_FOCUS", False); 139 140 const int key_lo = 8; 141 const int key_hi = 255; 142 int keysyms_per_keycode; 143 KeySym *keysyms = XGetKeyboardMapping(x_dpy, key_lo, key_hi-key_lo+1, &keysyms_per_keycode); 144 if (keysyms_per_keycode < 2) { 145 fprintf(stderr, "XGetKeyboardMapping returned too few keysyms per keycode: %d\n", keysyms_per_keycode); 146 exit(1); 147 } 148 int k; 149 for (k = key_lo; k <= key_hi; k++) { 150 onKeysym(k, 151 keysyms[(k-key_lo)*keysyms_per_keycode + 0], 152 keysyms[(k-key_lo)*keysyms_per_keycode + 1]); 153 } 154 } 155 156 void 157 processEvents() { 158 while (XPending(x_dpy)) { 159 XEvent ev; 160 XNextEvent(x_dpy, &ev); 161 switch (ev.type) { 162 case KeyPress: 163 case KeyRelease: 164 onKey(ev.xkey.window, ev.xkey.state, ev.xkey.keycode, ev.type == KeyPress ? 1 : 2); 165 break; 166 case ButtonPress: 167 case ButtonRelease: 168 onMouse(ev.xbutton.window, ev.xbutton.x, ev.xbutton.y, ev.xbutton.state, ev.xbutton.button, 169 ev.type == ButtonPress ? 1 : 2); 170 break; 171 case MotionNotify: 172 onMouse(ev.xmotion.window, ev.xmotion.x, ev.xmotion.y, ev.xmotion.state, 0, 0); 173 break; 174 case FocusIn: 175 case FocusOut: 176 onFocus(ev.xmotion.window, ev.type == FocusIn); 177 break; 178 case Expose: 179 // A non-zero Count means that there are more expose events coming. For 180 // example, a non-rectangular exposure (e.g. from a partially overlapped 181 // window) will result in multiple expose events whose dirty rectangles 182 // combine to define the dirty region. Go's paint events do not provide 183 // dirty regions, so we only pass on the final X11 expose event. 184 if (ev.xexpose.count == 0) { 185 onExpose(ev.xexpose.window); 186 } 187 break; 188 case ConfigureNotify: 189 onConfigure(ev.xconfigure.window, ev.xconfigure.x, ev.xconfigure.y, 190 ev.xconfigure.width, ev.xconfigure.height, 191 DisplayWidth(x_dpy, DefaultScreen(x_dpy)), 192 DisplayWidthMM(x_dpy, DefaultScreen(x_dpy))); 193 break; 194 case ClientMessage: 195 if ((ev.xclient.message_type != wm_protocols) || (ev.xclient.format != 32)) { 196 break; 197 } 198 Atom a = ev.xclient.data.l[0]; 199 if (a == wm_delete_window) { 200 onDeleteWindow(ev.xclient.window); 201 } else if (a == wm_take_focus) { 202 XSetInputFocus(x_dpy, ev.xclient.window, RevertToParent, ev.xclient.data.l[1]); 203 } 204 break; 205 } 206 } 207 } 208 209 void 210 makeCurrent(uintptr_t surface) { 211 EGLSurface surf = (EGLSurface)(surface); 212 if (!eglMakeCurrent(e_dpy, surf, surf, e_ctx)) { 213 fprintf(stderr, "eglMakeCurrent failed: %s\n", eglGetErrorStr()); 214 exit(1); 215 } 216 } 217 218 void 219 swapBuffers(uintptr_t surface) { 220 EGLSurface surf = (EGLSurface)(surface); 221 if (!eglSwapBuffers(e_dpy, surf)) { 222 fprintf(stderr, "eglSwapBuffers failed: %s\n", eglGetErrorStr()); 223 exit(1); 224 } 225 } 226 227 void 228 doCloseWindow(uintptr_t id) { 229 Window win = (Window)(id); 230 XDestroyWindow(x_dpy, win); 231 } 232 233 uintptr_t 234 doNewWindow(int width, int height, char* title, int title_len) { 235 XSetWindowAttributes attr; 236 attr.colormap = x_colormap; 237 attr.event_mask = 238 KeyPressMask | 239 KeyReleaseMask | 240 ButtonPressMask | 241 ButtonReleaseMask | 242 PointerMotionMask | 243 ExposureMask | 244 StructureNotifyMask | 245 FocusChangeMask; 246 247 Window win = XCreateWindow( 248 x_dpy, x_root, 0, 0, width, height, 0, x_visual_info->depth, InputOutput, 249 x_visual_info->visual, CWColormap | CWEventMask, &attr); 250 251 XSizeHints sizehints; 252 sizehints.width = width; 253 sizehints.height = height; 254 sizehints.flags = USSize; 255 XSetNormalHints(x_dpy, win, &sizehints); 256 257 Atom atoms[2]; 258 atoms[0] = wm_delete_window; 259 atoms[1] = wm_take_focus; 260 XSetWMProtocols(x_dpy, win, atoms, 2); 261 262 XSetStandardProperties(x_dpy, win, "", "App", None, (char **)NULL, 0, &sizehints); 263 XChangeProperty(x_dpy, win, net_wm_name, utf8_string, 8, PropModeReplace, title, title_len); 264 265 return win; 266 } 267 268 uintptr_t 269 doShowWindow(uintptr_t id) { 270 Window win = (Window)(id); 271 XMapWindow(x_dpy, win); 272 EGLSurface surf = eglCreateWindowSurface(e_dpy, e_config, win, NULL); 273 if (!surf) { 274 fprintf(stderr, "eglCreateWindowSurface failed: %s\n", eglGetErrorStr()); 275 exit(1); 276 } 277 return (uintptr_t)(surf); 278 } 279 280 uintptr_t 281 surfaceCreate() { 282 static const EGLint ctx_attribs[] = { 283 EGL_CONTEXT_CLIENT_VERSION, 3, 284 EGL_NONE 285 }; 286 EGLContext ctx = eglCreateContext(e_dpy, e_config, EGL_NO_CONTEXT, ctx_attribs); 287 if (!ctx) { 288 fprintf(stderr, "surface eglCreateContext failed: %s\n", eglGetErrorStr()); 289 return 0; 290 } 291 292 static const EGLint cfg_attribs[] = { 293 EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT, 294 EGL_SURFACE_TYPE, EGL_PBUFFER_BIT, 295 EGL_BLUE_SIZE, 8, 296 EGL_GREEN_SIZE, 8, 297 EGL_RED_SIZE, 8, 298 EGL_DEPTH_SIZE, 16, 299 EGL_CONFIG_CAVEAT, EGL_NONE, 300 EGL_NONE 301 }; 302 EGLConfig cfg; 303 EGLint num_configs; 304 if (!eglChooseConfig(e_dpy, cfg_attribs, &cfg, 1, &num_configs)) { 305 fprintf(stderr, "gldriver: surface eglChooseConfig failed: %s\n", eglGetErrorStr()); 306 return 0; 307 } 308 309 // TODO: use the size of the monitor as a bound for texture size. 310 static const EGLint attribs[] = { 311 EGL_WIDTH, 4096, 312 EGL_HEIGHT, 3072, 313 EGL_NONE 314 }; 315 EGLSurface surface = eglCreatePbufferSurface(e_dpy, cfg, attribs); 316 if (!surface) { 317 fprintf(stderr, "gldriver: surface eglCreatePbufferSurface failed: %s\n", eglGetErrorStr()); 318 return 0; 319 } 320 321 if (!eglMakeCurrent(e_dpy, surface, surface, ctx)) { 322 fprintf(stderr, "gldriver: surface eglMakeCurrent failed: %s\n", eglGetErrorStr()); 323 return 0; 324 } 325 326 return (uintptr_t)surface; 327 }