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  }