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 }