blob: c367ed954b407d5919be66085d31514029c96b71 [file] [log] [blame]
// Copyright 2015 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// +build linux,!android
#include "_cgo_export.h"
#include <EGL/egl.h>
#include <stdio.h>
#include <stdlib.h>
EGLConfig e_config;
EGLContext e_ctx;
EGLDisplay e_dpy;
Colormap x_colormap;
Display *x_dpy;
XVisualInfo *x_visual_info;
Window x_root;
// TODO: share code with eglErrString
char *
eglGetErrorStr() {
switch (eglGetError()) {
case EGL_SUCCESS:
return "EGL_SUCCESS";
case EGL_NOT_INITIALIZED:
return "EGL_NOT_INITIALIZED";
case EGL_BAD_ACCESS:
return "EGL_BAD_ACCESS";
case EGL_BAD_ALLOC:
return "EGL_BAD_ALLOC";
case EGL_BAD_ATTRIBUTE:
return "EGL_BAD_ATTRIBUTE";
case EGL_BAD_CONFIG:
return "EGL_BAD_CONFIG";
case EGL_BAD_CONTEXT:
return "EGL_BAD_CONTEXT";
case EGL_BAD_CURRENT_SURFACE:
return "EGL_BAD_CURRENT_SURFACE";
case EGL_BAD_DISPLAY:
return "EGL_BAD_DISPLAY";
case EGL_BAD_MATCH:
return "EGL_BAD_MATCH";
case EGL_BAD_NATIVE_PIXMAP:
return "EGL_BAD_NATIVE_PIXMAP";
case EGL_BAD_NATIVE_WINDOW:
return "EGL_BAD_NATIVE_WINDOW";
case EGL_BAD_PARAMETER:
return "EGL_BAD_PARAMETER";
case EGL_BAD_SURFACE:
return "EGL_BAD_SURFACE";
case EGL_CONTEXT_LOST:
return "EGL_CONTEXT_LOST";
}
return "unknown EGL error";
}
void
startDriver() {
x_dpy = XOpenDisplay(NULL);
if (!x_dpy) {
fprintf(stderr, "XOpenDisplay failed\n");
exit(1);
}
e_dpy = eglGetDisplay(x_dpy);
if (!e_dpy) {
fprintf(stderr, "eglGetDisplay failed: %s\n", eglGetErrorStr());
exit(1);
}
EGLint e_major, e_minor;
if (!eglInitialize(e_dpy, &e_major, &e_minor)) {
fprintf(stderr, "eglInitialize failed: %s\n", eglGetErrorStr());
exit(1);
}
if (!eglBindAPI(EGL_OPENGL_ES_API)) {
fprintf(stderr, "eglBindAPI failed: %s\n", eglGetErrorStr());
exit(1);
}
static const EGLint attribs[] = {
EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
EGL_BLUE_SIZE, 8,
EGL_GREEN_SIZE, 8,
EGL_RED_SIZE, 8,
EGL_DEPTH_SIZE, 16,
EGL_CONFIG_CAVEAT, EGL_NONE,
EGL_NONE
};
EGLint num_configs;
if (!eglChooseConfig(e_dpy, attribs, &e_config, 1, &num_configs)) {
fprintf(stderr, "eglChooseConfig failed: %s\n", eglGetErrorStr());
exit(1);
}
EGLint vid;
if (!eglGetConfigAttrib(e_dpy, e_config, EGL_NATIVE_VISUAL_ID, &vid)) {
fprintf(stderr, "eglGetConfigAttrib failed: %s\n", eglGetErrorStr());
exit(1);
}
XVisualInfo visTemplate;
visTemplate.visualid = vid;
int num_visuals;
x_visual_info = XGetVisualInfo(x_dpy, VisualIDMask, &visTemplate, &num_visuals);
if (!x_visual_info) {
fprintf(stderr, "XGetVisualInfo failed\n");
exit(1);
}
x_root = RootWindow(x_dpy, DefaultScreen(x_dpy));
x_colormap = XCreateColormap(x_dpy, x_root, x_visual_info->visual, AllocNone);
if (!x_colormap) {
fprintf(stderr, "XCreateColormap failed\n");
exit(1);
}
static const EGLint ctx_attribs[] = {
EGL_CONTEXT_CLIENT_VERSION, 2,
EGL_NONE
};
e_ctx = eglCreateContext(e_dpy, e_config, EGL_NO_CONTEXT, ctx_attribs);
if (!e_ctx) {
fprintf(stderr, "eglCreateContext failed: %s\n", eglGetErrorStr());
exit(1);
}
}
void
processEvents() {
while (XPending(x_dpy)) {
XEvent ev;
XNextEvent(x_dpy, &ev);
switch (ev.type) {
case ButtonPress:
case ButtonRelease:
onMouse(ev.xbutton.window, ev.xbutton.x, ev.xbutton.y, ev.xbutton.state, ev.xbutton.button,
ev.type == ButtonPress ? 1 : 2);
break;
case MotionNotify:
onMouse(ev.xmotion.window, ev.xmotion.x, ev.xmotion.y, ev.xmotion.state, 0, 0);
break;
case Expose:
// A non-zero Count means that there are more expose events coming. For
// example, a non-rectangular exposure (e.g. from a partially overlapped
// window) will result in multiple expose events whose dirty rectangles
// combine to define the dirty region. Go's paint events do not provide
// dirty regions, so we only pass on the final X11 expose event.
if (ev.xexpose.count == 0) {
onExpose(ev.xexpose.window);
}
break;
case ConfigureNotify:
onResize(ev.xconfigure.window, ev.xconfigure.width, ev.xconfigure.height);
break;
}
}
}
void
makeCurrent(uintptr_t surface) {
EGLSurface surf = (EGLSurface)(surface);
if (!eglMakeCurrent(e_dpy, surf, surf, e_ctx)) {
fprintf(stderr, "eglMakeCurrent failed: %s\n", eglGetErrorStr());
exit(1);
}
}
void
swapBuffers(uintptr_t surface) {
EGLSurface surf = (EGLSurface)(surface);
if (!eglSwapBuffers(e_dpy, surf)) {
fprintf(stderr, "eglSwapBuffers failed: %s\n", eglGetErrorStr());
exit(1);
}
}
uintptr_t
doNewWindow(int width, int height) {
XSetWindowAttributes attr;
attr.colormap = x_colormap;
attr.event_mask =
ButtonPressMask |
ButtonReleaseMask |
PointerMotionMask |
ButtonMotionMask |
ExposureMask |
StructureNotifyMask;
Window win = XCreateWindow(
x_dpy, x_root, 0, 0, width, height, 0, x_visual_info->depth, InputOutput,
x_visual_info->visual, CWColormap | CWEventMask, &attr);
XSizeHints sizehints;
sizehints.width = width;
sizehints.height = height;
sizehints.flags = USSize;
XSetNormalHints(x_dpy, win, &sizehints);
XSetStandardProperties(x_dpy, win, "App", "App", None, (char **)NULL, 0, &sizehints);
return win;
}
uintptr_t
doShowWindow(uintptr_t id) {
Window win = (Window)(id);
XMapWindow(x_dpy, win);
EGLSurface surf = eglCreateWindowSurface(e_dpy, e_config, win, NULL);
if (!surf) {
fprintf(stderr, "eglCreateWindowSurface failed: %s\n", eglGetErrorStr());
exit(1);
}
return (uintptr_t)(surf);
}