github.com/goki/mobile@v0.0.0-20230707090321-193544ec5700/app/x11.c (about)

     1  // Copyright 2014 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  // go:build linux && !android
     6  // +build linux,!android
     7  
     8  #include "_cgo_export.h"
     9  #include <EGL/egl.h>
    10  #include <GLES2/gl2.h>
    11  #include <X11/Xlib.h>
    12  #include <X11/Xutil.h>
    13  #include <stdio.h>
    14  #include <stdlib.h>
    15  
    16  static Atom wm_delete_window;
    17  
    18  static Window
    19  new_window(Display *x_dpy, EGLDisplay e_dpy, int w, int h, EGLContext *ctx, EGLSurface *surf)
    20  {
    21  	static const EGLint attribs[] = {
    22  		EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
    23  		EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
    24  		EGL_BLUE_SIZE, 8,
    25  		EGL_GREEN_SIZE, 8,
    26  		EGL_RED_SIZE, 8,
    27  		EGL_DEPTH_SIZE, 16,
    28  		EGL_CONFIG_CAVEAT, EGL_NONE,
    29  		EGL_NONE};
    30  	EGLConfig config;
    31  	EGLint num_configs;
    32  	if (!eglChooseConfig(e_dpy, attribs, &config, 1, &num_configs))
    33  	{
    34  		fprintf(stderr, "eglChooseConfig failed\n");
    35  		exit(1);
    36  	}
    37  	EGLint vid;
    38  	if (!eglGetConfigAttrib(e_dpy, config, EGL_NATIVE_VISUAL_ID, &vid))
    39  	{
    40  		fprintf(stderr, "eglGetConfigAttrib failed\n");
    41  		exit(1);
    42  	}
    43  
    44  	XVisualInfo visTemplate;
    45  	visTemplate.visualid = vid;
    46  	int num_visuals;
    47  	XVisualInfo *visInfo = XGetVisualInfo(x_dpy, VisualIDMask, &visTemplate, &num_visuals);
    48  	if (!visInfo)
    49  	{
    50  		fprintf(stderr, "XGetVisualInfo failed\n");
    51  		exit(1);
    52  	}
    53  
    54  	Window root = RootWindow(x_dpy, DefaultScreen(x_dpy));
    55  	XSetWindowAttributes attr;
    56  
    57  	attr.colormap = XCreateColormap(x_dpy, root, visInfo->visual, AllocNone);
    58  	if (!attr.colormap)
    59  	{
    60  		fprintf(stderr, "XCreateColormap failed\n");
    61  		exit(1);
    62  	}
    63  
    64  	attr.event_mask = StructureNotifyMask | ExposureMask |
    65  					  ButtonPressMask | ButtonReleaseMask | ButtonMotionMask;
    66  	Window win = XCreateWindow(
    67  		x_dpy, root, 0, 0, w, h, 0, visInfo->depth, InputOutput,
    68  		visInfo->visual, CWColormap | CWEventMask, &attr);
    69  	XFree(visInfo);
    70  
    71  	XSizeHints sizehints;
    72  	sizehints.width = w;
    73  	sizehints.height = h;
    74  	sizehints.flags = USSize;
    75  	XSetNormalHints(x_dpy, win, &sizehints);
    76  	XSetStandardProperties(x_dpy, win, "App", "App", None, (char **)NULL, 0, &sizehints);
    77  
    78  	static const EGLint ctx_attribs[] = {
    79  		EGL_CONTEXT_CLIENT_VERSION, 2,
    80  		EGL_NONE};
    81  	*ctx = eglCreateContext(e_dpy, config, EGL_NO_CONTEXT, ctx_attribs);
    82  	if (!*ctx)
    83  	{
    84  		fprintf(stderr, "eglCreateContext failed\n");
    85  		exit(1);
    86  	}
    87  	*surf = eglCreateWindowSurface(e_dpy, config, win, NULL);
    88  	if (!*surf)
    89  	{
    90  		fprintf(stderr, "eglCreateWindowSurface failed\n");
    91  		exit(1);
    92  	}
    93  	return win;
    94  }
    95  
    96  Display *x_dpy;
    97  EGLDisplay e_dpy;
    98  EGLContext e_ctx;
    99  EGLSurface e_surf;
   100  Window win;
   101  
   102  void createWindow(void)
   103  {
   104  	x_dpy = XOpenDisplay(NULL);
   105  	if (!x_dpy)
   106  	{
   107  		fprintf(stderr, "XOpenDisplay failed\n");
   108  		exit(1);
   109  	}
   110  	e_dpy = eglGetDisplay(x_dpy);
   111  	if (!e_dpy)
   112  	{
   113  		fprintf(stderr, "eglGetDisplay failed\n");
   114  		exit(1);
   115  	}
   116  	EGLint e_major, e_minor;
   117  	if (!eglInitialize(e_dpy, &e_major, &e_minor))
   118  	{
   119  		fprintf(stderr, "eglInitialize failed\n");
   120  		exit(1);
   121  	}
   122  	eglBindAPI(EGL_OPENGL_ES_API);
   123  	win = new_window(x_dpy, e_dpy, 600, 800, &e_ctx, &e_surf);
   124  
   125  	wm_delete_window = XInternAtom(x_dpy, "WM_DELETE_WINDOW", True);
   126  	if (wm_delete_window != None)
   127  	{
   128  		XSetWMProtocols(x_dpy, win, &wm_delete_window, 1);
   129  	}
   130  
   131  	XMapWindow(x_dpy, win);
   132  	if (!eglMakeCurrent(e_dpy, e_surf, e_surf, e_ctx))
   133  	{
   134  		fprintf(stderr, "eglMakeCurrent failed\n");
   135  		exit(1);
   136  	}
   137  
   138  	// Window size and DPI should be initialized before starting app.
   139  	XEvent ev;
   140  	while (1)
   141  	{
   142  		if (XCheckMaskEvent(x_dpy, StructureNotifyMask, &ev) == False)
   143  		{
   144  			continue;
   145  		}
   146  		if (ev.type == ConfigureNotify)
   147  		{
   148  			onResize(ev.xconfigure.width, ev.xconfigure.height);
   149  			break;
   150  		}
   151  	}
   152  }
   153  
   154  void processEvents(void)
   155  {
   156  	while (XPending(x_dpy))
   157  	{
   158  		XEvent ev;
   159  		XNextEvent(x_dpy, &ev);
   160  		switch (ev.type)
   161  		{
   162  		case ButtonPress:
   163  			onTouchBegin((float)ev.xbutton.x, (float)ev.xbutton.y);
   164  			break;
   165  		case ButtonRelease:
   166  			onTouchEnd((float)ev.xbutton.x, (float)ev.xbutton.y);
   167  			break;
   168  		case MotionNotify:
   169  			onTouchMove((float)ev.xmotion.x, (float)ev.xmotion.y);
   170  			break;
   171  		case ConfigureNotify:
   172  			onResize(ev.xconfigure.width, ev.xconfigure.height);
   173  			break;
   174  		case ClientMessage:
   175  			if (wm_delete_window != None && (Atom)ev.xclient.data.l[0] == wm_delete_window)
   176  			{
   177  				onStop();
   178  				return;
   179  			}
   180  			break;
   181  		}
   182  	}
   183  }
   184  
   185  void swapBuffers(void)
   186  {
   187  	if (eglSwapBuffers(e_dpy, e_surf) == EGL_FALSE)
   188  	{
   189  		fprintf(stderr, "eglSwapBuffer failed\n");
   190  		exit(1);
   191  	}
   192  }