x11.c (8917B)
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 openbsd 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 //TODO: use GetModifierMapping to figure out which modifier is the numlock modifier. 155 } 156 157 void 158 processEvents() { 159 while (XPending(x_dpy)) { 160 XEvent ev; 161 XNextEvent(x_dpy, &ev); 162 switch (ev.type) { 163 case KeyPress: 164 case KeyRelease: 165 onKey(ev.xkey.window, ev.xkey.state, ev.xkey.keycode, ev.type == KeyPress ? 1 : 2); 166 break; 167 case ButtonPress: 168 case ButtonRelease: 169 onMouse(ev.xbutton.window, ev.xbutton.x, ev.xbutton.y, ev.xbutton.state, ev.xbutton.button, 170 ev.type == ButtonPress ? 1 : 2); 171 break; 172 case MotionNotify: 173 onMouse(ev.xmotion.window, ev.xmotion.x, ev.xmotion.y, ev.xmotion.state, 0, 0); 174 break; 175 case FocusIn: 176 case FocusOut: 177 onFocus(ev.xmotion.window, ev.type == FocusIn); 178 break; 179 case Expose: 180 // A non-zero Count means that there are more expose events coming. For 181 // example, a non-rectangular exposure (e.g. from a partially overlapped 182 // window) will result in multiple expose events whose dirty rectangles 183 // combine to define the dirty region. Go's paint events do not provide 184 // dirty regions, so we only pass on the final X11 expose event. 185 if (ev.xexpose.count == 0) { 186 onExpose(ev.xexpose.window); 187 } 188 break; 189 case ConfigureNotify: 190 onConfigure(ev.xconfigure.window, ev.xconfigure.x, ev.xconfigure.y, 191 ev.xconfigure.width, ev.xconfigure.height, 192 DisplayWidth(x_dpy, DefaultScreen(x_dpy)), 193 DisplayWidthMM(x_dpy, DefaultScreen(x_dpy))); 194 break; 195 case ClientMessage: 196 if ((ev.xclient.message_type != wm_protocols) || (ev.xclient.format != 32)) { 197 break; 198 } 199 Atom a = ev.xclient.data.l[0]; 200 if (a == wm_delete_window) { 201 onDeleteWindow(ev.xclient.window); 202 } else if (a == wm_take_focus) { 203 XSetInputFocus(x_dpy, ev.xclient.window, RevertToParent, ev.xclient.data.l[1]); 204 } 205 break; 206 } 207 } 208 } 209 210 void 211 makeCurrent(uintptr_t surface) { 212 EGLSurface surf = (EGLSurface)(surface); 213 if (!eglMakeCurrent(e_dpy, surf, surf, e_ctx)) { 214 fprintf(stderr, "eglMakeCurrent failed: %s\n", eglGetErrorStr()); 215 exit(1); 216 } 217 } 218 219 void 220 swapBuffers(uintptr_t surface) { 221 EGLSurface surf = (EGLSurface)(surface); 222 if (!eglSwapBuffers(e_dpy, surf)) { 223 fprintf(stderr, "eglSwapBuffers failed: %s\n", eglGetErrorStr()); 224 exit(1); 225 } 226 } 227 228 void 229 doCloseWindow(uintptr_t id) { 230 Window win = (Window)(id); 231 XDestroyWindow(x_dpy, win); 232 } 233 234 uintptr_t 235 doNewWindow(int width, int height, char* title, int title_len) { 236 XSetWindowAttributes attr; 237 attr.colormap = x_colormap; 238 attr.event_mask = 239 KeyPressMask | 240 KeyReleaseMask | 241 ButtonPressMask | 242 ButtonReleaseMask | 243 PointerMotionMask | 244 ExposureMask | 245 StructureNotifyMask | 246 FocusChangeMask; 247 248 Window win = XCreateWindow( 249 x_dpy, x_root, 0, 0, width, height, 0, x_visual_info->depth, InputOutput, 250 x_visual_info->visual, CWColormap | CWEventMask, &attr); 251 252 XSizeHints sizehints; 253 sizehints.width = width; 254 sizehints.height = height; 255 sizehints.flags = USSize; 256 XSetNormalHints(x_dpy, win, &sizehints); 257 258 Atom atoms[2]; 259 atoms[0] = wm_delete_window; 260 atoms[1] = wm_take_focus; 261 XSetWMProtocols(x_dpy, win, atoms, 2); 262 263 XSetStandardProperties(x_dpy, win, "", "App", None, (char **)NULL, 0, &sizehints); 264 XChangeProperty(x_dpy, win, net_wm_name, utf8_string, 8, PropModeReplace, title, title_len); 265 266 return win; 267 } 268 269 uintptr_t 270 doShowWindow(uintptr_t id) { 271 Window win = (Window)(id); 272 XMapWindow(x_dpy, win); 273 EGLSurface surf = eglCreateWindowSurface(e_dpy, e_config, win, NULL); 274 if (!surf) { 275 fprintf(stderr, "eglCreateWindowSurface failed: %s\n", eglGetErrorStr()); 276 exit(1); 277 } 278 return (uintptr_t)(surf); 279 } 280 281 uintptr_t 282 surfaceCreate() { 283 static const EGLint ctx_attribs[] = { 284 EGL_CONTEXT_CLIENT_VERSION, 3, 285 EGL_NONE 286 }; 287 EGLContext ctx = eglCreateContext(e_dpy, e_config, EGL_NO_CONTEXT, ctx_attribs); 288 if (!ctx) { 289 fprintf(stderr, "surface eglCreateContext failed: %s\n", eglGetErrorStr()); 290 return 0; 291 } 292 293 static const EGLint cfg_attribs[] = { 294 EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT, 295 EGL_SURFACE_TYPE, EGL_PBUFFER_BIT, 296 EGL_BLUE_SIZE, 8, 297 EGL_GREEN_SIZE, 8, 298 EGL_RED_SIZE, 8, 299 EGL_DEPTH_SIZE, 16, 300 EGL_CONFIG_CAVEAT, EGL_NONE, 301 EGL_NONE 302 }; 303 EGLConfig cfg; 304 EGLint num_configs; 305 if (!eglChooseConfig(e_dpy, cfg_attribs, &cfg, 1, &num_configs)) { 306 fprintf(stderr, "gldriver: surface eglChooseConfig failed: %s\n", eglGetErrorStr()); 307 return 0; 308 } 309 310 // TODO: use the size of the monitor as a bound for texture size. 311 static const EGLint attribs[] = { 312 EGL_WIDTH, 4096, 313 EGL_HEIGHT, 3072, 314 EGL_NONE 315 }; 316 EGLSurface surface = eglCreatePbufferSurface(e_dpy, cfg, attribs); 317 if (!surface) { 318 fprintf(stderr, "gldriver: surface eglCreatePbufferSurface failed: %s\n", eglGetErrorStr()); 319 return 0; 320 } 321 322 if (!eglMakeCurrent(e_dpy, surface, surface, ctx)) { 323 fprintf(stderr, "gldriver: surface eglMakeCurrent failed: %s\n", eglGetErrorStr()); 324 return 0; 325 } 326 327 return (uintptr_t)surface; 328 }