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 *
eglGetErrorStr()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
startDriver()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
processEvents()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
makeCurrent(uintptr_t surface)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
swapBuffers(uintptr_t surface)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
doCloseWindow(uintptr_t id)229 doCloseWindow(uintptr_t id) {
230 Window win = (Window)(id);
231 XDestroyWindow(x_dpy, win);
232 }
233
234 uintptr_t
doNewWindow(int width,int height,char * title,int title_len)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
doShowWindow(uintptr_t id)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
surfaceCreate()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 }
329