zorldo

Goofing around with Ebiten
git clone git://bsandro.tech/zorldo
Log | Files | Refs | README

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 }