vendor dependencies, make some changes to how input is done

This commit is contained in:
2026-06-15 18:54:00 +02:00
parent 535130933c
commit 4800eb28d9
759 changed files with 360941 additions and 30 deletions
+2
View File
@@ -0,0 +1,2 @@
tags
whiteboard
+60 -19
View File
@@ -1,35 +1,76 @@
package main package main
import "github.com/hajimehoshi/ebiten/v2" import (
"github.com/hajimehoshi/ebiten/v2"
"github.com/hajimehoshi/ebiten/v2/inpututil"
)
type keymap []ebiten.Key type keymap struct{
keys []ebiten.Key
mouseButtons []ebiten.MouseButton
}
var resize = keymap{ var resize = keymap{
ebiten.KeyD, keys: []ebiten.Key{ebiten.KeyD, ebiten.KeyK,},
ebiten.KeyK,
} }
var drawBlack = keymap{ var drawBlack = keymap{
ebiten.KeyF, keys: []ebiten.Key{ebiten.KeyF, ebiten.KeyJ,},
ebiten.KeyJ,
} }
var drawWhite = keymap{ var drawWhite = keymap{
ebiten.KeyS, keys: []ebiten.Key{ebiten.KeyS, ebiten.KeyL,},
ebiten.KeyL,
} }
var undo = keymap{ var undo = keymap{
ebiten.KeyZ, keys: []ebiten.Key{ebiten.KeyZ, ebiten.KeyU,},
ebiten.KeyU,
} }
var redo = keymap{ var redo = keymap{
ebiten.KeyX, keys: []ebiten.Key{ebiten.KeyX, ebiten.KeyR,},
ebiten.KeyR,
} }
func (k keymap) check(checker func(ebiten.Key) bool) bool { func (k keymap) isJustPressed() bool {
for _, key := range k { for _, key := range k.keys {
if checker(key) { if inpututil.IsKeyJustPressed(key) {
return true return true
} }
} }
return false
for _, mouseButton := range k.mouseButtons {
if inpututil.IsMouseButtonJustPressed(mouseButton) {
return true
}
}
return false
}
func (k keymap) isJustReleased() bool {
for _, key := range k.keys {
if inpututil.IsKeyJustReleased(key) {
return true
}
}
for _, mouseButton := range k.mouseButtons {
if inpututil.IsMouseButtonJustReleased(mouseButton) {
return true
}
}
return false
}
func (k keymap) isPressed() bool {
for _, key := range k.keys {
if ebiten.IsKeyPressed(key) {
return true
}
}
for _, mouseButton := range k.mouseButtons {
if ebiten.IsMouseButtonPressed(mouseButton) {
return true
}
}
return false
} }
+6 -11
View File
@@ -6,7 +6,6 @@ import (
"math" "math"
"github.com/hajimehoshi/ebiten/v2" "github.com/hajimehoshi/ebiten/v2"
"github.com/hajimehoshi/ebiten/v2/inpututil"
"github.com/hajimehoshi/ebiten/v2/vector" "github.com/hajimehoshi/ebiten/v2/vector"
) )
@@ -56,17 +55,13 @@ func (a *app) Layout(_, _ int) (screenWidth int, screenHeight int) {
func (a *app) Update() error { func (a *app) Update() error {
check_any_drawing_input := func(checker func(ebiten.Key) bool) bool { is_drawing := drawWhite.isPressed() || drawBlack.isPressed()
return drawWhite.check(checker) || drawBlack.check(checker) just_started_drawing := drawWhite.isJustPressed() || drawBlack.isJustPressed()
}
is_drawing := check_any_drawing_input(ebiten.IsKeyPressed)
just_started_drawing := check_any_drawing_input(inpututil.IsKeyJustPressed)
var clr color.Color var clr color.Color
if drawBlack.check(ebiten.IsKeyPressed) { if drawBlack.isPressed() {
clr = color.Black clr = color.Black
} else if drawWhite.check(ebiten.IsKeyPressed) { } else if drawWhite.isPressed() {
clr = color.White clr = color.White
} }
@@ -91,11 +86,11 @@ func (a *app) Update() error {
} }
} }
if resize.check(inpututil.IsKeyJustPressed) { if resize.isJustPressed() {
a.sizeAnchorPos = cursorPosition() a.sizeAnchorPos = cursorPosition()
} }
if resize.check(ebiten.IsKeyPressed) { if resize.isPressed() {
prevPos := a.sizeAnchorPos prevPos := a.sizeAnchorPos
currPos := cursorPosition() currPos := cursorPosition()
reltivePos := Vec2{ reltivePos := Vec2{
+27
View File
@@ -0,0 +1,27 @@
Copyright (c) 2009 The Go Authors. All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the following disclaimer
in the documentation and/or other materials provided with the
distribution.
* Neither the name of Google Inc. nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+22
View File
@@ -0,0 +1,22 @@
Additional IP Rights Grant (Patents)
"This implementation" means the copyrightable works distributed by
Google as part of the Go project.
Google hereby grants to You a perpetual, worldwide, non-exclusive,
no-charge, royalty-free, irrevocable (except as stated in this section)
patent license to make, have made, use, offer to sell, sell, import,
transfer and otherwise run, modify and propagate the contents of this
implementation of Go, where such license applies only to those patent
claims, both currently owned or controlled by Google and acquired in
the future, licensable by Google that are necessarily infringed by this
implementation of Go. This grant does not include claims that would be
infringed only as a consequence of further modification of this
implementation. If you or your agent or exclusive licensee institute or
order or agree to the institution of patent litigation against any
entity (including a cross-claim or counterclaim in a lawsuit) alleging
that this implementation of Go or any code incorporated within this
implementation of Go constitutes direct or contributory patent
infringement, or inducement of patent infringement, then any patent
rights granted to you under this License for this implementation of Go
shall terminate as of the date such litigation is filed.
+67
View File
@@ -0,0 +1,67 @@
package org.golang.app;
import android.app.Activity;
import android.app.NativeActivity;
import android.content.Context;
import android.content.pm.ActivityInfo;
import android.content.pm.PackageManager;
import android.os.Bundle;
import android.util.Log;
import android.view.KeyCharacterMap;
public class GoNativeActivity extends NativeActivity {
private static GoNativeActivity goNativeActivity;
public GoNativeActivity() {
super();
goNativeActivity = this;
}
String getTmpdir() {
return getCacheDir().getAbsolutePath();
}
static int getRune(int deviceId, int keyCode, int metaState) {
try {
int rune = KeyCharacterMap.load(deviceId).get(keyCode, metaState);
if (rune == 0) {
return -1;
}
return rune;
} catch (KeyCharacterMap.UnavailableException e) {
return -1;
} catch (Exception e) {
Log.e("Go", "exception reading KeyCharacterMap", e);
return -1;
}
}
private void load() {
// Interestingly, NativeActivity uses a different method
// to find native code to execute, avoiding
// System.loadLibrary. The result is Java methods
// implemented in C with JNIEXPORT (and JNI_OnLoad) are not
// available unless an explicit call to System.loadLibrary
// is done. So we do it here, borrowing the name of the
// library from the same AndroidManifest.xml metadata used
// by NativeActivity.
try {
ActivityInfo ai = getPackageManager().getActivityInfo(
getIntent().getComponent(), PackageManager.GET_META_DATA);
if (ai.metaData == null) {
Log.e("Go", "loadLibrary: no manifest metadata found");
return;
}
String libName = ai.metaData.getString("android.app.lib_name");
System.loadLibrary(libName);
} catch (Exception e) {
Log.e("Go", "loadLibrary failed", e);
}
}
@Override
public void onCreate(Bundle savedInstanceState) {
load();
super.onCreate(savedInstanceState);
}
}
+201
View File
@@ -0,0 +1,201 @@
// Copyright 2014 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.
//go:build android
// +build android
#include <android/log.h>
#include <dlfcn.h>
#include <errno.h>
#include <fcntl.h>
#include <stdint.h>
#include <string.h>
#include "_cgo_export.h"
#define LOG_INFO(...) __android_log_print(ANDROID_LOG_INFO, "Go", __VA_ARGS__)
#define LOG_FATAL(...) __android_log_print(ANDROID_LOG_FATAL, "Go", __VA_ARGS__)
static jclass current_class;
static jclass find_class(JNIEnv *env, const char *class_name) {
jclass clazz = (*env)->FindClass(env, class_name);
if (clazz == NULL) {
(*env)->ExceptionClear(env);
LOG_FATAL("cannot find %s", class_name);
return NULL;
}
return clazz;
}
static jmethodID find_method(JNIEnv *env, jclass clazz, const char *name, const char *sig) {
jmethodID m = (*env)->GetMethodID(env, clazz, name, sig);
if (m == 0) {
(*env)->ExceptionClear(env);
LOG_FATAL("cannot find method %s %s", name, sig);
return 0;
}
return m;
}
static jmethodID find_static_method(JNIEnv *env, jclass clazz, const char *name, const char *sig) {
jmethodID m = (*env)->GetStaticMethodID(env, clazz, name, sig);
if (m == 0) {
(*env)->ExceptionClear(env);
LOG_FATAL("cannot find method %s %s", name, sig);
return 0;
}
return m;
}
static jmethodID key_rune_method;
jint JNI_OnLoad(JavaVM* vm, void* reserved) {
JNIEnv* env;
if ((*vm)->GetEnv(vm, (void**)&env, JNI_VERSION_1_6) != JNI_OK) {
return -1;
}
return JNI_VERSION_1_6;
}
static int main_running = 0;
// Entry point from our subclassed NativeActivity.
//
// By here, the Go runtime has been initialized (as we are running in
// -buildmode=c-shared) but the first time it is called, Go's main.main
// hasn't been called yet.
//
// The Activity may be created and destroyed multiple times throughout
// the life of a single process. Each time, onCreate is called.
void ANativeActivity_onCreate(ANativeActivity *activity, void* savedState, size_t savedStateSize) {
if (!main_running) {
JNIEnv* env = activity->env;
// Note that activity->clazz is mis-named.
current_class = (*env)->GetObjectClass(env, activity->clazz);
current_class = (*env)->NewGlobalRef(env, current_class);
key_rune_method = find_static_method(env, current_class, "getRune", "(III)I");
setCurrentContext(activity->vm, (*env)->NewGlobalRef(env, activity->clazz));
// Set TMPDIR.
jmethodID gettmpdir = find_method(env, current_class, "getTmpdir", "()Ljava/lang/String;");
jstring jpath = (jstring)(*env)->CallObjectMethod(env, activity->clazz, gettmpdir, NULL);
const char* tmpdir = (*env)->GetStringUTFChars(env, jpath, NULL);
if (setenv("TMPDIR", tmpdir, 1) != 0) {
LOG_INFO("setenv(\"TMPDIR\", \"%s\", 1) failed: %d", tmpdir, errno);
}
(*env)->ReleaseStringUTFChars(env, jpath, tmpdir);
// Call the Go main.main.
uintptr_t mainPC = (uintptr_t)dlsym(RTLD_DEFAULT, "main.main");
if (!mainPC) {
LOG_FATAL("missing main.main");
}
callMain(mainPC);
main_running = 1;
}
// These functions match the methods on Activity, described at
// http://developer.android.com/reference/android/app/Activity.html
//
// Note that onNativeWindowResized is not called on resize. Avoid it.
// https://code.google.com/p/android/issues/detail?id=180645
activity->callbacks->onStart = onStart;
activity->callbacks->onResume = onResume;
activity->callbacks->onSaveInstanceState = onSaveInstanceState;
activity->callbacks->onPause = onPause;
activity->callbacks->onStop = onStop;
activity->callbacks->onDestroy = onDestroy;
activity->callbacks->onWindowFocusChanged = onWindowFocusChanged;
activity->callbacks->onNativeWindowCreated = onNativeWindowCreated;
activity->callbacks->onNativeWindowRedrawNeeded = onNativeWindowRedrawNeeded;
activity->callbacks->onNativeWindowDestroyed = onNativeWindowDestroyed;
activity->callbacks->onInputQueueCreated = onInputQueueCreated;
activity->callbacks->onInputQueueDestroyed = onInputQueueDestroyed;
activity->callbacks->onConfigurationChanged = onConfigurationChanged;
activity->callbacks->onLowMemory = onLowMemory;
onCreate(activity);
}
// TODO(crawshaw): Test configuration on more devices.
static const EGLint RGB_888[] = {
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
};
EGLDisplay display = NULL;
EGLSurface surface = NULL;
static char* initEGLDisplay() {
display = eglGetDisplay(EGL_DEFAULT_DISPLAY);
if (!eglInitialize(display, 0, 0)) {
return "EGL initialize failed";
}
return NULL;
}
char* createEGLSurface(ANativeWindow* window) {
char* err;
EGLint numConfigs, format;
EGLConfig config;
EGLContext context;
if (display == 0) {
if ((err = initEGLDisplay()) != NULL) {
return err;
}
}
if (!eglChooseConfig(display, RGB_888, &config, 1, &numConfigs)) {
return "EGL choose RGB_888 config failed";
}
if (numConfigs <= 0) {
return "EGL no config found";
}
eglGetConfigAttrib(display, config, EGL_NATIVE_VISUAL_ID, &format);
if (ANativeWindow_setBuffersGeometry(window, 0, 0, format) != 0) {
return "EGL set buffers geometry failed";
}
surface = eglCreateWindowSurface(display, config, window, NULL);
if (surface == EGL_NO_SURFACE) {
return "EGL create surface failed";
}
const EGLint contextAttribs[] = { EGL_CONTEXT_CLIENT_VERSION, 2, EGL_NONE };
context = eglCreateContext(display, config, EGL_NO_CONTEXT, contextAttribs);
if (eglMakeCurrent(display, surface, surface, context) == EGL_FALSE) {
return "eglMakeCurrent failed";
}
return NULL;
}
char* destroyEGLSurface() {
if (!eglDestroySurface(display, surface)) {
return "EGL destroy surface failed";
}
return NULL;
}
int32_t getKeyRune(JNIEnv* env, AInputEvent* e) {
return (int32_t)(*env)->CallStaticIntMethod(
env,
current_class,
key_rune_method,
AInputEvent_getDeviceId(e),
AKeyEvent_getKeyCode(e),
AKeyEvent_getMetaState(e)
);
}
+824
View File
@@ -0,0 +1,824 @@
// Copyright 2014 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.
//go:build android
/*
Android Apps are built with -buildmode=c-shared. They are loaded by a
running Java process.
Before any entry point is reached, a global constructor initializes the
Go runtime, calling all Go init functions. All cgo calls will block
until this is complete. Next JNI_OnLoad is called. When that is
complete, one of two entry points is called.
All-Go apps built using NativeActivity enter at ANativeActivity_onCreate.
Go libraries (for example, those built with gomobile bind) do not use
the app package initialization.
*/
package app
/*
#cgo LDFLAGS: -landroid -llog -lEGL -lGLESv2
#include <android/configuration.h>
#include <android/input.h>
#include <android/keycodes.h>
#include <android/looper.h>
#include <android/native_activity.h>
#include <android/native_window.h>
#include <EGL/egl.h>
#include <jni.h>
#include <pthread.h>
#include <stdlib.h>
extern EGLDisplay display;
extern EGLSurface surface;
char* createEGLSurface(ANativeWindow* window);
char* destroyEGLSurface();
int32_t getKeyRune(JNIEnv* env, AInputEvent* e);
*/
import "C"
import (
"fmt"
"log"
"os"
"time"
"unsafe"
"github.com/ebitengine/gomobile/app/internal/callfn"
"github.com/ebitengine/gomobile/event/key"
"github.com/ebitengine/gomobile/event/lifecycle"
"github.com/ebitengine/gomobile/event/paint"
"github.com/ebitengine/gomobile/event/size"
"github.com/ebitengine/gomobile/event/touch"
"github.com/ebitengine/gomobile/geom"
"github.com/ebitengine/gomobile/internal/mobileinit"
)
// RunOnJVM runs fn on a new goroutine locked to an OS thread with a JNIEnv.
//
// RunOnJVM blocks until the call to fn is complete. Any Java
// exception or failure to attach to the JVM is returned as an error.
//
// The function fn takes vm, the current JavaVM*,
// env, the current JNIEnv*, and
// ctx, a jobject representing the global android.context.Context.
func RunOnJVM(fn func(vm, jniEnv, ctx uintptr) error) error {
return mobileinit.RunOnJVM(fn)
}
//export setCurrentContext
func setCurrentContext(vm *C.JavaVM, ctx C.jobject) {
mobileinit.SetCurrentContext(unsafe.Pointer(vm), uintptr(ctx))
}
//export callMain
func callMain(mainPC uintptr) {
for _, name := range []string{"TMPDIR", "PATH", "LD_LIBRARY_PATH"} {
n := C.CString(name)
os.Setenv(name, C.GoString(C.getenv(n)))
C.free(unsafe.Pointer(n))
}
// Set timezone.
//
// Note that Android zoneinfo is stored in /system/usr/share/zoneinfo,
// but it is in some kind of packed TZiff file that we do not support
// yet. As a stopgap, we build a fixed zone using the tm_zone name.
var curtime C.time_t
var curtm C.struct_tm
C.time(&curtime)
C.localtime_r(&curtime, &curtm)
tzOffset := int(curtm.tm_gmtoff)
tz := C.GoString(curtm.tm_zone)
time.Local = time.FixedZone(tz, tzOffset)
go callfn.CallFn(mainPC)
}
//export onStart
func onStart(activity *C.ANativeActivity) {
}
//export onResume
func onResume(activity *C.ANativeActivity) {
}
//export onSaveInstanceState
func onSaveInstanceState(activity *C.ANativeActivity, outSize *C.size_t) unsafe.Pointer {
return nil
}
//export onPause
func onPause(activity *C.ANativeActivity) {
}
//export onStop
func onStop(activity *C.ANativeActivity) {
}
//export onCreate
func onCreate(activity *C.ANativeActivity) {
// Set the initial configuration.
//
// Note we use unbuffered channels to talk to the activity loop, and
// NativeActivity calls these callbacks sequentially, so configuration
// will be set before <-windowRedrawNeeded is processed.
windowConfigChange <- windowConfigRead(activity)
}
//export onDestroy
func onDestroy(activity *C.ANativeActivity) {
}
//export onWindowFocusChanged
func onWindowFocusChanged(activity *C.ANativeActivity, hasFocus C.int) {
}
//export onNativeWindowCreated
func onNativeWindowCreated(activity *C.ANativeActivity, window *C.ANativeWindow) {
}
//export onNativeWindowRedrawNeeded
func onNativeWindowRedrawNeeded(activity *C.ANativeActivity, window *C.ANativeWindow) {
// Called on orientation change and window resize.
// Send a request for redraw, and block this function
// until a complete draw and buffer swap is completed.
// This is required by the redraw documentation to
// avoid bad draws.
windowRedrawNeeded <- window
<-windowRedrawDone
}
//export onNativeWindowDestroyed
func onNativeWindowDestroyed(activity *C.ANativeActivity, window *C.ANativeWindow) {
windowDestroyed <- window
}
//export onInputQueueCreated
func onInputQueueCreated(activity *C.ANativeActivity, q *C.AInputQueue) {
inputQueue <- q
<-inputQueueDone
}
//export onInputQueueDestroyed
func onInputQueueDestroyed(activity *C.ANativeActivity, q *C.AInputQueue) {
inputQueue <- nil
<-inputQueueDone
}
//export onContentRectChanged
func onContentRectChanged(activity *C.ANativeActivity, rect *C.ARect) {
}
type windowConfig struct {
orientation size.Orientation
pixelsPerPt float32
}
func windowConfigRead(activity *C.ANativeActivity) windowConfig {
aconfig := C.AConfiguration_new()
C.AConfiguration_fromAssetManager(aconfig, activity.assetManager)
orient := C.AConfiguration_getOrientation(aconfig)
density := C.AConfiguration_getDensity(aconfig)
C.AConfiguration_delete(aconfig)
// Calculate the screen resolution. This value is approximate. For example,
// a physical resolution of 200 DPI may be quantized to one of the
// ACONFIGURATION_DENSITY_XXX values such as 160 or 240.
//
// A more accurate DPI could possibly be calculated from
// https://developer.android.com/reference/android/util/DisplayMetrics.html#xdpi
// but this does not appear to be accessible via the NDK. In any case, the
// hardware might not even provide a more accurate number, as the system
// does not apparently use the reported value. See golang.org/issue/13366
// for a discussion.
var dpi int
switch density {
case C.ACONFIGURATION_DENSITY_DEFAULT:
dpi = 160
case C.ACONFIGURATION_DENSITY_LOW,
C.ACONFIGURATION_DENSITY_MEDIUM,
213, // C.ACONFIGURATION_DENSITY_TV
C.ACONFIGURATION_DENSITY_HIGH,
320, // ACONFIGURATION_DENSITY_XHIGH
480, // ACONFIGURATION_DENSITY_XXHIGH
640: // ACONFIGURATION_DENSITY_XXXHIGH
dpi = int(density)
case C.ACONFIGURATION_DENSITY_NONE:
log.Print("android device reports no screen density")
dpi = 72
default:
log.Printf("android device reports unknown density: %d", density)
// All we can do is guess.
if density > 0 {
dpi = int(density)
} else {
dpi = 72
}
}
o := size.OrientationUnknown
switch orient {
case C.ACONFIGURATION_ORIENTATION_PORT:
o = size.OrientationPortrait
case C.ACONFIGURATION_ORIENTATION_LAND:
o = size.OrientationLandscape
}
return windowConfig{
orientation: o,
pixelsPerPt: float32(dpi) / 72,
}
}
//export onConfigurationChanged
func onConfigurationChanged(activity *C.ANativeActivity) {
// A rotation event first triggers onConfigurationChanged, then
// calls onNativeWindowRedrawNeeded. We extract the orientation
// here and save it for the redraw event.
windowConfigChange <- windowConfigRead(activity)
}
//export onLowMemory
func onLowMemory(activity *C.ANativeActivity) {
}
var (
inputQueue = make(chan *C.AInputQueue)
inputQueueDone = make(chan struct{})
windowDestroyed = make(chan *C.ANativeWindow)
windowRedrawNeeded = make(chan *C.ANativeWindow)
windowRedrawDone = make(chan struct{})
windowConfigChange = make(chan windowConfig)
)
func init() {
theApp.registerGLViewportFilter()
}
func main(f func(App)) {
mainUserFn = f
// TODO: merge the runInputQueue and mainUI functions?
go func() {
if err := mobileinit.RunOnJVM(runInputQueue); err != nil {
log.Fatalf("app: %v", err)
}
}()
// Preserve this OS thread for:
// 1. the attached JNI thread
// 2. the GL context
if err := mobileinit.RunOnJVM(mainUI); err != nil {
log.Fatalf("app: %v", err)
}
}
var mainUserFn func(App)
func mainUI(vm, jniEnv, ctx uintptr) error {
workAvailable := theApp.worker.WorkAvailable()
donec := make(chan struct{})
go func() {
// close the donec channel in a defer statement
// so that we could still be able to return even
// if mainUserFn panics.
defer close(donec)
mainUserFn(theApp)
}()
var pixelsPerPt float32
var orientation size.Orientation
for {
select {
case <-donec:
return nil
case cfg := <-windowConfigChange:
pixelsPerPt = cfg.pixelsPerPt
orientation = cfg.orientation
case w := <-windowRedrawNeeded:
if C.surface == nil {
if errStr := C.createEGLSurface(w); errStr != nil {
return fmt.Errorf("%s (%s)", C.GoString(errStr), eglGetError())
}
}
theApp.sendLifecycle(lifecycle.StageFocused)
widthPx := int(C.ANativeWindow_getWidth(w))
heightPx := int(C.ANativeWindow_getHeight(w))
theApp.eventsIn <- size.Event{
WidthPx: widthPx,
HeightPx: heightPx,
WidthPt: geom.Pt(float32(widthPx) / pixelsPerPt),
HeightPt: geom.Pt(float32(heightPx) / pixelsPerPt),
PixelsPerPt: pixelsPerPt,
Orientation: orientation,
}
theApp.eventsIn <- paint.Event{External: true}
case <-windowDestroyed:
if C.surface != nil {
if errStr := C.destroyEGLSurface(); errStr != nil {
return fmt.Errorf("%s (%s)", C.GoString(errStr), eglGetError())
}
}
C.surface = nil
theApp.sendLifecycle(lifecycle.StageAlive)
case <-workAvailable:
theApp.worker.DoWork()
case <-theApp.publish:
// TODO: compare a generation number to redrawGen for stale paints?
if C.surface != nil {
// eglSwapBuffers blocks until vsync.
if C.eglSwapBuffers(C.display, C.surface) == C.EGL_FALSE {
log.Printf("app: failed to swap buffers (%s)", eglGetError())
}
}
select {
case windowRedrawDone <- struct{}{}:
default:
}
theApp.publishResult <- PublishResult{}
}
}
}
func runInputQueue(vm, jniEnv, ctx uintptr) error {
env := (*C.JNIEnv)(unsafe.Pointer(jniEnv)) // not a Go heap pointer
// Android loopers select on OS file descriptors, not Go channels, so we
// translate the inputQueue channel to an ALooper_wake call.
l := C.ALooper_prepare(C.ALOOPER_PREPARE_ALLOW_NON_CALLBACKS)
pending := make(chan *C.AInputQueue, 1)
go func() {
for q := range inputQueue {
pending <- q
C.ALooper_wake(l)
}
}()
var q *C.AInputQueue
for {
if C.ALooper_pollOnce(-1, nil, nil, nil) == C.ALOOPER_POLL_WAKE {
select {
default:
case p := <-pending:
if q != nil {
processEvents(env, q)
C.AInputQueue_detachLooper(q)
}
q = p
if q != nil {
C.AInputQueue_attachLooper(q, l, 0, nil, nil)
}
inputQueueDone <- struct{}{}
}
}
if q != nil {
processEvents(env, q)
}
}
}
func processEvents(env *C.JNIEnv, q *C.AInputQueue) {
var e *C.AInputEvent
for C.AInputQueue_getEvent(q, &e) >= 0 {
if C.AInputQueue_preDispatchEvent(q, e) != 0 {
continue
}
processEvent(env, e)
C.AInputQueue_finishEvent(q, e, 0)
}
}
func processEvent(env *C.JNIEnv, e *C.AInputEvent) {
switch C.AInputEvent_getType(e) {
case C.AINPUT_EVENT_TYPE_KEY:
processKey(env, e)
case C.AINPUT_EVENT_TYPE_MOTION:
// At most one of the events in this batch is an up or down event; get its index and change.
upDownIndex := C.size_t(C.AMotionEvent_getAction(e)&C.AMOTION_EVENT_ACTION_POINTER_INDEX_MASK) >> C.AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT
upDownType := touch.TypeMove
switch C.AMotionEvent_getAction(e) & C.AMOTION_EVENT_ACTION_MASK {
case C.AMOTION_EVENT_ACTION_DOWN, C.AMOTION_EVENT_ACTION_POINTER_DOWN:
upDownType = touch.TypeBegin
case C.AMOTION_EVENT_ACTION_UP, C.AMOTION_EVENT_ACTION_POINTER_UP:
upDownType = touch.TypeEnd
}
for i, n := C.size_t(0), C.AMotionEvent_getPointerCount(e); i < n; i++ {
t := touch.TypeMove
if i == upDownIndex {
t = upDownType
}
theApp.eventsIn <- touch.Event{
X: float32(C.AMotionEvent_getX(e, i)),
Y: float32(C.AMotionEvent_getY(e, i)),
Sequence: touch.Sequence(C.AMotionEvent_getPointerId(e, i)),
Type: t,
}
}
default:
log.Printf("unknown input event, type=%d", C.AInputEvent_getType(e))
}
}
func processKey(env *C.JNIEnv, e *C.AInputEvent) {
deviceID := C.AInputEvent_getDeviceId(e)
if deviceID == 0 {
// Software keyboard input, leaving for scribe/IME.
return
}
k := key.Event{
Rune: rune(C.getKeyRune(env, e)),
Code: convAndroidKeyCode(int32(C.AKeyEvent_getKeyCode(e))),
}
switch C.AKeyEvent_getAction(e) {
case C.AKEY_EVENT_ACTION_DOWN:
k.Direction = key.DirPress
case C.AKEY_EVENT_ACTION_UP:
k.Direction = key.DirRelease
default:
k.Direction = key.DirNone
}
// TODO(crawshaw): set Modifiers.
theApp.eventsIn <- k
}
func eglGetError() string {
switch errNum := C.eglGetError(); errNum {
case C.EGL_SUCCESS:
return "EGL_SUCCESS"
case C.EGL_NOT_INITIALIZED:
return "EGL_NOT_INITIALIZED"
case C.EGL_BAD_ACCESS:
return "EGL_BAD_ACCESS"
case C.EGL_BAD_ALLOC:
return "EGL_BAD_ALLOC"
case C.EGL_BAD_ATTRIBUTE:
return "EGL_BAD_ATTRIBUTE"
case C.EGL_BAD_CONTEXT:
return "EGL_BAD_CONTEXT"
case C.EGL_BAD_CONFIG:
return "EGL_BAD_CONFIG"
case C.EGL_BAD_CURRENT_SURFACE:
return "EGL_BAD_CURRENT_SURFACE"
case C.EGL_BAD_DISPLAY:
return "EGL_BAD_DISPLAY"
case C.EGL_BAD_SURFACE:
return "EGL_BAD_SURFACE"
case C.EGL_BAD_MATCH:
return "EGL_BAD_MATCH"
case C.EGL_BAD_PARAMETER:
return "EGL_BAD_PARAMETER"
case C.EGL_BAD_NATIVE_PIXMAP:
return "EGL_BAD_NATIVE_PIXMAP"
case C.EGL_BAD_NATIVE_WINDOW:
return "EGL_BAD_NATIVE_WINDOW"
case C.EGL_CONTEXT_LOST:
return "EGL_CONTEXT_LOST"
default:
return fmt.Sprintf("Unknown EGL err: %d", errNum)
}
}
func convAndroidKeyCode(aKeyCode int32) key.Code {
// Many Android key codes do not map into USB HID codes.
// For those, key.CodeUnknown is returned. This switch has all
// cases, even the unknown ones, to serve as a documentation
// and search aid.
switch aKeyCode {
case C.AKEYCODE_UNKNOWN:
case C.AKEYCODE_SOFT_LEFT:
case C.AKEYCODE_SOFT_RIGHT:
case C.AKEYCODE_HOME:
return key.CodeHome
case C.AKEYCODE_BACK:
case C.AKEYCODE_CALL:
case C.AKEYCODE_ENDCALL:
case C.AKEYCODE_0:
return key.Code0
case C.AKEYCODE_1:
return key.Code1
case C.AKEYCODE_2:
return key.Code2
case C.AKEYCODE_3:
return key.Code3
case C.AKEYCODE_4:
return key.Code4
case C.AKEYCODE_5:
return key.Code5
case C.AKEYCODE_6:
return key.Code6
case C.AKEYCODE_7:
return key.Code7
case C.AKEYCODE_8:
return key.Code8
case C.AKEYCODE_9:
return key.Code9
case C.AKEYCODE_STAR:
case C.AKEYCODE_POUND:
case C.AKEYCODE_DPAD_UP:
case C.AKEYCODE_DPAD_DOWN:
case C.AKEYCODE_DPAD_LEFT:
case C.AKEYCODE_DPAD_RIGHT:
case C.AKEYCODE_DPAD_CENTER:
case C.AKEYCODE_VOLUME_UP:
return key.CodeVolumeUp
case C.AKEYCODE_VOLUME_DOWN:
return key.CodeVolumeDown
case C.AKEYCODE_POWER:
case C.AKEYCODE_CAMERA:
case C.AKEYCODE_CLEAR:
case C.AKEYCODE_A:
return key.CodeA
case C.AKEYCODE_B:
return key.CodeB
case C.AKEYCODE_C:
return key.CodeC
case C.AKEYCODE_D:
return key.CodeD
case C.AKEYCODE_E:
return key.CodeE
case C.AKEYCODE_F:
return key.CodeF
case C.AKEYCODE_G:
return key.CodeG
case C.AKEYCODE_H:
return key.CodeH
case C.AKEYCODE_I:
return key.CodeI
case C.AKEYCODE_J:
return key.CodeJ
case C.AKEYCODE_K:
return key.CodeK
case C.AKEYCODE_L:
return key.CodeL
case C.AKEYCODE_M:
return key.CodeM
case C.AKEYCODE_N:
return key.CodeN
case C.AKEYCODE_O:
return key.CodeO
case C.AKEYCODE_P:
return key.CodeP
case C.AKEYCODE_Q:
return key.CodeQ
case C.AKEYCODE_R:
return key.CodeR
case C.AKEYCODE_S:
return key.CodeS
case C.AKEYCODE_T:
return key.CodeT
case C.AKEYCODE_U:
return key.CodeU
case C.AKEYCODE_V:
return key.CodeV
case C.AKEYCODE_W:
return key.CodeW
case C.AKEYCODE_X:
return key.CodeX
case C.AKEYCODE_Y:
return key.CodeY
case C.AKEYCODE_Z:
return key.CodeZ
case C.AKEYCODE_COMMA:
return key.CodeComma
case C.AKEYCODE_PERIOD:
return key.CodeFullStop
case C.AKEYCODE_ALT_LEFT:
return key.CodeLeftAlt
case C.AKEYCODE_ALT_RIGHT:
return key.CodeRightAlt
case C.AKEYCODE_SHIFT_LEFT:
return key.CodeLeftShift
case C.AKEYCODE_SHIFT_RIGHT:
return key.CodeRightShift
case C.AKEYCODE_TAB:
return key.CodeTab
case C.AKEYCODE_SPACE:
return key.CodeSpacebar
case C.AKEYCODE_SYM:
case C.AKEYCODE_EXPLORER:
case C.AKEYCODE_ENVELOPE:
case C.AKEYCODE_ENTER:
return key.CodeReturnEnter
case C.AKEYCODE_DEL:
return key.CodeDeleteBackspace
case C.AKEYCODE_GRAVE:
return key.CodeGraveAccent
case C.AKEYCODE_MINUS:
return key.CodeHyphenMinus
case C.AKEYCODE_EQUALS:
return key.CodeEqualSign
case C.AKEYCODE_LEFT_BRACKET:
return key.CodeLeftSquareBracket
case C.AKEYCODE_RIGHT_BRACKET:
return key.CodeRightSquareBracket
case C.AKEYCODE_BACKSLASH:
return key.CodeBackslash
case C.AKEYCODE_SEMICOLON:
return key.CodeSemicolon
case C.AKEYCODE_APOSTROPHE:
return key.CodeApostrophe
case C.AKEYCODE_SLASH:
return key.CodeSlash
case C.AKEYCODE_AT:
case C.AKEYCODE_NUM:
case C.AKEYCODE_HEADSETHOOK:
case C.AKEYCODE_FOCUS:
case C.AKEYCODE_PLUS:
case C.AKEYCODE_MENU:
case C.AKEYCODE_NOTIFICATION:
case C.AKEYCODE_SEARCH:
case C.AKEYCODE_MEDIA_PLAY_PAUSE:
case C.AKEYCODE_MEDIA_STOP:
case C.AKEYCODE_MEDIA_NEXT:
case C.AKEYCODE_MEDIA_PREVIOUS:
case C.AKEYCODE_MEDIA_REWIND:
case C.AKEYCODE_MEDIA_FAST_FORWARD:
case C.AKEYCODE_MUTE:
case C.AKEYCODE_PAGE_UP:
return key.CodePageUp
case C.AKEYCODE_PAGE_DOWN:
return key.CodePageDown
case C.AKEYCODE_PICTSYMBOLS:
case C.AKEYCODE_SWITCH_CHARSET:
case C.AKEYCODE_BUTTON_A:
case C.AKEYCODE_BUTTON_B:
case C.AKEYCODE_BUTTON_C:
case C.AKEYCODE_BUTTON_X:
case C.AKEYCODE_BUTTON_Y:
case C.AKEYCODE_BUTTON_Z:
case C.AKEYCODE_BUTTON_L1:
case C.AKEYCODE_BUTTON_R1:
case C.AKEYCODE_BUTTON_L2:
case C.AKEYCODE_BUTTON_R2:
case C.AKEYCODE_BUTTON_THUMBL:
case C.AKEYCODE_BUTTON_THUMBR:
case C.AKEYCODE_BUTTON_START:
case C.AKEYCODE_BUTTON_SELECT:
case C.AKEYCODE_BUTTON_MODE:
case C.AKEYCODE_ESCAPE:
return key.CodeEscape
case C.AKEYCODE_FORWARD_DEL:
return key.CodeDeleteForward
case C.AKEYCODE_CTRL_LEFT:
return key.CodeLeftControl
case C.AKEYCODE_CTRL_RIGHT:
return key.CodeRightControl
case C.AKEYCODE_CAPS_LOCK:
return key.CodeCapsLock
case C.AKEYCODE_SCROLL_LOCK:
case C.AKEYCODE_META_LEFT:
return key.CodeLeftGUI
case C.AKEYCODE_META_RIGHT:
return key.CodeRightGUI
case C.AKEYCODE_FUNCTION:
case C.AKEYCODE_SYSRQ:
case C.AKEYCODE_BREAK:
case C.AKEYCODE_MOVE_HOME:
case C.AKEYCODE_MOVE_END:
case C.AKEYCODE_INSERT:
return key.CodeInsert
case C.AKEYCODE_FORWARD:
case C.AKEYCODE_MEDIA_PLAY:
case C.AKEYCODE_MEDIA_PAUSE:
case C.AKEYCODE_MEDIA_CLOSE:
case C.AKEYCODE_MEDIA_EJECT:
case C.AKEYCODE_MEDIA_RECORD:
case C.AKEYCODE_F1:
return key.CodeF1
case C.AKEYCODE_F2:
return key.CodeF2
case C.AKEYCODE_F3:
return key.CodeF3
case C.AKEYCODE_F4:
return key.CodeF4
case C.AKEYCODE_F5:
return key.CodeF5
case C.AKEYCODE_F6:
return key.CodeF6
case C.AKEYCODE_F7:
return key.CodeF7
case C.AKEYCODE_F8:
return key.CodeF8
case C.AKEYCODE_F9:
return key.CodeF9
case C.AKEYCODE_F10:
return key.CodeF10
case C.AKEYCODE_F11:
return key.CodeF11
case C.AKEYCODE_F12:
return key.CodeF12
case C.AKEYCODE_NUM_LOCK:
return key.CodeKeypadNumLock
case C.AKEYCODE_NUMPAD_0:
return key.CodeKeypad0
case C.AKEYCODE_NUMPAD_1:
return key.CodeKeypad1
case C.AKEYCODE_NUMPAD_2:
return key.CodeKeypad2
case C.AKEYCODE_NUMPAD_3:
return key.CodeKeypad3
case C.AKEYCODE_NUMPAD_4:
return key.CodeKeypad4
case C.AKEYCODE_NUMPAD_5:
return key.CodeKeypad5
case C.AKEYCODE_NUMPAD_6:
return key.CodeKeypad6
case C.AKEYCODE_NUMPAD_7:
return key.CodeKeypad7
case C.AKEYCODE_NUMPAD_8:
return key.CodeKeypad8
case C.AKEYCODE_NUMPAD_9:
return key.CodeKeypad9
case C.AKEYCODE_NUMPAD_DIVIDE:
return key.CodeKeypadSlash
case C.AKEYCODE_NUMPAD_MULTIPLY:
return key.CodeKeypadAsterisk
case C.AKEYCODE_NUMPAD_SUBTRACT:
return key.CodeKeypadHyphenMinus
case C.AKEYCODE_NUMPAD_ADD:
return key.CodeKeypadPlusSign
case C.AKEYCODE_NUMPAD_DOT:
return key.CodeKeypadFullStop
case C.AKEYCODE_NUMPAD_COMMA:
case C.AKEYCODE_NUMPAD_ENTER:
return key.CodeKeypadEnter
case C.AKEYCODE_NUMPAD_EQUALS:
return key.CodeKeypadEqualSign
case C.AKEYCODE_NUMPAD_LEFT_PAREN:
case C.AKEYCODE_NUMPAD_RIGHT_PAREN:
case C.AKEYCODE_VOLUME_MUTE:
return key.CodeMute
case C.AKEYCODE_INFO:
case C.AKEYCODE_CHANNEL_UP:
case C.AKEYCODE_CHANNEL_DOWN:
case C.AKEYCODE_ZOOM_IN:
case C.AKEYCODE_ZOOM_OUT:
case C.AKEYCODE_TV:
case C.AKEYCODE_WINDOW:
case C.AKEYCODE_GUIDE:
case C.AKEYCODE_DVR:
case C.AKEYCODE_BOOKMARK:
case C.AKEYCODE_CAPTIONS:
case C.AKEYCODE_SETTINGS:
case C.AKEYCODE_TV_POWER:
case C.AKEYCODE_TV_INPUT:
case C.AKEYCODE_STB_POWER:
case C.AKEYCODE_STB_INPUT:
case C.AKEYCODE_AVR_POWER:
case C.AKEYCODE_AVR_INPUT:
case C.AKEYCODE_PROG_RED:
case C.AKEYCODE_PROG_GREEN:
case C.AKEYCODE_PROG_YELLOW:
case C.AKEYCODE_PROG_BLUE:
case C.AKEYCODE_APP_SWITCH:
case C.AKEYCODE_BUTTON_1:
case C.AKEYCODE_BUTTON_2:
case C.AKEYCODE_BUTTON_3:
case C.AKEYCODE_BUTTON_4:
case C.AKEYCODE_BUTTON_5:
case C.AKEYCODE_BUTTON_6:
case C.AKEYCODE_BUTTON_7:
case C.AKEYCODE_BUTTON_8:
case C.AKEYCODE_BUTTON_9:
case C.AKEYCODE_BUTTON_10:
case C.AKEYCODE_BUTTON_11:
case C.AKEYCODE_BUTTON_12:
case C.AKEYCODE_BUTTON_13:
case C.AKEYCODE_BUTTON_14:
case C.AKEYCODE_BUTTON_15:
case C.AKEYCODE_BUTTON_16:
case C.AKEYCODE_LANGUAGE_SWITCH:
case C.AKEYCODE_MANNER_MODE:
case C.AKEYCODE_3D_MODE:
case C.AKEYCODE_CONTACTS:
case C.AKEYCODE_CALENDAR:
case C.AKEYCODE_MUSIC:
case C.AKEYCODE_CALCULATOR:
}
/* Defined in an NDK API version beyond what we use today:
C.AKEYCODE_ASSIST
C.AKEYCODE_BRIGHTNESS_DOWN
C.AKEYCODE_BRIGHTNESS_UP
C.AKEYCODE_EISU
C.AKEYCODE_HENKAN
C.AKEYCODE_KANA
C.AKEYCODE_KATAKANA_HIRAGANA
C.AKEYCODE_MEDIA_AUDIO_TRACK
C.AKEYCODE_MUHENKAN
C.AKEYCODE_RO
C.AKEYCODE_YEN
C.AKEYCODE_ZENKAKU_HANKAKU
*/
return key.CodeUnknown
}
+213
View File
@@ -0,0 +1,213 @@
// Copyright 2014 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.
//go:build linux || darwin || windows
package app
import (
"github.com/ebitengine/gomobile/event/lifecycle"
"github.com/ebitengine/gomobile/event/size"
"github.com/ebitengine/gomobile/gl"
_ "github.com/ebitengine/gomobile/internal/mobileinit"
)
// Main is called by the main.main function to run the mobile application.
//
// It calls f on the App, in a separate goroutine, as some OS-specific
// libraries require being on 'the main thread'.
func Main(f func(App)) {
main(f)
}
// App is how a GUI mobile application interacts with the OS.
type App interface {
// Events returns the events channel. It carries events from the system to
// the app. The type of such events include:
// - lifecycle.Event
// - mouse.Event
// - paint.Event
// - size.Event
// - touch.Event
// from the github.com/ebitengine/gomobile/event/etc packages. Other packages may
// define other event types that are carried on this channel.
Events() <-chan interface{}
// Send sends an event on the events channel. It does not block.
Send(event interface{})
// Publish flushes any pending drawing commands, such as OpenGL calls, and
// swaps the back buffer to the screen.
Publish() PublishResult
// TODO: replace filters (and the Events channel) with a NextEvent method?
// Filter calls each registered event filter function in sequence.
Filter(event interface{}) interface{}
// RegisterFilter registers a event filter function to be called by Filter. The
// function can return a different event, or return nil to consume the event,
// but the function can also return its argument unchanged, where its purpose
// is to trigger a side effect rather than modify the event.
RegisterFilter(f func(interface{}) interface{})
}
// PublishResult is the result of an App.Publish call.
type PublishResult struct {
// BackBufferPreserved is whether the contents of the back buffer was
// preserved. If false, the contents are undefined.
BackBufferPreserved bool
}
var theApp = &app{
eventsOut: make(chan interface{}),
lifecycleStage: lifecycle.StageDead,
publish: make(chan struct{}),
publishResult: make(chan PublishResult),
}
func init() {
theApp.eventsIn = pump(theApp.eventsOut)
theApp.glctx, theApp.worker = gl.NewContext()
}
func (a *app) sendLifecycle(to lifecycle.Stage) {
if a.lifecycleStage == to {
return
}
a.eventsIn <- lifecycle.Event{
From: a.lifecycleStage,
To: to,
DrawContext: a.glctx,
}
a.lifecycleStage = to
}
type app struct {
filters []func(interface{}) interface{}
eventsOut chan interface{}
eventsIn chan interface{}
lifecycleStage lifecycle.Stage
publish chan struct{}
publishResult chan PublishResult
glctx gl.Context
worker gl.Worker
}
func (a *app) Events() <-chan interface{} {
return a.eventsOut
}
func (a *app) Send(event interface{}) {
a.eventsIn <- event
}
func (a *app) Publish() PublishResult {
// gl.Flush is a lightweight (on modern GL drivers) blocking call
// that ensures all GL functions pending in the gl package have
// been passed onto the GL driver before the app package attempts
// to swap the screen buffer.
//
// This enforces that the final receive (for this paint cycle) on
// gl.WorkAvailable happens before the send on endPaint.
a.glctx.Flush()
a.publish <- struct{}{}
return <-a.publishResult
}
func (a *app) Filter(event interface{}) interface{} {
for _, f := range a.filters {
event = f(event)
}
return event
}
func (a *app) RegisterFilter(f func(interface{}) interface{}) {
a.filters = append(a.filters, f)
}
type stopPumping struct{}
// pump returns a channel src such that sending on src will eventually send on
// dst, in order, but that src will always be ready to send/receive soon, even
// if dst currently isn't. It is effectively an infinitely buffered channel.
//
// In particular, goroutine A sending on src will not deadlock even if goroutine
// B that's responsible for receiving on dst is currently blocked trying to
// send to A on a separate channel.
//
// Send a stopPumping on the src channel to close the dst channel after all queued
// events are sent on dst. After that, other goroutines can still send to src,
// so that such sends won't block forever, but such events will be ignored.
func pump(dst chan interface{}) (src chan interface{}) {
src = make(chan interface{})
go func() {
// initialSize is the initial size of the circular buffer. It must be a
// power of 2.
const initialSize = 16
i, j, buf, mask := 0, 0, make([]interface{}, initialSize), initialSize-1
srcActive := true
for {
maybeDst := dst
if i == j {
maybeDst = nil
}
if maybeDst == nil && !srcActive {
// Pump is stopped and empty.
break
}
select {
case maybeDst <- buf[i&mask]:
buf[i&mask] = nil
i++
case e := <-src:
if _, ok := e.(stopPumping); ok {
srcActive = false
continue
}
if !srcActive {
continue
}
// Allocate a bigger buffer if necessary.
if i+len(buf) == j {
b := make([]interface{}, 2*len(buf))
n := copy(b, buf[j&mask:])
copy(b[n:], buf[:j&mask])
i, j = 0, len(buf)
buf, mask = b, len(b)-1
}
buf[j&mask] = e
j++
}
}
close(dst)
// Block forever.
for range src {
}
}()
return src
}
// TODO: do this for all build targets, not just linux (x11 and Android)? If
// so, should package gl instead of this package call RegisterFilter??
//
// TODO: does Android need this?? It seems to work without it (Nexus 7,
// KitKat). If only x11 needs this, should we move this to x11.go??
func (a *app) registerGLViewportFilter() {
a.RegisterFilter(func(e interface{}) interface{} {
if e, ok := e.(size.Event); ok {
a.glctx.Viewport(0, 0, e.WidthPx, e.HeightPx)
}
return e
})
}
+13
View File
@@ -0,0 +1,13 @@
// 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.
package app
import (
"log"
)
func main(f func(a App)) {
log.Fatal("main is not implemented on Windows")
}
+495
View File
@@ -0,0 +1,495 @@
// Copyright 2014 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.
//go:build darwin && !ios
package app
// Simple on-screen app debugging for OS X. Not an officially supported
// development target for apps, as screens with mice are very different
// than screens with touch panels.
/*
#cgo CFLAGS: -x objective-c -DGL_SILENCE_DEPRECATION
#cgo LDFLAGS: -framework Cocoa -framework OpenGL
#import <Carbon/Carbon.h> // for HIToolbox/Events.h
#import <Cocoa/Cocoa.h>
#include <pthread.h>
void runApp(void);
void stopApp(void);
void makeCurrentContext(GLintptr);
uint64 threadID();
*/
import "C"
import (
"log"
"runtime"
"sync"
"github.com/ebitengine/gomobile/event/key"
"github.com/ebitengine/gomobile/event/lifecycle"
"github.com/ebitengine/gomobile/event/paint"
"github.com/ebitengine/gomobile/event/size"
"github.com/ebitengine/gomobile/event/touch"
"github.com/ebitengine/gomobile/geom"
)
var initThreadID uint64
func init() {
// Lock the goroutine responsible for initialization to an OS thread.
// This means the goroutine running main (and calling runApp below)
// is locked to the OS thread that started the program. This is
// necessary for the correct delivery of Cocoa events to the process.
//
// A discussion on this topic:
// https://groups.google.com/forum/#!msg/golang-nuts/IiWZ2hUuLDA/SNKYYZBelsYJ
runtime.LockOSThread()
initThreadID = uint64(C.threadID())
}
func main(f func(App)) {
if tid := uint64(C.threadID()); tid != initThreadID {
log.Fatalf("app.Main called on thread %d, but app.init ran on %d", tid, initThreadID)
}
go func() {
f(theApp)
C.stopApp()
}()
C.runApp()
}
// loop is the primary drawing loop.
//
// After Cocoa has captured the initial OS thread for processing Cocoa
// events in runApp, it starts loop on another goroutine. It is locked
// to an OS thread for its OpenGL context.
//
// The loop processes GL calls until a publish event appears.
// Then it runs any remaining GL calls and flushes the screen.
//
// As NSOpenGLCPSwapInterval is set to 1, the call to CGLFlushDrawable
// blocks until the screen refresh.
func (a *app) loop(ctx C.GLintptr) {
runtime.LockOSThread()
C.makeCurrentContext(ctx)
workAvailable := a.worker.WorkAvailable()
for {
select {
case <-workAvailable:
a.worker.DoWork()
case <-theApp.publish:
loop1:
for {
select {
case <-workAvailable:
a.worker.DoWork()
default:
break loop1
}
}
C.CGLFlushDrawable(C.CGLGetCurrentContext())
theApp.publishResult <- PublishResult{}
select {
case drawDone <- struct{}{}:
default:
}
}
}
}
var drawDone = make(chan struct{})
// drawgl is used by Cocoa to occasionally request screen updates.
//
//export drawgl
func drawgl() {
switch theApp.lifecycleStage {
case lifecycle.StageFocused, lifecycle.StageVisible:
theApp.Send(paint.Event{
External: true,
})
<-drawDone
}
}
//export startloop
func startloop(ctx C.GLintptr) {
go theApp.loop(ctx)
}
var windowHeightPx float32
//export setGeom
func setGeom(pixelsPerPt float32, widthPx, heightPx int) {
windowHeightPx = float32(heightPx)
theApp.eventsIn <- size.Event{
WidthPx: widthPx,
HeightPx: heightPx,
WidthPt: geom.Pt(float32(widthPx) / pixelsPerPt),
HeightPt: geom.Pt(float32(heightPx) / pixelsPerPt),
PixelsPerPt: pixelsPerPt,
}
}
var touchEvents struct {
sync.Mutex
pending []touch.Event
}
func sendTouch(t touch.Type, x, y float32) {
theApp.eventsIn <- touch.Event{
X: x,
Y: windowHeightPx - y,
Sequence: 0,
Type: t,
}
}
//export eventMouseDown
func eventMouseDown(x, y float32) { sendTouch(touch.TypeBegin, x, y) }
//export eventMouseDragged
func eventMouseDragged(x, y float32) { sendTouch(touch.TypeMove, x, y) }
//export eventMouseEnd
func eventMouseEnd(x, y float32) { sendTouch(touch.TypeEnd, x, y) }
//export lifecycleDead
func lifecycleDead() { theApp.sendLifecycle(lifecycle.StageDead) }
//export eventKey
func eventKey(runeVal int32, direction uint8, code uint16, flags uint32) {
var modifiers key.Modifiers
for _, mod := range mods {
if flags&mod.flags == mod.flags {
modifiers |= mod.mod
}
}
theApp.eventsIn <- key.Event{
Rune: convRune(rune(runeVal)),
Code: convVirtualKeyCode(code),
Modifiers: modifiers,
Direction: key.Direction(direction),
}
}
//export eventFlags
func eventFlags(flags uint32) {
for _, mod := range mods {
if flags&mod.flags == mod.flags && lastFlags&mod.flags != mod.flags {
eventKey(-1, uint8(key.DirPress), mod.code, flags)
}
if lastFlags&mod.flags == mod.flags && flags&mod.flags != mod.flags {
eventKey(-1, uint8(key.DirRelease), mod.code, flags)
}
}
lastFlags = flags
}
var lastFlags uint32
var mods = [...]struct {
flags uint32
code uint16
mod key.Modifiers
}{
// Left and right variants of modifier keys have their own masks,
// but they are not documented. These were determined empirically.
{1<<17 | 0x102, C.kVK_Shift, key.ModShift},
{1<<17 | 0x104, C.kVK_RightShift, key.ModShift},
{1<<18 | 0x101, C.kVK_Control, key.ModControl},
// TODO key.ControlRight
{1<<19 | 0x120, C.kVK_Option, key.ModAlt},
{1<<19 | 0x140, C.kVK_RightOption, key.ModAlt},
{1<<20 | 0x108, C.kVK_Command, key.ModMeta},
{1<<20 | 0x110, C.kVK_Command, key.ModMeta}, // TODO: missing kVK_RightCommand
}
//export lifecycleAlive
func lifecycleAlive() { theApp.sendLifecycle(lifecycle.StageAlive) }
//export lifecycleVisible
func lifecycleVisible() {
theApp.sendLifecycle(lifecycle.StageVisible)
}
//export lifecycleFocused
func lifecycleFocused() { theApp.sendLifecycle(lifecycle.StageFocused) }
// convRune marks the Carbon/Cocoa private-range unicode rune representing
// a non-unicode key event to -1, used for Rune in the key package.
//
// http://www.unicode.org/Public/MAPPINGS/VENDORS/APPLE/CORPCHAR.TXT
func convRune(r rune) rune {
if '\uE000' <= r && r <= '\uF8FF' {
return -1
}
return r
}
// convVirtualKeyCode converts a Carbon/Cocoa virtual key code number
// into the standard keycodes used by the key package.
//
// To get a sense of the key map, see the diagram on
//
// http://boredzo.org/blog/archives/2007-05-22/virtual-key-codes
func convVirtualKeyCode(vkcode uint16) key.Code {
switch vkcode {
case C.kVK_ANSI_A:
return key.CodeA
case C.kVK_ANSI_B:
return key.CodeB
case C.kVK_ANSI_C:
return key.CodeC
case C.kVK_ANSI_D:
return key.CodeD
case C.kVK_ANSI_E:
return key.CodeE
case C.kVK_ANSI_F:
return key.CodeF
case C.kVK_ANSI_G:
return key.CodeG
case C.kVK_ANSI_H:
return key.CodeH
case C.kVK_ANSI_I:
return key.CodeI
case C.kVK_ANSI_J:
return key.CodeJ
case C.kVK_ANSI_K:
return key.CodeK
case C.kVK_ANSI_L:
return key.CodeL
case C.kVK_ANSI_M:
return key.CodeM
case C.kVK_ANSI_N:
return key.CodeN
case C.kVK_ANSI_O:
return key.CodeO
case C.kVK_ANSI_P:
return key.CodeP
case C.kVK_ANSI_Q:
return key.CodeQ
case C.kVK_ANSI_R:
return key.CodeR
case C.kVK_ANSI_S:
return key.CodeS
case C.kVK_ANSI_T:
return key.CodeT
case C.kVK_ANSI_U:
return key.CodeU
case C.kVK_ANSI_V:
return key.CodeV
case C.kVK_ANSI_W:
return key.CodeW
case C.kVK_ANSI_X:
return key.CodeX
case C.kVK_ANSI_Y:
return key.CodeY
case C.kVK_ANSI_Z:
return key.CodeZ
case C.kVK_ANSI_1:
return key.Code1
case C.kVK_ANSI_2:
return key.Code2
case C.kVK_ANSI_3:
return key.Code3
case C.kVK_ANSI_4:
return key.Code4
case C.kVK_ANSI_5:
return key.Code5
case C.kVK_ANSI_6:
return key.Code6
case C.kVK_ANSI_7:
return key.Code7
case C.kVK_ANSI_8:
return key.Code8
case C.kVK_ANSI_9:
return key.Code9
case C.kVK_ANSI_0:
return key.Code0
// TODO: move the rest of these codes to constants in key.go
// if we are happy with them.
case C.kVK_Return:
return key.CodeReturnEnter
case C.kVK_Escape:
return key.CodeEscape
case C.kVK_Delete:
return key.CodeDeleteBackspace
case C.kVK_Tab:
return key.CodeTab
case C.kVK_Space:
return key.CodeSpacebar
case C.kVK_ANSI_Minus:
return key.CodeHyphenMinus
case C.kVK_ANSI_Equal:
return key.CodeEqualSign
case C.kVK_ANSI_LeftBracket:
return key.CodeLeftSquareBracket
case C.kVK_ANSI_RightBracket:
return key.CodeRightSquareBracket
case C.kVK_ANSI_Backslash:
return key.CodeBackslash
// 50: Keyboard Non-US "#" and ~
case C.kVK_ANSI_Semicolon:
return key.CodeSemicolon
case C.kVK_ANSI_Quote:
return key.CodeApostrophe
case C.kVK_ANSI_Grave:
return key.CodeGraveAccent
case C.kVK_ANSI_Comma:
return key.CodeComma
case C.kVK_ANSI_Period:
return key.CodeFullStop
case C.kVK_ANSI_Slash:
return key.CodeSlash
case C.kVK_CapsLock:
return key.CodeCapsLock
case C.kVK_F1:
return key.CodeF1
case C.kVK_F2:
return key.CodeF2
case C.kVK_F3:
return key.CodeF3
case C.kVK_F4:
return key.CodeF4
case C.kVK_F5:
return key.CodeF5
case C.kVK_F6:
return key.CodeF6
case C.kVK_F7:
return key.CodeF7
case C.kVK_F8:
return key.CodeF8
case C.kVK_F9:
return key.CodeF9
case C.kVK_F10:
return key.CodeF10
case C.kVK_F11:
return key.CodeF11
case C.kVK_F12:
return key.CodeF12
// 70: PrintScreen
// 71: Scroll Lock
// 72: Pause
// 73: Insert
case C.kVK_Home:
return key.CodeHome
case C.kVK_PageUp:
return key.CodePageUp
case C.kVK_ForwardDelete:
return key.CodeDeleteForward
case C.kVK_End:
return key.CodeEnd
case C.kVK_PageDown:
return key.CodePageDown
case C.kVK_RightArrow:
return key.CodeRightArrow
case C.kVK_LeftArrow:
return key.CodeLeftArrow
case C.kVK_DownArrow:
return key.CodeDownArrow
case C.kVK_UpArrow:
return key.CodeUpArrow
case C.kVK_ANSI_KeypadClear:
return key.CodeKeypadNumLock
case C.kVK_ANSI_KeypadDivide:
return key.CodeKeypadSlash
case C.kVK_ANSI_KeypadMultiply:
return key.CodeKeypadAsterisk
case C.kVK_ANSI_KeypadMinus:
return key.CodeKeypadHyphenMinus
case C.kVK_ANSI_KeypadPlus:
return key.CodeKeypadPlusSign
case C.kVK_ANSI_KeypadEnter:
return key.CodeKeypadEnter
case C.kVK_ANSI_Keypad1:
return key.CodeKeypad1
case C.kVK_ANSI_Keypad2:
return key.CodeKeypad2
case C.kVK_ANSI_Keypad3:
return key.CodeKeypad3
case C.kVK_ANSI_Keypad4:
return key.CodeKeypad4
case C.kVK_ANSI_Keypad5:
return key.CodeKeypad5
case C.kVK_ANSI_Keypad6:
return key.CodeKeypad6
case C.kVK_ANSI_Keypad7:
return key.CodeKeypad7
case C.kVK_ANSI_Keypad8:
return key.CodeKeypad8
case C.kVK_ANSI_Keypad9:
return key.CodeKeypad9
case C.kVK_ANSI_Keypad0:
return key.CodeKeypad0
case C.kVK_ANSI_KeypadDecimal:
return key.CodeKeypadFullStop
case C.kVK_ANSI_KeypadEquals:
return key.CodeKeypadEqualSign
case C.kVK_F13:
return key.CodeF13
case C.kVK_F14:
return key.CodeF14
case C.kVK_F15:
return key.CodeF15
case C.kVK_F16:
return key.CodeF16
case C.kVK_F17:
return key.CodeF17
case C.kVK_F18:
return key.CodeF18
case C.kVK_F19:
return key.CodeF19
case C.kVK_F20:
return key.CodeF20
// 116: Keyboard Execute
case C.kVK_Help:
return key.CodeHelp
// 118: Keyboard Menu
// 119: Keyboard Select
// 120: Keyboard Stop
// 121: Keyboard Again
// 122: Keyboard Undo
// 123: Keyboard Cut
// 124: Keyboard Copy
// 125: Keyboard Paste
// 126: Keyboard Find
case C.kVK_Mute:
return key.CodeMute
case C.kVK_VolumeUp:
return key.CodeVolumeUp
case C.kVK_VolumeDown:
return key.CodeVolumeDown
// 130: Keyboard Locking Caps Lock
// 131: Keyboard Locking Num Lock
// 132: Keyboard Locking Scroll Lock
// 133: Keyboard Comma
// 134: Keyboard Equal Sign
// ...: Bunch of stuff
case C.kVK_Control:
return key.CodeLeftControl
case C.kVK_Shift:
return key.CodeLeftShift
case C.kVK_Option:
return key.CodeLeftAlt
case C.kVK_Command:
return key.CodeLeftGUI
case C.kVK_RightControl:
return key.CodeRightControl
case C.kVK_RightShift:
return key.CodeRightShift
case C.kVK_RightOption:
return key.CodeRightAlt
// TODO key.CodeRightGUI
default:
return key.CodeUnknown
}
}
+251
View File
@@ -0,0 +1,251 @@
// Copyright 2014 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.
//go:build darwin && !ios
// +build darwin
// +build !ios
#include "_cgo_export.h"
#include <pthread.h>
#include <stdio.h>
#import <Cocoa/Cocoa.h>
#import <Foundation/Foundation.h>
#import <OpenGL/gl3.h>
void makeCurrentContext(GLintptr context) {
NSOpenGLContext* ctx = (NSOpenGLContext*)context;
[ctx makeCurrentContext];
}
uint64 threadID() {
uint64 id;
if (pthread_threadid_np(pthread_self(), &id)) {
abort();
}
return id;
}
@interface MobileGLView : NSOpenGLView<NSApplicationDelegate, NSWindowDelegate>
{
}
@end
@implementation MobileGLView
- (void)prepareOpenGL {
[super prepareOpenGL];
[self setWantsBestResolutionOpenGLSurface:YES];
GLint swapInt = 1;
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
[[self openGLContext] setValues:&swapInt forParameter:NSOpenGLCPSwapInterval];
#pragma clang diagnostic pop
// Using attribute arrays in OpenGL 3.3 requires the use of a VBA.
// But VBAs don't exist in ES 2. So we bind a default one.
GLuint vba;
glGenVertexArrays(1, &vba);
glBindVertexArray(vba);
startloop((GLintptr)[self openGLContext]);
}
- (void)reshape {
[super reshape];
// Calculate screen PPI.
//
// Note that the backingScaleFactor converts from logical
// pixels to actual pixels, but both of these units vary
// independently from real world size. E.g.
//
// 13" Retina Macbook Pro, 2560x1600, 227ppi, backingScaleFactor=2, scale=3.15
// 15" Retina Macbook Pro, 2880x1800, 220ppi, backingScaleFactor=2, scale=3.06
// 27" iMac, 2560x1440, 109ppi, backingScaleFactor=1, scale=1.51
// 27" Retina iMac, 5120x2880, 218ppi, backingScaleFactor=2, scale=3.03
NSScreen *screen = [NSScreen mainScreen];
double screenPixW = [screen frame].size.width * [screen backingScaleFactor];
CGDirectDisplayID display = (CGDirectDisplayID)[[[screen deviceDescription] valueForKey:@"NSScreenNumber"] intValue];
CGSize screenSizeMM = CGDisplayScreenSize(display); // in millimeters
float ppi = 25.4 * screenPixW / screenSizeMM.width;
float pixelsPerPt = ppi/72.0;
// The width and height reported to the geom package are the
// bounds of the OpenGL view. Several steps are necessary.
// First, [self bounds] gives us the number of logical pixels
// in the view. Multiplying this by the backingScaleFactor
// gives us the number of actual pixels.
NSRect r = [self bounds];
int w = r.size.width * [screen backingScaleFactor];
int h = r.size.height * [screen backingScaleFactor];
setGeom(pixelsPerPt, w, h);
}
- (void)drawRect:(NSRect)theRect {
// Called during resize. This gets rid of flicker when resizing.
drawgl();
}
- (void)mouseDown:(NSEvent *)theEvent {
double scale = [[NSScreen mainScreen] backingScaleFactor];
NSPoint p = [theEvent locationInWindow];
eventMouseDown(p.x * scale, p.y * scale);
}
- (void)mouseUp:(NSEvent *)theEvent {
double scale = [[NSScreen mainScreen] backingScaleFactor];
NSPoint p = [theEvent locationInWindow];
eventMouseEnd(p.x * scale, p.y * scale);
}
- (void)mouseDragged:(NSEvent *)theEvent {
double scale = [[NSScreen mainScreen] backingScaleFactor];
NSPoint p = [theEvent locationInWindow];
eventMouseDragged(p.x * scale, p.y * scale);
}
- (void)windowDidBecomeKey:(NSNotification *)notification {
lifecycleFocused();
}
- (void)windowDidResignKey:(NSNotification *)notification {
if (![NSApp isHidden]) {
lifecycleVisible();
}
}
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification {
lifecycleAlive();
[[NSRunningApplication currentApplication] activateWithOptions:(NSApplicationActivateAllWindows | NSApplicationActivateIgnoringOtherApps)];
[self.window makeKeyAndOrderFront:self];
lifecycleVisible();
}
- (void)applicationWillTerminate:(NSNotification *)aNotification {
lifecycleDead();
}
- (void)applicationDidHide:(NSNotification *)aNotification {
lifecycleAlive();
}
- (void)applicationWillUnhide:(NSNotification *)notification {
lifecycleVisible();
}
- (void)windowWillClose:(NSNotification *)notification {
lifecycleAlive();
}
@end
@interface MobileResponder : NSResponder
{
}
@end
@implementation MobileResponder
- (void)keyDown:(NSEvent *)theEvent {
[self key:theEvent];
}
- (void)keyUp:(NSEvent *)theEvent {
[self key:theEvent];
}
- (void)key:(NSEvent *)theEvent {
NSRange range = [theEvent.characters rangeOfComposedCharacterSequenceAtIndex:0];
uint8_t buf[4] = {0, 0, 0, 0};
if (![theEvent.characters getBytes:buf
maxLength:4
usedLength:nil
encoding:NSUTF32LittleEndianStringEncoding
options:NSStringEncodingConversionAllowLossy
range:range
remainingRange:nil]) {
NSLog(@"failed to read key event %@", theEvent);
return;
}
uint32_t rune = (uint32_t)buf[0]<<0 | (uint32_t)buf[1]<<8 | (uint32_t)buf[2]<<16 | (uint32_t)buf[3]<<24;
uint8_t direction;
if ([theEvent isARepeat]) {
direction = 0;
} else if (theEvent.type == NSEventTypeKeyDown) {
direction = 1;
} else {
direction = 2;
}
eventKey((int32_t)rune, direction, theEvent.keyCode, theEvent.modifierFlags);
}
- (void)flagsChanged:(NSEvent *)theEvent {
eventFlags(theEvent.modifierFlags);
}
@end
void
runApp(void) {
[NSAutoreleasePool new];
[NSApplication sharedApplication];
[NSApp setActivationPolicy:NSApplicationActivationPolicyRegular];
id menuBar = [[NSMenu new] autorelease];
id menuItem = [[NSMenuItem new] autorelease];
[menuBar addItem:menuItem];
[NSApp setMainMenu:menuBar];
id menu = [[NSMenu new] autorelease];
id name = [[NSProcessInfo processInfo] processName];
id hideMenuItem = [[[NSMenuItem alloc] initWithTitle:@"Hide"
action:@selector(hide:) keyEquivalent:@"h"]
autorelease];
[menu addItem:hideMenuItem];
id quitMenuItem = [[[NSMenuItem alloc] initWithTitle:@"Quit"
action:@selector(terminate:) keyEquivalent:@"q"]
autorelease];
[menu addItem:quitMenuItem];
[menuItem setSubmenu:menu];
NSRect rect = NSMakeRect(0, 0, 600, 800);
NSWindow* window = [[[NSWindow alloc] initWithContentRect:rect
styleMask:NSWindowStyleMaskTitled
backing:NSBackingStoreBuffered
defer:NO]
autorelease];
window.styleMask |= NSWindowStyleMaskResizable;
window.styleMask |= NSWindowStyleMaskMiniaturizable;
window.styleMask |= NSWindowStyleMaskClosable;
window.title = name;
[window cascadeTopLeftFromPoint:NSMakePoint(20,20)];
NSOpenGLPixelFormatAttribute attr[] = {
NSOpenGLPFAOpenGLProfile, NSOpenGLProfileVersion3_2Core,
NSOpenGLPFAColorSize, 24,
NSOpenGLPFAAlphaSize, 8,
NSOpenGLPFADepthSize, 16,
NSOpenGLPFAAccelerated,
NSOpenGLPFADoubleBuffer,
NSOpenGLPFAAllowOfflineRenderers,
0
};
id pixFormat = [[NSOpenGLPixelFormat alloc] initWithAttributes:attr];
MobileGLView* view = [[MobileGLView alloc] initWithFrame:rect pixelFormat:pixFormat];
[window setContentView:view];
[window setDelegate:view];
[NSApp setDelegate:view];
window.nextResponder = [[[MobileResponder alloc] init] autorelease];
[NSApp run];
}
void stopApp(void) {
[NSApp terminate:nil];
}
+215
View File
@@ -0,0 +1,215 @@
// 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.
//go:build darwin && ios
package app
/*
#cgo CFLAGS: -x objective-c -DGL_SILENCE_DEPRECATION -DGLES_SILENCE_DEPRECATION
#cgo LDFLAGS: -framework Foundation -framework UIKit -framework GLKit -framework OpenGLES -framework QuartzCore
#include <sys/utsname.h>
#include <stdint.h>
#include <pthread.h>
#include <UIKit/UIDevice.h>
#import <GLKit/GLKit.h>
extern struct utsname sysInfo;
void runApp(void);
void makeCurrentContext(GLintptr ctx);
void swapBuffers(GLintptr ctx);
uint64_t threadID();
*/
import "C"
import (
"log"
"runtime"
"strings"
"sync"
"github.com/ebitengine/gomobile/event/lifecycle"
"github.com/ebitengine/gomobile/event/paint"
"github.com/ebitengine/gomobile/event/size"
"github.com/ebitengine/gomobile/event/touch"
"github.com/ebitengine/gomobile/geom"
)
var initThreadID uint64
func init() {
// Lock the goroutine responsible for initialization to an OS thread.
// This means the goroutine running main (and calling the run function
// below) is locked to the OS thread that started the program. This is
// necessary for the correct delivery of UIKit events to the process.
//
// A discussion on this topic:
// https://groups.google.com/forum/#!msg/golang-nuts/IiWZ2hUuLDA/SNKYYZBelsYJ
runtime.LockOSThread()
initThreadID = uint64(C.threadID())
}
func main(f func(App)) {
if tid := uint64(C.threadID()); tid != initThreadID {
log.Fatalf("app.Run called on thread %d, but app.init ran on %d", tid, initThreadID)
}
go func() {
f(theApp)
// TODO(crawshaw): trigger runApp to return
}()
C.runApp()
panic("unexpected return from app.runApp")
}
var pixelsPerPt float32
var screenScale int // [UIScreen mainScreen].scale, either 1, 2, or 3.
//export setScreen
func setScreen(scale int) {
C.uname(&C.sysInfo)
name := C.GoString(&C.sysInfo.machine[0])
var v float32
switch {
case strings.HasPrefix(name, "iPhone"):
v = 163
case strings.HasPrefix(name, "iPad"):
// TODO: is there a better way to distinguish the iPad Mini?
switch name {
case "iPad2,5", "iPad2,6", "iPad2,7", "iPad4,4", "iPad4,5", "iPad4,6", "iPad4,7":
v = 163 // iPad Mini
default:
v = 132
}
default:
v = 163 // names like i386 and x86_64 are the simulator
}
if v == 0 {
log.Printf("unknown machine: %s", name)
v = 163 // emergency fallback
}
pixelsPerPt = v * float32(scale) / 72
screenScale = scale
}
//export updateConfig
func updateConfig(width, height, orientation int32) {
o := size.OrientationUnknown
switch orientation {
case C.UIDeviceOrientationPortrait, C.UIDeviceOrientationPortraitUpsideDown:
o = size.OrientationPortrait
case C.UIDeviceOrientationLandscapeLeft, C.UIDeviceOrientationLandscapeRight:
o = size.OrientationLandscape
}
widthPx := screenScale * int(width)
heightPx := screenScale * int(height)
theApp.eventsIn <- size.Event{
WidthPx: widthPx,
HeightPx: heightPx,
WidthPt: geom.Pt(float32(widthPx) / pixelsPerPt),
HeightPt: geom.Pt(float32(heightPx) / pixelsPerPt),
PixelsPerPt: pixelsPerPt,
Orientation: o,
}
theApp.eventsIn <- paint.Event{External: true}
}
// touchIDs is the current active touches. The position in the array
// is the ID, the value is the UITouch* pointer value.
//
// It is widely reported that the iPhone can handle up to 5 simultaneous
// touch events, while the iPad can handle 11.
var touchIDs [11]uintptr
var touchEvents struct {
sync.Mutex
pending []touch.Event
}
//export sendTouch
func sendTouch(cTouch, cTouchType uintptr, x, y float32) {
id := -1
for i, val := range touchIDs {
if val == cTouch {
id = i
break
}
}
if id == -1 {
for i, val := range touchIDs {
if val == 0 {
touchIDs[i] = cTouch
id = i
break
}
}
if id == -1 {
panic("out of touchIDs")
}
}
t := touch.Type(cTouchType)
if t == touch.TypeEnd {
touchIDs[id] = 0
}
theApp.eventsIn <- touch.Event{
X: x,
Y: y,
Sequence: touch.Sequence(id),
Type: t,
}
}
//export lifecycleDead
func lifecycleDead() { theApp.sendLifecycle(lifecycle.StageDead) }
//export lifecycleAlive
func lifecycleAlive() { theApp.sendLifecycle(lifecycle.StageAlive) }
//export lifecycleVisible
func lifecycleVisible() { theApp.sendLifecycle(lifecycle.StageVisible) }
//export lifecycleFocused
func lifecycleFocused() { theApp.sendLifecycle(lifecycle.StageFocused) }
//export startloop
func startloop(ctx C.GLintptr) {
go theApp.loop(ctx)
}
// loop is the primary drawing loop.
//
// After UIKit has captured the initial OS thread for processing UIKit
// events in runApp, it starts loop on another goroutine. It is locked
// to an OS thread for its OpenGL context.
func (a *app) loop(ctx C.GLintptr) {
runtime.LockOSThread()
C.makeCurrentContext(ctx)
workAvailable := a.worker.WorkAvailable()
for {
select {
case <-workAvailable:
a.worker.DoWork()
case <-theApp.publish:
loop1:
for {
select {
case <-workAvailable:
a.worker.DoWork()
default:
break loop1
}
}
C.swapBuffers(ctx)
theApp.publishResult <- PublishResult{}
}
}
}
+167
View File
@@ -0,0 +1,167 @@
// 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.
//go:build darwin && ios
// +build darwin
// +build ios
#include "_cgo_export.h"
#include <pthread.h>
#include <stdio.h>
#include <sys/utsname.h>
#import <UIKit/UIKit.h>
#import <GLKit/GLKit.h>
struct utsname sysInfo;
@interface GoAppAppController : GLKViewController<UIContentContainer, GLKViewDelegate>
@end
@interface GoAppAppDelegate : UIResponder<UIApplicationDelegate>
@property (strong, nonatomic) UIWindow *window;
@property (strong, nonatomic) GoAppAppController *controller;
@end
@implementation GoAppAppDelegate
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
lifecycleAlive();
self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
self.controller = [[GoAppAppController alloc] initWithNibName:nil bundle:nil];
self.window.rootViewController = self.controller;
[self.window makeKeyAndVisible];
return YES;
}
- (void)applicationDidBecomeActive:(UIApplication * )application {
lifecycleFocused();
}
- (void)applicationWillResignActive:(UIApplication *)application {
lifecycleVisible();
}
- (void)applicationDidEnterBackground:(UIApplication *)application {
lifecycleAlive();
}
- (void)applicationWillTerminate:(UIApplication *)application {
lifecycleDead();
}
@end
@interface GoAppAppController ()
@property (strong, nonatomic) EAGLContext *context;
@property (strong, nonatomic) GLKView *glview;
@end
@implementation GoAppAppController
- (void)viewWillAppear:(BOOL)animated
{
// TODO: replace by swapping out GLKViewController for a UIVIewController.
[super viewWillAppear:animated];
self.paused = YES;
}
- (void)viewDidLoad {
[super viewDidLoad];
self.context = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES2];
self.glview = (GLKView*)self.view;
self.glview.drawableDepthFormat = GLKViewDrawableDepthFormat24;
self.glview.multipleTouchEnabled = true; // TODO expose setting to user.
self.glview.context = self.context;
self.glview.userInteractionEnabled = YES;
self.glview.enableSetNeedsDisplay = YES; // only invoked once
// Do not use the GLKViewController draw loop.
self.paused = YES;
self.resumeOnDidBecomeActive = NO;
self.preferredFramesPerSecond = 0;
int scale = 1;
if ([[UIScreen mainScreen] respondsToSelector:@selector(displayLinkWithTarget:selector:)]) {
scale = (int)[UIScreen mainScreen].scale; // either 1.0, 2.0, or 3.0.
}
setScreen(scale);
CGSize size = [UIScreen mainScreen].bounds.size;
UIInterfaceOrientation orientation = [[UIApplication sharedApplication] statusBarOrientation];
updateConfig((int)size.width, (int)size.height, orientation);
}
- (void)viewWillTransitionToSize:(CGSize)size withTransitionCoordinator:(id<UIViewControllerTransitionCoordinator>)coordinator {
[coordinator animateAlongsideTransition:^(id<UIViewControllerTransitionCoordinatorContext> context) {
// TODO(crawshaw): come up with a plan to handle animations.
} completion:^(id<UIViewControllerTransitionCoordinatorContext> context) {
UIInterfaceOrientation orientation = [[UIApplication sharedApplication] statusBarOrientation];
updateConfig((int)size.width, (int)size.height, orientation);
}];
}
- (void)glkView:(GLKView *)view drawInRect:(CGRect)rect {
// Now that we have been asked to do the first draw, disable any
// future draw and hand control over to the Go paint.Event cycle.
self.glview.enableSetNeedsDisplay = NO;
startloop((GLintptr)self.context);
}
#define TOUCH_TYPE_BEGIN 0 // touch.TypeBegin
#define TOUCH_TYPE_MOVE 1 // touch.TypeMove
#define TOUCH_TYPE_END 2 // touch.TypeEnd
static void sendTouches(int change, NSSet* touches) {
CGFloat scale = [UIScreen mainScreen].scale;
for (UITouch* touch in touches) {
CGPoint p = [touch locationInView:touch.view];
sendTouch((GoUintptr)touch, (GoUintptr)change, p.x*scale, p.y*scale);
}
}
- (void)touchesBegan:(NSSet*)touches withEvent:(UIEvent*)event {
sendTouches(TOUCH_TYPE_BEGIN, touches);
}
- (void)touchesMoved:(NSSet*)touches withEvent:(UIEvent*)event {
sendTouches(TOUCH_TYPE_MOVE, touches);
}
- (void)touchesEnded:(NSSet*)touches withEvent:(UIEvent*)event {
sendTouches(TOUCH_TYPE_END, touches);
}
- (void)touchesCanceled:(NSSet*)touches withEvent:(UIEvent*)event {
sendTouches(TOUCH_TYPE_END, touches);
}
@end
void runApp(void) {
char* argv[] = {};
@autoreleasepool {
UIApplicationMain(0, argv, nil, NSStringFromClass([GoAppAppDelegate class]));
}
}
void makeCurrentContext(GLintptr context) {
EAGLContext* ctx = (EAGLContext*)context;
if (![EAGLContext setCurrentContext:ctx]) {
// TODO(crawshaw): determine how terrible this is. Exit?
NSLog(@"failed to set current context");
}
}
void swapBuffers(GLintptr context) {
__block EAGLContext* ctx = (EAGLContext*)context;
dispatch_sync(dispatch_get_main_queue(), ^{
[EAGLContext setCurrentContext:ctx];
[ctx presentRenderbuffer:GL_RENDERBUFFER];
});
}
uint64_t threadID() {
uint64_t id;
if (pthread_threadid_np(pthread_self(), &id)) {
abort();
}
return id;
}
+88
View File
@@ -0,0 +1,88 @@
// Copyright 2014 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.
/*
Package app lets you write portable all-Go apps for Android and iOS.
There are typically two ways to use Go on Android and iOS. The first
is to write a Go library and use `gomobile bind` to generate language
bindings for Java and Objective-C. Building a library does not
require the app package. The `gomobile bind` command produces output
that you can include in an Android Studio or Xcode project. For more
on language bindings, see https://github.com/ebitengine/gomobile/cmd/gobind.
The second way is to write an app entirely in Go. The APIs are limited
to those that are portable between both Android and iOS, in particular
OpenGL, audio, and other Android NDK-like APIs. An all-Go app should
use this app package to initialize the app, manage its lifecycle, and
receive events.
# Building apps
Apps written entirely in Go have a main function, and can be built
with `gomobile build`, which directly produces runnable output for
Android and iOS.
The gomobile tool can get installed with go get. For reference, see
https://github.com/ebitengine/gomobile/cmd/gomobile.
For detailed instructions and documentation, see
https://golang.org/wiki/Mobile.
# Event processing in Native Apps
The Go runtime is initialized on Android when NativeActivity onCreate is
called, and on iOS when the process starts. In both cases, Go init functions
run before the app lifecycle has started.
An app is expected to call the Main function in main.main. When the function
exits, the app exits. Inside the func passed to Main, call Filter on every
event received, and then switch on its type. Registered filters run when the
event is received, not when it is sent, so that filters run in the same
goroutine as other code that calls OpenGL.
package main
import (
"log"
"github.com/ebitengine/gomobile/app"
"github.com/ebitengine/gomobile/event/lifecycle"
"github.com/ebitengine/gomobile/event/paint"
)
func main() {
app.Main(func(a app.App) {
for e := range a.Events() {
switch e := a.Filter(e).(type) {
case lifecycle.Event:
// ...
case paint.Event:
log.Print("Call OpenGL here.")
a.Publish()
}
}
})
}
An event is represented by the empty interface type interface{}. Any value can
be an event. Commonly used types include Event types defined by the following
packages:
- github.com/ebitengine/gomobile/event/lifecycle
- github.com/ebitengine/gomobile/event/mouse
- github.com/ebitengine/gomobile/event/paint
- github.com/ebitengine/gomobile/event/size
- github.com/ebitengine/gomobile/event/touch
For example, touch.Event is the type that represents touch events. Other
packages may define their own events, and send them on an app's event channel.
Other packages can also register event filters, e.g. to manage resources in
response to lifecycle events. Such packages should call:
app.RegisterFilter(etc)
in an init function inside that package.
*/
package app // import "github.com/ebitengine/gomobile/app"
+16
View File
@@ -0,0 +1,16 @@
// 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.
//go:build android && (arm || 386 || amd64 || arm64)
// Package callfn provides an android entry point.
//
// It is a separate package from app because it contains Go assembly,
// which does not compile in a package using cgo.
package callfn
// CallFn calls a zero-argument function by its program counter.
// It is only intended for calling main.main. Using it for
// anything else will not end well.
func CallFn(fn uintptr)
+11
View File
@@ -0,0 +1,11 @@
// 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.
#include "textflag.h"
#include "funcdata.h"
TEXT ·CallFn(SB),$0-4
MOVL fn+0(FP), AX
CALL AX
RET
@@ -0,0 +1,11 @@
// 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.
#include "textflag.h"
#include "funcdata.h"
TEXT ·CallFn(SB),$0-8
MOVQ fn+0(FP), AX
CALL AX
RET
+11
View File
@@ -0,0 +1,11 @@
// 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.
#include "textflag.h"
#include "funcdata.h"
TEXT ·CallFn(SB),$0-4
MOVW fn+0(FP), R0
BL (R0)
RET
@@ -0,0 +1,11 @@
// Copyright 2016 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.
#include "textflag.h"
#include "funcdata.h"
TEXT ·CallFn(SB),$0-8
MOVD fn+0(FP), R0
BL (R0)
RET
+175
View File
@@ -0,0 +1,175 @@
// Copyright 2014 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.
//go:build linux && !android
// +build linux,!android
#include "_cgo_export.h"
#include <EGL/egl.h>
#include <GLES2/gl2.h>
#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <stdio.h>
#include <stdlib.h>
static Atom wm_delete_window;
static Window
new_window(Display *x_dpy, EGLDisplay e_dpy, int w, int h, EGLContext *ctx, EGLSurface *surf) {
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
};
EGLConfig config;
EGLint num_configs;
if (!eglChooseConfig(e_dpy, attribs, &config, 1, &num_configs)) {
fprintf(stderr, "eglChooseConfig failed\n");
exit(1);
}
EGLint vid;
if (!eglGetConfigAttrib(e_dpy, config, EGL_NATIVE_VISUAL_ID, &vid)) {
fprintf(stderr, "eglGetConfigAttrib failed\n");
exit(1);
}
XVisualInfo visTemplate;
visTemplate.visualid = vid;
int num_visuals;
XVisualInfo *visInfo = XGetVisualInfo(x_dpy, VisualIDMask, &visTemplate, &num_visuals);
if (!visInfo) {
fprintf(stderr, "XGetVisualInfo failed\n");
exit(1);
}
Window root = RootWindow(x_dpy, DefaultScreen(x_dpy));
XSetWindowAttributes attr;
attr.colormap = XCreateColormap(x_dpy, root, visInfo->visual, AllocNone);
if (!attr.colormap) {
fprintf(stderr, "XCreateColormap failed\n");
exit(1);
}
attr.event_mask = StructureNotifyMask | ExposureMask |
ButtonPressMask | ButtonReleaseMask | ButtonMotionMask;
Window win = XCreateWindow(
x_dpy, root, 0, 0, w, h, 0, visInfo->depth, InputOutput,
visInfo->visual, CWColormap | CWEventMask, &attr);
XFree(visInfo);
XSizeHints sizehints;
sizehints.width = w;
sizehints.height = h;
sizehints.flags = USSize;
XSetNormalHints(x_dpy, win, &sizehints);
XSetStandardProperties(x_dpy, win, "App", "App", None, (char **)NULL, 0, &sizehints);
static const EGLint ctx_attribs[] = {
EGL_CONTEXT_CLIENT_VERSION, 2,
EGL_NONE
};
*ctx = eglCreateContext(e_dpy, config, EGL_NO_CONTEXT, ctx_attribs);
if (!*ctx) {
fprintf(stderr, "eglCreateContext failed\n");
exit(1);
}
*surf = eglCreateWindowSurface(e_dpy, config, win, NULL);
if (!*surf) {
fprintf(stderr, "eglCreateWindowSurface failed\n");
exit(1);
}
return win;
}
Display *x_dpy;
EGLDisplay e_dpy;
EGLContext e_ctx;
EGLSurface e_surf;
Window win;
void
createWindow(void) {
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\n");
exit(1);
}
EGLint e_major, e_minor;
if (!eglInitialize(e_dpy, &e_major, &e_minor)) {
fprintf(stderr, "eglInitialize failed\n");
exit(1);
}
eglBindAPI(EGL_OPENGL_ES_API);
win = new_window(x_dpy, e_dpy, 600, 800, &e_ctx, &e_surf);
wm_delete_window = XInternAtom(x_dpy, "WM_DELETE_WINDOW", True);
if (wm_delete_window != None) {
XSetWMProtocols(x_dpy, win, &wm_delete_window, 1);
}
XMapWindow(x_dpy, win);
if (!eglMakeCurrent(e_dpy, e_surf, e_surf, e_ctx)) {
fprintf(stderr, "eglMakeCurrent failed\n");
exit(1);
}
// Window size and DPI should be initialized before starting app.
XEvent ev;
while (1) {
if (XCheckMaskEvent(x_dpy, StructureNotifyMask, &ev) == False) {
continue;
}
if (ev.type == ConfigureNotify) {
onResize(ev.xconfigure.width, ev.xconfigure.height);
break;
}
}
}
void
processEvents(void) {
while (XPending(x_dpy)) {
XEvent ev;
XNextEvent(x_dpy, &ev);
switch (ev.type) {
case ButtonPress:
onTouchBegin((float)ev.xbutton.x, (float)ev.xbutton.y);
break;
case ButtonRelease:
onTouchEnd((float)ev.xbutton.x, (float)ev.xbutton.y);
break;
case MotionNotify:
onTouchMove((float)ev.xmotion.x, (float)ev.xmotion.y);
break;
case ConfigureNotify:
onResize(ev.xconfigure.width, ev.xconfigure.height);
break;
case ClientMessage:
if (wm_delete_window != None && (Atom)ev.xclient.data.l[0] == wm_delete_window) {
onStop();
return;
}
break;
}
}
}
void
swapBuffers(void) {
if (eglSwapBuffers(e_dpy, e_surf) == EGL_FALSE) {
fprintf(stderr, "eglSwapBuffer failed\n");
exit(1);
}
}
+126
View File
@@ -0,0 +1,126 @@
// Copyright 2014 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.
//go:build linux && !android
package app
/*
Simple on-screen app debugging for X11. Not an officially supported
development target for apps, as screens with mice are very different
than screens with touch panels.
*/
/*
#cgo LDFLAGS: -lEGL -lGLESv2 -lX11
void createWindow(void);
void processEvents(void);
void swapBuffers(void);
*/
import "C"
import (
"runtime"
"time"
"github.com/ebitengine/gomobile/event/lifecycle"
"github.com/ebitengine/gomobile/event/paint"
"github.com/ebitengine/gomobile/event/size"
"github.com/ebitengine/gomobile/event/touch"
"github.com/ebitengine/gomobile/geom"
)
func init() {
theApp.registerGLViewportFilter()
}
func main(f func(App)) {
runtime.LockOSThread()
workAvailable := theApp.worker.WorkAvailable()
C.createWindow()
// TODO: send lifecycle events when e.g. the X11 window is iconified or moved off-screen.
theApp.sendLifecycle(lifecycle.StageFocused)
// TODO: translate X11 expose events to shiny paint events, instead of
// sending this synthetic paint event as a hack.
theApp.eventsIn <- paint.Event{}
donec := make(chan struct{})
go func() {
// close the donec channel in a defer statement
// so that we could still be able to return even
// if f panics.
defer close(donec)
f(theApp)
}()
// TODO: can we get the actual vsync signal?
ticker := time.NewTicker(time.Second / 60)
defer ticker.Stop()
var tc <-chan time.Time
for {
select {
case <-donec:
return
case <-workAvailable:
theApp.worker.DoWork()
case <-theApp.publish:
C.swapBuffers()
tc = ticker.C
case <-tc:
tc = nil
theApp.publishResult <- PublishResult{}
}
C.processEvents()
}
}
//export onResize
func onResize(w, h int) {
// TODO(nigeltao): don't assume 72 DPI. DisplayWidth and DisplayWidthMM
// is probably the best place to start looking.
pixelsPerPt := float32(1)
theApp.eventsIn <- size.Event{
WidthPx: w,
HeightPx: h,
WidthPt: geom.Pt(w),
HeightPt: geom.Pt(h),
PixelsPerPt: pixelsPerPt,
}
}
func sendTouch(t touch.Type, x, y float32) {
theApp.eventsIn <- touch.Event{
X: x,
Y: y,
Sequence: 0, // TODO: button??
Type: t,
}
}
//export onTouchBegin
func onTouchBegin(x, y float32) { sendTouch(touch.TypeBegin, x, y) }
//export onTouchMove
func onTouchMove(x, y float32) { sendTouch(touch.TypeMove, x, y) }
//export onTouchEnd
func onTouchEnd(x, y float32) { sendTouch(touch.TypeEnd, x, y) }
var stopped bool
//export onStop
func onStop() {
if stopped {
return
}
stopped = true
theApp.sendLifecycle(lifecycle.StageDead)
theApp.eventsIn <- stopPumping{}
}
+60
View File
@@ -0,0 +1,60 @@
// Code generated by "stringer -type=Code"; DO NOT EDIT
package key
import "fmt"
const (
_Code_name_0 = "CodeUnknown"
_Code_name_1 = "CodeACodeBCodeCCodeDCodeECodeFCodeGCodeHCodeICodeJCodeKCodeLCodeMCodeNCodeOCodePCodeQCodeRCodeSCodeTCodeUCodeVCodeWCodeXCodeYCodeZCode1Code2Code3Code4Code5Code6Code7Code8Code9Code0CodeReturnEnterCodeEscapeCodeDeleteBackspaceCodeTabCodeSpacebarCodeHyphenMinusCodeEqualSignCodeLeftSquareBracketCodeRightSquareBracketCodeBackslash"
_Code_name_2 = "CodeSemicolonCodeApostropheCodeGraveAccentCodeCommaCodeFullStopCodeSlashCodeCapsLockCodeF1CodeF2CodeF3CodeF4CodeF5CodeF6CodeF7CodeF8CodeF9CodeF10CodeF11CodeF12"
_Code_name_3 = "CodePauseCodeInsertCodeHomeCodePageUpCodeDeleteForwardCodeEndCodePageDownCodeRightArrowCodeLeftArrowCodeDownArrowCodeUpArrowCodeKeypadNumLockCodeKeypadSlashCodeKeypadAsteriskCodeKeypadHyphenMinusCodeKeypadPlusSignCodeKeypadEnterCodeKeypad1CodeKeypad2CodeKeypad3CodeKeypad4CodeKeypad5CodeKeypad6CodeKeypad7CodeKeypad8CodeKeypad9CodeKeypad0CodeKeypadFullStop"
_Code_name_4 = "CodeKeypadEqualSignCodeF13CodeF14CodeF15CodeF16CodeF17CodeF18CodeF19CodeF20CodeF21CodeF22CodeF23CodeF24"
_Code_name_5 = "CodeHelp"
_Code_name_6 = "CodeMuteCodeVolumeUpCodeVolumeDown"
_Code_name_7 = "CodeLeftControlCodeLeftShiftCodeLeftAltCodeLeftGUICodeRightControlCodeRightShiftCodeRightAltCodeRightGUI"
_Code_name_8 = "CodeCompose"
)
var (
_Code_index_0 = [...]uint8{0, 11}
_Code_index_1 = [...]uint16{0, 5, 10, 15, 20, 25, 30, 35, 40, 45, 50, 55, 60, 65, 70, 75, 80, 85, 90, 95, 100, 105, 110, 115, 120, 125, 130, 135, 140, 145, 150, 155, 160, 165, 170, 175, 180, 195, 205, 224, 231, 243, 258, 271, 292, 314, 327}
_Code_index_2 = [...]uint8{0, 13, 27, 42, 51, 63, 72, 84, 90, 96, 102, 108, 114, 120, 126, 132, 138, 145, 152, 159}
_Code_index_3 = [...]uint16{0, 9, 19, 27, 37, 54, 61, 73, 87, 100, 113, 124, 141, 156, 174, 195, 213, 228, 239, 250, 261, 272, 283, 294, 305, 316, 327, 338, 356}
_Code_index_4 = [...]uint8{0, 19, 26, 33, 40, 47, 54, 61, 68, 75, 82, 89, 96, 103}
_Code_index_5 = [...]uint8{0, 8}
_Code_index_6 = [...]uint8{0, 8, 20, 34}
_Code_index_7 = [...]uint8{0, 15, 28, 39, 50, 66, 80, 92, 104}
_Code_index_8 = [...]uint8{0, 11}
)
func (i Code) String() string {
switch {
case i == 0:
return _Code_name_0
case 4 <= i && i <= 49:
i -= 4
return _Code_name_1[_Code_index_1[i]:_Code_index_1[i+1]]
case 51 <= i && i <= 69:
i -= 51
return _Code_name_2[_Code_index_2[i]:_Code_index_2[i+1]]
case 72 <= i && i <= 99:
i -= 72
return _Code_name_3[_Code_index_3[i]:_Code_index_3[i+1]]
case 103 <= i && i <= 115:
i -= 103
return _Code_name_4[_Code_index_4[i]:_Code_index_4[i+1]]
case i == 117:
return _Code_name_5
case 127 <= i && i <= 129:
i -= 127
return _Code_name_6[_Code_index_6[i]:_Code_index_6[i+1]]
case 224 <= i && i <= 231:
i -= 224
return _Code_name_7[_Code_index_7[i]:_Code_index_7[i+1]]
case i == 65536:
return _Code_name_8
default:
return fmt.Sprintf("Code(%d)", i)
}
}
+270
View File
@@ -0,0 +1,270 @@
// 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.
//go:generate stringer -type=Code
// Package key defines an event for physical keyboard keys.
//
// On-screen software keyboards do not send key events.
//
// See the github.com/ebitengine/gomobile/app package for details on the event model.
package key
import (
"fmt"
"strings"
)
// Event is a key event.
type Event struct {
// Rune is the meaning of the key event as determined by the
// operating system. The mapping is determined by system-dependent
// current layout, modifiers, lock-states, etc.
//
// If non-negative, it is a Unicode codepoint: pressing the 'a' key
// generates different Runes 'a' or 'A' (but the same Code) depending on
// the state of the shift key.
//
// If -1, the key does not generate a Unicode codepoint. To distinguish
// them, look at Code.
Rune rune
// Code is the identity of the physical key relative to a notional
// "standard" keyboard, independent of current layout, modifiers,
// lock-states, etc
//
// For standard key codes, its value matches USB HID key codes.
// Compare its value to uint32-typed constants in this package, such
// as CodeLeftShift and CodeEscape.
//
// Pressing the regular '2' key and number-pad '2' key (with Num-Lock)
// generate different Codes (but the same Rune).
Code Code
// Modifiers is a bitmask representing a set of modifier keys: ModShift,
// ModAlt, etc.
Modifiers Modifiers
// Direction is the direction of the key event: DirPress, DirRelease,
// or DirNone (for key repeats).
Direction Direction
// TODO: add a Device ID, for multiple input devices?
// TODO: add a time.Time?
}
func (e Event) String() string {
if e.Rune >= 0 {
return fmt.Sprintf("key.Event{%q (%v), %v, %v}", e.Rune, e.Code, e.Modifiers, e.Direction)
}
return fmt.Sprintf("key.Event{(%v), %v, %v}", e.Code, e.Modifiers, e.Direction)
}
// Direction is the direction of the key event.
type Direction uint8
const (
DirNone Direction = 0
DirPress Direction = 1
DirRelease Direction = 2
)
// Modifiers is a bitmask representing a set of modifier keys.
type Modifiers uint32
const (
ModShift Modifiers = 1 << 0
ModControl Modifiers = 1 << 1
ModAlt Modifiers = 1 << 2
ModMeta Modifiers = 1 << 3 // called "Command" on OS X
)
// Code is the identity of a key relative to a notional "standard" keyboard.
type Code uint32
// Physical key codes.
//
// For standard key codes, its value matches USB HID key codes.
// TODO: add missing codes.
const (
CodeUnknown Code = 0
CodeA Code = 4
CodeB Code = 5
CodeC Code = 6
CodeD Code = 7
CodeE Code = 8
CodeF Code = 9
CodeG Code = 10
CodeH Code = 11
CodeI Code = 12
CodeJ Code = 13
CodeK Code = 14
CodeL Code = 15
CodeM Code = 16
CodeN Code = 17
CodeO Code = 18
CodeP Code = 19
CodeQ Code = 20
CodeR Code = 21
CodeS Code = 22
CodeT Code = 23
CodeU Code = 24
CodeV Code = 25
CodeW Code = 26
CodeX Code = 27
CodeY Code = 28
CodeZ Code = 29
Code1 Code = 30
Code2 Code = 31
Code3 Code = 32
Code4 Code = 33
Code5 Code = 34
Code6 Code = 35
Code7 Code = 36
Code8 Code = 37
Code9 Code = 38
Code0 Code = 39
CodeReturnEnter Code = 40
CodeEscape Code = 41
CodeDeleteBackspace Code = 42
CodeTab Code = 43
CodeSpacebar Code = 44
CodeHyphenMinus Code = 45 // -
CodeEqualSign Code = 46 // =
CodeLeftSquareBracket Code = 47 // [
CodeRightSquareBracket Code = 48 // ]
CodeBackslash Code = 49 // \
CodeSemicolon Code = 51 // ;
CodeApostrophe Code = 52 // '
CodeGraveAccent Code = 53 // `
CodeComma Code = 54 // ,
CodeFullStop Code = 55 // .
CodeSlash Code = 56 // /
CodeCapsLock Code = 57
CodeF1 Code = 58
CodeF2 Code = 59
CodeF3 Code = 60
CodeF4 Code = 61
CodeF5 Code = 62
CodeF6 Code = 63
CodeF7 Code = 64
CodeF8 Code = 65
CodeF9 Code = 66
CodeF10 Code = 67
CodeF11 Code = 68
CodeF12 Code = 69
CodePause Code = 72
CodeInsert Code = 73
CodeHome Code = 74
CodePageUp Code = 75
CodeDeleteForward Code = 76
CodeEnd Code = 77
CodePageDown Code = 78
CodeRightArrow Code = 79
CodeLeftArrow Code = 80
CodeDownArrow Code = 81
CodeUpArrow Code = 82
CodeKeypadNumLock Code = 83
CodeKeypadSlash Code = 84 // /
CodeKeypadAsterisk Code = 85 // *
CodeKeypadHyphenMinus Code = 86 // -
CodeKeypadPlusSign Code = 87 // +
CodeKeypadEnter Code = 88
CodeKeypad1 Code = 89
CodeKeypad2 Code = 90
CodeKeypad3 Code = 91
CodeKeypad4 Code = 92
CodeKeypad5 Code = 93
CodeKeypad6 Code = 94
CodeKeypad7 Code = 95
CodeKeypad8 Code = 96
CodeKeypad9 Code = 97
CodeKeypad0 Code = 98
CodeKeypadFullStop Code = 99 // .
CodeKeypadEqualSign Code = 103 // =
CodeF13 Code = 104
CodeF14 Code = 105
CodeF15 Code = 106
CodeF16 Code = 107
CodeF17 Code = 108
CodeF18 Code = 109
CodeF19 Code = 110
CodeF20 Code = 111
CodeF21 Code = 112
CodeF22 Code = 113
CodeF23 Code = 114
CodeF24 Code = 115
CodeHelp Code = 117
CodeMute Code = 127
CodeVolumeUp Code = 128
CodeVolumeDown Code = 129
CodeLeftControl Code = 224
CodeLeftShift Code = 225
CodeLeftAlt Code = 226
CodeLeftGUI Code = 227
CodeRightControl Code = 228
CodeRightShift Code = 229
CodeRightAlt Code = 230
CodeRightGUI Code = 231
// The following codes are not part of the standard USB HID Usage IDs for
// keyboards. See http://www.usb.org/developers/hidpage/Hut1_12v2.pdf
//
// Usage IDs are uint16s, so these non-standard values start at 0x10000.
// CodeCompose is the Code for a compose key, sometimes called a multi key,
// used to input non-ASCII characters such as ñ being composed of n and ~.
//
// See https://en.wikipedia.org/wiki/Compose_key
CodeCompose Code = 0x10000
)
// TODO: Given we use runes outside the unicode space, should we provide a
// printing function? Related: it's a little unfortunate that printing a
// key.Event with %v gives not very readable output like:
// {100 7 key.Modifiers() Press}
var mods = [...]struct {
m Modifiers
s string
}{
{ModShift, "Shift"},
{ModControl, "Control"},
{ModAlt, "Alt"},
{ModMeta, "Meta"},
}
func (m Modifiers) String() string {
var match []string
for _, mod := range mods {
if mod.m&m != 0 {
match = append(match, mod.s)
}
}
return "key.Modifiers(" + strings.Join(match, "|") + ")"
}
func (d Direction) String() string {
switch d {
case DirNone:
return "None"
case DirPress:
return "Press"
case DirRelease:
return "Release"
default:
return fmt.Sprintf("key.Direction(%d)", d)
}
}
+136
View File
@@ -0,0 +1,136 @@
// 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.
// Package lifecycle defines an event for an app's lifecycle.
//
// The app lifecycle consists of moving back and forth between an ordered
// sequence of stages. For example, being at a stage greater than or equal to
// StageVisible means that the app is visible on the screen.
//
// A lifecycle event is a change from one stage to another, which crosses every
// intermediate stage. For example, changing from StageAlive to StageFocused
// implicitly crosses StageVisible.
//
// Crosses can be in a positive or negative direction. A positive crossing of
// StageFocused means that the app has gained the focus. A negative crossing
// means it has lost the focus.
//
// See the github.com/ebitengine/gomobile/app package for details on the event model.
package lifecycle // import "github.com/ebitengine/gomobile/event/lifecycle"
import (
"fmt"
)
// Cross is whether a lifecycle stage was crossed.
type Cross uint32
func (c Cross) String() string {
switch c {
case CrossOn:
return "on"
case CrossOff:
return "off"
}
return "none"
}
const (
CrossNone Cross = 0
CrossOn Cross = 1
CrossOff Cross = 2
)
// Event is a lifecycle change from an old stage to a new stage.
type Event struct {
From, To Stage
// DrawContext is the state used for painting, if any is valid.
//
// For OpenGL apps, a non-nil DrawContext is a gl.Context.
//
// TODO: make this an App method if we move away from an event channel?
DrawContext interface{}
}
func (e Event) String() string {
return fmt.Sprintf("lifecycle.Event{From:%v, To:%v, DrawContext:%v}", e.From, e.To, e.DrawContext)
}
// Crosses reports whether the transition from From to To crosses the stage s:
// - It returns CrossOn if it does, and the lifecycle change is positive.
// - It returns CrossOff if it does, and the lifecycle change is negative.
// - Otherwise, it returns CrossNone.
//
// See the documentation for Stage for more discussion of positive and negative
// crosses.
func (e Event) Crosses(s Stage) Cross {
switch {
case e.From < s && e.To >= s:
return CrossOn
case e.From >= s && e.To < s:
return CrossOff
}
return CrossNone
}
// Stage is a stage in the app's lifecycle. The values are ordered, so that a
// lifecycle change from stage From to stage To implicitly crosses every stage
// in the range (min, max], exclusive on the low end and inclusive on the high
// end, where min is the minimum of From and To, and max is the maximum.
//
// The documentation for individual stages talk about positive and negative
// crosses. A positive lifecycle change is one where its From stage is less
// than its To stage. Similarly, a negative lifecycle change is one where From
// is greater than To. Thus, a positive lifecycle change crosses every stage in
// the range (From, To] in increasing order, and a negative lifecycle change
// crosses every stage in the range (To, From] in decreasing order.
type Stage uint32
// TODO: how does iOS map to these stages? What do cross-platform mobile
// abstractions do?
const (
// StageDead is the zero stage. No lifecycle change crosses this stage,
// but:
// - A positive change from this stage is the very first lifecycle change.
// - A negative change to this stage is the very last lifecycle change.
StageDead Stage = iota
// StageAlive means that the app is alive.
// - A positive cross means that the app has been created.
// - A negative cross means that the app is being destroyed.
// Each cross, either from or to StageDead, will occur only once.
// On Android, these correspond to onCreate and onDestroy.
StageAlive
// StageVisible means that the app window is visible.
// - A positive cross means that the app window has become visible.
// - A negative cross means that the app window has become invisible.
// On Android, these correspond to onStart and onStop.
// On Desktop, an app window can become invisible if e.g. it is minimized,
// unmapped, or not on a visible workspace.
StageVisible
// StageFocused means that the app window has the focus.
// - A positive cross means that the app window has gained the focus.
// - A negative cross means that the app window has lost the focus.
// On Android, these correspond to onResume and onFreeze.
StageFocused
)
func (s Stage) String() string {
switch s {
case StageDead:
return "StageDead"
case StageAlive:
return "StageAlive"
case StageVisible:
return "StageVisible"
case StageFocused:
return "StageFocused"
default:
return fmt.Sprintf("lifecycle.Stage(%d)", s)
}
}
+24
View File
@@ -0,0 +1,24 @@
// 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.
// Package paint defines an event for the app being ready to paint.
//
// See the github.com/ebitengine/gomobile/app package for details on the event model.
package paint // import "github.com/ebitengine/gomobile/event/paint"
// Event indicates that the app is ready to paint the next frame of the GUI.
//
// A frame is completed by calling the App's Publish method.
type Event struct {
// External is true for paint events sent by the screen driver.
//
// An external event may be sent at any time in response to an
// operating system event, for example the window opened, was
// resized, or the screen memory was lost.
//
// Programs actively drawing to the screen as fast as vsync allows
// should ignore external paint events to avoid a backlog of paint
// events building up.
External bool
}
+92
View File
@@ -0,0 +1,92 @@
// 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.
// Package size defines an event for the dimensions, physical resolution and
// orientation of the app's window.
//
// See the github.com/ebitengine/gomobile/app package for details on the event model.
package size // import "github.com/ebitengine/gomobile/event/size"
import (
"image"
"github.com/ebitengine/gomobile/geom"
)
// Event holds the dimensions, physical resolution and orientation of the app's
// window.
type Event struct {
// WidthPx and HeightPx are the window's dimensions in pixels.
WidthPx, HeightPx int
// WidthPt and HeightPt are the window's physical dimensions in points
// (1/72 of an inch).
//
// The values are based on PixelsPerPt and are therefore approximate, as
// per the comment on PixelsPerPt.
WidthPt, HeightPt geom.Pt
// PixelsPerPt is the window's physical resolution. It is the number of
// pixels in a single geom.Pt, from the github.com/ebitengine/gomobile/geom package.
//
// There are a wide variety of pixel densities in existing phones and
// tablets, so apps should be written to expect various non-integer
// PixelsPerPt values. In general, work in geom.Pt.
//
// The value is approximate, in that the OS, drivers or hardware may report
// approximate or quantized values. An N x N pixel square should be roughly
// 1 square inch for N = int(PixelsPerPt * 72), although different square
// lengths (in pixels) might be closer to 1 inch in practice. Nonetheless,
// this PixelsPerPt value should be consistent with e.g. the ratio of
// WidthPx to WidthPt.
PixelsPerPt float32
// Orientation is the orientation of the device screen.
Orientation Orientation
}
// Size returns the window's size in pixels, at the time this size event was
// sent.
func (e Event) Size() image.Point {
return image.Point{e.WidthPx, e.HeightPx}
}
// Bounds returns the window's bounds in pixels, at the time this size event
// was sent.
//
// The top-left pixel is always (0, 0). The bottom-right pixel is given by the
// width and height.
func (e Event) Bounds() image.Rectangle {
return image.Rectangle{Max: image.Point{e.WidthPx, e.HeightPx}}
}
// Orientation is the orientation of the device screen.
type Orientation int
const (
// OrientationUnknown means device orientation cannot be determined.
//
// Equivalent on Android to Configuration.ORIENTATION_UNKNOWN
// and on iOS to:
// UIDeviceOrientationUnknown
// UIDeviceOrientationFaceUp
// UIDeviceOrientationFaceDown
OrientationUnknown Orientation = iota
// OrientationPortrait is a device oriented so it is tall and thin.
//
// Equivalent on Android to Configuration.ORIENTATION_PORTRAIT
// and on iOS to:
// UIDeviceOrientationPortrait
// UIDeviceOrientationPortraitUpsideDown
OrientationPortrait
// OrientationLandscape is a device oriented so it is short and wide.
//
// Equivalent on Android to Configuration.ORIENTATION_LANDSCAPE
// and on iOS to:
// UIDeviceOrientationLandscapeLeft
// UIDeviceOrientationLandscapeRight
OrientationLandscape
)
+72
View File
@@ -0,0 +1,72 @@
// 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.
// Package touch defines an event for touch input.
//
// See the github.com/ebitengine/gomobile/app package for details on the event model.
package touch // import "github.com/ebitengine/gomobile/event/touch"
// The best source on android input events is the NDK: include/android/input.h
//
// iOS event handling guide:
// https://developer.apple.com/library/ios/documentation/EventHandling/Conceptual/EventHandlingiPhoneOS
import (
"fmt"
)
// Event is a touch event.
type Event struct {
// X and Y are the touch location, in pixels.
X, Y float32
// Sequence is the sequence number. The same number is shared by all events
// in a sequence. A sequence begins with a single TypeBegin, is followed by
// zero or more TypeMoves, and ends with a single TypeEnd. A Sequence
// distinguishes concurrent sequences but its value is subsequently reused.
Sequence Sequence
// Type is the touch type.
Type Type
}
// Sequence identifies a sequence of touch events.
type Sequence int64
// Type describes the type of a touch event.
type Type byte
const (
// TypeBegin is a user first touching the device.
//
// On Android, this is a AMOTION_EVENT_ACTION_DOWN.
// On iOS, this is a call to touchesBegan.
TypeBegin Type = iota
// TypeMove is a user dragging across the device.
//
// A TypeMove is delivered between a TypeBegin and TypeEnd.
//
// On Android, this is a AMOTION_EVENT_ACTION_MOVE.
// On iOS, this is a call to touchesMoved.
TypeMove
// TypeEnd is a user no longer touching the device.
//
// On Android, this is a AMOTION_EVENT_ACTION_UP.
// On iOS, this is a call to touchesEnded.
TypeEnd
)
func (t Type) String() string {
switch t {
case TypeBegin:
return "begin"
case TypeMove:
return "move"
case TypeEnd:
return "end"
}
return fmt.Sprintf("touch.Type(%d)", t)
}
+102
View File
@@ -0,0 +1,102 @@
// Copyright 2014 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.
/*
Package geom defines a two-dimensional coordinate system.
The coordinate system is based on an left-handed Cartesian plane.
That is, X increases to the right and Y increases down. For (x,y),
(0,0) → (1,0)
↓ ↘
(0,1) (1,1)
The display window places the origin (0, 0) in the upper-left corner of
the screen. Positions on the plane are measured in typographic points,
1/72 of an inch, which is represented by the Pt type.
Any interface that draws to the screen using types from the geom package
scales the number of pixels to maintain a Pt as 1/72 of an inch.
*/
package geom // import "github.com/ebitengine/gomobile/geom"
/*
Notes on the various underlying coordinate systems.
Both Android and iOS (UIKit) use upper-left-origin coordinate systems
with for events, however they have different units.
UIKit measures distance in points. A point is a single-pixel on a
pre-Retina display. UIKit maintains a scale factor that to turn points
into pixels. On current retina devices, the scale factor is 2.0.
A UIKit point does not correspond to a fixed physical distance, as the
iPhone has a 163 DPI/PPI (326 PPI retina) display, and the iPad has a
132 PPI (264 retina) display. Points are 32-bit floats.
Even though point is the official UIKit term, they are commonly called
pixels. Indeed, the units were equivalent until the retina display was
introduced.
N.b. as a UIKit point is unrelated to a typographic point, it is not
related to this packages's Pt and Point types.
More details about iOS drawing:
https://developer.apple.com/library/ios/documentation/2ddrawing/conceptual/drawingprintingios/GraphicsDrawingOverview/GraphicsDrawingOverview.html
Android uses pixels. Sub-pixel precision is possible, so pixels are
represented as 32-bit floats. The ACONFIGURATION_DENSITY enum provides
the screen DPI/PPI, which varies frequently between devices.
It would be tempting to adopt the pixel, given the clear pixel/DPI split
in the core android events API. However, the plot thickens:
http://developer.android.com/training/multiscreen/screendensities.html
Android promotes the notion of a density-independent pixel in many of
their interfaces, often prefixed by "dp". 1dp is a real physical length,
as "independent" means it is assumed to be 1/160th of an inch and is
adjusted for the current screen.
In addition, android has a scale-indepdendent pixel used for expressing
a user's preferred text size. The user text size preference is a useful
notion not yet expressed in the geom package.
For the sake of clarity when working across platforms, the geom package
tries to put distance between it and the word pixel.
*/
import "fmt"
// Pt is a length.
//
// The unit Pt is a typographical point, 1/72 of an inch (0.3527 mm).
//
// It can be converted to a length in current device pixels by
// multiplying with PixelsPerPt after app initialization is complete.
type Pt float32
// Px converts the length to current device pixels.
func (p Pt) Px(pixelsPerPt float32) float32 { return float32(p) * pixelsPerPt }
// String returns a string representation of p like "3.2pt".
func (p Pt) String() string { return fmt.Sprintf("%.2fpt", p) }
// Point is a point in a two-dimensional plane.
type Point struct {
X, Y Pt
}
// String returns a string representation of p like "(1.2,3.4)".
func (p Point) String() string { return fmt.Sprintf("(%.2f,%.2f)", p.X, p.Y) }
// A Rectangle is region of points.
// The top-left point is Min, and the bottom-right point is Max.
type Rectangle struct {
Min, Max Point
}
// String returns a string representation of r like "(3,4)-(6,5)".
func (r Rectangle) String() string { return r.Min.String() + "-" + r.Max.String() }
+657
View File
@@ -0,0 +1,657 @@
// Copyright 2014 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.
package gl
/*
Partially generated from the Khronos OpenGL API specification in XML
format, which is covered by the license:
Copyright (c) 2013-2014 The Khronos Group Inc.
Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and/or associated documentation files (the
"Materials"), to deal in the Materials without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Materials, and to
permit persons to whom the Materials are furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be included
in all copies or substantial portions of the Materials.
THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS.
*/
const (
POINTS = 0x0000
LINES = 0x0001
LINE_LOOP = 0x0002
LINE_STRIP = 0x0003
TRIANGLES = 0x0004
TRIANGLE_STRIP = 0x0005
TRIANGLE_FAN = 0x0006
SRC_COLOR = 0x0300
ONE_MINUS_SRC_COLOR = 0x0301
SRC_ALPHA = 0x0302
ONE_MINUS_SRC_ALPHA = 0x0303
DST_ALPHA = 0x0304
ONE_MINUS_DST_ALPHA = 0x0305
DST_COLOR = 0x0306
ONE_MINUS_DST_COLOR = 0x0307
SRC_ALPHA_SATURATE = 0x0308
FUNC_ADD = 0x8006
BLEND_EQUATION = 0x8009
BLEND_EQUATION_RGB = 0x8009
BLEND_EQUATION_ALPHA = 0x883D
FUNC_SUBTRACT = 0x800A
FUNC_REVERSE_SUBTRACT = 0x800B
BLEND_DST_RGB = 0x80C8
BLEND_SRC_RGB = 0x80C9
BLEND_DST_ALPHA = 0x80CA
BLEND_SRC_ALPHA = 0x80CB
CONSTANT_COLOR = 0x8001
ONE_MINUS_CONSTANT_COLOR = 0x8002
CONSTANT_ALPHA = 0x8003
ONE_MINUS_CONSTANT_ALPHA = 0x8004
BLEND_COLOR = 0x8005
ARRAY_BUFFER = 0x8892
ELEMENT_ARRAY_BUFFER = 0x8893
ARRAY_BUFFER_BINDING = 0x8894
ELEMENT_ARRAY_BUFFER_BINDING = 0x8895
STREAM_DRAW = 0x88E0
STATIC_DRAW = 0x88E4
DYNAMIC_DRAW = 0x88E8
BUFFER_SIZE = 0x8764
BUFFER_USAGE = 0x8765
CURRENT_VERTEX_ATTRIB = 0x8626
FRONT = 0x0404
BACK = 0x0405
FRONT_AND_BACK = 0x0408
TEXTURE_2D = 0x0DE1
CULL_FACE = 0x0B44
BLEND = 0x0BE2
DITHER = 0x0BD0
STENCIL_TEST = 0x0B90
DEPTH_TEST = 0x0B71
SCISSOR_TEST = 0x0C11
POLYGON_OFFSET_FILL = 0x8037
SAMPLE_ALPHA_TO_COVERAGE = 0x809E
SAMPLE_COVERAGE = 0x80A0
INVALID_ENUM = 0x0500
INVALID_VALUE = 0x0501
INVALID_OPERATION = 0x0502
OUT_OF_MEMORY = 0x0505
CW = 0x0900
CCW = 0x0901
LINE_WIDTH = 0x0B21
ALIASED_POINT_SIZE_RANGE = 0x846D
ALIASED_LINE_WIDTH_RANGE = 0x846E
CULL_FACE_MODE = 0x0B45
FRONT_FACE = 0x0B46
DEPTH_RANGE = 0x0B70
DEPTH_WRITEMASK = 0x0B72
DEPTH_CLEAR_VALUE = 0x0B73
DEPTH_FUNC = 0x0B74
STENCIL_CLEAR_VALUE = 0x0B91
STENCIL_FUNC = 0x0B92
STENCIL_FAIL = 0x0B94
STENCIL_PASS_DEPTH_FAIL = 0x0B95
STENCIL_PASS_DEPTH_PASS = 0x0B96
STENCIL_REF = 0x0B97
STENCIL_VALUE_MASK = 0x0B93
STENCIL_WRITEMASK = 0x0B98
STENCIL_BACK_FUNC = 0x8800
STENCIL_BACK_FAIL = 0x8801
STENCIL_BACK_PASS_DEPTH_FAIL = 0x8802
STENCIL_BACK_PASS_DEPTH_PASS = 0x8803
STENCIL_BACK_REF = 0x8CA3
STENCIL_BACK_VALUE_MASK = 0x8CA4
STENCIL_BACK_WRITEMASK = 0x8CA5
VIEWPORT = 0x0BA2
SCISSOR_BOX = 0x0C10
COLOR_CLEAR_VALUE = 0x0C22
COLOR_WRITEMASK = 0x0C23
UNPACK_ALIGNMENT = 0x0CF5
PACK_ALIGNMENT = 0x0D05
MAX_TEXTURE_SIZE = 0x0D33
MAX_VIEWPORT_DIMS = 0x0D3A
SUBPIXEL_BITS = 0x0D50
RED_BITS = 0x0D52
GREEN_BITS = 0x0D53
BLUE_BITS = 0x0D54
ALPHA_BITS = 0x0D55
DEPTH_BITS = 0x0D56
STENCIL_BITS = 0x0D57
POLYGON_OFFSET_UNITS = 0x2A00
POLYGON_OFFSET_FACTOR = 0x8038
TEXTURE_BINDING_2D = 0x8069
SAMPLE_BUFFERS = 0x80A8
SAMPLES = 0x80A9
SAMPLE_COVERAGE_VALUE = 0x80AA
SAMPLE_COVERAGE_INVERT = 0x80AB
NUM_COMPRESSED_TEXTURE_FORMATS = 0x86A2
COMPRESSED_TEXTURE_FORMATS = 0x86A3
DONT_CARE = 0x1100
FASTEST = 0x1101
NICEST = 0x1102
GENERATE_MIPMAP_HINT = 0x8192
BYTE = 0x1400
UNSIGNED_BYTE = 0x1401
SHORT = 0x1402
UNSIGNED_SHORT = 0x1403
INT = 0x1404
UNSIGNED_INT = 0x1405
FLOAT = 0x1406
FIXED = 0x140C
DEPTH_COMPONENT = 0x1902
ALPHA = 0x1906
RGB = 0x1907
RGBA = 0x1908
LUMINANCE = 0x1909
LUMINANCE_ALPHA = 0x190A
UNSIGNED_SHORT_4_4_4_4 = 0x8033
UNSIGNED_SHORT_5_5_5_1 = 0x8034
UNSIGNED_SHORT_5_6_5 = 0x8363
MAX_VERTEX_ATTRIBS = 0x8869
MAX_VERTEX_UNIFORM_VECTORS = 0x8DFB
MAX_VARYING_VECTORS = 0x8DFC
MAX_COMBINED_TEXTURE_IMAGE_UNITS = 0x8B4D
MAX_VERTEX_TEXTURE_IMAGE_UNITS = 0x8B4C
MAX_TEXTURE_IMAGE_UNITS = 0x8872
MAX_FRAGMENT_UNIFORM_VECTORS = 0x8DFD
SHADER_TYPE = 0x8B4F
DELETE_STATUS = 0x8B80
LINK_STATUS = 0x8B82
VALIDATE_STATUS = 0x8B83
ATTACHED_SHADERS = 0x8B85
ACTIVE_UNIFORMS = 0x8B86
ACTIVE_UNIFORM_MAX_LENGTH = 0x8B87
ACTIVE_ATTRIBUTES = 0x8B89
ACTIVE_ATTRIBUTE_MAX_LENGTH = 0x8B8A
SHADING_LANGUAGE_VERSION = 0x8B8C
CURRENT_PROGRAM = 0x8B8D
NEVER = 0x0200
LESS = 0x0201
EQUAL = 0x0202
LEQUAL = 0x0203
GREATER = 0x0204
NOTEQUAL = 0x0205
GEQUAL = 0x0206
ALWAYS = 0x0207
KEEP = 0x1E00
REPLACE = 0x1E01
INCR = 0x1E02
DECR = 0x1E03
INVERT = 0x150A
INCR_WRAP = 0x8507
DECR_WRAP = 0x8508
VENDOR = 0x1F00
RENDERER = 0x1F01
VERSION = 0x1F02
EXTENSIONS = 0x1F03
NEAREST = 0x2600
LINEAR = 0x2601
NEAREST_MIPMAP_NEAREST = 0x2700
LINEAR_MIPMAP_NEAREST = 0x2701
NEAREST_MIPMAP_LINEAR = 0x2702
LINEAR_MIPMAP_LINEAR = 0x2703
TEXTURE_MAG_FILTER = 0x2800
TEXTURE_MIN_FILTER = 0x2801
TEXTURE_WRAP_S = 0x2802
TEXTURE_WRAP_T = 0x2803
TEXTURE = 0x1702
TEXTURE_CUBE_MAP = 0x8513
TEXTURE_BINDING_CUBE_MAP = 0x8514
TEXTURE_CUBE_MAP_POSITIVE_X = 0x8515
TEXTURE_CUBE_MAP_NEGATIVE_X = 0x8516
TEXTURE_CUBE_MAP_POSITIVE_Y = 0x8517
TEXTURE_CUBE_MAP_NEGATIVE_Y = 0x8518
TEXTURE_CUBE_MAP_POSITIVE_Z = 0x8519
TEXTURE_CUBE_MAP_NEGATIVE_Z = 0x851A
MAX_CUBE_MAP_TEXTURE_SIZE = 0x851C
TEXTURE0 = 0x84C0
TEXTURE1 = 0x84C1
TEXTURE2 = 0x84C2
TEXTURE3 = 0x84C3
TEXTURE4 = 0x84C4
TEXTURE5 = 0x84C5
TEXTURE6 = 0x84C6
TEXTURE7 = 0x84C7
TEXTURE8 = 0x84C8
TEXTURE9 = 0x84C9
TEXTURE10 = 0x84CA
TEXTURE11 = 0x84CB
TEXTURE12 = 0x84CC
TEXTURE13 = 0x84CD
TEXTURE14 = 0x84CE
TEXTURE15 = 0x84CF
TEXTURE16 = 0x84D0
TEXTURE17 = 0x84D1
TEXTURE18 = 0x84D2
TEXTURE19 = 0x84D3
TEXTURE20 = 0x84D4
TEXTURE21 = 0x84D5
TEXTURE22 = 0x84D6
TEXTURE23 = 0x84D7
TEXTURE24 = 0x84D8
TEXTURE25 = 0x84D9
TEXTURE26 = 0x84DA
TEXTURE27 = 0x84DB
TEXTURE28 = 0x84DC
TEXTURE29 = 0x84DD
TEXTURE30 = 0x84DE
TEXTURE31 = 0x84DF
ACTIVE_TEXTURE = 0x84E0
REPEAT = 0x2901
CLAMP_TO_EDGE = 0x812F
MIRRORED_REPEAT = 0x8370
VERTEX_ATTRIB_ARRAY_ENABLED = 0x8622
VERTEX_ATTRIB_ARRAY_SIZE = 0x8623
VERTEX_ATTRIB_ARRAY_STRIDE = 0x8624
VERTEX_ATTRIB_ARRAY_TYPE = 0x8625
VERTEX_ATTRIB_ARRAY_NORMALIZED = 0x886A
VERTEX_ATTRIB_ARRAY_POINTER = 0x8645
VERTEX_ATTRIB_ARRAY_BUFFER_BINDING = 0x889F
IMPLEMENTATION_COLOR_READ_TYPE = 0x8B9A
IMPLEMENTATION_COLOR_READ_FORMAT = 0x8B9B
COMPILE_STATUS = 0x8B81
INFO_LOG_LENGTH = 0x8B84
SHADER_SOURCE_LENGTH = 0x8B88
SHADER_COMPILER = 0x8DFA
SHADER_BINARY_FORMATS = 0x8DF8
NUM_SHADER_BINARY_FORMATS = 0x8DF9
LOW_FLOAT = 0x8DF0
MEDIUM_FLOAT = 0x8DF1
HIGH_FLOAT = 0x8DF2
LOW_INT = 0x8DF3
MEDIUM_INT = 0x8DF4
HIGH_INT = 0x8DF5
FRAMEBUFFER = 0x8D40
RENDERBUFFER = 0x8D41
RGBA4 = 0x8056
RGB5_A1 = 0x8057
RGB565 = 0x8D62
DEPTH_COMPONENT16 = 0x81A5
STENCIL_INDEX8 = 0x8D48
RENDERBUFFER_WIDTH = 0x8D42
RENDERBUFFER_HEIGHT = 0x8D43
RENDERBUFFER_INTERNAL_FORMAT = 0x8D44
RENDERBUFFER_RED_SIZE = 0x8D50
RENDERBUFFER_GREEN_SIZE = 0x8D51
RENDERBUFFER_BLUE_SIZE = 0x8D52
RENDERBUFFER_ALPHA_SIZE = 0x8D53
RENDERBUFFER_DEPTH_SIZE = 0x8D54
RENDERBUFFER_STENCIL_SIZE = 0x8D55
FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE = 0x8CD0
FRAMEBUFFER_ATTACHMENT_OBJECT_NAME = 0x8CD1
FRAMEBUFFER_ATTACHMENT_TEXTURE_LEVEL = 0x8CD2
FRAMEBUFFER_ATTACHMENT_TEXTURE_CUBE_MAP_FACE = 0x8CD3
COLOR_ATTACHMENT0 = 0x8CE0
DEPTH_ATTACHMENT = 0x8D00
STENCIL_ATTACHMENT = 0x8D20
FRAMEBUFFER_COMPLETE = 0x8CD5
FRAMEBUFFER_INCOMPLETE_ATTACHMENT = 0x8CD6
FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT = 0x8CD7
FRAMEBUFFER_INCOMPLETE_DIMENSIONS = 0x8CD9
FRAMEBUFFER_UNSUPPORTED = 0x8CDD
FRAMEBUFFER_BINDING = 0x8CA6
RENDERBUFFER_BINDING = 0x8CA7
MAX_RENDERBUFFER_SIZE = 0x84E8
INVALID_FRAMEBUFFER_OPERATION = 0x0506
)
const (
DEPTH_BUFFER_BIT = 0x00000100
STENCIL_BUFFER_BIT = 0x00000400
COLOR_BUFFER_BIT = 0x00004000
)
const (
FLOAT_VEC2 = 0x8B50
FLOAT_VEC3 = 0x8B51
FLOAT_VEC4 = 0x8B52
INT_VEC2 = 0x8B53
INT_VEC3 = 0x8B54
INT_VEC4 = 0x8B55
BOOL = 0x8B56
BOOL_VEC2 = 0x8B57
BOOL_VEC3 = 0x8B58
BOOL_VEC4 = 0x8B59
FLOAT_MAT2 = 0x8B5A
FLOAT_MAT3 = 0x8B5B
FLOAT_MAT4 = 0x8B5C
SAMPLER_2D = 0x8B5E
SAMPLER_CUBE = 0x8B60
)
const (
FRAGMENT_SHADER = 0x8B30
VERTEX_SHADER = 0x8B31
)
const (
FALSE = 0
TRUE = 1
ZERO = 0
ONE = 1
NO_ERROR = 0
NONE = 0
)
// GL ES 3.0 constants.
const (
ACTIVE_UNIFORM_BLOCK_MAX_NAME_LENGTH = 0x8A35
ACTIVE_UNIFORM_BLOCKS = 0x8A36
ALREADY_SIGNALED = 0x911A
ANY_SAMPLES_PASSED = 0x8C2F
ANY_SAMPLES_PASSED_CONSERVATIVE = 0x8D6A
BLUE = 0x1905
BUFFER_ACCESS_FLAGS = 0x911F
BUFFER_MAP_LENGTH = 0x9120
BUFFER_MAP_OFFSET = 0x9121
BUFFER_MAPPED = 0x88BC
BUFFER_MAP_POINTER = 0x88BD
COLOR = 0x1800
COLOR_ATTACHMENT10 = 0x8CEA
COLOR_ATTACHMENT1 = 0x8CE1
COLOR_ATTACHMENT11 = 0x8CEB
COLOR_ATTACHMENT12 = 0x8CEC
COLOR_ATTACHMENT13 = 0x8CED
COLOR_ATTACHMENT14 = 0x8CEE
COLOR_ATTACHMENT15 = 0x8CEF
COLOR_ATTACHMENT2 = 0x8CE2
COLOR_ATTACHMENT3 = 0x8CE3
COLOR_ATTACHMENT4 = 0x8CE4
COLOR_ATTACHMENT5 = 0x8CE5
COLOR_ATTACHMENT6 = 0x8CE6
COLOR_ATTACHMENT7 = 0x8CE7
COLOR_ATTACHMENT8 = 0x8CE8
COLOR_ATTACHMENT9 = 0x8CE9
COMPARE_REF_TO_TEXTURE = 0x884E
COMPRESSED_R11_EAC = 0x9270
COMPRESSED_RG11_EAC = 0x9272
COMPRESSED_RGB8_ETC2 = 0x9274
COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2 = 0x9276
COMPRESSED_RGBA8_ETC2_EAC = 0x9278
COMPRESSED_SIGNED_R11_EAC = 0x9271
COMPRESSED_SIGNED_RG11_EAC = 0x9273
COMPRESSED_SRGB8_ALPHA8_ETC2_EAC = 0x9279
COMPRESSED_SRGB8_ETC2 = 0x9275
COMPRESSED_SRGB8_PUNCHTHROUGH_ALPHA1_ETC2 = 0x9277
CONDITION_SATISFIED = 0x911C
COPY_READ_BUFFER = 0x8F36
COPY_READ_BUFFER_BINDING = 0x8F36
COPY_WRITE_BUFFER = 0x8F37
COPY_WRITE_BUFFER_BINDING = 0x8F37
CURRENT_QUERY = 0x8865
DEPTH = 0x1801
DEPTH24_STENCIL8 = 0x88F0
DEPTH32F_STENCIL8 = 0x8CAD
DEPTH_COMPONENT24 = 0x81A6
DEPTH_COMPONENT32F = 0x8CAC
DEPTH_STENCIL = 0x84F9
DEPTH_STENCIL_ATTACHMENT = 0x821A
DRAW_BUFFER0 = 0x8825
DRAW_BUFFER10 = 0x882F
DRAW_BUFFER1 = 0x8826
DRAW_BUFFER11 = 0x8830
DRAW_BUFFER12 = 0x8831
DRAW_BUFFER13 = 0x8832
DRAW_BUFFER14 = 0x8833
DRAW_BUFFER15 = 0x8834
DRAW_BUFFER2 = 0x8827
DRAW_BUFFER3 = 0x8828
DRAW_BUFFER4 = 0x8829
DRAW_BUFFER5 = 0x882A
DRAW_BUFFER6 = 0x882B
DRAW_BUFFER7 = 0x882C
DRAW_BUFFER8 = 0x882D
DRAW_BUFFER9 = 0x882E
DRAW_FRAMEBUFFER = 0x8CA9
DRAW_FRAMEBUFFER_BINDING = 0x8CA6
DYNAMIC_COPY = 0x88EA
DYNAMIC_READ = 0x88E9
FLOAT_32_UNSIGNED_INT_24_8_REV = 0x8DAD
FLOAT_MAT2x3 = 0x8B65
FLOAT_MAT2x4 = 0x8B66
FLOAT_MAT3x2 = 0x8B67
FLOAT_MAT3x4 = 0x8B68
FLOAT_MAT4x2 = 0x8B69
FLOAT_MAT4x3 = 0x8B6A
FRAGMENT_SHADER_DERIVATIVE_HINT = 0x8B8B
FRAMEBUFFER_ATTACHMENT_ALPHA_SIZE = 0x8215
FRAMEBUFFER_ATTACHMENT_BLUE_SIZE = 0x8214
FRAMEBUFFER_ATTACHMENT_COLOR_ENCODING = 0x8210
FRAMEBUFFER_ATTACHMENT_COMPONENT_TYPE = 0x8211
FRAMEBUFFER_ATTACHMENT_DEPTH_SIZE = 0x8216
FRAMEBUFFER_ATTACHMENT_GREEN_SIZE = 0x8213
FRAMEBUFFER_ATTACHMENT_RED_SIZE = 0x8212
FRAMEBUFFER_ATTACHMENT_STENCIL_SIZE = 0x8217
FRAMEBUFFER_ATTACHMENT_TEXTURE_LAYER = 0x8CD4
FRAMEBUFFER_DEFAULT = 0x8218
FRAMEBUFFER_INCOMPLETE_MULTISAMPLE = 0x8D56
FRAMEBUFFER_UNDEFINED = 0x8219
GREEN = 0x1904
HALF_FLOAT = 0x140B
INT_2_10_10_10_REV = 0x8D9F
INTERLEAVED_ATTRIBS = 0x8C8C
INT_SAMPLER_2D = 0x8DCA
INT_SAMPLER_2D_ARRAY = 0x8DCF
INT_SAMPLER_3D = 0x8DCB
INT_SAMPLER_CUBE = 0x8DCC
INVALID_INDEX = 0xFFFFFFFF
MAJOR_VERSION = 0x821B
MAP_FLUSH_EXPLICIT_BIT = 0x0010
MAP_INVALIDATE_BUFFER_BIT = 0x0008
MAP_INVALIDATE_RANGE_BIT = 0x0004
MAP_READ_BIT = 0x0001
MAP_UNSYNCHRONIZED_BIT = 0x0020
MAP_WRITE_BIT = 0x0002
MAX = 0x8008
MAX_3D_TEXTURE_SIZE = 0x8073
MAX_ARRAY_TEXTURE_LAYERS = 0x88FF
MAX_COLOR_ATTACHMENTS = 0x8CDF
MAX_COMBINED_FRAGMENT_UNIFORM_COMPONENTS = 0x8A33
MAX_COMBINED_UNIFORM_BLOCKS = 0x8A2E
MAX_COMBINED_VERTEX_UNIFORM_COMPONENTS = 0x8A31
MAX_DRAW_BUFFERS = 0x8824
MAX_ELEMENT_INDEX = 0x8D6B
MAX_ELEMENTS_INDICES = 0x80E9
MAX_ELEMENTS_VERTICES = 0x80E8
MAX_FRAGMENT_INPUT_COMPONENTS = 0x9125
MAX_FRAGMENT_UNIFORM_BLOCKS = 0x8A2D
MAX_FRAGMENT_UNIFORM_COMPONENTS = 0x8B49
MAX_PROGRAM_TEXEL_OFFSET = 0x8905
MAX_SAMPLES = 0x8D57
MAX_SERVER_WAIT_TIMEOUT = 0x9111
MAX_TEXTURE_LOD_BIAS = 0x84FD
MAX_TRANSFORM_FEEDBACK_INTERLEAVED_COMPONENTS = 0x8C8A
MAX_TRANSFORM_FEEDBACK_SEPARATE_ATTRIBS = 0x8C8B
MAX_TRANSFORM_FEEDBACK_SEPARATE_COMPONENTS = 0x8C80
MAX_UNIFORM_BLOCK_SIZE = 0x8A30
MAX_UNIFORM_BUFFER_BINDINGS = 0x8A2F
MAX_VARYING_COMPONENTS = 0x8B4B
MAX_VERTEX_OUTPUT_COMPONENTS = 0x9122
MAX_VERTEX_UNIFORM_BLOCKS = 0x8A2B
MAX_VERTEX_UNIFORM_COMPONENTS = 0x8B4A
MIN = 0x8007
MINOR_VERSION = 0x821C
MIN_PROGRAM_TEXEL_OFFSET = 0x8904
NUM_EXTENSIONS = 0x821D
NUM_PROGRAM_BINARY_FORMATS = 0x87FE
NUM_SAMPLE_COUNTS = 0x9380
OBJECT_TYPE = 0x9112
PACK_ROW_LENGTH = 0x0D02
PACK_SKIP_PIXELS = 0x0D04
PACK_SKIP_ROWS = 0x0D03
PIXEL_PACK_BUFFER = 0x88EB
PIXEL_PACK_BUFFER_BINDING = 0x88ED
PIXEL_UNPACK_BUFFER = 0x88EC
PIXEL_UNPACK_BUFFER_BINDING = 0x88EF
PRIMITIVE_RESTART_FIXED_INDEX = 0x8D69
PROGRAM_BINARY_FORMATS = 0x87FF
PROGRAM_BINARY_LENGTH = 0x8741
PROGRAM_BINARY_RETRIEVABLE_HINT = 0x8257
QUERY_RESULT = 0x8866
QUERY_RESULT_AVAILABLE = 0x8867
R11F_G11F_B10F = 0x8C3A
R16F = 0x822D
R16I = 0x8233
R16UI = 0x8234
R32F = 0x822E
R32I = 0x8235
R32UI = 0x8236
R8 = 0x8229
R8I = 0x8231
R8_SNORM = 0x8F94
R8UI = 0x8232
RASTERIZER_DISCARD = 0x8C89
READ_BUFFER = 0x0C02
READ_FRAMEBUFFER = 0x8CA8
READ_FRAMEBUFFER_BINDING = 0x8CAA
RED = 0x1903
RED_INTEGER = 0x8D94
RENDERBUFFER_SAMPLES = 0x8CAB
RG = 0x8227
RG16F = 0x822F
RG16I = 0x8239
RG16UI = 0x823A
RG32F = 0x8230
RG32I = 0x823B
RG32UI = 0x823C
RG8 = 0x822B
RG8I = 0x8237
RG8_SNORM = 0x8F95
RG8UI = 0x8238
RGB10_A2 = 0x8059
RGB10_A2UI = 0x906F
RGB16F = 0x881B
RGB16I = 0x8D89
RGB16UI = 0x8D77
RGB32F = 0x8815
RGB32I = 0x8D83
RGB32UI = 0x8D71
RGB8 = 0x8051
RGB8I = 0x8D8F
RGB8_SNORM = 0x8F96
RGB8UI = 0x8D7D
RGB9_E5 = 0x8C3D
RGBA16F = 0x881A
RGBA16I = 0x8D88
RGBA16UI = 0x8D76
RGBA32F = 0x8814
RGBA32I = 0x8D82
RGBA32UI = 0x8D70
RGBA8 = 0x8058
RGBA8I = 0x8D8E
RGBA8_SNORM = 0x8F97
RGBA8UI = 0x8D7C
RGBA_INTEGER = 0x8D99
RGB_INTEGER = 0x8D98
RG_INTEGER = 0x8228
SAMPLER_2D_ARRAY = 0x8DC1
SAMPLER_2D_ARRAY_SHADOW = 0x8DC4
SAMPLER_2D_SHADOW = 0x8B62
SAMPLER_3D = 0x8B5F
SAMPLER_BINDING = 0x8919
SAMPLER_CUBE_SHADOW = 0x8DC5
SEPARATE_ATTRIBS = 0x8C8D
SIGNALED = 0x9119
SIGNED_NORMALIZED = 0x8F9C
SRGB = 0x8C40
SRGB8 = 0x8C41
SRGB8_ALPHA8 = 0x8C43
STATIC_COPY = 0x88E6
STATIC_READ = 0x88E5
STENCIL = 0x1802
STREAM_COPY = 0x88E2
STREAM_READ = 0x88E1
SYNC_CONDITION = 0x9113
SYNC_FENCE = 0x9116
SYNC_FLAGS = 0x9115
SYNC_FLUSH_COMMANDS_BIT = 0x00000001
SYNC_GPU_COMMANDS_COMPLETE = 0x9117
SYNC_STATUS = 0x9114
TEXTURE_2D_ARRAY = 0x8C1A
TEXTURE_3D = 0x806F
TEXTURE_BASE_LEVEL = 0x813C
TEXTURE_BINDING_2D_ARRAY = 0x8C1D
TEXTURE_BINDING_3D = 0x806A
TEXTURE_COMPARE_FUNC = 0x884D
TEXTURE_COMPARE_MODE = 0x884C
TEXTURE_IMMUTABLE_FORMAT = 0x912F
TEXTURE_IMMUTABLE_LEVELS = 0x82DF
TEXTURE_MAX_LEVEL = 0x813D
TEXTURE_MAX_LOD = 0x813B
TEXTURE_MIN_LOD = 0x813A
TEXTURE_SWIZZLE_A = 0x8E45
TEXTURE_SWIZZLE_B = 0x8E44
TEXTURE_SWIZZLE_G = 0x8E43
TEXTURE_SWIZZLE_R = 0x8E42
TEXTURE_WRAP_R = 0x8072
TIMEOUT_EXPIRED = 0x911B
TIMEOUT_IGNORED = 0xFFFFFFFFFFFFFFFF
TRANSFORM_FEEDBACK = 0x8E22
TRANSFORM_FEEDBACK_ACTIVE = 0x8E24
TRANSFORM_FEEDBACK_BINDING = 0x8E25
TRANSFORM_FEEDBACK_BUFFER = 0x8C8E
TRANSFORM_FEEDBACK_BUFFER_BINDING = 0x8C8F
TRANSFORM_FEEDBACK_BUFFER_MODE = 0x8C7F
TRANSFORM_FEEDBACK_BUFFER_SIZE = 0x8C85
TRANSFORM_FEEDBACK_BUFFER_START = 0x8C84
TRANSFORM_FEEDBACK_PAUSED = 0x8E23
TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN = 0x8C88
TRANSFORM_FEEDBACK_VARYING_MAX_LENGTH = 0x8C76
TRANSFORM_FEEDBACK_VARYINGS = 0x8C83
UNIFORM_ARRAY_STRIDE = 0x8A3C
UNIFORM_BLOCK_ACTIVE_UNIFORM_INDICES = 0x8A43
UNIFORM_BLOCK_ACTIVE_UNIFORMS = 0x8A42
UNIFORM_BLOCK_BINDING = 0x8A3F
UNIFORM_BLOCK_DATA_SIZE = 0x8A40
UNIFORM_BLOCK_INDEX = 0x8A3A
UNIFORM_BLOCK_NAME_LENGTH = 0x8A41
UNIFORM_BLOCK_REFERENCED_BY_FRAGMENT_SHADER = 0x8A46
UNIFORM_BLOCK_REFERENCED_BY_VERTEX_SHADER = 0x8A44
UNIFORM_BUFFER = 0x8A11
UNIFORM_BUFFER_BINDING = 0x8A28
UNIFORM_BUFFER_OFFSET_ALIGNMENT = 0x8A34
UNIFORM_BUFFER_SIZE = 0x8A2A
UNIFORM_BUFFER_START = 0x8A29
UNIFORM_IS_ROW_MAJOR = 0x8A3E
UNIFORM_MATRIX_STRIDE = 0x8A3D
UNIFORM_NAME_LENGTH = 0x8A39
UNIFORM_OFFSET = 0x8A3B
UNIFORM_SIZE = 0x8A38
UNIFORM_TYPE = 0x8A37
UNPACK_IMAGE_HEIGHT = 0x806E
UNPACK_ROW_LENGTH = 0x0CF2
UNPACK_SKIP_IMAGES = 0x806D
UNPACK_SKIP_PIXELS = 0x0CF4
UNPACK_SKIP_ROWS = 0x0CF3
UNSIGNALED = 0x9118
UNSIGNED_INT_10F_11F_11F_REV = 0x8C3B
UNSIGNED_INT_2_10_10_10_REV = 0x8368
UNSIGNED_INT_24_8 = 0x84FA
UNSIGNED_INT_5_9_9_9_REV = 0x8C3E
UNSIGNED_INT_SAMPLER_2D = 0x8DD2
UNSIGNED_INT_SAMPLER_2D_ARRAY = 0x8DD7
UNSIGNED_INT_SAMPLER_3D = 0x8DD3
UNSIGNED_INT_SAMPLER_CUBE = 0x8DD4
UNSIGNED_INT_VEC2 = 0x8DC6
UNSIGNED_INT_VEC3 = 0x8DC7
UNSIGNED_INT_VEC4 = 0x8DC8
UNSIGNED_NORMALIZED = 0x8C17
VERTEX_ARRAY_BINDING = 0x85B5
VERTEX_ATTRIB_ARRAY_DIVISOR = 0x88FE
VERTEX_ATTRIB_ARRAY_INTEGER = 0x88FD
WAIT_FAILED = 0x911D
)
+243
View File
@@ -0,0 +1,243 @@
// 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.
package gl
import (
"archive/tar"
"compress/gzip"
"debug/pe"
"fmt"
"io"
"io/ioutil"
"log"
"net/http"
"os"
"path/filepath"
"runtime"
)
var debug = log.New(ioutil.Discard, "gl: ", log.LstdFlags)
func downloadDLLs() (path string, err error) {
url := "https://dl.google.com/go/mobile/angle-bd3f8780b-" + runtime.GOARCH + ".tgz"
debug.Printf("downloading %s", url)
resp, err := http.Get(url)
if err != nil {
return "", fmt.Errorf("gl: %v", err)
}
defer func() {
err2 := resp.Body.Close()
if err == nil && err2 != nil {
err = fmt.Errorf("gl: error reading body from %v: %v", url, err2)
}
}()
if resp.StatusCode != http.StatusOK {
err := fmt.Errorf("gl: error fetching %v, status: %v", url, resp.Status)
return "", err
}
r, err := gzip.NewReader(resp.Body)
if err != nil {
return "", fmt.Errorf("gl: error reading gzip from %v: %v", url, err)
}
tr := tar.NewReader(r)
var bytesGLESv2, bytesEGL, bytesD3DCompiler []byte
for {
header, err := tr.Next()
if err == io.EOF {
break
}
if err != nil {
return "", fmt.Errorf("gl: error reading tar from %v: %v", url, err)
}
switch header.Name {
case "angle-" + runtime.GOARCH + "/libglesv2.dll":
bytesGLESv2, err = ioutil.ReadAll(tr)
case "angle-" + runtime.GOARCH + "/libegl.dll":
bytesEGL, err = ioutil.ReadAll(tr)
case "angle-" + runtime.GOARCH + "/d3dcompiler_47.dll":
bytesD3DCompiler, err = ioutil.ReadAll(tr)
default: // skip
}
if err != nil {
return "", fmt.Errorf("gl: error reading %v from %v: %v", header.Name, url, err)
}
}
if len(bytesGLESv2) == 0 || len(bytesEGL) == 0 || len(bytesD3DCompiler) == 0 {
return "", fmt.Errorf("gl: did not find all DLLs in %v", url)
}
writeDLLs := func(path string) error {
if err := ioutil.WriteFile(filepath.Join(path, "libglesv2.dll"), bytesGLESv2, 0755); err != nil {
return fmt.Errorf("gl: cannot install ANGLE: %v", err)
}
if err := ioutil.WriteFile(filepath.Join(path, "libegl.dll"), bytesEGL, 0755); err != nil {
return fmt.Errorf("gl: cannot install ANGLE: %v", err)
}
if err := ioutil.WriteFile(filepath.Join(path, "d3dcompiler_47.dll"), bytesD3DCompiler, 0755); err != nil {
return fmt.Errorf("gl: cannot install ANGLE: %v", err)
}
return nil
}
// First, we attempt to install these DLLs in LOCALAPPDATA/Shiny.
//
// Traditionally we would use the system32 directory, but it is
// no longer writable by normal programs.
os.MkdirAll(appdataPath(), 0775)
if err := writeDLLs(appdataPath()); err == nil {
return appdataPath(), nil
}
debug.Printf("DLLs could not be written to %s", appdataPath())
// Second, install in GOPATH/pkg if it exists.
gopath := os.Getenv("GOPATH")
gopathpkg := filepath.Join(gopath, "pkg")
if _, err := os.Stat(gopathpkg); err == nil && gopath != "" {
if err := writeDLLs(gopathpkg); err == nil {
return gopathpkg, nil
}
}
debug.Printf("DLLs could not be written to GOPATH")
// Third, pick a temporary directory.
tmp := os.TempDir()
if err := writeDLLs(tmp); err != nil {
return "", fmt.Errorf("gl: unable to install ANGLE DLLs: %v", err)
}
return tmp, nil
}
func appdataPath() string {
return filepath.Join(os.Getenv("LOCALAPPDATA"), "GoGL", runtime.GOARCH)
}
func containsDLLs(dir string) bool {
compatible := func(name string) bool {
file, err := pe.Open(filepath.Join(dir, name))
if err != nil {
return false
}
defer file.Close()
switch file.Machine {
case pe.IMAGE_FILE_MACHINE_AMD64:
return "amd64" == runtime.GOARCH
case pe.IMAGE_FILE_MACHINE_ARM:
return "arm" == runtime.GOARCH
case pe.IMAGE_FILE_MACHINE_I386:
return "386" == runtime.GOARCH
}
return false
}
return compatible("libglesv2.dll") && compatible("libegl.dll") && compatible("d3dcompiler_47.dll")
}
func chromePath() string {
// dlls are stored in:
// <BASE>/<VERSION>/libglesv2.dll
var installdirs = []string{
// Chrome User
filepath.Join(os.Getenv("LOCALAPPDATA"), "Google", "Chrome", "Application"),
// Chrome System
filepath.Join(os.Getenv("ProgramFiles(x86)"), "Google", "Chrome", "Application"),
// Chromium
filepath.Join(os.Getenv("LOCALAPPDATA"), "Chromium", "Application"),
// Chrome Canary
filepath.Join(os.Getenv("LOCALAPPDATA"), "Google", "Chrome SxS", "Application"),
}
for _, installdir := range installdirs {
versiondirs, err := ioutil.ReadDir(installdir)
if err != nil {
continue
}
for _, versiondir := range versiondirs {
if !versiondir.IsDir() {
continue
}
versionpath := filepath.Join(installdir, versiondir.Name())
if containsDLLs(versionpath) {
return versionpath
}
}
}
return ""
}
func findDLLs() (err error) {
load := func(path string) (bool, error) {
if path != "" {
// don't try to start when one of the files is missing
if !containsDLLs(path) {
return false, nil
}
LibD3DCompiler.Name = filepath.Join(path, filepath.Base(LibD3DCompiler.Name))
LibGLESv2.Name = filepath.Join(path, filepath.Base(LibGLESv2.Name))
LibEGL.Name = filepath.Join(path, filepath.Base(LibEGL.Name))
}
if err := LibGLESv2.Load(); err == nil {
if err := LibEGL.Load(); err != nil {
return false, fmt.Errorf("gl: loaded libglesv2 but not libegl: %v", err)
}
if err := LibD3DCompiler.Load(); err != nil {
return false, fmt.Errorf("gl: loaded libglesv2, libegl but not d3dcompiler: %v", err)
}
if path == "" {
debug.Printf("DLLs found")
} else {
debug.Printf("DLLs found in: %q", path)
}
return true, nil
}
return false, nil
}
// Look in the system directory.
if ok, err := load(""); ok || err != nil {
return err
}
// Look in the AppData directory.
if ok, err := load(appdataPath()); ok || err != nil {
return err
}
// Look for a Chrome installation
if dir := chromePath(); dir != "" {
if ok, err := load(dir); ok || err != nil {
return err
}
}
// Look in GOPATH/pkg.
if ok, err := load(filepath.Join(os.Getenv("GOPATH"), "pkg")); ok || err != nil {
return err
}
// Look in temporary directory.
if ok, err := load(os.TempDir()); ok || err != nil {
return err
}
// Download the DLL binary.
path, err := downloadDLLs()
if err != nil {
return err
}
debug.Printf("DLLs written to %s", path)
if ok, err := load(path); !ok || err != nil {
return fmt.Errorf("gl: unable to load ANGLE after installation: %v", err)
}
return nil
}
+66
View File
@@ -0,0 +1,66 @@
// Copyright 2014 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.
/*
Package gl implements Go bindings for OpenGL ES 2.0 and ES 3.0.
The GL functions are defined on a Context object that is responsible for
tracking a GL context. Typically a windowing system package (such as
golang.org/x/exp/shiny/screen) will call NewContext and provide
a gl.Context for a user application.
If the gl package is compiled on a platform capable of supporting ES 3.0,
the gl.Context object also implements gl.Context3.
The bindings are deliberately minimal, staying as close the C API as
possible. The semantics of each function maps onto functions
described in the Khronos documentation:
https://www.khronos.org/opengles/sdk/docs/man/
One notable departure from the C API is the introduction of types
to represent common uses of GLint: Texture, Surface, Buffer, etc.
# Debug Logging
A tracing version of the OpenGL bindings is behind the `gldebug` build
tag. It acts as a simplified version of apitrace. Build your Go binary
with
-tags gldebug
and each call to a GL function will log its input, output, and any
error messages. For example,
I/GoLog (27668): gl.GenBuffers(1) [Buffer(70001)]
I/GoLog (27668): gl.BindBuffer(ARRAY_BUFFER, Buffer(70001))
I/GoLog (27668): gl.BufferData(ARRAY_BUFFER, 36, len(36), STATIC_DRAW)
I/GoLog (27668): gl.BindBuffer(ARRAY_BUFFER, Buffer(70001))
I/GoLog (27668): gl.VertexAttribPointer(Attrib(0), 6, FLOAT, false, 0, 0) error: [INVALID_VALUE]
The gldebug tracing has very high overhead, so make sure to remove
the build tag before deploying any binaries.
*/
package gl // import "github.com/ebitengine/gomobile/gl"
/*
Implementation details.
All GL function calls fill out a C.struct_fnargs and drop it on the work
queue. The Start function drains the work queue and hands over a batch
of calls to C.process which runs them. This allows multiple GL calls to
be executed in a single cgo call.
A GL call is marked as blocking if it returns a value, or if it takes a
Go pointer. In this case the call will not return until C.process sends a
value on the retvalue channel.
This implementation ensures any goroutine can make GL calls, but it does
not make the GL interface safe for simultaneous use by multiple goroutines.
For the purpose of analyzing this code for race conditions, picture two
separate goroutines: one blocked on gl.Start, and another making calls to
the gl package exported functions.
*/
//go:generate go run gendebug.go -o gldebug.go
+210
View File
@@ -0,0 +1,210 @@
// 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.
package gl
import "unsafe"
type call struct {
args fnargs
parg unsafe.Pointer
blocking bool
}
type fnargs struct {
fn glfn
a0 uintptr
a1 uintptr
a2 uintptr
a3 uintptr
a4 uintptr
a5 uintptr
a6 uintptr
a7 uintptr
a8 uintptr
a9 uintptr
}
type glfn int
const (
glfnUNDEFINED glfn = iota
glfnActiveTexture
glfnAttachShader
glfnBindAttribLocation
glfnBindBuffer
glfnBindFramebuffer
glfnBindRenderbuffer
glfnBindTexture
glfnBindVertexArray
glfnBlendColor
glfnBlendEquation
glfnBlendEquationSeparate
glfnBlendFunc
glfnBlendFuncSeparate
glfnBufferData
glfnBufferSubData
glfnCheckFramebufferStatus
glfnClear
glfnClearColor
glfnClearDepthf
glfnClearStencil
glfnColorMask
glfnCompileShader
glfnCompressedTexImage2D
glfnCompressedTexSubImage2D
glfnCopyTexImage2D
glfnCopyTexSubImage2D
glfnCreateProgram
glfnCreateShader
glfnCullFace
glfnDeleteBuffer
glfnDeleteFramebuffer
glfnDeleteProgram
glfnDeleteRenderbuffer
glfnDeleteShader
glfnDeleteTexture
glfnDeleteVertexArray
glfnDepthFunc
glfnDepthRangef
glfnDepthMask
glfnDetachShader
glfnDisable
glfnDisableVertexAttribArray
glfnDrawArrays
glfnDrawElements
glfnEnable
glfnEnableVertexAttribArray
glfnFinish
glfnFlush
glfnFramebufferRenderbuffer
glfnFramebufferTexture2D
glfnFrontFace
glfnGenBuffer
glfnGenFramebuffer
glfnGenRenderbuffer
glfnGenTexture
glfnGenVertexArray
glfnGenerateMipmap
glfnGetActiveAttrib
glfnGetActiveUniform
glfnGetAttachedShaders
glfnGetAttribLocation
glfnGetBooleanv
glfnGetBufferParameteri
glfnGetError
glfnGetFloatv
glfnGetFramebufferAttachmentParameteriv
glfnGetIntegerv
glfnGetProgramInfoLog
glfnGetProgramiv
glfnGetRenderbufferParameteriv
glfnGetShaderInfoLog
glfnGetShaderPrecisionFormat
glfnGetShaderSource
glfnGetShaderiv
glfnGetString
glfnGetTexParameterfv
glfnGetTexParameteriv
glfnGetUniformLocation
glfnGetUniformfv
glfnGetUniformiv
glfnGetVertexAttribfv
glfnGetVertexAttribiv
glfnHint
glfnIsBuffer
glfnIsEnabled
glfnIsFramebuffer
glfnIsProgram
glfnIsRenderbuffer
glfnIsShader
glfnIsTexture
glfnLineWidth
glfnLinkProgram
glfnPixelStorei
glfnPolygonOffset
glfnReadPixels
glfnReleaseShaderCompiler
glfnRenderbufferStorage
glfnSampleCoverage
glfnScissor
glfnShaderSource
glfnStencilFunc
glfnStencilFuncSeparate
glfnStencilMask
glfnStencilMaskSeparate
glfnStencilOp
glfnStencilOpSeparate
glfnTexImage2D
glfnTexParameterf
glfnTexParameterfv
glfnTexParameteri
glfnTexParameteriv
glfnTexSubImage2D
glfnUniform1f
glfnUniform1fv
glfnUniform1i
glfnUniform1iv
glfnUniform2f
glfnUniform2fv
glfnUniform2i
glfnUniform2iv
glfnUniform3f
glfnUniform3fv
glfnUniform3i
glfnUniform3iv
glfnUniform4f
glfnUniform4fv
glfnUniform4i
glfnUniform4iv
glfnUniformMatrix2fv
glfnUniformMatrix3fv
glfnUniformMatrix4fv
glfnUseProgram
glfnValidateProgram
glfnVertexAttrib1f
glfnVertexAttrib1fv
glfnVertexAttrib2f
glfnVertexAttrib2fv
glfnVertexAttrib3f
glfnVertexAttrib3fv
glfnVertexAttrib4f
glfnVertexAttrib4fv
glfnVertexAttribPointer
glfnViewport
// ES 3.0 functions
glfnUniformMatrix2x3fv
glfnUniformMatrix3x2fv
glfnUniformMatrix2x4fv
glfnUniformMatrix4x2fv
glfnUniformMatrix3x4fv
glfnUniformMatrix4x3fv
glfnBlitFramebuffer
glfnUniform1ui
glfnUniform2ui
glfnUniform3ui
glfnUniform4ui
glfnUniform1uiv
glfnUniform2uiv
glfnUniform3uiv
glfnUniform4uiv
)
func goString(buf []byte) string {
for i, b := range buf {
if b == 0 {
return string(buf[:i])
}
}
panic("buf is not NUL-terminated")
}
func glBoolean(b bool) uintptr {
if b {
return TRUE
}
return FALSE
}
+1851
View File
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+889
View File
@@ -0,0 +1,889 @@
// Copyright 2014 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.
package gl
// Context is an OpenGL ES context.
//
// A Context has a method for every GL function supported by ES 2 or later.
// In a program compiled with ES 3 support, a Context is also a Context3.
// For example, a program can:
//
// func f(glctx gl.Context) {
// glctx.(gl.Context3).BlitFramebuffer(...)
// }
//
// Calls are not safe for concurrent use. However calls can be made from
// any goroutine, the gl package removes the notion of thread-local
// context.
//
// Contexts are independent. Two contexts can be used concurrently.
type Context interface {
// ActiveTexture sets the active texture unit.
//
// http://www.khronos.org/opengles/sdk/docs/man3/html/glActiveTexture.xhtml
ActiveTexture(texture Enum)
// AttachShader attaches a shader to a program.
//
// http://www.khronos.org/opengles/sdk/docs/man3/html/glAttachShader.xhtml
AttachShader(p Program, s Shader)
// BindAttribLocation binds a vertex attribute index with a named
// variable.
//
// http://www.khronos.org/opengles/sdk/docs/man3/html/glBindAttribLocation.xhtml
BindAttribLocation(p Program, a Attrib, name string)
// BindBuffer binds a buffer.
//
// http://www.khronos.org/opengles/sdk/docs/man3/html/glBindBuffer.xhtml
BindBuffer(target Enum, b Buffer)
// BindFramebuffer binds a framebuffer.
//
// http://www.khronos.org/opengles/sdk/docs/man3/html/glBindFramebuffer.xhtml
BindFramebuffer(target Enum, fb Framebuffer)
// BindRenderbuffer binds a render buffer.
//
// http://www.khronos.org/opengles/sdk/docs/man3/html/glBindRenderbuffer.xhtml
BindRenderbuffer(target Enum, rb Renderbuffer)
// BindTexture binds a texture.
//
// http://www.khronos.org/opengles/sdk/docs/man3/html/glBindTexture.xhtml
BindTexture(target Enum, t Texture)
// BindVertexArray binds a vertex array.
//
// http://www.khronos.org/opengles/sdk/docs/man3/html/glBindVertexArray.xhtml
BindVertexArray(rb VertexArray)
// BlendColor sets the blend color.
//
// http://www.khronos.org/opengles/sdk/docs/man3/html/glBlendColor.xhtml
BlendColor(red, green, blue, alpha float32)
// BlendEquation sets both RGB and alpha blend equations.
//
// http://www.khronos.org/opengles/sdk/docs/man3/html/glBlendEquation.xhtml
BlendEquation(mode Enum)
// BlendEquationSeparate sets RGB and alpha blend equations separately.
//
// http://www.khronos.org/opengles/sdk/docs/man3/html/glBlendEquationSeparate.xhtml
BlendEquationSeparate(modeRGB, modeAlpha Enum)
// BlendFunc sets the pixel blending factors.
//
// http://www.khronos.org/opengles/sdk/docs/man3/html/glBlendFunc.xhtml
BlendFunc(sfactor, dfactor Enum)
// BlendFunc sets the pixel RGB and alpha blending factors separately.
//
// http://www.khronos.org/opengles/sdk/docs/man3/html/glBlendFuncSeparate.xhtml
BlendFuncSeparate(sfactorRGB, dfactorRGB, sfactorAlpha, dfactorAlpha Enum)
// BufferData creates a new data store for the bound buffer object.
//
// http://www.khronos.org/opengles/sdk/docs/man3/html/glBufferData.xhtml
BufferData(target Enum, src []byte, usage Enum)
// BufferInit creates a new uninitialized data store for the bound buffer object.
//
// http://www.khronos.org/opengles/sdk/docs/man3/html/glBufferData.xhtml
BufferInit(target Enum, size int, usage Enum)
// BufferSubData sets some of data in the bound buffer object.
//
// http://www.khronos.org/opengles/sdk/docs/man3/html/glBufferSubData.xhtml
BufferSubData(target Enum, offset int, data []byte)
// CheckFramebufferStatus reports the completeness status of the
// active framebuffer.
//
// http://www.khronos.org/opengles/sdk/docs/man3/html/glCheckFramebufferStatus.xhtml
CheckFramebufferStatus(target Enum) Enum
// Clear clears the window.
//
// The behavior of Clear is influenced by the pixel ownership test,
// the scissor test, dithering, and the buffer writemasks.
//
// http://www.khronos.org/opengles/sdk/docs/man3/html/glClear.xhtml
Clear(mask Enum)
// ClearColor specifies the RGBA values used to clear color buffers.
//
// http://www.khronos.org/opengles/sdk/docs/man3/html/glClearColor.xhtml
ClearColor(red, green, blue, alpha float32)
// ClearDepthf sets the depth value used to clear the depth buffer.
//
// http://www.khronos.org/opengles/sdk/docs/man3/html/glClearDepthf.xhtml
ClearDepthf(d float32)
// ClearStencil sets the index used to clear the stencil buffer.
//
// http://www.khronos.org/opengles/sdk/docs/man3/html/glClearStencil.xhtml
ClearStencil(s int)
// ColorMask specifies whether color components in the framebuffer
// can be written.
//
// http://www.khronos.org/opengles/sdk/docs/man3/html/glColorMask.xhtml
ColorMask(red, green, blue, alpha bool)
// CompileShader compiles the source code of s.
//
// http://www.khronos.org/opengles/sdk/docs/man3/html/glCompileShader.xhtml
CompileShader(s Shader)
// CompressedTexImage2D writes a compressed 2D texture.
//
// http://www.khronos.org/opengles/sdk/docs/man3/html/glCompressedTexImage2D.xhtml
CompressedTexImage2D(target Enum, level int, internalformat Enum, width, height, border int, data []byte)
// CompressedTexSubImage2D writes a subregion of a compressed 2D texture.
//
// http://www.khronos.org/opengles/sdk/docs/man3/html/glCompressedTexSubImage2D.xhtml
CompressedTexSubImage2D(target Enum, level, xoffset, yoffset, width, height int, format Enum, data []byte)
// CopyTexImage2D writes a 2D texture from the current framebuffer.
//
// http://www.khronos.org/opengles/sdk/docs/man3/html/glCopyTexImage2D.xhtml
CopyTexImage2D(target Enum, level int, internalformat Enum, x, y, width, height, border int)
// CopyTexSubImage2D writes a 2D texture subregion from the
// current framebuffer.
//
// http://www.khronos.org/opengles/sdk/docs/man3/html/glCopyTexSubImage2D.xhtml
CopyTexSubImage2D(target Enum, level, xoffset, yoffset, x, y, width, height int)
// CreateBuffer creates a buffer object.
//
// http://www.khronos.org/opengles/sdk/docs/man3/html/glGenBuffers.xhtml
CreateBuffer() Buffer
// CreateFramebuffer creates a framebuffer object.
//
// http://www.khronos.org/opengles/sdk/docs/man3/html/glGenFramebuffers.xhtml
CreateFramebuffer() Framebuffer
// CreateProgram creates a new empty program object.
//
// http://www.khronos.org/opengles/sdk/docs/man3/html/glCreateProgram.xhtml
CreateProgram() Program
// CreateRenderbuffer create a renderbuffer object.
//
// http://www.khronos.org/opengles/sdk/docs/man3/html/glGenRenderbuffers.xhtml
CreateRenderbuffer() Renderbuffer
// CreateShader creates a new empty shader object.
//
// http://www.khronos.org/opengles/sdk/docs/man3/html/glCreateShader.xhtml
CreateShader(ty Enum) Shader
// CreateTexture creates a texture object.
//
// http://www.khronos.org/opengles/sdk/docs/man3/html/glGenTextures.xhtml
CreateTexture() Texture
// CreateTVertexArray creates a vertex array.
//
// http://www.khronos.org/opengles/sdk/docs/man3/html/glGenVertexArrays.xhtml
CreateVertexArray() VertexArray
// CullFace specifies which polygons are candidates for culling.
//
// Valid modes: FRONT, BACK, FRONT_AND_BACK.
//
// http://www.khronos.org/opengles/sdk/docs/man3/html/glCullFace.xhtml
CullFace(mode Enum)
// DeleteBuffer deletes the given buffer object.
//
// http://www.khronos.org/opengles/sdk/docs/man3/html/glDeleteBuffers.xhtml
DeleteBuffer(v Buffer)
// DeleteFramebuffer deletes the given framebuffer object.
//
// http://www.khronos.org/opengles/sdk/docs/man3/html/glDeleteFramebuffers.xhtml
DeleteFramebuffer(v Framebuffer)
// DeleteProgram deletes the given program object.
//
// http://www.khronos.org/opengles/sdk/docs/man3/html/glDeleteProgram.xhtml
DeleteProgram(p Program)
// DeleteRenderbuffer deletes the given render buffer object.
//
// http://www.khronos.org/opengles/sdk/docs/man3/html/glDeleteRenderbuffers.xhtml
DeleteRenderbuffer(v Renderbuffer)
// DeleteShader deletes shader s.
//
// http://www.khronos.org/opengles/sdk/docs/man3/html/glDeleteShader.xhtml
DeleteShader(s Shader)
// DeleteTexture deletes the given texture object.
//
// http://www.khronos.org/opengles/sdk/docs/man3/html/glDeleteTextures.xhtml
DeleteTexture(v Texture)
// DeleteVertexArray deletes the given render buffer object.
//
// http://www.khronos.org/opengles/sdk/docs/man3/html/glDeleteVertexArrays.xhtml
DeleteVertexArray(v VertexArray)
// DepthFunc sets the function used for depth buffer comparisons.
//
// Valid fn values:
// NEVER
// LESS
// EQUAL
// LEQUAL
// GREATER
// NOTEQUAL
// GEQUAL
// ALWAYS
//
// http://www.khronos.org/opengles/sdk/docs/man3/html/glDepthFunc.xhtml
DepthFunc(fn Enum)
// DepthMask sets the depth buffer enabled for writing.
//
// http://www.khronos.org/opengles/sdk/docs/man3/html/glDepthMask.xhtml
DepthMask(flag bool)
// DepthRangef sets the mapping from normalized device coordinates to
// window coordinates.
//
// http://www.khronos.org/opengles/sdk/docs/man3/html/glDepthRangef.xhtml
DepthRangef(n, f float32)
// DetachShader detaches the shader s from the program p.
//
// http://www.khronos.org/opengles/sdk/docs/man3/html/glDetachShader.xhtml
DetachShader(p Program, s Shader)
// Disable disables various GL capabilities.
//
// http://www.khronos.org/opengles/sdk/docs/man3/html/glDisable.xhtml
Disable(cap Enum)
// DisableVertexAttribArray disables a vertex attribute array.
//
// http://www.khronos.org/opengles/sdk/docs/man3/html/glDisableVertexAttribArray.xhtml
DisableVertexAttribArray(a Attrib)
// DrawArrays renders geometric primitives from the bound data.
//
// http://www.khronos.org/opengles/sdk/docs/man3/html/glDrawArrays.xhtml
DrawArrays(mode Enum, first, count int)
// DrawElements renders primitives from a bound buffer.
//
// http://www.khronos.org/opengles/sdk/docs/man3/html/glDrawElements.xhtml
DrawElements(mode Enum, count int, ty Enum, offset int)
// TODO(crawshaw): consider DrawElements8 / DrawElements16 / DrawElements32
// Enable enables various GL capabilities.
//
// http://www.khronos.org/opengles/sdk/docs/man3/html/glEnable.xhtml
Enable(cap Enum)
// EnableVertexAttribArray enables a vertex attribute array.
//
// http://www.khronos.org/opengles/sdk/docs/man3/html/glEnableVertexAttribArray.xhtml
EnableVertexAttribArray(a Attrib)
// Finish blocks until the effects of all previously called GL
// commands are complete.
//
// http://www.khronos.org/opengles/sdk/docs/man3/html/glFinish.xhtml
Finish()
// Flush empties all buffers. It does not block.
//
// An OpenGL implementation may buffer network communication,
// the command stream, or data inside the graphics accelerator.
//
// http://www.khronos.org/opengles/sdk/docs/man3/html/glFlush.xhtml
Flush()
// FramebufferRenderbuffer attaches rb to the current frame buffer.
//
// http://www.khronos.org/opengles/sdk/docs/man3/html/glFramebufferRenderbuffer.xhtml
FramebufferRenderbuffer(target, attachment, rbTarget Enum, rb Renderbuffer)
// FramebufferTexture2D attaches the t to the current frame buffer.
//
// http://www.khronos.org/opengles/sdk/docs/man3/html/glFramebufferTexture2D.xhtml
FramebufferTexture2D(target, attachment, texTarget Enum, t Texture, level int)
// FrontFace defines which polygons are front-facing.
//
// Valid modes: CW, CCW.
//
// http://www.khronos.org/opengles/sdk/docs/man3/html/glFrontFace.xhtml
FrontFace(mode Enum)
// GenerateMipmap generates mipmaps for the current texture.
//
// http://www.khronos.org/opengles/sdk/docs/man3/html/glGenerateMipmap.xhtml
GenerateMipmap(target Enum)
// GetActiveAttrib returns details about an active attribute variable.
// A value of 0 for index selects the first active attribute variable.
// Permissible values for index range from 0 to the number of active
// attribute variables minus 1.
//
// http://www.khronos.org/opengles/sdk/docs/man3/html/glGetActiveAttrib.xhtml
GetActiveAttrib(p Program, index uint32) (name string, size int, ty Enum)
// GetActiveUniform returns details about an active uniform variable.
// A value of 0 for index selects the first active uniform variable.
// Permissible values for index range from 0 to the number of active
// uniform variables minus 1.
//
// http://www.khronos.org/opengles/sdk/docs/man3/html/glGetActiveUniform.xhtml
GetActiveUniform(p Program, index uint32) (name string, size int, ty Enum)
// GetAttachedShaders returns the shader objects attached to program p.
//
// http://www.khronos.org/opengles/sdk/docs/man3/html/glGetAttachedShaders.xhtml
GetAttachedShaders(p Program) []Shader
// GetAttribLocation returns the location of an attribute variable.
//
// http://www.khronos.org/opengles/sdk/docs/man3/html/glGetAttribLocation.xhtml
GetAttribLocation(p Program, name string) Attrib
// GetBooleanv returns the boolean values of parameter pname.
//
// Many boolean parameters can be queried more easily using IsEnabled.
//
// http://www.khronos.org/opengles/sdk/docs/man3/html/glGet.xhtml
GetBooleanv(dst []bool, pname Enum)
// GetFloatv returns the float values of parameter pname.
//
// http://www.khronos.org/opengles/sdk/docs/man3/html/glGet.xhtml
GetFloatv(dst []float32, pname Enum)
// GetIntegerv returns the int values of parameter pname.
//
// Single values may be queried more easily using GetInteger.
//
// http://www.khronos.org/opengles/sdk/docs/man3/html/glGet.xhtml
GetIntegerv(dst []int32, pname Enum)
// GetInteger returns the int value of parameter pname.
//
// http://www.khronos.org/opengles/sdk/docs/man3/html/glGet.xhtml
GetInteger(pname Enum) int
// GetBufferParameteri returns a parameter for the active buffer.
//
// http://www.khronos.org/opengles/sdk/docs/man3/html/glGetBufferParameter.xhtml
GetBufferParameteri(target, value Enum) int
// GetError returns the next error.
//
// http://www.khronos.org/opengles/sdk/docs/man3/html/glGetError.xhtml
GetError() Enum
// GetFramebufferAttachmentParameteri returns attachment parameters
// for the active framebuffer object.
//
// http://www.khronos.org/opengles/sdk/docs/man3/html/glGetFramebufferAttachmentParameteriv.xhtml
GetFramebufferAttachmentParameteri(target, attachment, pname Enum) int
// GetProgrami returns a parameter value for a program.
//
// http://www.khronos.org/opengles/sdk/docs/man3/html/glGetProgramiv.xhtml
GetProgrami(p Program, pname Enum) int
// GetProgramInfoLog returns the information log for a program.
//
// http://www.khronos.org/opengles/sdk/docs/man3/html/glGetProgramInfoLog.xhtml
GetProgramInfoLog(p Program) string
// GetRenderbufferParameteri returns a parameter value for a render buffer.
//
// http://www.khronos.org/opengles/sdk/docs/man3/html/glGetRenderbufferParameteriv.xhtml
GetRenderbufferParameteri(target, pname Enum) int
// GetShaderi returns a parameter value for a shader.
//
// http://www.khronos.org/opengles/sdk/docs/man3/html/glGetShaderiv.xhtml
GetShaderi(s Shader, pname Enum) int
// GetShaderInfoLog returns the information log for a shader.
//
// http://www.khronos.org/opengles/sdk/docs/man3/html/glGetShaderInfoLog.xhtml
GetShaderInfoLog(s Shader) string
// GetShaderPrecisionFormat returns range and precision limits for
// shader types.
//
// http://www.khronos.org/opengles/sdk/docs/man3/html/glGetShaderPrecisionFormat.xhtml
GetShaderPrecisionFormat(shadertype, precisiontype Enum) (rangeLow, rangeHigh, precision int)
// GetShaderSource returns source code of shader s.
//
// http://www.khronos.org/opengles/sdk/docs/man3/html/glGetShaderSource.xhtml
GetShaderSource(s Shader) string
// GetString reports current GL state.
//
// Valid name values:
// EXTENSIONS
// RENDERER
// SHADING_LANGUAGE_VERSION
// VENDOR
// VERSION
//
// http://www.khronos.org/opengles/sdk/docs/man3/html/glGetString.xhtml
GetString(pname Enum) string
// GetTexParameterfv returns the float values of a texture parameter.
//
// http://www.khronos.org/opengles/sdk/docs/man3/html/glGetTexParameter.xhtml
GetTexParameterfv(dst []float32, target, pname Enum)
// GetTexParameteriv returns the int values of a texture parameter.
//
// http://www.khronos.org/opengles/sdk/docs/man3/html/glGetTexParameter.xhtml
GetTexParameteriv(dst []int32, target, pname Enum)
// GetUniformfv returns the float values of a uniform variable.
//
// http://www.khronos.org/opengles/sdk/docs/man3/html/glGetUniform.xhtml
GetUniformfv(dst []float32, src Uniform, p Program)
// GetUniformiv returns the float values of a uniform variable.
//
// http://www.khronos.org/opengles/sdk/docs/man3/html/glGetUniform.xhtml
GetUniformiv(dst []int32, src Uniform, p Program)
// GetUniformLocation returns the location of a uniform variable.
//
// http://www.khronos.org/opengles/sdk/docs/man3/html/glGetUniformLocation.xhtml
GetUniformLocation(p Program, name string) Uniform
// GetVertexAttribf reads the float value of a vertex attribute.
//
// http://www.khronos.org/opengles/sdk/docs/man3/html/glGetVertexAttrib.xhtml
GetVertexAttribf(src Attrib, pname Enum) float32
// GetVertexAttribfv reads float values of a vertex attribute.
//
// http://www.khronos.org/opengles/sdk/docs/man3/html/glGetVertexAttrib.xhtml
GetVertexAttribfv(dst []float32, src Attrib, pname Enum)
// GetVertexAttribi reads the int value of a vertex attribute.
//
// http://www.khronos.org/opengles/sdk/docs/man3/html/glGetVertexAttrib.xhtml
GetVertexAttribi(src Attrib, pname Enum) int32
// GetVertexAttribiv reads int values of a vertex attribute.
//
// http://www.khronos.org/opengles/sdk/docs/man3/html/glGetVertexAttrib.xhtml
GetVertexAttribiv(dst []int32, src Attrib, pname Enum)
// TODO(crawshaw): glGetVertexAttribPointerv
// Hint sets implementation-specific modes.
//
// http://www.khronos.org/opengles/sdk/docs/man3/html/glHint.xhtml
Hint(target, mode Enum)
// IsBuffer reports if b is a valid buffer.
//
// http://www.khronos.org/opengles/sdk/docs/man3/html/glIsBuffer.xhtml
IsBuffer(b Buffer) bool
// IsEnabled reports if cap is an enabled capability.
//
// http://www.khronos.org/opengles/sdk/docs/man3/html/glIsEnabled.xhtml
IsEnabled(cap Enum) bool
// IsFramebuffer reports if fb is a valid frame buffer.
//
// http://www.khronos.org/opengles/sdk/docs/man3/html/glIsFramebuffer.xhtml
IsFramebuffer(fb Framebuffer) bool
// IsProgram reports if p is a valid program object.
//
// http://www.khronos.org/opengles/sdk/docs/man3/html/glIsProgram.xhtml
IsProgram(p Program) bool
// IsRenderbuffer reports if rb is a valid render buffer.
//
// http://www.khronos.org/opengles/sdk/docs/man3/html/glIsRenderbuffer.xhtml
IsRenderbuffer(rb Renderbuffer) bool
// IsShader reports if s is valid shader.
//
// http://www.khronos.org/opengles/sdk/docs/man3/html/glIsShader.xhtml
IsShader(s Shader) bool
// IsTexture reports if t is a valid texture.
//
// http://www.khronos.org/opengles/sdk/docs/man3/html/glIsTexture.xhtml
IsTexture(t Texture) bool
// LineWidth specifies the width of lines.
//
// http://www.khronos.org/opengles/sdk/docs/man3/html/glLineWidth.xhtml
LineWidth(width float32)
// LinkProgram links the specified program.
//
// http://www.khronos.org/opengles/sdk/docs/man3/html/glLinkProgram.xhtml
LinkProgram(p Program)
// PixelStorei sets pixel storage parameters.
//
// http://www.khronos.org/opengles/sdk/docs/man3/html/glPixelStorei.xhtml
PixelStorei(pname Enum, param int32)
// PolygonOffset sets the scaling factors for depth offsets.
//
// http://www.khronos.org/opengles/sdk/docs/man3/html/glPolygonOffset.xhtml
PolygonOffset(factor, units float32)
// ReadPixels returns pixel data from a buffer.
//
// In GLES 3, the source buffer is controlled with ReadBuffer.
//
// http://www.khronos.org/opengles/sdk/docs/man3/html/glReadPixels.xhtml
ReadPixels(dst []byte, x, y, width, height int, format, ty Enum)
// ReleaseShaderCompiler frees resources allocated by the shader compiler.
//
// http://www.khronos.org/opengles/sdk/docs/man3/html/glReleaseShaderCompiler.xhtml
ReleaseShaderCompiler()
// RenderbufferStorage establishes the data storage, format, and
// dimensions of a renderbuffer object's image.
//
// http://www.khronos.org/opengles/sdk/docs/man3/html/glRenderbufferStorage.xhtml
RenderbufferStorage(target, internalFormat Enum, width, height int)
// SampleCoverage sets multisample coverage parameters.
//
// http://www.khronos.org/opengles/sdk/docs/man3/html/glSampleCoverage.xhtml
SampleCoverage(value float32, invert bool)
// Scissor defines the scissor box rectangle, in window coordinates.
//
// http://www.khronos.org/opengles/sdk/docs/man3/html/glScissor.xhtml
Scissor(x, y, width, height int32)
// TODO(crawshaw): ShaderBinary
// ShaderSource sets the source code of s to the given source code.
//
// http://www.khronos.org/opengles/sdk/docs/man3/html/glShaderSource.xhtml
ShaderSource(s Shader, src string)
// StencilFunc sets the front and back stencil test reference value.
//
// http://www.khronos.org/opengles/sdk/docs/man3/html/glStencilFunc.xhtml
StencilFunc(fn Enum, ref int, mask uint32)
// StencilFunc sets the front or back stencil test reference value.
//
// http://www.khronos.org/opengles/sdk/docs/man3/html/glStencilFuncSeparate.xhtml
StencilFuncSeparate(face, fn Enum, ref int, mask uint32)
// StencilMask controls the writing of bits in the stencil planes.
//
// http://www.khronos.org/opengles/sdk/docs/man3/html/glStencilMask.xhtml
StencilMask(mask uint32)
// StencilMaskSeparate controls the writing of bits in the stencil planes.
//
// http://www.khronos.org/opengles/sdk/docs/man3/html/glStencilMaskSeparate.xhtml
StencilMaskSeparate(face Enum, mask uint32)
// StencilOp sets front and back stencil test actions.
//
// http://www.khronos.org/opengles/sdk/docs/man3/html/glStencilOp.xhtml
StencilOp(fail, zfail, zpass Enum)
// StencilOpSeparate sets front or back stencil tests.
//
// http://www.khronos.org/opengles/sdk/docs/man3/html/glStencilOpSeparate.xhtml
StencilOpSeparate(face, sfail, dpfail, dppass Enum)
// TexImage2D writes a 2D texture image.
//
// http://www.khronos.org/opengles/sdk/docs/man3/html/glTexImage2D.xhtml
TexImage2D(target Enum, level int, internalFormat int, width, height int, format Enum, ty Enum, data []byte)
// TexSubImage2D writes a subregion of a 2D texture image.
//
// http://www.khronos.org/opengles/sdk/docs/man3/html/glTexSubImage2D.xhtml
TexSubImage2D(target Enum, level int, x, y, width, height int, format, ty Enum, data []byte)
// TexParameterf sets a float texture parameter.
//
// http://www.khronos.org/opengles/sdk/docs/man3/html/glTexParameter.xhtml
TexParameterf(target, pname Enum, param float32)
// TexParameterfv sets a float texture parameter array.
//
// http://www.khronos.org/opengles/sdk/docs/man3/html/glTexParameter.xhtml
TexParameterfv(target, pname Enum, params []float32)
// TexParameteri sets an integer texture parameter.
//
// http://www.khronos.org/opengles/sdk/docs/man3/html/glTexParameter.xhtml
TexParameteri(target, pname Enum, param int)
// TexParameteriv sets an integer texture parameter array.
//
// http://www.khronos.org/opengles/sdk/docs/man3/html/glTexParameter.xhtml
TexParameteriv(target, pname Enum, params []int32)
// Uniform1f writes a float uniform variable.
//
// http://www.khronos.org/opengles/sdk/docs/man3/html/glUniform.xhtml
Uniform1f(dst Uniform, v float32)
// Uniform1fv writes a [len(src)]float uniform array.
//
// http://www.khronos.org/opengles/sdk/docs/man3/html/glUniform.xhtml
Uniform1fv(dst Uniform, src []float32)
// Uniform1i writes an int uniform variable.
//
// Uniform1i and Uniform1iv are the only two functions that may be used
// to load uniform variables defined as sampler types. Loading samplers
// with any other function will result in a INVALID_OPERATION error.
//
// http://www.khronos.org/opengles/sdk/docs/man3/html/glUniform.xhtml
Uniform1i(dst Uniform, v int)
// Uniform1iv writes a int uniform array of len(src) elements.
//
// Uniform1i and Uniform1iv are the only two functions that may be used
// to load uniform variables defined as sampler types. Loading samplers
// with any other function will result in a INVALID_OPERATION error.
//
// http://www.khronos.org/opengles/sdk/docs/man3/html/glUniform.xhtml
Uniform1iv(dst Uniform, src []int32)
// Uniform2f writes a vec2 uniform variable.
//
// http://www.khronos.org/opengles/sdk/docs/man3/html/glUniform.xhtml
Uniform2f(dst Uniform, v0, v1 float32)
// Uniform2fv writes a vec2 uniform array of len(src)/2 elements.
//
// http://www.khronos.org/opengles/sdk/docs/man3/html/glUniform.xhtml
Uniform2fv(dst Uniform, src []float32)
// Uniform2i writes an ivec2 uniform variable.
//
// http://www.khronos.org/opengles/sdk/docs/man3/html/glUniform.xhtml
Uniform2i(dst Uniform, v0, v1 int)
// Uniform2iv writes an ivec2 uniform array of len(src)/2 elements.
//
// http://www.khronos.org/opengles/sdk/docs/man3/html/glUniform.xhtml
Uniform2iv(dst Uniform, src []int32)
// Uniform3f writes a vec3 uniform variable.
//
// http://www.khronos.org/opengles/sdk/docs/man3/html/glUniform.xhtml
Uniform3f(dst Uniform, v0, v1, v2 float32)
// Uniform3fv writes a vec3 uniform array of len(src)/3 elements.
//
// http://www.khronos.org/opengles/sdk/docs/man3/html/glUniform.xhtml
Uniform3fv(dst Uniform, src []float32)
// Uniform3i writes an ivec3 uniform variable.
//
// http://www.khronos.org/opengles/sdk/docs/man3/html/glUniform.xhtml
Uniform3i(dst Uniform, v0, v1, v2 int32)
// Uniform3iv writes an ivec3 uniform array of len(src)/3 elements.
//
// http://www.khronos.org/opengles/sdk/docs/man3/html/glUniform.xhtml
Uniform3iv(dst Uniform, src []int32)
// Uniform4f writes a vec4 uniform variable.
//
// http://www.khronos.org/opengles/sdk/docs/man3/html/glUniform.xhtml
Uniform4f(dst Uniform, v0, v1, v2, v3 float32)
// Uniform4fv writes a vec4 uniform array of len(src)/4 elements.
//
// http://www.khronos.org/opengles/sdk/docs/man3/html/glUniform.xhtml
Uniform4fv(dst Uniform, src []float32)
// Uniform4i writes an ivec4 uniform variable.
//
// http://www.khronos.org/opengles/sdk/docs/man3/html/glUniform.xhtml
Uniform4i(dst Uniform, v0, v1, v2, v3 int32)
// Uniform4i writes an ivec4 uniform array of len(src)/4 elements.
//
// http://www.khronos.org/opengles/sdk/docs/man3/html/glUniform.xhtml
Uniform4iv(dst Uniform, src []int32)
// UniformMatrix2fv writes 2x2 matrices. Each matrix uses four
// float32 values, so the number of matrices written is len(src)/4.
//
// Each matrix must be supplied in column major order.
//
// http://www.khronos.org/opengles/sdk/docs/man3/html/glUniform.xhtml
UniformMatrix2fv(dst Uniform, src []float32)
// UniformMatrix3fv writes 3x3 matrices. Each matrix uses nine
// float32 values, so the number of matrices written is len(src)/9.
//
// Each matrix must be supplied in column major order.
//
// http://www.khronos.org/opengles/sdk/docs/man3/html/glUniform.xhtml
UniformMatrix3fv(dst Uniform, src []float32)
// UniformMatrix4fv writes 4x4 matrices. Each matrix uses 16
// float32 values, so the number of matrices written is len(src)/16.
//
// Each matrix must be supplied in column major order.
//
// http://www.khronos.org/opengles/sdk/docs/man3/html/glUniform.xhtml
UniformMatrix4fv(dst Uniform, src []float32)
// UseProgram sets the active program.
//
// http://www.khronos.org/opengles/sdk/docs/man3/html/glUseProgram.xhtml
UseProgram(p Program)
// ValidateProgram checks to see whether the executables contained in
// program can execute given the current OpenGL state.
//
// Typically only used for debugging.
//
// http://www.khronos.org/opengles/sdk/docs/man3/html/glValidateProgram.xhtml
ValidateProgram(p Program)
// VertexAttrib1f writes a float vertex attribute.
//
// http://www.khronos.org/opengles/sdk/docs/man3/html/glVertexAttrib.xhtml
VertexAttrib1f(dst Attrib, x float32)
// VertexAttrib1fv writes a float vertex attribute.
//
// http://www.khronos.org/opengles/sdk/docs/man3/html/glVertexAttrib.xhtml
VertexAttrib1fv(dst Attrib, src []float32)
// VertexAttrib2f writes a vec2 vertex attribute.
//
// http://www.khronos.org/opengles/sdk/docs/man3/html/glVertexAttrib.xhtml
VertexAttrib2f(dst Attrib, x, y float32)
// VertexAttrib2fv writes a vec2 vertex attribute.
//
// http://www.khronos.org/opengles/sdk/docs/man3/html/glVertexAttrib.xhtml
VertexAttrib2fv(dst Attrib, src []float32)
// VertexAttrib3f writes a vec3 vertex attribute.
//
// http://www.khronos.org/opengles/sdk/docs/man3/html/glVertexAttrib.xhtml
VertexAttrib3f(dst Attrib, x, y, z float32)
// VertexAttrib3fv writes a vec3 vertex attribute.
//
// http://www.khronos.org/opengles/sdk/docs/man3/html/glVertexAttrib.xhtml
VertexAttrib3fv(dst Attrib, src []float32)
// VertexAttrib4f writes a vec4 vertex attribute.
//
// http://www.khronos.org/opengles/sdk/docs/man3/html/glVertexAttrib.xhtml
VertexAttrib4f(dst Attrib, x, y, z, w float32)
// VertexAttrib4fv writes a vec4 vertex attribute.
//
// http://www.khronos.org/opengles/sdk/docs/man3/html/glVertexAttrib.xhtml
VertexAttrib4fv(dst Attrib, src []float32)
// VertexAttribPointer uses a bound buffer to define vertex attribute data.
//
// Direct use of VertexAttribPointer to load data into OpenGL is not
// supported via the Go bindings. Instead, use BindBuffer with an
// ARRAY_BUFFER and then fill it using BufferData.
//
// The size argument specifies the number of components per attribute,
// between 1-4. The stride argument specifies the byte offset between
// consecutive vertex attributes.
//
// http://www.khronos.org/opengles/sdk/docs/man3/html/glVertexAttribPointer.xhtml
VertexAttribPointer(dst Attrib, size int, ty Enum, normalized bool, stride, offset int)
// Viewport sets the viewport, an affine transformation that
// normalizes device coordinates to window coordinates.
//
// http://www.khronos.org/opengles/sdk/docs/man3/html/glViewport.xhtml
Viewport(x, y, width, height int)
}
// Context3 is an OpenGL ES 3 context.
//
// When the gl package is compiled with GL ES 3 support, the produced
// Context object also implements the Context3 interface.
type Context3 interface {
Context
// BlitFramebuffer copies a block of pixels between framebuffers.
//
// https://www.khronos.org/opengles/sdk/docs/man3/html/glBlitFramebuffer.xhtml
BlitFramebuffer(srcX0, srcY0, srcX1, srcY1, dstX0, dstY0, dstX1, dstY1 int, mask uint, filter Enum)
}
// Worker is used by display driver code to execute OpenGL calls.
//
// Typically display driver code creates a gl.Context for an application,
// and along with it establishes a locked OS thread to execute the cgo
// calls:
//
// go func() {
// runtime.LockOSThread()
// // ... platform-specific cgo call to bind a C OpenGL context
// // into thread-local storage.
//
// glctx, worker := gl.NewContext()
// workAvailable := worker.WorkAvailable()
// go userAppCode(glctx)
// for {
// select {
// case <-workAvailable:
// worker.DoWork()
// case <-drawEvent:
// // ... platform-specific cgo call to draw screen
// }
// }
// }()
//
// This interface is an internal implementation detail and should only be used
// by the package responsible for managing the screen, such as
// github.com/ebitengine/gomobile/app.
type Worker interface {
// WorkAvailable returns a channel that communicates when DoWork should be
// called.
WorkAvailable() <-chan struct{}
// DoWork performs any pending OpenGL calls.
DoWork()
}
+81
View File
@@ -0,0 +1,81 @@
// Copyright 2014 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.
//go:build (darwin || linux || openbsd || windows) && gldebug
package gl
// Alternate versions of the types defined in types.go with extra
// debugging information attached. For documentation, see types.go.
import "fmt"
type Enum uint32
type Attrib struct {
Value uint
name string
}
type Program struct {
Init bool
Value uint32
}
type Shader struct {
Value uint32
}
type Buffer struct {
Value uint32
}
type Framebuffer struct {
Value uint32
}
type Renderbuffer struct {
Value uint32
}
type Texture struct {
Value uint32
}
type Uniform struct {
Value int32
name string
}
type VertexArray struct {
Value uint32
}
func (v Attrib) c() uintptr { return uintptr(v.Value) }
func (v Enum) c() uintptr { return uintptr(v) }
func (v Program) c() uintptr {
if !v.Init {
ret := uintptr(0)
ret--
return ret
}
return uintptr(v.Value)
}
func (v Shader) c() uintptr { return uintptr(v.Value) }
func (v Buffer) c() uintptr { return uintptr(v.Value) }
func (v Framebuffer) c() uintptr { return uintptr(v.Value) }
func (v Renderbuffer) c() uintptr { return uintptr(v.Value) }
func (v Texture) c() uintptr { return uintptr(v.Value) }
func (v Uniform) c() uintptr { return uintptr(v.Value) }
func (v VertexArray) c() uintptr { return uintptr(v.Value) }
func (v Attrib) String() string { return fmt.Sprintf("Attrib(%d:%s)", v.Value, v.name) }
func (v Program) String() string { return fmt.Sprintf("Program(%d)", v.Value) }
func (v Shader) String() string { return fmt.Sprintf("Shader(%d)", v.Value) }
func (v Buffer) String() string { return fmt.Sprintf("Buffer(%d)", v.Value) }
func (v Framebuffer) String() string { return fmt.Sprintf("Framebuffer(%d)", v.Value) }
func (v Renderbuffer) String() string { return fmt.Sprintf("Renderbuffer(%d)", v.Value) }
func (v Texture) String() string { return fmt.Sprintf("Texture(%d)", v.Value) }
func (v Uniform) String() string { return fmt.Sprintf("Uniform(%d:%s)", v.Value, v.name) }
func (v VertexArray) String() string { return fmt.Sprintf("VertexArray(%d)", v.Value) }
+92
View File
@@ -0,0 +1,92 @@
// Copyright 2014 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.
//go:build (darwin || linux || openbsd || windows) && !gldebug
package gl
import "fmt"
// Enum is equivalent to GLenum, and is normally used with one of the
// constants defined in this package.
type Enum uint32
// Types are defined a structs so that in debug mode they can carry
// extra information, such as a string name. See typesdebug.go.
// Attrib identifies the location of a specific attribute variable.
type Attrib struct {
Value uint
}
// Program identifies a compiled shader program.
type Program struct {
// Init is set by CreateProgram, as some GL drivers (in particular,
// ANGLE) return true for glIsProgram(0).
Init bool
Value uint32
}
// Shader identifies a GLSL shader.
type Shader struct {
Value uint32
}
// Buffer identifies a GL buffer object.
type Buffer struct {
Value uint32
}
// Framebuffer identifies a GL framebuffer.
type Framebuffer struct {
Value uint32
}
// A Renderbuffer is a GL object that holds an image in an internal format.
type Renderbuffer struct {
Value uint32
}
// A Texture identifies a GL texture unit.
type Texture struct {
Value uint32
}
// Uniform identifies the location of a specific uniform variable.
type Uniform struct {
Value int32
}
// A VertexArray is a GL object that holds vertices in an internal format.
type VertexArray struct {
Value uint32
}
func (v Attrib) c() uintptr { return uintptr(v.Value) }
func (v Enum) c() uintptr { return uintptr(v) }
func (v Program) c() uintptr {
if !v.Init {
ret := uintptr(0)
ret--
return ret
}
return uintptr(v.Value)
}
func (v Shader) c() uintptr { return uintptr(v.Value) }
func (v Buffer) c() uintptr { return uintptr(v.Value) }
func (v Framebuffer) c() uintptr { return uintptr(v.Value) }
func (v Renderbuffer) c() uintptr { return uintptr(v.Value) }
func (v Texture) c() uintptr { return uintptr(v.Value) }
func (v Uniform) c() uintptr { return uintptr(v.Value) }
func (v VertexArray) c() uintptr { return uintptr(v.Value) }
func (v Attrib) String() string { return fmt.Sprintf("Attrib(%d)", v.Value) }
func (v Program) String() string { return fmt.Sprintf("Program(%d)", v.Value) }
func (v Shader) String() string { return fmt.Sprintf("Shader(%d)", v.Value) }
func (v Buffer) String() string { return fmt.Sprintf("Buffer(%d)", v.Value) }
func (v Framebuffer) String() string { return fmt.Sprintf("Framebuffer(%d)", v.Value) }
func (v Renderbuffer) String() string { return fmt.Sprintf("Renderbuffer(%d)", v.Value) }
func (v Texture) String() string { return fmt.Sprintf("Texture(%d)", v.Value) }
func (v Uniform) String() string { return fmt.Sprintf("Uniform(%d)", v.Value) }
func (v VertexArray) String() string { return fmt.Sprintf("VertexArray(%d)", v.Value) }
+557
View File
@@ -0,0 +1,557 @@
// 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.
//go:build darwin || linux || openbsd
// +build darwin linux openbsd
#include <stdlib.h>
#include "_cgo_export.h"
#include "work.h"
#if defined(GL_ES_VERSION_3_0) && GL_ES_VERSION_3_0
#else
#include <stdio.h>
static void gles3missing() {
printf("GLES3 function is missing\n");
exit(2);
}
static void glUniformMatrix2x3fv(GLint location, GLsizei count, GLboolean transpose, const GLfloat *value) { gles3missing(); }
static void glUniformMatrix3x2fv(GLint location, GLsizei count, GLboolean transpose, const GLfloat *value) { gles3missing(); }
static void glUniformMatrix2x4fv(GLint location, GLsizei count, GLboolean transpose, const GLfloat *value) { gles3missing(); }
static void glUniformMatrix4x2fv(GLint location, GLsizei count, GLboolean transpose, const GLfloat *value) { gles3missing(); }
static void glUniformMatrix3x4fv(GLint location, GLsizei count, GLboolean transpose, const GLfloat *value) { gles3missing(); }
static void glUniformMatrix4x3fv(GLint location, GLsizei count, GLboolean transpose, const GLfloat *value) { gles3missing(); }
static void glBlitFramebuffer(GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1, GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1, GLbitfield mask, GLenum filter) { gles3missing(); }
static void glUniform1ui(GLint location, GLuint v0) { gles3missing(); }
static void glUniform2ui(GLint location, GLuint v0, GLuint v1) { gles3missing(); }
static void glUniform3ui(GLint location, GLuint v0, GLuint v1, GLuint v2) { gles3missing(); }
static void glUniform4ui(GLint location, GLuint v0, GLuint v1, GLuint v2, GLuint v3) { gles3missing(); }
static void glUniform1uiv(GLint location, GLsizei count, const GLuint *value) { gles3missing(); }
static void glUniform2uiv(GLint location, GLsizei count, const GLuint *value) { gles3missing(); }
static void glUniform3uiv(GLint location, GLsizei count, const GLuint *value) { gles3missing(); }
static void glUniform4uiv(GLint location, GLsizei count, const GLuint *value) { gles3missing(); }
static void glBindVertexArray(GLuint array) { gles3missing(); }
static void glGenVertexArrays(GLsizei n, GLuint *arrays) { gles3missing(); }
static void glDeleteVertexArrays(GLsizei n, const GLuint *arrays) { gles3missing(); }
#endif
uintptr_t processFn(struct fnargs* args, char* parg) {
uintptr_t ret = 0;
switch (args->fn) {
case glfnUNDEFINED:
abort(); // bad glfn
break;
case glfnActiveTexture:
glActiveTexture((GLenum)args->a0);
break;
case glfnAttachShader:
glAttachShader((GLint)args->a0, (GLint)args->a1);
break;
case glfnBindAttribLocation:
glBindAttribLocation((GLint)args->a0, (GLint)args->a1, (GLchar*)args->a2);
break;
case glfnBindBuffer:
glBindBuffer((GLenum)args->a0, (GLuint)args->a1);
break;
case glfnBindFramebuffer:
glBindFramebuffer((GLenum)args->a0, (GLint)args->a1);
break;
case glfnBindRenderbuffer:
glBindRenderbuffer((GLenum)args->a0, (GLint)args->a1);
break;
case glfnBindTexture:
glBindTexture((GLenum)args->a0, (GLint)args->a1);
break;
case glfnBindVertexArray:
glBindVertexArray((GLenum)args->a0);
break;
case glfnBlendColor:
glBlendColor(*(GLfloat*)&args->a0, *(GLfloat*)&args->a1, *(GLfloat*)&args->a2, *(GLfloat*)&args->a3);
break;
case glfnBlendEquation:
glBlendEquation((GLenum)args->a0);
break;
case glfnBlendEquationSeparate:
glBlendEquationSeparate((GLenum)args->a0, (GLenum)args->a1);
break;
case glfnBlendFunc:
glBlendFunc((GLenum)args->a0, (GLenum)args->a1);
break;
case glfnBlendFuncSeparate:
glBlendFuncSeparate((GLenum)args->a0, (GLenum)args->a1, (GLenum)args->a2, (GLenum)args->a3);
break;
case glfnBlitFramebuffer:
glBlitFramebuffer((GLint)args->a0, (GLint)args->a1, (GLint)args->a2, (GLint)args->a3, (GLint)args->a4, (GLint)args->a5, (GLint)args->a6, (GLint)args->a7, (GLbitfield)args->a8, (GLenum)args->a9);
break;
case glfnBufferData:
glBufferData((GLenum)args->a0, (GLsizeiptr)args->a1, (GLvoid*)parg, (GLenum)args->a2);
break;
case glfnBufferSubData:
glBufferSubData((GLenum)args->a0, (GLint)args->a1, (GLsizeiptr)args->a2, (GLvoid*)parg);
break;
case glfnCheckFramebufferStatus:
ret = glCheckFramebufferStatus((GLenum)args->a0);
break;
case glfnClear:
glClear((GLenum)args->a0);
break;
case glfnClearColor:
glClearColor(*(GLfloat*)&args->a0, *(GLfloat*)&args->a1, *(GLfloat*)&args->a2, *(GLfloat*)&args->a3);
break;
case glfnClearDepthf:
glClearDepthf(*(GLfloat*)&args->a0);
break;
case glfnClearStencil:
glClearStencil((GLint)args->a0);
break;
case glfnColorMask:
glColorMask((GLboolean)args->a0, (GLboolean)args->a1, (GLboolean)args->a2, (GLboolean)args->a3);
break;
case glfnCompileShader:
glCompileShader((GLint)args->a0);
break;
case glfnCompressedTexImage2D:
glCompressedTexImage2D((GLenum)args->a0, (GLint)args->a1, (GLenum)args->a2, (GLint)args->a3, (GLint)args->a4, (GLint)args->a5, (GLsizeiptr)args->a6, (GLvoid*)parg);
break;
case glfnCompressedTexSubImage2D:
glCompressedTexSubImage2D((GLenum)args->a0, (GLint)args->a1, (GLint)args->a2, (GLint)args->a3, (GLint)args->a4, (GLint)args->a5, (GLenum)args->a6, (GLsizeiptr)args->a7, (GLvoid*)parg);
break;
case glfnCopyTexImage2D:
glCopyTexImage2D((GLenum)args->a0, (GLint)args->a1, (GLenum)args->a2, (GLint)args->a3, (GLint)args->a4, (GLint)args->a5, (GLint)args->a6, (GLint)args->a7);
break;
case glfnCopyTexSubImage2D:
glCopyTexSubImage2D((GLenum)args->a0, (GLint)args->a1, (GLint)args->a2, (GLint)args->a3, (GLint)args->a4, (GLint)args->a5, (GLint)args->a6, (GLint)args->a7);
break;
case glfnCreateProgram:
ret = glCreateProgram();
break;
case glfnCreateShader:
ret = glCreateShader((GLenum)args->a0);
break;
case glfnCullFace:
glCullFace((GLenum)args->a0);
break;
case glfnDeleteBuffer:
glDeleteBuffers(1, (const GLuint*)(&args->a0));
break;
case glfnDeleteFramebuffer:
glDeleteFramebuffers(1, (const GLuint*)(&args->a0));
break;
case glfnDeleteProgram:
glDeleteProgram((GLint)args->a0);
break;
case glfnDeleteRenderbuffer:
glDeleteRenderbuffers(1, (const GLuint*)(&args->a0));
break;
case glfnDeleteShader:
glDeleteShader((GLint)args->a0);
break;
case glfnDeleteTexture:
glDeleteTextures(1, (const GLuint*)(&args->a0));
break;
case glfnDeleteVertexArray:
glDeleteVertexArrays(1, (const GLuint*)(&args->a0));
break;
case glfnDepthFunc:
glDepthFunc((GLenum)args->a0);
break;
case glfnDepthMask:
glDepthMask((GLboolean)args->a0);
break;
case glfnDepthRangef:
glDepthRangef(*(GLfloat*)&args->a0, *(GLfloat*)&args->a1);
break;
case glfnDetachShader:
glDetachShader((GLint)args->a0, (GLint)args->a1);
break;
case glfnDisable:
glDisable((GLenum)args->a0);
break;
case glfnDisableVertexAttribArray:
glDisableVertexAttribArray((GLint)args->a0);
break;
case glfnDrawArrays:
glDrawArrays((GLenum)args->a0, (GLint)args->a1, (GLint)args->a2);
break;
case glfnDrawElements:
glDrawElements((GLenum)args->a0, (GLint)args->a1, (GLenum)args->a2, (void*)args->a3);
break;
case glfnEnable:
glEnable((GLenum)args->a0);
break;
case glfnEnableVertexAttribArray:
glEnableVertexAttribArray((GLint)args->a0);
break;
case glfnFinish:
glFinish();
break;
case glfnFlush:
glFlush();
break;
case glfnFramebufferRenderbuffer:
glFramebufferRenderbuffer((GLenum)args->a0, (GLenum)args->a1, (GLenum)args->a2, (GLint)args->a3);
break;
case glfnFramebufferTexture2D:
glFramebufferTexture2D((GLenum)args->a0, (GLenum)args->a1, (GLenum)args->a2, (GLint)args->a3, (GLint)args->a4);
break;
case glfnFrontFace:
glFrontFace((GLenum)args->a0);
break;
case glfnGenBuffer:
glGenBuffers(1, (GLuint*)&ret);
break;
case glfnGenFramebuffer:
glGenFramebuffers(1, (GLuint*)&ret);
break;
case glfnGenRenderbuffer:
glGenRenderbuffers(1, (GLuint*)&ret);
break;
case glfnGenTexture:
glGenTextures(1, (GLuint*)&ret);
break;
case glfnGenVertexArray:
glGenVertexArrays(1, (GLuint*)&ret);
break;
case glfnGenerateMipmap:
glGenerateMipmap((GLenum)args->a0);
break;
case glfnGetActiveAttrib:
glGetActiveAttrib(
(GLuint)args->a0,
(GLuint)args->a1,
(GLsizei)args->a2,
NULL,
(GLint*)&ret,
(GLenum*)args->a3,
(GLchar*)parg);
break;
case glfnGetActiveUniform:
glGetActiveUniform(
(GLuint)args->a0,
(GLuint)args->a1,
(GLsizei)args->a2,
NULL,
(GLint*)&ret,
(GLenum*)args->a3,
(GLchar*)parg);
break;
case glfnGetAttachedShaders:
glGetAttachedShaders((GLuint)args->a0, (GLsizei)args->a1, (GLsizei*)&ret, (GLuint*)parg);
break;
case glfnGetAttribLocation:
ret = glGetAttribLocation((GLint)args->a0, (GLchar*)args->a1);
break;
case glfnGetBooleanv:
glGetBooleanv((GLenum)args->a0, (GLboolean*)parg);
break;
case glfnGetBufferParameteri:
glGetBufferParameteriv((GLenum)args->a0, (GLenum)args->a1, (GLint*)&ret);
break;
case glfnGetFloatv:
glGetFloatv((GLenum)args->a0, (GLfloat*)parg);
break;
case glfnGetIntegerv:
glGetIntegerv((GLenum)args->a0, (GLint*)parg);
break;
case glfnGetError:
ret = glGetError();
break;
case glfnGetFramebufferAttachmentParameteriv:
glGetFramebufferAttachmentParameteriv((GLenum)args->a0, (GLenum)args->a1, (GLenum)args->a2, (GLint*)&ret);
break;
case glfnGetProgramiv:
glGetProgramiv((GLint)args->a0, (GLenum)args->a1, (GLint*)&ret);
break;
case glfnGetProgramInfoLog:
glGetProgramInfoLog((GLuint)args->a0, (GLsizei)args->a1, 0, (GLchar*)parg);
break;
case glfnGetRenderbufferParameteriv:
glGetRenderbufferParameteriv((GLenum)args->a0, (GLenum)args->a1, (GLint*)&ret);
break;
case glfnGetShaderiv:
glGetShaderiv((GLint)args->a0, (GLenum)args->a1, (GLint*)&ret);
break;
case glfnGetShaderInfoLog:
glGetShaderInfoLog((GLuint)args->a0, (GLsizei)args->a1, 0, (GLchar*)parg);
break;
case glfnGetShaderPrecisionFormat:
glGetShaderPrecisionFormat((GLenum)args->a0, (GLenum)args->a1, (GLint*)parg, &((GLint*)parg)[2]);
break;
case glfnGetShaderSource:
glGetShaderSource((GLuint)args->a0, (GLsizei)args->a1, 0, (GLchar*)parg);
break;
case glfnGetString:
ret = (uintptr_t)glGetString((GLenum)args->a0);
break;
case glfnGetTexParameterfv:
glGetTexParameterfv((GLenum)args->a0, (GLenum)args->a1, (GLfloat*)parg);
break;
case glfnGetTexParameteriv:
glGetTexParameteriv((GLenum)args->a0, (GLenum)args->a1, (GLint*)parg);
break;
case glfnGetUniformfv:
glGetUniformfv((GLuint)args->a0, (GLint)args->a1, (GLfloat*)parg);
break;
case glfnGetUniformiv:
glGetUniformiv((GLuint)args->a0, (GLint)args->a1, (GLint*)parg);
break;
case glfnGetUniformLocation:
ret = glGetUniformLocation((GLint)args->a0, (GLchar*)args->a1);
break;
case glfnGetVertexAttribfv:
glGetVertexAttribfv((GLuint)args->a0, (GLenum)args->a1, (GLfloat*)parg);
break;
case glfnGetVertexAttribiv:
glGetVertexAttribiv((GLuint)args->a0, (GLenum)args->a1, (GLint*)parg);
break;
case glfnHint:
glHint((GLenum)args->a0, (GLenum)args->a1);
break;
case glfnIsBuffer:
ret = glIsBuffer((GLint)args->a0);
break;
case glfnIsEnabled:
ret = glIsEnabled((GLenum)args->a0);
break;
case glfnIsFramebuffer:
ret = glIsFramebuffer((GLint)args->a0);
break;
case glfnIsProgram:
ret = glIsProgram((GLint)args->a0);
break;
case glfnIsRenderbuffer:
ret = glIsRenderbuffer((GLint)args->a0);
break;
case glfnIsShader:
ret = glIsShader((GLint)args->a0);
break;
case glfnIsTexture:
ret = glIsTexture((GLint)args->a0);
break;
case glfnLineWidth:
glLineWidth(*(GLfloat*)&args->a0);
break;
case glfnLinkProgram:
glLinkProgram((GLint)args->a0);
break;
case glfnPixelStorei:
glPixelStorei((GLenum)args->a0, (GLint)args->a1);
break;
case glfnPolygonOffset:
glPolygonOffset(*(GLfloat*)&args->a0, *(GLfloat*)&args->a1);
break;
case glfnReadPixels:
glReadPixels((GLint)args->a0, (GLint)args->a1, (GLsizei)args->a2, (GLsizei)args->a3, (GLenum)args->a4, (GLenum)args->a5, (void*)parg);
break;
case glfnReleaseShaderCompiler:
glReleaseShaderCompiler();
break;
case glfnRenderbufferStorage:
glRenderbufferStorage((GLenum)args->a0, (GLenum)args->a1, (GLint)args->a2, (GLint)args->a3);
break;
case glfnSampleCoverage:
glSampleCoverage(*(GLfloat*)&args->a0, (GLboolean)args->a1);
break;
case glfnScissor:
glScissor((GLint)args->a0, (GLint)args->a1, (GLint)args->a2, (GLint)args->a3);
break;
case glfnShaderSource:
#if defined(os_ios) || defined(os_macos)
glShaderSource((GLuint)args->a0, (GLsizei)args->a1, (const GLchar *const *)args->a2, NULL);
#else
glShaderSource((GLuint)args->a0, (GLsizei)args->a1, (const GLchar **)args->a2, NULL);
#endif
break;
case glfnStencilFunc:
glStencilFunc((GLenum)args->a0, (GLint)args->a1, (GLuint)args->a2);
break;
case glfnStencilFuncSeparate:
glStencilFuncSeparate((GLenum)args->a0, (GLenum)args->a1, (GLint)args->a2, (GLuint)args->a3);
break;
case glfnStencilMask:
glStencilMask((GLuint)args->a0);
break;
case glfnStencilMaskSeparate:
glStencilMaskSeparate((GLenum)args->a0, (GLuint)args->a1);
break;
case glfnStencilOp:
glStencilOp((GLenum)args->a0, (GLenum)args->a1, (GLenum)args->a2);
break;
case glfnStencilOpSeparate:
glStencilOpSeparate((GLenum)args->a0, (GLenum)args->a1, (GLenum)args->a2, (GLenum)args->a3);
break;
case glfnTexImage2D:
glTexImage2D(
(GLenum)args->a0,
(GLint)args->a1,
(GLint)args->a2,
(GLsizei)args->a3,
(GLsizei)args->a4,
0, // border
(GLenum)args->a5,
(GLenum)args->a6,
(const GLvoid*)parg);
break;
case glfnTexSubImage2D:
glTexSubImage2D(
(GLenum)args->a0,
(GLint)args->a1,
(GLint)args->a2,
(GLint)args->a3,
(GLsizei)args->a4,
(GLsizei)args->a5,
(GLenum)args->a6,
(GLenum)args->a7,
(const GLvoid*)parg);
break;
case glfnTexParameterf:
glTexParameterf((GLenum)args->a0, (GLenum)args->a1, *(GLfloat*)&args->a2);
break;
case glfnTexParameterfv:
glTexParameterfv((GLenum)args->a0, (GLenum)args->a1, (GLfloat*)parg);
break;
case glfnTexParameteri:
glTexParameteri((GLenum)args->a0, (GLenum)args->a1, (GLint)args->a2);
break;
case glfnTexParameteriv:
glTexParameteriv((GLenum)args->a0, (GLenum)args->a1, (GLint*)parg);
break;
case glfnUniform1f:
glUniform1f((GLint)args->a0, *(GLfloat*)&args->a1);
break;
case glfnUniform1fv:
glUniform1fv((GLint)args->a0, (GLsizeiptr)args->a1, (GLvoid*)parg);
break;
case glfnUniform1i:
glUniform1i((GLint)args->a0, (GLint)args->a1);
break;
case glfnUniform1ui:
glUniform1ui((GLint)args->a0, (GLuint)args->a1);
break;
case glfnUniform1uiv:
glUniform1uiv((GLint)args->a0, (GLsizeiptr)args->a1, (GLuint*)parg);
break;
case glfnUniform1iv:
glUniform1iv((GLint)args->a0, (GLsizeiptr)args->a1, (GLvoid*)parg);
break;
case glfnUniform2f:
glUniform2f((GLint)args->a0, *(GLfloat*)&args->a1, *(GLfloat*)&args->a2);
break;
case glfnUniform2fv:
glUniform2fv((GLint)args->a0, (GLsizeiptr)args->a1, (GLvoid*)parg);
break;
case glfnUniform2i:
glUniform2i((GLint)args->a0, (GLint)args->a1, (GLint)args->a2);
break;
case glfnUniform2ui:
glUniform2ui((GLint)args->a0, (GLuint)args->a1, (GLuint)args->a2);
break;
case glfnUniform2uiv:
glUniform2uiv((GLint)args->a0, (GLsizeiptr)args->a1, (GLuint*)parg);
break;
case glfnUniform2iv:
glUniform2iv((GLint)args->a0, (GLsizeiptr)args->a1, (GLvoid*)parg);
break;
case glfnUniform3f:
glUniform3f((GLint)args->a0, *(GLfloat*)&args->a1, *(GLfloat*)&args->a2, *(GLfloat*)&args->a3);
break;
case glfnUniform3fv:
glUniform3fv((GLint)args->a0, (GLsizeiptr)args->a1, (GLvoid*)parg);
break;
case glfnUniform3i:
glUniform3i((GLint)args->a0, (GLint)args->a1, (GLint)args->a2, (GLint)args->a3);
break;
case glfnUniform3ui:
glUniform3ui((GLint)args->a0, (GLuint)args->a1, (GLuint)args->a2, (GLuint)args->a3);
break;
case glfnUniform3uiv:
glUniform3uiv((GLint)args->a0, (GLsizeiptr)args->a1, (GLuint*)parg);
break;
case glfnUniform3iv:
glUniform3iv((GLint)args->a0, (GLsizeiptr)args->a1, (GLvoid*)parg);
break;
case glfnUniform4f:
glUniform4f((GLint)args->a0, *(GLfloat*)&args->a1, *(GLfloat*)&args->a2, *(GLfloat*)&args->a3, *(GLfloat*)&args->a4);
break;
case glfnUniform4fv:
glUniform4fv((GLint)args->a0, (GLsizeiptr)args->a1, (GLvoid*)parg);
break;
case glfnUniform4i:
glUniform4i((GLint)args->a0, (GLint)args->a1, (GLint)args->a2, (GLint)args->a3, (GLint)args->a4);
break;
case glfnUniform4ui:
glUniform4ui((GLint)args->a0, (GLuint)args->a1, (GLuint)args->a2, (GLuint)args->a3, (GLuint)args->a4);
break;
case glfnUniform4uiv:
glUniform4uiv((GLint)args->a0, (GLsizeiptr)args->a1, (GLuint*)parg);
break;
case glfnUniform4iv:
glUniform4iv((GLint)args->a0, (GLsizeiptr)args->a1, (GLvoid*)parg);
break;
case glfnUniformMatrix2fv:
glUniformMatrix2fv((GLint)args->a0, (GLsizeiptr)args->a1, 0, (GLvoid*)parg);
break;
case glfnUniformMatrix3fv:
glUniformMatrix3fv((GLint)args->a0, (GLsizeiptr)args->a1, 0, (GLvoid*)parg);
break;
case glfnUniformMatrix4fv:
glUniformMatrix4fv((GLint)args->a0, (GLsizeiptr)args->a1, 0, (GLvoid*)parg);
break;
case glfnUniformMatrix2x3fv:
glUniformMatrix2x3fv((GLint)args->a0, (GLsizeiptr)args->a1, 0, (GLvoid*)parg);
break;
case glfnUniformMatrix3x2fv:
glUniformMatrix3x2fv((GLint)args->a0, (GLsizeiptr)args->a1, 0, (GLvoid*)parg);
break;
case glfnUniformMatrix2x4fv:
glUniformMatrix2x4fv((GLint)args->a0, (GLsizeiptr)args->a1, 0, (GLvoid*)parg);
break;
case glfnUniformMatrix4x2fv:
glUniformMatrix4x2fv((GLint)args->a0, (GLsizeiptr)args->a1, 0, (GLvoid*)parg);
break;
case glfnUniformMatrix3x4fv:
glUniformMatrix3x4fv((GLint)args->a0, (GLsizeiptr)args->a1, 0, (GLvoid*)parg);
break;
case glfnUniformMatrix4x3fv:
glUniformMatrix4x3fv((GLint)args->a0, (GLsizeiptr)args->a1, 0, (GLvoid*)parg);
break;
case glfnUseProgram:
glUseProgram((GLint)args->a0);
break;
case glfnValidateProgram:
glValidateProgram((GLint)args->a0);
break;
case glfnVertexAttrib1f:
glVertexAttrib1f((GLint)args->a0, *(GLfloat*)&args->a1);
break;
case glfnVertexAttrib1fv:
glVertexAttrib1fv((GLint)args->a0, (GLfloat*)parg);
break;
case glfnVertexAttrib2f:
glVertexAttrib2f((GLint)args->a0, *(GLfloat*)&args->a1, *(GLfloat*)&args->a2);
break;
case glfnVertexAttrib2fv:
glVertexAttrib2fv((GLint)args->a0, (GLfloat*)parg);
break;
case glfnVertexAttrib3f:
glVertexAttrib3f((GLint)args->a0, *(GLfloat*)&args->a1, *(GLfloat*)&args->a2, *(GLfloat*)&args->a3);
break;
case glfnVertexAttrib3fv:
glVertexAttrib3fv((GLint)args->a0, (GLfloat*)parg);
break;
case glfnVertexAttrib4f:
glVertexAttrib4f((GLint)args->a0, *(GLfloat*)&args->a1, *(GLfloat*)&args->a2, *(GLfloat*)&args->a3, *(GLfloat*)&args->a4);
break;
case glfnVertexAttrib4fv:
glVertexAttrib4fv((GLint)args->a0, (GLfloat*)parg);
break;
case glfnVertexAttribPointer:
glVertexAttribPointer((GLuint)args->a0, (GLint)args->a1, (GLenum)args->a2, (GLboolean)args->a3, (GLsizei)args->a4, (const GLvoid*)args->a5);
break;
case glfnViewport:
glViewport((GLint)args->a0, (GLint)args->a1, (GLint)args->a2, (GLint)args->a3);
break;
}
return ret;
}
+173
View File
@@ -0,0 +1,173 @@
// 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.
//go:build darwin || linux || openbsd
package gl
/*
#cgo ios LDFLAGS: -framework OpenGLES
#cgo darwin,!ios LDFLAGS: -framework OpenGL
#cgo linux LDFLAGS: -lGLESv2
#cgo openbsd LDFLAGS: -L/usr/X11R6/lib/ -lGLESv2
#cgo android CFLAGS: -Dos_android
#cgo ios CFLAGS: -Dos_ios
#cgo darwin,!ios CFLAGS: -Dos_macos
#cgo darwin CFLAGS: -DGL_SILENCE_DEPRECATION -DGLES_SILENCE_DEPRECATION
#cgo linux CFLAGS: -Dos_linux
#cgo openbsd CFLAGS: -Dos_openbsd
#cgo openbsd CFLAGS: -I/usr/X11R6/include/
#include <stdint.h>
#include "work.h"
uintptr_t process(struct fnargs* cargs, char* parg0, char* parg1, char* parg2, int count) {
uintptr_t ret;
ret = processFn(&cargs[0], parg0);
if (count > 1) {
ret = processFn(&cargs[1], parg1);
}
if (count > 2) {
ret = processFn(&cargs[2], parg2);
}
return ret;
}
*/
import "C"
import "unsafe"
const workbufLen = 3
type context struct {
cptr uintptr
debug int32
workAvailable chan struct{}
// work is a queue of calls to execute.
work chan call
// retvalue is sent a return value when blocking calls complete.
// It is safe to use a global unbuffered channel here as calls
// cannot currently be made concurrently.
//
// TODO: the comment above about concurrent calls isn't actually true: package
// app calls package gl, but it has to do so in a separate goroutine, which
// means that its gl calls (which may be blocking) can race with other gl calls
// in the main program. We should make it safe to issue blocking gl calls
// concurrently, or get the gl calls out of package app, or both.
retvalue chan C.uintptr_t
cargs [workbufLen]C.struct_fnargs
parg [workbufLen]*C.char
}
func (ctx *context) WorkAvailable() <-chan struct{} { return ctx.workAvailable }
type context3 struct {
*context
}
// NewContext creates a cgo OpenGL context.
//
// See the Worker interface for more details on how it is used.
func NewContext() (Context, Worker) {
glctx := &context{
workAvailable: make(chan struct{}, 1),
work: make(chan call, workbufLen),
retvalue: make(chan C.uintptr_t),
}
if C.GLES_VERSION == "GL_ES_2_0" {
return glctx, glctx
}
return context3{glctx}, glctx
}
// Version returns a GL ES version string, either "GL_ES_2_0" or "GL_ES_3_0".
// Future versions of the gl package may return "GL_ES_3_1".
func Version() string {
return C.GLES_VERSION
}
func (ctx *context) enqueue(c call) uintptr {
ctx.work <- c
select {
case ctx.workAvailable <- struct{}{}:
default:
}
if c.blocking {
return uintptr(<-ctx.retvalue)
}
return 0
}
func (ctx *context) DoWork() {
queue := make([]call, 0, workbufLen)
for {
// Wait until at least one piece of work is ready.
// Accumulate work until a piece is marked as blocking.
select {
case w := <-ctx.work:
queue = append(queue, w)
default:
return
}
blocking := queue[len(queue)-1].blocking
enqueue:
for len(queue) < cap(queue) && !blocking {
select {
case w := <-ctx.work:
queue = append(queue, w)
blocking = queue[len(queue)-1].blocking
default:
break enqueue
}
}
// Process the queued GL functions.
for i, q := range queue {
ctx.cargs[i] = *(*C.struct_fnargs)(unsafe.Pointer(&q.args))
ctx.parg[i] = (*C.char)(q.parg)
}
ret := C.process(&ctx.cargs[0], ctx.parg[0], ctx.parg[1], ctx.parg[2], C.int(len(queue)))
// Cleanup and signal.
queue = queue[:0]
if blocking {
ctx.retvalue <- ret
}
}
}
func init() {
if unsafe.Sizeof(C.GLint(0)) != unsafe.Sizeof(int32(0)) {
panic("GLint is not an int32")
}
}
// cString creates C string off the Go heap.
// ret is a *char.
func (ctx *context) cString(str string) (uintptr, func()) {
ptr := unsafe.Pointer(C.CString(str))
return uintptr(ptr), func() { C.free(ptr) }
}
// cString creates a pointer to a C string off the Go heap.
// ret is a **char.
func (ctx *context) cStringPtr(str string) (uintptr, func()) {
s, free := ctx.cString(str)
ptr := C.malloc(C.size_t(unsafe.Sizeof((*int)(nil))))
*(*uintptr)(ptr) = s
return uintptr(ptr), func() {
free()
C.free(ptr)
}
}
+217
View File
@@ -0,0 +1,217 @@
// 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.
#ifdef os_android
// TODO(crawshaw): We could include <android/api-level.h> and
// condition on __ANDROID_API__ to get GLES3 headers. However
// we also need to add -lGLESv3 to LDFLAGS, which we cannot do
// from inside an ifdef.
#include <GLES2/gl2.h>
#elif os_linux
#include <GLES3/gl3.h> // install on Ubuntu with: sudo apt-get install libegl1-mesa-dev libgles2-mesa-dev libx11-dev
#elif os_openbsd
#include <GLES3/gl3.h>
#endif
#ifdef os_ios
#include <OpenGLES/ES2/glext.h>
#endif
#ifdef os_macos
#include <OpenGL/gl3.h>
#define GL_ES_VERSION_3_0 1
#endif
#if defined(GL_ES_VERSION_3_0) && GL_ES_VERSION_3_0
#define GLES_VERSION "GL_ES_3_0"
#else
#define GLES_VERSION "GL_ES_2_0"
#endif
#include <stdint.h>
#include <stdlib.h>
// TODO: generate this enum from fn.go.
typedef enum {
glfnUNDEFINED,
glfnActiveTexture,
glfnAttachShader,
glfnBindAttribLocation,
glfnBindBuffer,
glfnBindFramebuffer,
glfnBindRenderbuffer,
glfnBindTexture,
glfnBindVertexArray,
glfnBlendColor,
glfnBlendEquation,
glfnBlendEquationSeparate,
glfnBlendFunc,
glfnBlendFuncSeparate,
glfnBufferData,
glfnBufferSubData,
glfnCheckFramebufferStatus,
glfnClear,
glfnClearColor,
glfnClearDepthf,
glfnClearStencil,
glfnColorMask,
glfnCompileShader,
glfnCompressedTexImage2D,
glfnCompressedTexSubImage2D,
glfnCopyTexImage2D,
glfnCopyTexSubImage2D,
glfnCreateProgram,
glfnCreateShader,
glfnCullFace,
glfnDeleteBuffer,
glfnDeleteFramebuffer,
glfnDeleteProgram,
glfnDeleteRenderbuffer,
glfnDeleteShader,
glfnDeleteTexture,
glfnDeleteVertexArray,
glfnDepthFunc,
glfnDepthRangef,
glfnDepthMask,
glfnDetachShader,
glfnDisable,
glfnDisableVertexAttribArray,
glfnDrawArrays,
glfnDrawElements,
glfnEnable,
glfnEnableVertexAttribArray,
glfnFinish,
glfnFlush,
glfnFramebufferRenderbuffer,
glfnFramebufferTexture2D,
glfnFrontFace,
glfnGenBuffer,
glfnGenFramebuffer,
glfnGenRenderbuffer,
glfnGenTexture,
glfnGenVertexArray,
glfnGenerateMipmap,
glfnGetActiveAttrib,
glfnGetActiveUniform,
glfnGetAttachedShaders,
glfnGetAttribLocation,
glfnGetBooleanv,
glfnGetBufferParameteri,
glfnGetError,
glfnGetFloatv,
glfnGetFramebufferAttachmentParameteriv,
glfnGetIntegerv,
glfnGetProgramInfoLog,
glfnGetProgramiv,
glfnGetRenderbufferParameteriv,
glfnGetShaderInfoLog,
glfnGetShaderPrecisionFormat,
glfnGetShaderSource,
glfnGetShaderiv,
glfnGetString,
glfnGetTexParameterfv,
glfnGetTexParameteriv,
glfnGetUniformLocation,
glfnGetUniformfv,
glfnGetUniformiv,
glfnGetVertexAttribfv,
glfnGetVertexAttribiv,
glfnHint,
glfnIsBuffer,
glfnIsEnabled,
glfnIsFramebuffer,
glfnIsProgram,
glfnIsRenderbuffer,
glfnIsShader,
glfnIsTexture,
glfnLineWidth,
glfnLinkProgram,
glfnPixelStorei,
glfnPolygonOffset,
glfnReadPixels,
glfnReleaseShaderCompiler,
glfnRenderbufferStorage,
glfnSampleCoverage,
glfnScissor,
glfnShaderSource,
glfnStencilFunc,
glfnStencilFuncSeparate,
glfnStencilMask,
glfnStencilMaskSeparate,
glfnStencilOp,
glfnStencilOpSeparate,
glfnTexImage2D,
glfnTexParameterf,
glfnTexParameterfv,
glfnTexParameteri,
glfnTexParameteriv,
glfnTexSubImage2D,
glfnUniform1f,
glfnUniform1fv,
glfnUniform1i,
glfnUniform1iv,
glfnUniform2f,
glfnUniform2fv,
glfnUniform2i,
glfnUniform2iv,
glfnUniform3f,
glfnUniform3fv,
glfnUniform3i,
glfnUniform3iv,
glfnUniform4f,
glfnUniform4fv,
glfnUniform4i,
glfnUniform4iv,
glfnUniformMatrix2fv,
glfnUniformMatrix3fv,
glfnUniformMatrix4fv,
glfnUseProgram,
glfnValidateProgram,
glfnVertexAttrib1f,
glfnVertexAttrib1fv,
glfnVertexAttrib2f,
glfnVertexAttrib2fv,
glfnVertexAttrib3f,
glfnVertexAttrib3fv,
glfnVertexAttrib4f,
glfnVertexAttrib4fv,
glfnVertexAttribPointer,
glfnViewport,
// ES 3.0 functions
glfnUniformMatrix2x3fv,
glfnUniformMatrix3x2fv,
glfnUniformMatrix2x4fv,
glfnUniformMatrix4x2fv,
glfnUniformMatrix3x4fv,
glfnUniformMatrix4x3fv,
glfnBlitFramebuffer,
glfnUniform1ui,
glfnUniform2ui,
glfnUniform3ui,
glfnUniform4ui,
glfnUniform1uiv,
glfnUniform2uiv,
glfnUniform3uiv,
glfnUniform4uiv,
} glfn;
// TODO: generate this type from fn.go.
struct fnargs {
glfn fn;
uintptr_t a0;
uintptr_t a1;
uintptr_t a2;
uintptr_t a3;
uintptr_t a4;
uintptr_t a5;
uintptr_t a6;
uintptr_t a7;
uintptr_t a8;
uintptr_t a9;
};
extern uintptr_t processFn(struct fnargs* args, char* parg);
+35
View File
@@ -0,0 +1,35 @@
// Copyright 2019 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.
//go:build (!cgo || (!darwin && !linux && !openbsd)) && !windows
package gl
// This file contains stub implementations of what the other work*.go files
// provide. These stubs don't do anything, other than compile (e.g. when cgo is
// disabled).
type context struct{}
func (*context) enqueue(c call) uintptr {
panic("unimplemented; GOOS/CGO combination not supported")
}
func (*context) cString(str string) (uintptr, func()) {
panic("unimplemented; GOOS/CGO combination not supported")
}
func (*context) cStringPtr(str string) (uintptr, func()) {
panic("unimplemented; GOOS/CGO combination not supported")
}
type context3 = context
func NewContext() (Context, Worker) {
panic("unimplemented; GOOS/CGO combination not supported")
}
func Version() string {
panic("unimplemented; GOOS/CGO combination not supported")
}
+569
View File
@@ -0,0 +1,569 @@
// 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.
package gl
import (
"runtime"
"syscall"
"unsafe"
)
// context is described in work.go.
type context struct {
debug int32
workAvailable chan struct{}
work chan call
retvalue chan uintptr
// TODO(crawshaw): will not work with a moving collector
cStringCounter int
cStrings map[int]unsafe.Pointer
}
func (ctx *context) WorkAvailable() <-chan struct{} { return ctx.workAvailable }
type context3 struct {
*context
}
func NewContext() (Context, Worker) {
if err := findDLLs(); err != nil {
panic(err)
}
glctx := &context{
workAvailable: make(chan struct{}, 1),
work: make(chan call, 3),
retvalue: make(chan uintptr),
cStrings: make(map[int]unsafe.Pointer),
}
return glctx, glctx
}
func (ctx *context) enqueue(c call) uintptr {
ctx.work <- c
select {
case ctx.workAvailable <- struct{}{}:
default:
}
if c.blocking {
return <-ctx.retvalue
}
return 0
}
func (ctx *context) DoWork() {
// TODO: add a work queue
for {
select {
case w := <-ctx.work:
ret := ctx.doWork(w)
if w.blocking {
ctx.retvalue <- ret
}
default:
return
}
}
}
func (ctx *context) cString(s string) (uintptr, func()) {
buf := make([]byte, len(s)+1)
for i := 0; i < len(s); i++ {
buf[i] = s[i]
}
ret := unsafe.Pointer(&buf[0])
id := ctx.cStringCounter
ctx.cStringCounter++
ctx.cStrings[id] = ret
return uintptr(ret), func() { delete(ctx.cStrings, id) }
}
func (ctx *context) cStringPtr(str string) (uintptr, func()) {
s, sfree := ctx.cString(str)
sptr := [2]uintptr{s, 0}
ret := unsafe.Pointer(&sptr[0])
id := ctx.cStringCounter
ctx.cStringCounter++
ctx.cStrings[id] = ret
return uintptr(ret), func() { sfree(); delete(ctx.cStrings, id) }
}
// fixFloat copies the first four arguments into the XMM registers.
// This is for the windows/amd64 calling convention, that wants
// floating point arguments to be passed in XMM.
//
// Mercifully, type information is not required to implement
// this calling convention. In particular see the mixed int/float
// examples:
//
// https://msdn.microsoft.com/en-us/library/zthk2dkh.aspx
//
// This means it could be fixed in syscall.Syscall. The relevant
// issue is
//
// https://golang.org/issue/6510
func fixFloat(x0, x1, x2, x3 uintptr)
func (ctx *context) doWork(c call) (ret uintptr) {
if runtime.GOARCH == "amd64" {
fixFloat(c.args.a0, c.args.a1, c.args.a2, c.args.a3)
}
switch c.args.fn {
case glfnActiveTexture:
syscall.Syscall(glActiveTexture.Addr(), 1, c.args.a0, 0, 0)
case glfnAttachShader:
syscall.Syscall(glAttachShader.Addr(), 2, c.args.a0, c.args.a1, 0)
case glfnBindAttribLocation:
syscall.Syscall(glBindAttribLocation.Addr(), 3, c.args.a0, c.args.a1, c.args.a2)
case glfnBindBuffer:
syscall.Syscall(glBindBuffer.Addr(), 2, c.args.a0, c.args.a1, 0)
case glfnBindFramebuffer:
syscall.Syscall(glBindFramebuffer.Addr(), 2, c.args.a0, c.args.a1, 0)
case glfnBindRenderbuffer:
syscall.Syscall(glBindRenderbuffer.Addr(), 2, c.args.a0, c.args.a1, 0)
case glfnBindTexture:
syscall.Syscall(glBindTexture.Addr(), 2, c.args.a0, c.args.a1, 0)
case glfnBindVertexArray:
syscall.Syscall(glBindVertexArray.Addr(), 1, c.args.a0, 0, 0)
case glfnBlendColor:
syscall.Syscall6(glBlendColor.Addr(), 4, c.args.a0, c.args.a1, c.args.a2, c.args.a3, 0, 0)
case glfnBlendEquation:
syscall.Syscall(glBlendEquation.Addr(), 1, c.args.a0, 0, 0)
case glfnBlendEquationSeparate:
syscall.Syscall(glBlendEquationSeparate.Addr(), 2, c.args.a0, c.args.a1, 0)
case glfnBlendFunc:
syscall.Syscall(glBlendFunc.Addr(), 2, c.args.a0, c.args.a1, 0)
case glfnBlendFuncSeparate:
syscall.Syscall6(glBlendFuncSeparate.Addr(), 4, c.args.a0, c.args.a1, c.args.a2, c.args.a3, 0, 0)
case glfnBufferData:
syscall.Syscall6(glBufferData.Addr(), 4, c.args.a0, c.args.a1, uintptr(c.parg), c.args.a2, 0, 0)
case glfnBufferSubData:
syscall.Syscall6(glBufferSubData.Addr(), 4, c.args.a0, c.args.a1, c.args.a2, uintptr(c.parg), 0, 0)
case glfnCheckFramebufferStatus:
ret, _, _ = syscall.Syscall(glCheckFramebufferStatus.Addr(), 1, c.args.a0, 0, 0)
case glfnClear:
syscall.Syscall(glClear.Addr(), 1, c.args.a0, 0, 0)
case glfnClearColor:
syscall.Syscall6(glClearColor.Addr(), 4, c.args.a0, c.args.a1, c.args.a2, c.args.a3, 0, 0)
case glfnClearDepthf:
syscall.Syscall6(glClearDepthf.Addr(), 1, c.args.a0, 0, 0, 0, 0, 0)
case glfnClearStencil:
syscall.Syscall(glClearStencil.Addr(), 1, c.args.a0, 0, 0)
case glfnColorMask:
syscall.Syscall6(glColorMask.Addr(), 4, c.args.a0, c.args.a1, c.args.a2, c.args.a3, 0, 0)
case glfnCompileShader:
syscall.Syscall(glCompileShader.Addr(), 1, c.args.a0, 0, 0)
case glfnCompressedTexImage2D:
syscall.Syscall9(glCompressedTexImage2D.Addr(), 8, c.args.a0, c.args.a1, c.args.a2, c.args.a3, c.args.a4, c.args.a5, c.args.a6, uintptr(c.parg), 0)
case glfnCompressedTexSubImage2D:
syscall.Syscall9(glCompressedTexSubImage2D.Addr(), 9, c.args.a0, c.args.a1, c.args.a2, c.args.a3, c.args.a4, c.args.a5, c.args.a6, c.args.a7, uintptr(c.parg))
case glfnCopyTexImage2D:
syscall.Syscall9(glCopyTexImage2D.Addr(), 8, c.args.a0, c.args.a1, c.args.a2, c.args.a3, c.args.a4, c.args.a5, c.args.a6, c.args.a7, 0)
case glfnCopyTexSubImage2D:
syscall.Syscall9(glCopyTexSubImage2D.Addr(), 8, c.args.a0, c.args.a1, c.args.a2, c.args.a3, c.args.a4, c.args.a5, c.args.a6, c.args.a7, 0)
case glfnCreateProgram:
ret, _, _ = syscall.Syscall(glCreateProgram.Addr(), 0, 0, 0, 0)
case glfnCreateShader:
ret, _, _ = syscall.Syscall(glCreateShader.Addr(), 1, c.args.a0, 0, 0)
case glfnCullFace:
syscall.Syscall(glCullFace.Addr(), 1, c.args.a0, 0, 0)
case glfnDeleteBuffer:
syscall.Syscall(glDeleteBuffers.Addr(), 2, 1, uintptr(unsafe.Pointer(&c.args.a0)), 0)
case glfnDeleteFramebuffer:
syscall.Syscall(glDeleteFramebuffers.Addr(), 2, 1, uintptr(unsafe.Pointer(&c.args.a0)), 0)
case glfnDeleteProgram:
syscall.Syscall(glDeleteProgram.Addr(), 1, c.args.a0, 0, 0)
case glfnDeleteRenderbuffer:
syscall.Syscall(glDeleteRenderbuffers.Addr(), 2, 1, uintptr(unsafe.Pointer(&c.args.a0)), 0)
case glfnDeleteShader:
syscall.Syscall(glDeleteShader.Addr(), 1, c.args.a0, 0, 0)
case glfnDeleteVertexArray:
syscall.Syscall(glDeleteVertexArrays.Addr(), 2, 1, uintptr(unsafe.Pointer(&c.args.a0)), 0)
case glfnDeleteTexture:
syscall.Syscall(glDeleteTextures.Addr(), 2, 1, uintptr(unsafe.Pointer(&c.args.a0)), 0)
case glfnDepthFunc:
syscall.Syscall(glDepthFunc.Addr(), 1, c.args.a0, 0, 0)
case glfnDepthRangef:
syscall.Syscall6(glDepthRangef.Addr(), 2, c.args.a0, c.args.a1, 0, 0, 0, 0)
case glfnDepthMask:
syscall.Syscall(glDepthMask.Addr(), 1, c.args.a0, 0, 0)
case glfnDetachShader:
syscall.Syscall(glDetachShader.Addr(), 2, c.args.a0, c.args.a1, 0)
case glfnDisable:
syscall.Syscall(glDisable.Addr(), 1, c.args.a0, 0, 0)
case glfnDisableVertexAttribArray:
syscall.Syscall(glDisableVertexAttribArray.Addr(), 1, c.args.a0, 0, 0)
case glfnDrawArrays:
syscall.Syscall(glDrawArrays.Addr(), 3, c.args.a0, c.args.a1, c.args.a2)
case glfnDrawElements:
syscall.Syscall6(glDrawElements.Addr(), 4, c.args.a0, c.args.a1, c.args.a2, c.args.a3, 0, 0)
case glfnEnable:
syscall.Syscall(glEnable.Addr(), 1, c.args.a0, 0, 0)
case glfnEnableVertexAttribArray:
syscall.Syscall(glEnableVertexAttribArray.Addr(), 1, c.args.a0, 0, 0)
case glfnFinish:
syscall.Syscall(glFinish.Addr(), 0, 0, 0, 0)
case glfnFlush:
syscall.Syscall(glFlush.Addr(), 0, 0, 0, 0)
case glfnFramebufferRenderbuffer:
syscall.Syscall6(glFramebufferRenderbuffer.Addr(), 4, c.args.a0, c.args.a1, c.args.a2, c.args.a3, 0, 0)
case glfnFramebufferTexture2D:
syscall.Syscall6(glFramebufferTexture2D.Addr(), 5, c.args.a0, c.args.a1, c.args.a2, c.args.a3, c.args.a4, 0)
case glfnFrontFace:
syscall.Syscall(glFrontFace.Addr(), 1, c.args.a0, 0, 0)
case glfnGenBuffer:
syscall.Syscall(glGenBuffers.Addr(), 2, 1, uintptr(unsafe.Pointer(&ret)), 0)
case glfnGenFramebuffer:
syscall.Syscall(glGenFramebuffers.Addr(), 2, 1, uintptr(unsafe.Pointer(&ret)), 0)
case glfnGenRenderbuffer:
syscall.Syscall(glGenRenderbuffers.Addr(), 2, 1, uintptr(unsafe.Pointer(&ret)), 0)
case glfnGenVertexArray:
syscall.Syscall(glGenVertexArrays.Addr(), 2, 1, uintptr(unsafe.Pointer(&ret)), 0)
case glfnGenTexture:
syscall.Syscall(glGenTextures.Addr(), 2, 1, uintptr(unsafe.Pointer(&ret)), 0)
case glfnGenerateMipmap:
syscall.Syscall(glGenerateMipmap.Addr(), 1, c.args.a0, 0, 0)
case glfnGetActiveAttrib:
syscall.Syscall9(glGetActiveAttrib.Addr(), 7, c.args.a0, c.args.a1, c.args.a2, 0, uintptr(unsafe.Pointer(&ret)), c.args.a3, uintptr(c.parg), 0, 0)
case glfnGetActiveUniform:
syscall.Syscall9(glGetActiveUniform.Addr(), 7, c.args.a0, c.args.a1, c.args.a2, 0, uintptr(unsafe.Pointer(&ret)), c.args.a3, uintptr(c.parg), 0, 0)
case glfnGetAttachedShaders:
syscall.Syscall6(glGetAttachedShaders.Addr(), 4, c.args.a0, c.args.a1, uintptr(unsafe.Pointer(&ret)), uintptr(c.parg), 0, 0)
case glfnGetAttribLocation:
ret, _, _ = syscall.Syscall(glGetAttribLocation.Addr(), 2, c.args.a0, c.args.a1, 0)
case glfnGetBooleanv:
syscall.Syscall(glGetBooleanv.Addr(), 2, c.args.a0, uintptr(c.parg), 0)
case glfnGetBufferParameteri:
syscall.Syscall(glGetBufferParameteri.Addr(), 3, c.args.a0, c.args.a1, uintptr(unsafe.Pointer(&ret)))
case glfnGetError:
ret, _, _ = syscall.Syscall(glGetError.Addr(), 0, 0, 0, 0)
case glfnGetFloatv:
syscall.Syscall(glGetFloatv.Addr(), 2, c.args.a0, uintptr(c.parg), 0)
case glfnGetFramebufferAttachmentParameteriv:
syscall.Syscall6(glGetFramebufferAttachmentParameteriv.Addr(), 4, c.args.a0, c.args.a1, c.args.a2, uintptr(unsafe.Pointer(&ret)), 0, 0)
case glfnGetIntegerv:
syscall.Syscall(glGetIntegerv.Addr(), 2, c.args.a0, uintptr(c.parg), 0)
case glfnGetProgramInfoLog:
syscall.Syscall6(glGetProgramInfoLog.Addr(), 4, c.args.a0, c.args.a1, 0, uintptr(c.parg), 0, 0)
case glfnGetProgramiv:
syscall.Syscall(glGetProgramiv.Addr(), 3, c.args.a0, c.args.a1, uintptr(unsafe.Pointer(&ret)))
case glfnGetRenderbufferParameteriv:
syscall.Syscall(glGetRenderbufferParameteriv.Addr(), 3, c.args.a0, c.args.a1, uintptr(unsafe.Pointer(&ret)))
case glfnGetShaderInfoLog:
syscall.Syscall6(glGetShaderInfoLog.Addr(), 4, c.args.a0, c.args.a1, 0, uintptr(c.parg), 0, 0)
case glfnGetShaderPrecisionFormat:
// c.parg is a [3]int32. The first [2]int32 of the array is one
// parameter, the final *int32 is another parameter.
syscall.Syscall6(glGetShaderPrecisionFormat.Addr(), 4, c.args.a0, c.args.a1, uintptr(c.parg), uintptr(c.parg)+2*unsafe.Sizeof(uintptr(0)), 0, 0)
case glfnGetShaderSource:
syscall.Syscall6(glGetShaderSource.Addr(), 4, c.args.a0, c.args.a1, 0, uintptr(c.parg), 0, 0)
case glfnGetShaderiv:
syscall.Syscall(glGetShaderiv.Addr(), 3, c.args.a0, c.args.a1, uintptr(unsafe.Pointer(&ret)))
case glfnGetString:
ret, _, _ = syscall.Syscall(glGetString.Addr(), 1, c.args.a0, 0, 0)
case glfnGetTexParameterfv:
syscall.Syscall(glGetTexParameterfv.Addr(), 3, c.args.a0, c.args.a1, uintptr(c.parg))
case glfnGetTexParameteriv:
syscall.Syscall(glGetTexParameteriv.Addr(), 3, c.args.a0, c.args.a1, uintptr(c.parg))
case glfnGetUniformLocation:
ret, _, _ = syscall.Syscall(glGetUniformLocation.Addr(), 2, c.args.a0, c.args.a1, 0)
case glfnGetUniformfv:
syscall.Syscall(glGetUniformfv.Addr(), 3, c.args.a0, c.args.a1, uintptr(c.parg))
case glfnGetUniformiv:
syscall.Syscall(glGetUniformiv.Addr(), 3, c.args.a0, c.args.a1, uintptr(c.parg))
case glfnGetVertexAttribfv:
syscall.Syscall(glGetVertexAttribfv.Addr(), 3, c.args.a0, c.args.a1, uintptr(c.parg))
case glfnGetVertexAttribiv:
syscall.Syscall(glGetVertexAttribiv.Addr(), 3, c.args.a0, c.args.a1, uintptr(c.parg))
case glfnHint:
syscall.Syscall(glHint.Addr(), 2, c.args.a0, c.args.a1, 0)
case glfnIsBuffer:
syscall.Syscall(glIsBuffer.Addr(), 1, c.args.a0, 0, 0)
case glfnIsEnabled:
syscall.Syscall(glIsEnabled.Addr(), 1, c.args.a0, 0, 0)
case glfnIsFramebuffer:
syscall.Syscall(glIsFramebuffer.Addr(), 1, c.args.a0, 0, 0)
case glfnIsProgram:
ret, _, _ = syscall.Syscall(glIsProgram.Addr(), 1, c.args.a0, 0, 0)
case glfnIsRenderbuffer:
syscall.Syscall(glIsRenderbuffer.Addr(), 1, c.args.a0, 0, 0)
case glfnIsShader:
syscall.Syscall(glIsShader.Addr(), 1, c.args.a0, 0, 0)
case glfnIsTexture:
syscall.Syscall(glIsTexture.Addr(), 1, c.args.a0, 0, 0)
case glfnLineWidth:
syscall.Syscall(glLineWidth.Addr(), 1, c.args.a0, 0, 0)
case glfnLinkProgram:
syscall.Syscall(glLinkProgram.Addr(), 1, c.args.a0, 0, 0)
case glfnPixelStorei:
syscall.Syscall(glPixelStorei.Addr(), 2, c.args.a0, c.args.a1, 0)
case glfnPolygonOffset:
syscall.Syscall6(glPolygonOffset.Addr(), 2, c.args.a0, c.args.a1, 0, 0, 0, 0)
case glfnReadPixels:
syscall.Syscall9(glReadPixels.Addr(), 7, c.args.a0, c.args.a1, c.args.a2, c.args.a3, c.args.a4, c.args.a5, uintptr(c.parg), 0, 0)
case glfnReleaseShaderCompiler:
syscall.Syscall(glReleaseShaderCompiler.Addr(), 0, 0, 0, 0)
case glfnRenderbufferStorage:
syscall.Syscall6(glRenderbufferStorage.Addr(), 4, c.args.a0, c.args.a1, c.args.a2, c.args.a3, 0, 0)
case glfnSampleCoverage:
syscall.Syscall6(glSampleCoverage.Addr(), 1, c.args.a0, 0, 0, 0, 0, 0)
case glfnScissor:
syscall.Syscall6(glScissor.Addr(), 4, c.args.a0, c.args.a1, c.args.a2, c.args.a3, 0, 0)
case glfnShaderSource:
syscall.Syscall6(glShaderSource.Addr(), 4, c.args.a0, c.args.a1, c.args.a2, 0, 0, 0)
case glfnStencilFunc:
syscall.Syscall(glStencilFunc.Addr(), 3, c.args.a0, c.args.a1, c.args.a2)
case glfnStencilFuncSeparate:
syscall.Syscall6(glStencilFuncSeparate.Addr(), 4, c.args.a0, c.args.a1, c.args.a2, c.args.a3, 0, 0)
case glfnStencilMask:
syscall.Syscall(glStencilMask.Addr(), 1, c.args.a0, 0, 0)
case glfnStencilMaskSeparate:
syscall.Syscall(glStencilMaskSeparate.Addr(), 2, c.args.a0, c.args.a1, 0)
case glfnStencilOp:
syscall.Syscall(glStencilOp.Addr(), 3, c.args.a0, c.args.a1, c.args.a2)
case glfnStencilOpSeparate:
syscall.Syscall6(glStencilOpSeparate.Addr(), 4, c.args.a0, c.args.a1, c.args.a2, c.args.a3, 0, 0)
case glfnTexImage2D:
syscall.Syscall9(glTexImage2D.Addr(), 9, c.args.a0, c.args.a1, c.args.a2, c.args.a3, c.args.a4, 0, c.args.a5, c.args.a6, uintptr(c.parg))
case glfnTexParameterf:
syscall.Syscall6(glTexParameterf.Addr(), 3, c.args.a0, c.args.a1, c.args.a2, c.args.a3, c.args.a4, c.args.a5)
case glfnTexParameterfv:
syscall.Syscall(glTexParameterfv.Addr(), 3, c.args.a0, c.args.a1, uintptr(c.parg))
case glfnTexParameteri:
syscall.Syscall(glTexParameteri.Addr(), 3, c.args.a0, c.args.a1, c.args.a2)
case glfnTexParameteriv:
syscall.Syscall(glTexParameteriv.Addr(), 3, c.args.a0, c.args.a1, uintptr(c.parg))
case glfnTexSubImage2D:
syscall.Syscall9(glTexSubImage2D.Addr(), 9, c.args.a0, c.args.a1, c.args.a2, c.args.a3, c.args.a4, c.args.a5, c.args.a6, c.args.a7, uintptr(c.parg))
case glfnUniform1f:
syscall.Syscall6(glUniform1f.Addr(), 2, c.args.a0, c.args.a1, c.args.a2, c.args.a3, c.args.a4, c.args.a5)
case glfnUniform1fv:
syscall.Syscall(glUniform1fv.Addr(), 3, c.args.a0, c.args.a1, uintptr(c.parg))
case glfnUniform1i:
syscall.Syscall(glUniform1i.Addr(), 2, c.args.a0, c.args.a1, 0)
case glfnUniform1iv:
syscall.Syscall(glUniform1iv.Addr(), 3, c.args.a0, c.args.a1, uintptr(c.parg))
case glfnUniform2f:
syscall.Syscall6(glUniform2f.Addr(), 3, c.args.a0, c.args.a1, c.args.a2, c.args.a3, c.args.a4, c.args.a5)
case glfnUniform2fv:
syscall.Syscall(glUniform2fv.Addr(), 3, c.args.a0, c.args.a1, uintptr(c.parg))
case glfnUniform2i:
syscall.Syscall(glUniform2i.Addr(), 3, c.args.a0, c.args.a1, c.args.a2)
case glfnUniform2iv:
syscall.Syscall(glUniform2iv.Addr(), 3, c.args.a0, c.args.a1, uintptr(c.parg))
case glfnUniform3f:
syscall.Syscall6(glUniform3f.Addr(), 4, c.args.a0, c.args.a1, c.args.a2, c.args.a3, c.args.a4, c.args.a5)
case glfnUniform3fv:
syscall.Syscall(glUniform3fv.Addr(), 3, c.args.a0, c.args.a1, uintptr(c.parg))
case glfnUniform3i:
syscall.Syscall6(glUniform3i.Addr(), 4, c.args.a0, c.args.a1, c.args.a2, c.args.a3, 0, 0)
case glfnUniform3iv:
syscall.Syscall(glUniform3iv.Addr(), 3, c.args.a0, c.args.a1, uintptr(c.parg))
case glfnUniform4f:
syscall.Syscall6(glUniform4f.Addr(), 5, c.args.a0, c.args.a1, c.args.a2, c.args.a3, c.args.a4, c.args.a5)
case glfnUniform4fv:
syscall.Syscall(glUniform4fv.Addr(), 3, c.args.a0, c.args.a1, uintptr(c.parg))
case glfnUniform4i:
syscall.Syscall6(glUniform4i.Addr(), 5, c.args.a0, c.args.a1, c.args.a2, c.args.a3, c.args.a4, 0)
case glfnUniform4iv:
syscall.Syscall(glUniform4iv.Addr(), 3, c.args.a0, c.args.a1, uintptr(c.parg))
case glfnUniformMatrix2fv:
syscall.Syscall6(glUniformMatrix2fv.Addr(), 4, c.args.a0, c.args.a1, 0, uintptr(c.parg), 0, 0)
case glfnUniformMatrix3fv:
syscall.Syscall6(glUniformMatrix3fv.Addr(), 4, c.args.a0, c.args.a1, 0, uintptr(c.parg), 0, 0)
case glfnUniformMatrix4fv:
syscall.Syscall6(glUniformMatrix4fv.Addr(), 4, c.args.a0, c.args.a1, 0, uintptr(c.parg), 0, 0)
case glfnUseProgram:
syscall.Syscall(glUseProgram.Addr(), 1, c.args.a0, 0, 0)
case glfnValidateProgram:
syscall.Syscall(glValidateProgram.Addr(), 1, c.args.a0, 0, 0)
case glfnVertexAttrib1f:
syscall.Syscall6(glVertexAttrib1f.Addr(), 2, c.args.a0, c.args.a1, c.args.a2, c.args.a3, c.args.a4, c.args.a5)
case glfnVertexAttrib1fv:
syscall.Syscall(glVertexAttrib1fv.Addr(), 2, c.args.a0, uintptr(c.parg), 0)
case glfnVertexAttrib2f:
syscall.Syscall6(glVertexAttrib2f.Addr(), 3, c.args.a0, c.args.a1, c.args.a2, c.args.a3, c.args.a4, c.args.a5)
case glfnVertexAttrib2fv:
syscall.Syscall(glVertexAttrib2fv.Addr(), 2, c.args.a0, uintptr(c.parg), 0)
case glfnVertexAttrib3f:
syscall.Syscall6(glVertexAttrib3f.Addr(), 4, c.args.a0, c.args.a1, c.args.a2, c.args.a3, c.args.a4, c.args.a5)
case glfnVertexAttrib3fv:
syscall.Syscall(glVertexAttrib3fv.Addr(), 2, c.args.a0, uintptr(c.parg), 0)
case glfnVertexAttrib4f:
syscall.Syscall6(glVertexAttrib4f.Addr(), 5, c.args.a0, c.args.a1, c.args.a2, c.args.a3, c.args.a4, c.args.a5)
case glfnVertexAttrib4fv:
syscall.Syscall(glVertexAttrib4fv.Addr(), 2, c.args.a0, uintptr(c.parg), 0)
case glfnVertexAttribPointer:
syscall.Syscall6(glVertexAttribPointer.Addr(), 6, c.args.a0, c.args.a1, c.args.a2, c.args.a3, c.args.a4, c.args.a5)
case glfnViewport:
syscall.Syscall6(glViewport.Addr(), 4, c.args.a0, c.args.a1, c.args.a2, c.args.a3, 0, 0)
default:
panic("unknown GL function")
}
return ret
}
// Exported libraries for a Windows GUI driver.
//
// LibEGL is not used directly by the gl package, but is needed by any
// driver hoping to use OpenGL ES.
//
// LibD3DCompiler is needed by libglesv2.dll for compiling shaders.
var (
LibGLESv2 = syscall.NewLazyDLL("libglesv2.dll")
LibEGL = syscall.NewLazyDLL("libegl.dll")
LibD3DCompiler = syscall.NewLazyDLL("d3dcompiler_47.dll")
)
var (
libGLESv2 = LibGLESv2
glActiveTexture = libGLESv2.NewProc("glActiveTexture")
glAttachShader = libGLESv2.NewProc("glAttachShader")
glBindAttribLocation = libGLESv2.NewProc("glBindAttribLocation")
glBindBuffer = libGLESv2.NewProc("glBindBuffer")
glBindFramebuffer = libGLESv2.NewProc("glBindFramebuffer")
glBindRenderbuffer = libGLESv2.NewProc("glBindRenderbuffer")
glBindTexture = libGLESv2.NewProc("glBindTexture")
glBindVertexArray = libGLESv2.NewProc("glBindVertexArray")
glBlendColor = libGLESv2.NewProc("glBlendColor")
glBlendEquation = libGLESv2.NewProc("glBlendEquation")
glBlendEquationSeparate = libGLESv2.NewProc("glBlendEquationSeparate")
glBlendFunc = libGLESv2.NewProc("glBlendFunc")
glBlendFuncSeparate = libGLESv2.NewProc("glBlendFuncSeparate")
glBufferData = libGLESv2.NewProc("glBufferData")
glBufferSubData = libGLESv2.NewProc("glBufferSubData")
glCheckFramebufferStatus = libGLESv2.NewProc("glCheckFramebufferStatus")
glClear = libGLESv2.NewProc("glClear")
glClearColor = libGLESv2.NewProc("glClearColor")
glClearDepthf = libGLESv2.NewProc("glClearDepthf")
glClearStencil = libGLESv2.NewProc("glClearStencil")
glColorMask = libGLESv2.NewProc("glColorMask")
glCompileShader = libGLESv2.NewProc("glCompileShader")
glCompressedTexImage2D = libGLESv2.NewProc("glCompressedTexImage2D")
glCompressedTexSubImage2D = libGLESv2.NewProc("glCompressedTexSubImage2D")
glCopyTexImage2D = libGLESv2.NewProc("glCopyTexImage2D")
glCopyTexSubImage2D = libGLESv2.NewProc("glCopyTexSubImage2D")
glCreateProgram = libGLESv2.NewProc("glCreateProgram")
glCreateShader = libGLESv2.NewProc("glCreateShader")
glCullFace = libGLESv2.NewProc("glCullFace")
glDeleteBuffers = libGLESv2.NewProc("glDeleteBuffers")
glDeleteFramebuffers = libGLESv2.NewProc("glDeleteFramebuffers")
glDeleteProgram = libGLESv2.NewProc("glDeleteProgram")
glDeleteRenderbuffers = libGLESv2.NewProc("glDeleteRenderbuffers")
glDeleteShader = libGLESv2.NewProc("glDeleteShader")
glDeleteTextures = libGLESv2.NewProc("glDeleteTextures")
glDeleteVertexArrays = libGLESv2.NewProc("glDeleteVertexArrays")
glDepthFunc = libGLESv2.NewProc("glDepthFunc")
glDepthRangef = libGLESv2.NewProc("glDepthRangef")
glDepthMask = libGLESv2.NewProc("glDepthMask")
glDetachShader = libGLESv2.NewProc("glDetachShader")
glDisable = libGLESv2.NewProc("glDisable")
glDisableVertexAttribArray = libGLESv2.NewProc("glDisableVertexAttribArray")
glDrawArrays = libGLESv2.NewProc("glDrawArrays")
glDrawElements = libGLESv2.NewProc("glDrawElements")
glEnable = libGLESv2.NewProc("glEnable")
glEnableVertexAttribArray = libGLESv2.NewProc("glEnableVertexAttribArray")
glFinish = libGLESv2.NewProc("glFinish")
glFlush = libGLESv2.NewProc("glFlush")
glFramebufferRenderbuffer = libGLESv2.NewProc("glFramebufferRenderbuffer")
glFramebufferTexture2D = libGLESv2.NewProc("glFramebufferTexture2D")
glFrontFace = libGLESv2.NewProc("glFrontFace")
glGenBuffers = libGLESv2.NewProc("glGenBuffers")
glGenFramebuffers = libGLESv2.NewProc("glGenFramebuffers")
glGenRenderbuffers = libGLESv2.NewProc("glGenRenderbuffers")
glGenTextures = libGLESv2.NewProc("glGenTextures")
glGenVertexArrays = libGLESv2.NewProc("glGenVertexArrays")
glGenerateMipmap = libGLESv2.NewProc("glGenerateMipmap")
glGetActiveAttrib = libGLESv2.NewProc("glGetActiveAttrib")
glGetActiveUniform = libGLESv2.NewProc("glGetActiveUniform")
glGetAttachedShaders = libGLESv2.NewProc("glGetAttachedShaders")
glGetAttribLocation = libGLESv2.NewProc("glGetAttribLocation")
glGetBooleanv = libGLESv2.NewProc("glGetBooleanv")
glGetBufferParameteri = libGLESv2.NewProc("glGetBufferParameteri")
glGetError = libGLESv2.NewProc("glGetError")
glGetFloatv = libGLESv2.NewProc("glGetFloatv")
glGetFramebufferAttachmentParameteriv = libGLESv2.NewProc("glGetFramebufferAttachmentParameteriv")
glGetIntegerv = libGLESv2.NewProc("glGetIntegerv")
glGetProgramInfoLog = libGLESv2.NewProc("glGetProgramInfoLog")
glGetProgramiv = libGLESv2.NewProc("glGetProgramiv")
glGetRenderbufferParameteriv = libGLESv2.NewProc("glGetRenderbufferParameteriv")
glGetShaderInfoLog = libGLESv2.NewProc("glGetShaderInfoLog")
glGetShaderPrecisionFormat = libGLESv2.NewProc("glGetShaderPrecisionFormat")
glGetShaderSource = libGLESv2.NewProc("glGetShaderSource")
glGetShaderiv = libGLESv2.NewProc("glGetShaderiv")
glGetString = libGLESv2.NewProc("glGetString")
glGetTexParameterfv = libGLESv2.NewProc("glGetTexParameterfv")
glGetTexParameteriv = libGLESv2.NewProc("glGetTexParameteriv")
glGetUniformLocation = libGLESv2.NewProc("glGetUniformLocation")
glGetUniformfv = libGLESv2.NewProc("glGetUniformfv")
glGetUniformiv = libGLESv2.NewProc("glGetUniformiv")
glGetVertexAttribfv = libGLESv2.NewProc("glGetVertexAttribfv")
glGetVertexAttribiv = libGLESv2.NewProc("glGetVertexAttribiv")
glHint = libGLESv2.NewProc("glHint")
glIsBuffer = libGLESv2.NewProc("glIsBuffer")
glIsEnabled = libGLESv2.NewProc("glIsEnabled")
glIsFramebuffer = libGLESv2.NewProc("glIsFramebuffer")
glIsProgram = libGLESv2.NewProc("glIsProgram")
glIsRenderbuffer = libGLESv2.NewProc("glIsRenderbuffer")
glIsShader = libGLESv2.NewProc("glIsShader")
glIsTexture = libGLESv2.NewProc("glIsTexture")
glLineWidth = libGLESv2.NewProc("glLineWidth")
glLinkProgram = libGLESv2.NewProc("glLinkProgram")
glPixelStorei = libGLESv2.NewProc("glPixelStorei")
glPolygonOffset = libGLESv2.NewProc("glPolygonOffset")
glReadPixels = libGLESv2.NewProc("glReadPixels")
glReleaseShaderCompiler = libGLESv2.NewProc("glReleaseShaderCompiler")
glRenderbufferStorage = libGLESv2.NewProc("glRenderbufferStorage")
glSampleCoverage = libGLESv2.NewProc("glSampleCoverage")
glScissor = libGLESv2.NewProc("glScissor")
glShaderSource = libGLESv2.NewProc("glShaderSource")
glStencilFunc = libGLESv2.NewProc("glStencilFunc")
glStencilFuncSeparate = libGLESv2.NewProc("glStencilFuncSeparate")
glStencilMask = libGLESv2.NewProc("glStencilMask")
glStencilMaskSeparate = libGLESv2.NewProc("glStencilMaskSeparate")
glStencilOp = libGLESv2.NewProc("glStencilOp")
glStencilOpSeparate = libGLESv2.NewProc("glStencilOpSeparate")
glTexImage2D = libGLESv2.NewProc("glTexImage2D")
glTexParameterf = libGLESv2.NewProc("glTexParameterf")
glTexParameterfv = libGLESv2.NewProc("glTexParameterfv")
glTexParameteri = libGLESv2.NewProc("glTexParameteri")
glTexParameteriv = libGLESv2.NewProc("glTexParameteriv")
glTexSubImage2D = libGLESv2.NewProc("glTexSubImage2D")
glUniform1f = libGLESv2.NewProc("glUniform1f")
glUniform1fv = libGLESv2.NewProc("glUniform1fv")
glUniform1i = libGLESv2.NewProc("glUniform1i")
glUniform1iv = libGLESv2.NewProc("glUniform1iv")
glUniform2f = libGLESv2.NewProc("glUniform2f")
glUniform2fv = libGLESv2.NewProc("glUniform2fv")
glUniform2i = libGLESv2.NewProc("glUniform2i")
glUniform2iv = libGLESv2.NewProc("glUniform2iv")
glUniform3f = libGLESv2.NewProc("glUniform3f")
glUniform3fv = libGLESv2.NewProc("glUniform3fv")
glUniform3i = libGLESv2.NewProc("glUniform3i")
glUniform3iv = libGLESv2.NewProc("glUniform3iv")
glUniform4f = libGLESv2.NewProc("glUniform4f")
glUniform4fv = libGLESv2.NewProc("glUniform4fv")
glUniform4i = libGLESv2.NewProc("glUniform4i")
glUniform4iv = libGLESv2.NewProc("glUniform4iv")
glUniformMatrix2fv = libGLESv2.NewProc("glUniformMatrix2fv")
glUniformMatrix3fv = libGLESv2.NewProc("glUniformMatrix3fv")
glUniformMatrix4fv = libGLESv2.NewProc("glUniformMatrix4fv")
glUseProgram = libGLESv2.NewProc("glUseProgram")
glValidateProgram = libGLESv2.NewProc("glValidateProgram")
glVertexAttrib1f = libGLESv2.NewProc("glVertexAttrib1f")
glVertexAttrib1fv = libGLESv2.NewProc("glVertexAttrib1fv")
glVertexAttrib2f = libGLESv2.NewProc("glVertexAttrib2f")
glVertexAttrib2fv = libGLESv2.NewProc("glVertexAttrib2fv")
glVertexAttrib3f = libGLESv2.NewProc("glVertexAttrib3f")
glVertexAttrib3fv = libGLESv2.NewProc("glVertexAttrib3fv")
glVertexAttrib4f = libGLESv2.NewProc("glVertexAttrib4f")
glVertexAttrib4fv = libGLESv2.NewProc("glVertexAttrib4fv")
glVertexAttribPointer = libGLESv2.NewProc("glVertexAttribPointer")
glViewport = libGLESv2.NewProc("glViewport")
)
+9
View File
@@ -0,0 +1,9 @@
// Copyright 2016 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.
#include "textflag.h"
// fixFloat is unnecessary for windows/386
TEXT ·fixFloat(SB),NOSPLIT,$0-16
RET
+12
View File
@@ -0,0 +1,12 @@
// 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.
#include "textflag.h"
TEXT ·fixFloat(SB),NOSPLIT,$0-32
MOVQ x0+0(FP), X0
MOVQ x1+8(FP), X1
MOVQ x2+16(FP), X2
MOVQ x3+24(FP), X3
RET
@@ -0,0 +1,124 @@
// 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.
package mobileinit
/*
#include <jni.h>
#include <stdlib.h>
static char* lockJNI(JavaVM *vm, uintptr_t* envp, int* attachedp) {
JNIEnv* env;
if (vm == NULL) {
return "no current JVM";
}
*attachedp = 0;
switch ((*vm)->GetEnv(vm, (void**)&env, JNI_VERSION_1_6)) {
case JNI_OK:
break;
case JNI_EDETACHED:
if ((*vm)->AttachCurrentThread(vm, &env, 0) != 0) {
return "cannot attach to JVM";
}
*attachedp = 1;
break;
case JNI_EVERSION:
return "bad JNI version";
default:
return "unknown JNI error from GetEnv";
}
*envp = (uintptr_t)env;
return NULL;
}
static char* checkException(uintptr_t jnienv) {
jthrowable exc;
JNIEnv* env = (JNIEnv*)jnienv;
if (!(*env)->ExceptionCheck(env)) {
return NULL;
}
exc = (*env)->ExceptionOccurred(env);
(*env)->ExceptionClear(env);
jclass clazz = (*env)->FindClass(env, "java/lang/Throwable");
jmethodID toString = (*env)->GetMethodID(env, clazz, "toString", "()Ljava/lang/String;");
jobject msgStr = (*env)->CallObjectMethod(env, exc, toString);
return (char*)(*env)->GetStringUTFChars(env, msgStr, 0);
}
static void unlockJNI(JavaVM *vm) {
(*vm)->DetachCurrentThread(vm);
}
*/
import "C"
import (
"errors"
"runtime"
"unsafe"
)
// currentVM is stored to initialize other cgo packages.
//
// As all the Go packages in a program form a single shared library,
// there can only be one JNI_OnLoad function for initialization. In
// OpenJDK there is JNI_GetCreatedJavaVMs, but this is not available
// on android.
var currentVM *C.JavaVM
// currentCtx is Android's android.context.Context. May be NULL.
var currentCtx C.jobject
// SetCurrentContext populates the global Context object with the specified
// current JavaVM instance (vm) and android.context.Context object (ctx).
// The android.context.Context object must be a global reference.
func SetCurrentContext(vm unsafe.Pointer, ctx uintptr) {
currentVM = (*C.JavaVM)(vm)
currentCtx = (C.jobject)(ctx)
}
// RunOnJVM runs fn on a new goroutine locked to an OS thread with a JNIEnv.
//
// RunOnJVM blocks until the call to fn is complete. Any Java
// exception or failure to attach to the JVM is returned as an error.
//
// The function fn takes vm, the current JavaVM*,
// env, the current JNIEnv*, and
// ctx, a jobject representing the global android.context.Context.
func RunOnJVM(fn func(vm, env, ctx uintptr) error) error {
errch := make(chan error)
go func() {
runtime.LockOSThread()
defer runtime.UnlockOSThread()
env := C.uintptr_t(0)
attached := C.int(0)
if errStr := C.lockJNI(currentVM, &env, &attached); errStr != nil {
errch <- errors.New(C.GoString(errStr))
return
}
if attached != 0 {
defer C.unlockJNI(currentVM)
}
vm := uintptr(unsafe.Pointer(currentVM))
if err := fn(vm, uintptr(env), uintptr(currentCtx)); err != nil {
errch <- err
return
}
if exc := C.checkException(env); exc != nil {
errch <- errors.New(C.GoString(exc))
C.free(unsafe.Pointer(exc))
return
}
errch <- nil
}()
return <-errch
}
@@ -0,0 +1,11 @@
// 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.
// Package mobileinit contains common initialization logic for mobile platforms
// that is relevant to both all-Go apps and gobind-based apps.
//
// Long-term, some code in this package should consider moving into Go stdlib.
package mobileinit
import "C"
@@ -0,0 +1,93 @@
// Copyright 2014 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.
package mobileinit
/*
To view the log output run:
adb logcat GoLog:I *:S
*/
// Android redirects stdout and stderr to /dev/null.
// As these are common debugging utilities in Go,
// we redirect them to logcat.
//
// Unfortunately, logcat is line oriented, so we must buffer.
/*
#cgo LDFLAGS: -landroid -llog
#include <android/log.h>
#include <stdlib.h>
#include <string.h>
*/
import "C"
import (
"bufio"
"log"
"os"
"syscall"
"unsafe"
)
var (
ctag = C.CString("GoLog")
// Store the writer end of the redirected stderr and stdout
// so that they are not garbage collected and closed.
stderr, stdout *os.File
)
type infoWriter struct{}
func (infoWriter) Write(p []byte) (n int, err error) {
cstr := C.CString(string(p))
C.__android_log_write(C.ANDROID_LOG_INFO, ctag, cstr)
C.free(unsafe.Pointer(cstr))
return len(p), nil
}
func lineLog(f *os.File, priority C.int) {
const logSize = 1024 // matches android/log.h.
r := bufio.NewReaderSize(f, logSize)
for {
line, _, err := r.ReadLine()
str := string(line)
if err != nil {
str += " " + err.Error()
}
cstr := C.CString(str)
C.__android_log_write(priority, ctag, cstr)
C.free(unsafe.Pointer(cstr))
if err != nil {
break
}
}
}
func init() {
log.SetOutput(infoWriter{})
// android logcat includes all of log.LstdFlags
log.SetFlags(log.Flags() &^ log.LstdFlags)
r, w, err := os.Pipe()
if err != nil {
panic(err)
}
stderr = w
if err := syscall.Dup3(int(w.Fd()), int(os.Stderr.Fd()), 0); err != nil {
panic(err)
}
go lineLog(r, C.ANDROID_LOG_ERROR)
r, w, err = os.Pipe()
if err != nil {
panic(err)
}
stdout = w
if err := syscall.Dup3(int(w.Fd()), int(os.Stdout.Fd()), 0); err != nil {
panic(err)
}
go lineLog(r, C.ANDROID_LOG_INFO)
}
@@ -0,0 +1,41 @@
// 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.
package mobileinit
import (
"io"
"log"
"os"
"unsafe"
)
/*
#include <stdlib.h>
#include <os/log.h>
os_log_t create_os_log() {
return os_log_create("org.golang.mobile", "os_log");
}
void os_log_wrap(os_log_t log, const char *str) {
os_log(log, "%s", str);
}
*/
import "C"
type osWriter struct {
w C.os_log_t
}
func (o osWriter) Write(p []byte) (n int, err error) {
cstr := C.CString(string(p))
C.os_log_wrap(o.w, cstr)
C.free(unsafe.Pointer(cstr))
return len(p), nil
}
func init() {
log.SetOutput(io.MultiWriter(os.Stderr, osWriter{C.create_os_log()}))
}
+1
View File
@@ -0,0 +1 @@
*~
+201
View File
@@ -0,0 +1,201 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "{}"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright {yyyy} {name of copyright owner}
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
+15
View File
@@ -0,0 +1,15 @@
# HideConsole
Package hideconsole is a utility package to hide a console automatically even without `-ldflags "-Hwindowsgui"` on Windows.
On non-Windows, this package does nothing.
## Usage
Import this package with a blank identifier.
```go
import (
_ "github.com/ebitengine/hideconsole"
)
```
+16
View File
@@ -0,0 +1,16 @@
// SPDX-License-Identifier: Apache-2.0
// SPDX-FileCopyrightText: 2024 The Ebitengine Authors
// Package hideconsole is a utility package to hide a console automatically
// even without `-ldflags "-Hwindowsgui"` on Windows.
//
// On non-Windows, this package does nothing.
//
// # Usage
//
// Import this package with a blank identifier:
//
// import (
// _ "github.com/ebitengine/hideconsole"
// )
package hideconsole
+63
View File
@@ -0,0 +1,63 @@
// SPDX-License-Identifier: Apache-2.0
// SPDX-FileCopyrightText: 2024 The Ebitengine Authors
package hideconsole
import (
"fmt"
"golang.org/x/sys/windows"
)
var (
kernel32 = windows.NewLazySystemDLL("kernel32.dll")
user32 = windows.NewLazySystemDLL("user32.dll")
procFreeConsoleWindow = kernel32.NewProc("FreeConsole")
procGetConsoleWindow = kernel32.NewProc("GetConsoleWindow")
)
func freeConsole() error {
r, _, e := procFreeConsoleWindow.Call()
if int32(r) == 0 {
if e != nil && e != windows.ERROR_SUCCESS {
return fmt.Errorf("FreeConsole failed: %w", e)
}
return fmt.Errorf("FreeConsole returned 0")
}
return nil
}
func getConsoleWindow() windows.HWND {
r, _, _ := procGetConsoleWindow.Call()
return windows.HWND(r)
}
// hideConsoleWindow will hide the console window that is showing when
// compiling on Windows without specifying the '-ldflags "-Hwindowsgui"' flag.
func hideConsoleWindow() {
// In Xbox, GetWindowThreadProcessId might not exist.
if user32.NewProc("GetWindowThreadProcessId").Find() != nil {
return
}
pid := windows.GetCurrentProcessId()
// Get the process ID of the console's creator.
var cpid uint32
if _, err := windows.GetWindowThreadProcessId(getConsoleWindow(), &cpid); err != nil {
// Even if closing the console fails, this is not harmful.
// Ignore error.
return
}
if pid == cpid {
// The current process created its own console. Hide this.
// Ignore error.
_ = freeConsole()
}
}
func init() {
hideConsoleWindow()
}
+1
View File
@@ -0,0 +1 @@
*~
+201
View File
@@ -0,0 +1,201 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "{}"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright {yyyy} {name of copyright owner}
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
+96
View File
@@ -0,0 +1,96 @@
# purego
[![Go Reference](https://pkg.go.dev/badge/github.com/ebitengine/purego?GOOS=darwin.svg)](https://pkg.go.dev/github.com/ebitengine/purego?GOOS=darwin)
A library for calling C functions from Go without Cgo.
> This is beta software so expect bugs and potentially API breaking changes
> but each release will be tagged to avoid breaking people's code.
> Bug reports are encouraged.
## Motivation
The [Ebitengine](https://github.com/hajimehoshi/ebiten) game engine was ported to use only Go on Windows. This enabled
cross-compiling to Windows from any other operating system simply by setting `GOOS=windows`. The purego project was
born to bring that same vision to the other platforms supported by Ebitengine.
## Benefits
- **Simple Cross-Compilation**: No C means you can build for other platforms easily without a C compiler.
- **Faster Compilation**: Efficiently cache your entirely Go builds.
- **Smaller Binaries**: Using Cgo generates a C wrapper function for each C function called. Purego doesn't!
- **Dynamic Linking**: Load symbols at runtime and use it as a plugin system.
- **Foreign Function Interface**: Call into other languages that are compiled into shared objects.
- **Cgo Fallback**: Works even with CGO_ENABLED=1 so incremental porting is possible.
This also means unsupported GOARCHs (freebsd/riscv64, linux/mips, etc.) will still work
except for float arguments and return values.
## Supported Platforms
- **FreeBSD**: amd64, arm64
- **Linux**: amd64, arm64
- **macOS / iOS**: amd64, arm64
- **Windows**: 386*, amd64, arm*, arm64
`*` These architectures only support SyscallN and NewCallback
## Example
This example only works on macOS and Linux. For a complete example look at [libc](https://github.com/ebitengine/purego/tree/main/examples/libc) which supports Windows and FreeBSD.
```go
package main
import (
"fmt"
"runtime"
"github.com/ebitengine/purego"
)
func getSystemLibrary() string {
switch runtime.GOOS {
case "darwin":
return "/usr/lib/libSystem.B.dylib"
case "linux":
return "libc.so.6"
default:
panic(fmt.Errorf("GOOS=%s is not supported", runtime.GOOS))
}
}
func main() {
libc, err := purego.Dlopen(getSystemLibrary(), purego.RTLD_NOW|purego.RTLD_GLOBAL)
if err != nil {
panic(err)
}
var puts func(string)
purego.RegisterLibFunc(&puts, libc, "puts")
puts("Calling C from Go without Cgo!")
}
```
Then to run: `CGO_ENABLED=0 go run main.go`
## Questions
If you have questions about how to incorporate purego in your project or want to discuss
how it works join the [Discord](https://discord.com/channels/842049801528016967/1123106378731487345)!
### External Code
Purego uses code that originates from the Go runtime. These files are under the BSD-3
License that can be found [in the Go Source](https://github.com/golang/go/blob/master/LICENSE).
This is a list of the copied files:
* `abi_*.h` from package `runtime/cgo`
* `zcallback_darwin_*.s` from package `runtime`
* `internal/fakecgo/abi_*.h` from package `runtime/cgo`
* `internal/fakecgo/asm_GOARCH.s` from package `runtime/cgo`
* `internal/fakecgo/callbacks.go` from package `runtime/cgo`
* `internal/fakecgo/go_GOOS_GOARCH.go` from package `runtime/cgo`
* `internal/fakecgo/iscgo.go` from package `runtime/cgo`
* `internal/fakecgo/setenv.go` from package `runtime/cgo`
* `internal/fakecgo/freebsd.go` from package `runtime/cgo`
The files `abi_*.h` and `internal/fakecgo/abi_*.h` are the same because Bazel does not support cross-package use of
`#include` so we need each one once per package. (cf. [issue](https://github.com/bazelbuild/rules_go/issues/3636))
+99
View File
@@ -0,0 +1,99 @@
// Copyright 2021 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.
// Macros for transitioning from the host ABI to Go ABI0.
//
// These save the frame pointer, so in general, functions that use
// these should have zero frame size to suppress the automatic frame
// pointer, though it's harmless to not do this.
#ifdef GOOS_windows
// REGS_HOST_TO_ABI0_STACK is the stack bytes used by
// PUSH_REGS_HOST_TO_ABI0.
#define REGS_HOST_TO_ABI0_STACK (28*8 + 8)
// PUSH_REGS_HOST_TO_ABI0 prepares for transitioning from
// the host ABI to Go ABI0 code. It saves all registers that are
// callee-save in the host ABI and caller-save in Go ABI0 and prepares
// for entry to Go.
//
// Save DI SI BP BX R12 R13 R14 R15 X6-X15 registers and the DF flag.
// Clear the DF flag for the Go ABI.
// MXCSR matches the Go ABI, so we don't have to set that,
// and Go doesn't modify it, so we don't have to save it.
#define PUSH_REGS_HOST_TO_ABI0() \
PUSHFQ \
CLD \
ADJSP $(REGS_HOST_TO_ABI0_STACK - 8) \
MOVQ DI, (0*0)(SP) \
MOVQ SI, (1*8)(SP) \
MOVQ BP, (2*8)(SP) \
MOVQ BX, (3*8)(SP) \
MOVQ R12, (4*8)(SP) \
MOVQ R13, (5*8)(SP) \
MOVQ R14, (6*8)(SP) \
MOVQ R15, (7*8)(SP) \
MOVUPS X6, (8*8)(SP) \
MOVUPS X7, (10*8)(SP) \
MOVUPS X8, (12*8)(SP) \
MOVUPS X9, (14*8)(SP) \
MOVUPS X10, (16*8)(SP) \
MOVUPS X11, (18*8)(SP) \
MOVUPS X12, (20*8)(SP) \
MOVUPS X13, (22*8)(SP) \
MOVUPS X14, (24*8)(SP) \
MOVUPS X15, (26*8)(SP)
#define POP_REGS_HOST_TO_ABI0() \
MOVQ (0*0)(SP), DI \
MOVQ (1*8)(SP), SI \
MOVQ (2*8)(SP), BP \
MOVQ (3*8)(SP), BX \
MOVQ (4*8)(SP), R12 \
MOVQ (5*8)(SP), R13 \
MOVQ (6*8)(SP), R14 \
MOVQ (7*8)(SP), R15 \
MOVUPS (8*8)(SP), X6 \
MOVUPS (10*8)(SP), X7 \
MOVUPS (12*8)(SP), X8 \
MOVUPS (14*8)(SP), X9 \
MOVUPS (16*8)(SP), X10 \
MOVUPS (18*8)(SP), X11 \
MOVUPS (20*8)(SP), X12 \
MOVUPS (22*8)(SP), X13 \
MOVUPS (24*8)(SP), X14 \
MOVUPS (26*8)(SP), X15 \
ADJSP $-(REGS_HOST_TO_ABI0_STACK - 8) \
POPFQ
#else
// SysV ABI
#define REGS_HOST_TO_ABI0_STACK (6*8)
// SysV MXCSR matches the Go ABI, so we don't have to set that,
// and Go doesn't modify it, so we don't have to save it.
// Both SysV and Go require DF to be cleared, so that's already clear.
// The SysV and Go frame pointer conventions are compatible.
#define PUSH_REGS_HOST_TO_ABI0() \
ADJSP $(REGS_HOST_TO_ABI0_STACK) \
MOVQ BP, (5*8)(SP) \
LEAQ (5*8)(SP), BP \
MOVQ BX, (0*8)(SP) \
MOVQ R12, (1*8)(SP) \
MOVQ R13, (2*8)(SP) \
MOVQ R14, (3*8)(SP) \
MOVQ R15, (4*8)(SP)
#define POP_REGS_HOST_TO_ABI0() \
MOVQ (0*8)(SP), BX \
MOVQ (1*8)(SP), R12 \
MOVQ (2*8)(SP), R13 \
MOVQ (3*8)(SP), R14 \
MOVQ (4*8)(SP), R15 \
MOVQ (5*8)(SP), BP \
ADJSP $-(REGS_HOST_TO_ABI0_STACK)
#endif
+39
View File
@@ -0,0 +1,39 @@
// Copyright 2021 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.
// Macros for transitioning from the host ABI to Go ABI0.
//
// These macros save and restore the callee-saved registers
// from the stack, but they don't adjust stack pointer, so
// the user should prepare stack space in advance.
// SAVE_R19_TO_R28(offset) saves R19 ~ R28 to the stack space
// of ((offset)+0*8)(RSP) ~ ((offset)+9*8)(RSP).
//
// SAVE_F8_TO_F15(offset) saves F8 ~ F15 to the stack space
// of ((offset)+0*8)(RSP) ~ ((offset)+7*8)(RSP).
//
// R29 is not saved because Go will save and restore it.
#define SAVE_R19_TO_R28(offset) \
STP (R19, R20), ((offset)+0*8)(RSP) \
STP (R21, R22), ((offset)+2*8)(RSP) \
STP (R23, R24), ((offset)+4*8)(RSP) \
STP (R25, R26), ((offset)+6*8)(RSP) \
STP (R27, g), ((offset)+8*8)(RSP)
#define RESTORE_R19_TO_R28(offset) \
LDP ((offset)+0*8)(RSP), (R19, R20) \
LDP ((offset)+2*8)(RSP), (R21, R22) \
LDP ((offset)+4*8)(RSP), (R23, R24) \
LDP ((offset)+6*8)(RSP), (R25, R26) \
LDP ((offset)+8*8)(RSP), (R27, g) /* R28 */
#define SAVE_F8_TO_F15(offset) \
FSTPD (F8, F9), ((offset)+0*8)(RSP) \
FSTPD (F10, F11), ((offset)+2*8)(RSP) \
FSTPD (F12, F13), ((offset)+4*8)(RSP) \
FSTPD (F14, F15), ((offset)+6*8)(RSP)
#define RESTORE_F8_TO_F15(offset) \
FLDPD ((offset)+0*8)(RSP), (F8, F9) \
FLDPD ((offset)+2*8)(RSP), (F10, F11) \
FLDPD ((offset)+4*8)(RSP), (F12, F13) \
FLDPD ((offset)+6*8)(RSP), (F14, F15)
+19
View File
@@ -0,0 +1,19 @@
// SPDX-License-Identifier: Apache-2.0
// SPDX-FileCopyrightText: 2022 The Ebitengine Authors
//go:build cgo && (darwin || freebsd || linux)
package purego
// if CGO_ENABLED=1 import the Cgo runtime to ensure that it is set up properly.
// This is required since some frameworks need TLS setup the C way which Go doesn't do.
// We currently don't support ios in fakecgo mode so force Cgo or fail
// Even if CGO_ENABLED=1 the Cgo runtime is not imported unless `import "C"` is used.
// which will import this package automatically. Normally this isn't an issue since it
// usually isn't possible to call into C without using that import. However, with purego
// it is since we don't use `import "C"`!
import (
_ "runtime/cgo"
_ "github.com/ebitengine/purego/internal/cgo"
)
+15
View File
@@ -0,0 +1,15 @@
// SPDX-License-Identifier: Apache-2.0
// SPDX-FileCopyrightText: 2023 The Ebitengine Authors
//go:build darwin || freebsd || linux
package purego
// Dlerror represents an error value returned from Dlopen, Dlsym, or Dlclose.
type Dlerror struct {
s string
}
func (e Dlerror) Error() string {
return e.s
}
+94
View File
@@ -0,0 +1,94 @@
// SPDX-License-Identifier: Apache-2.0
// SPDX-FileCopyrightText: 2022 The Ebitengine Authors
//go:build darwin || freebsd || linux
package purego
import (
"unsafe"
)
// Unix Specification for dlfcn.h: https://pubs.opengroup.org/onlinepubs/7908799/xsh/dlfcn.h.html
var (
fnDlopen func(path string, mode int) uintptr
fnDlsym func(handle uintptr, name string) uintptr
fnDlerror func() string
fnDlclose func(handle uintptr) bool
)
func init() {
RegisterFunc(&fnDlopen, dlopenABI0)
RegisterFunc(&fnDlsym, dlsymABI0)
RegisterFunc(&fnDlerror, dlerrorABI0)
RegisterFunc(&fnDlclose, dlcloseABI0)
}
// Dlopen examines the dynamic library or bundle file specified by path. If the file is compatible
// with the current process and has not already been loaded into the
// current process, it is loaded and linked. After being linked, if it contains
// any initializer functions, they are called, before Dlopen
// returns. It returns a handle that can be used with Dlsym and Dlclose.
// A second call to Dlopen with the same path will return the same handle, but the internal
// reference count for the handle will be incremented. Therefore, all
// Dlopen calls should be balanced with a Dlclose call.
func Dlopen(path string, mode int) (uintptr, error) {
u := fnDlopen(path, mode)
if u == 0 {
return 0, Dlerror{fnDlerror()}
}
return u, nil
}
// Dlsym takes a "handle" of a dynamic library returned by Dlopen and the symbol name.
// It returns the address where that symbol is loaded into memory. If the symbol is not found,
// in the specified library or any of the libraries that were automatically loaded by Dlopen
// when that library was loaded, Dlsym returns zero.
func Dlsym(handle uintptr, name string) (uintptr, error) {
u := fnDlsym(handle, name)
if u == 0 {
return 0, Dlerror{fnDlerror()}
}
return u, nil
}
// Dlclose decrements the reference count on the dynamic library handle.
// If the reference count drops to zero and no other loaded libraries
// use symbols in it, then the dynamic library is unloaded.
func Dlclose(handle uintptr) error {
if fnDlclose(handle) {
return Dlerror{fnDlerror()}
}
return nil
}
//go:linkname openLibrary openLibrary
func openLibrary(name string) (uintptr, error) {
return Dlopen(name, RTLD_NOW|RTLD_GLOBAL)
}
func loadSymbol(handle uintptr, name string) (uintptr, error) {
return Dlsym(handle, name)
}
// these functions exist in dlfcn_stubs.s and are calling C functions linked to in dlfcn_GOOS.go
// the indirection is necessary because a function is actually a pointer to the pointer to the code.
// sadly, I do not know of anyway to remove the assembly stubs entirely because //go:linkname doesn't
// appear to work if you link directly to the C function on darwin arm64.
//go:linkname dlopen dlopen
var dlopen uintptr
var dlopenABI0 = uintptr(unsafe.Pointer(&dlopen))
//go:linkname dlsym dlsym
var dlsym uintptr
var dlsymABI0 = uintptr(unsafe.Pointer(&dlsym))
//go:linkname dlclose dlclose
var dlclose uintptr
var dlcloseABI0 = uintptr(unsafe.Pointer(&dlclose))
//go:linkname dlerror dlerror
var dlerror uintptr
var dlerrorABI0 = uintptr(unsafe.Pointer(&dlerror))
+24
View File
@@ -0,0 +1,24 @@
// SPDX-License-Identifier: Apache-2.0
// SPDX-FileCopyrightText: 2022 The Ebitengine Authors
package purego
// Source for constants: https://opensource.apple.com/source/dyld/dyld-360.14/include/dlfcn.h.auto.html
const (
RTLD_DEFAULT = ^uintptr(0) - 1 // Pseudo-handle for dlsym so search for any loaded symbol
RTLD_LAZY = 0x1 // Relocations are performed at an implementation-dependent time.
RTLD_NOW = 0x2 // Relocations are performed when the object is loaded.
RTLD_LOCAL = 0x4 // All symbols are not made available for relocation processing by other modules.
RTLD_GLOBAL = 0x8 // All symbols are available for relocation processing of other modules.
)
//go:cgo_import_dynamic purego_dlopen dlopen "/usr/lib/libSystem.B.dylib"
//go:cgo_import_dynamic purego_dlsym dlsym "/usr/lib/libSystem.B.dylib"
//go:cgo_import_dynamic purego_dlerror dlerror "/usr/lib/libSystem.B.dylib"
//go:cgo_import_dynamic purego_dlclose dlclose "/usr/lib/libSystem.B.dylib"
//go:cgo_import_dynamic purego_dlopen dlopen "/usr/lib/libSystem.B.dylib"
//go:cgo_import_dynamic purego_dlsym dlsym "/usr/lib/libSystem.B.dylib"
//go:cgo_import_dynamic purego_dlerror dlerror "/usr/lib/libSystem.B.dylib"
//go:cgo_import_dynamic purego_dlclose dlclose "/usr/lib/libSystem.B.dylib"
+13
View File
@@ -0,0 +1,13 @@
// SPDX-License-Identifier: Apache-2.0
// SPDX-FileCopyrightText: 2022 The Ebitengine Authors
package purego
// Constants as defined in https://github.com/freebsd/freebsd-src/blob/main/include/dlfcn.h
const (
RTLD_DEFAULT = ^uintptr(0) - 2 // Pseudo-handle for dlsym so search for any loaded symbol
RTLD_LAZY = 0x00001 // Relocations are performed at an implementation-dependent time.
RTLD_NOW = 0x00002 // Relocations are performed when the object is loaded.
RTLD_LOCAL = 0x00000 // All symbols are not made available for relocation processing by other modules.
RTLD_GLOBAL = 0x00100 // All symbols are available for relocation processing of other modules.
)
+14
View File
@@ -0,0 +1,14 @@
// SPDX-License-Identifier: Apache-2.0
// SPDX-FileCopyrightText: 2022 The Ebitengine Authors
package purego
// Source for constants: https://codebrowser.dev/glibc/glibc/bits/dlfcn.h.html
const (
RTLD_DEFAULT = 0x00000 // Pseudo-handle for dlsym so search for any loaded symbol
RTLD_LAZY = 0x00001 // Relocations are performed at an implementation-dependent time.
RTLD_NOW = 0x00002 // Relocations are performed when the object is loaded.
RTLD_LOCAL = 0x00000 // All symbols are not made available for relocation processing by other modules.
RTLD_GLOBAL = 0x00100 // All symbols are available for relocation processing of other modules.
)
+11
View File
@@ -0,0 +1,11 @@
// SPDX-License-Identifier: Apache-2.0
// SPDX-FileCopyrightText: 2022 The Ebitengine Authors
//go:build !cgo
package purego
//go:cgo_import_dynamic purego_dlopen dlopen "libc.so.7"
//go:cgo_import_dynamic purego_dlsym dlsym "libc.so.7"
//go:cgo_import_dynamic purego_dlerror dlerror "libc.so.7"
//go:cgo_import_dynamic purego_dlclose dlclose "libc.so.7"
+19
View File
@@ -0,0 +1,19 @@
// SPDX-License-Identifier: Apache-2.0
// SPDX-FileCopyrightText: 2022 The Ebitengine Authors
//go:build !cgo
package purego
// if there is no Cgo we must link to each of the functions from dlfcn.h
// then the functions are called inside dlfcn_stubs.s
//go:cgo_import_dynamic purego_dlopen dlopen "libdl.so.2"
//go:cgo_import_dynamic purego_dlsym dlsym "libdl.so.2"
//go:cgo_import_dynamic purego_dlerror dlerror "libdl.so.2"
//go:cgo_import_dynamic purego_dlclose dlclose "libdl.so.2"
// on amd64 we don't need the following line - on 386 we do...
// anyway - with those lines the output is better (but doesn't matter) - without it on amd64 we get multiple DT_NEEDED with "libc.so.6" etc
//go:cgo_import_dynamic _ _ "libdl.so.2"
+26
View File
@@ -0,0 +1,26 @@
// SPDX-License-Identifier: Apache-2.0
// SPDX-FileCopyrightText: 2022 The Ebitengine Authors
//go:build darwin || !cgo && (freebsd || linux)
#include "textflag.h"
// func dlopen(path *byte, mode int) (ret uintptr)
TEXT dlopen(SB), NOSPLIT|NOFRAME, $0-0
JMP purego_dlopen(SB)
RET
// func dlsym(handle uintptr, symbol *byte) (ret uintptr)
TEXT dlsym(SB), NOSPLIT|NOFRAME, $0-0
JMP purego_dlsym(SB)
RET
// func dlerror() (ret *byte)
TEXT dlerror(SB), NOSPLIT|NOFRAME, $0-0
JMP purego_dlerror(SB)
RET
// func dlclose(handle uintptr) (ret int)
TEXT dlclose(SB), NOSPLIT|NOFRAME, $0-0
JMP purego_dlclose(SB)
RET
+409
View File
@@ -0,0 +1,409 @@
// SPDX-License-Identifier: Apache-2.0
// SPDX-FileCopyrightText: 2022 The Ebitengine Authors
//go:build darwin || freebsd || linux || windows
package purego
import (
"fmt"
"math"
"reflect"
"runtime"
"unsafe"
"github.com/ebitengine/purego/internal/strings"
)
// RegisterLibFunc is a wrapper around RegisterFunc that uses the C function returned from Dlsym(handle, name).
// It panics if it can't find the name symbol.
func RegisterLibFunc(fptr interface{}, handle uintptr, name string) {
sym, err := loadSymbol(handle, name)
if err != nil {
panic(err)
}
RegisterFunc(fptr, sym)
}
// RegisterFunc takes a pointer to a Go function representing the calling convention of the C function.
// fptr will be set to a function that when called will call the C function given by cfn with the
// parameters passed in the correct registers and stack.
//
// A panic is produced if the type is not a function pointer or if the function returns more than 1 value.
//
// These conversions describe how a Go type in the fptr will be used to call
// the C function. It is important to note that there is no way to verify that fptr
// matches the C function. This also holds true for struct types where the padding
// needs to be ensured to match that of C; RegisterFunc does not verify this.
//
// # Type Conversions (Go <=> C)
//
// string <=> char*
// bool <=> _Bool
// uintptr <=> uintptr_t
// uint <=> uint32_t or uint64_t
// uint8 <=> uint8_t
// uint16 <=> uint16_t
// uint32 <=> uint32_t
// uint64 <=> uint64_t
// int <=> int32_t or int64_t
// int8 <=> int8_t
// int16 <=> int16_t
// int32 <=> int32_t
// int64 <=> int64_t
// float32 <=> float
// float64 <=> double
// struct <=> struct (WIP - darwin only)
// func <=> C function
// unsafe.Pointer, *T <=> void*
// []T => void*
//
// There is a special case when the last argument of fptr is a variadic interface (or []interface}
// it will be expanded into a call to the C function as if it had the arguments in that slice.
// This means that using arg ...interface{} is like a cast to the function with the arguments inside arg.
// This is not the same as C variadic.
//
// # Memory
//
// In general it is not possible for purego to guarantee the lifetimes of objects returned or received from
// calling functions using RegisterFunc. For arguments to a C function it is important that the C function doesn't
// hold onto a reference to Go memory. This is the same as the [Cgo rules].
//
// However, there are some special cases. When passing a string as an argument if the string does not end in a null
// terminated byte (\x00) then the string will be copied into memory maintained by purego. The memory is only valid for
// that specific call. Therefore, if the C code keeps a reference to that string it may become invalid at some
// undefined time. However, if the string does already contain a null-terminated byte then no copy is done.
// It is then the responsibility of the caller to ensure the string stays alive as long as it's needed in C memory.
// This can be done using runtime.KeepAlive or allocating the string in C memory using malloc. When a C function
// returns a null-terminated pointer to char a Go string can be used. Purego will allocate a new string in Go memory
// and copy the data over. This string will be garbage collected whenever Go decides it's no longer referenced.
// This C created string will not be freed by purego. If the pointer to char is not null-terminated or must continue
// to point to C memory (because it's a buffer for example) then use a pointer to byte and then convert that to a slice
// using unsafe.Slice. Doing this means that it becomes the responsibility of the caller to care about the lifetime
// of the pointer
//
// # Structs
//
// Purego can handle the most common structs that have fields of builtin types like int8, uint16, float32, etc. However,
// it does not support aligning fields properly. It is therefore the responsibility of the caller to ensure
// that all padding is added to the Go struct to match the C one. See `BoolStructFn` in struct_test.go for an example.
//
// # Example
//
// All functions below call this C function:
//
// char *foo(char *str);
//
// // Let purego convert types
// var foo func(s string) string
// goString := foo("copied")
// // Go will garbage collect this string
//
// // Manually, handle allocations
// var foo2 func(b string) *byte
// mustFree := foo2("not copied\x00")
// defer free(mustFree)
//
// [Cgo rules]: https://pkg.go.dev/cmd/cgo#hdr-Go_references_to_C
func RegisterFunc(fptr interface{}, cfn uintptr) {
fn := reflect.ValueOf(fptr).Elem()
ty := fn.Type()
if ty.Kind() != reflect.Func {
panic("purego: fptr must be a function pointer")
}
if ty.NumOut() > 1 {
panic("purego: function can only return zero or one values")
}
if cfn == 0 {
panic("purego: cfn is nil")
}
{
// this code checks how many registers and stack this function will use
// to avoid crashing with too many arguments
var ints int
var floats int
var stack int
for i := 0; i < ty.NumIn(); i++ {
arg := ty.In(i)
switch arg.Kind() {
case reflect.String, reflect.Uintptr, reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64,
reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Ptr, reflect.UnsafePointer, reflect.Slice,
reflect.Func, reflect.Bool:
if ints < numOfIntegerRegisters() {
ints++
} else {
stack++
}
case reflect.Float32, reflect.Float64:
if is32bit {
panic("purego: floats only supported on 64bit platforms")
}
if floats < numOfFloats {
floats++
} else {
stack++
}
case reflect.Struct:
if runtime.GOOS != "darwin" || (runtime.GOARCH != "amd64" && runtime.GOARCH != "arm64") {
panic("purego: struct arguments are only supported on darwin amd64 & arm64")
}
if arg.Size() == 0 {
continue
}
addInt := func(u uintptr) {
ints++
}
addFloat := func(u uintptr) {
floats++
}
addStack := func(u uintptr) {
stack++
}
_ = addStruct(reflect.New(arg).Elem(), &ints, &floats, &stack, addInt, addFloat, addStack, nil)
default:
panic("purego: unsupported kind " + arg.Kind().String())
}
}
if ty.NumOut() == 1 && ty.Out(0).Kind() == reflect.Struct {
if runtime.GOOS != "darwin" {
panic("purego: struct return values only supported on darwin arm64 & amd64")
}
outType := ty.Out(0)
checkStructFieldsSupported(outType)
if runtime.GOARCH == "amd64" && outType.Size() > maxRegAllocStructSize {
// on amd64 if struct is bigger than 16 bytes allocate the return struct
// and pass it in as a hidden first argument.
ints++
}
}
sizeOfStack := maxArgs - numOfIntegerRegisters()
if stack > sizeOfStack {
panic("purego: too many arguments")
}
}
v := reflect.MakeFunc(ty, func(args []reflect.Value) (results []reflect.Value) {
if len(args) > 0 {
if variadic, ok := args[len(args)-1].Interface().([]interface{}); ok {
// subtract one from args bc the last argument in args is []interface{}
// which we are currently expanding
tmp := make([]reflect.Value, len(args)-1+len(variadic))
n := copy(tmp, args[:len(args)-1])
for i, v := range variadic {
tmp[n+i] = reflect.ValueOf(v)
}
args = tmp
}
}
var sysargs [maxArgs]uintptr
stack := sysargs[numOfIntegerRegisters():]
var floats [numOfFloats]uintptr
var numInts int
var numFloats int
var numStack int
var addStack, addInt, addFloat func(x uintptr)
if runtime.GOARCH == "arm64" || runtime.GOOS != "windows" {
// Windows arm64 uses the same calling convention as macOS and Linux
addStack = func(x uintptr) {
stack[numStack] = x
numStack++
}
addInt = func(x uintptr) {
if numInts >= numOfIntegerRegisters() {
addStack(x)
} else {
sysargs[numInts] = x
numInts++
}
}
addFloat = func(x uintptr) {
if numFloats < len(floats) {
floats[numFloats] = x
numFloats++
} else {
addStack(x)
}
}
} else {
// On Windows amd64 the arguments are passed in the numbered registered.
// So the first int is in the first integer register and the first float
// is in the second floating register if there is already a first int.
// This is in contrast to how macOS and Linux pass arguments which
// tries to use as many registers as possible in the calling convention.
addStack = func(x uintptr) {
sysargs[numStack] = x
numStack++
}
addInt = addStack
addFloat = addStack
}
var keepAlive []interface{}
defer func() {
runtime.KeepAlive(keepAlive)
runtime.KeepAlive(args)
}()
var syscall syscall15Args
if ty.NumOut() == 1 && ty.Out(0).Kind() == reflect.Struct {
outType := ty.Out(0)
if runtime.GOARCH == "amd64" && outType.Size() > maxRegAllocStructSize {
val := reflect.New(outType)
keepAlive = append(keepAlive, val)
addInt(val.Pointer())
} else if runtime.GOARCH == "arm64" && outType.Size() > maxRegAllocStructSize {
if !isAllSameFloat(outType) || outType.NumField() > 4 {
val := reflect.New(outType)
keepAlive = append(keepAlive, val)
syscall.arm64_r8 = val.Pointer()
}
}
}
for _, v := range args {
switch v.Kind() {
case reflect.String:
ptr := strings.CString(v.String())
keepAlive = append(keepAlive, ptr)
addInt(uintptr(unsafe.Pointer(ptr)))
case reflect.Uintptr, reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
addInt(uintptr(v.Uint()))
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
addInt(uintptr(v.Int()))
case reflect.Ptr, reflect.UnsafePointer, reflect.Slice:
// There is no need to keepAlive this pointer separately because it is kept alive in the args variable
addInt(v.Pointer())
case reflect.Func:
addInt(NewCallback(v.Interface()))
case reflect.Bool:
if v.Bool() {
addInt(1)
} else {
addInt(0)
}
case reflect.Float32:
addFloat(uintptr(math.Float32bits(float32(v.Float()))))
case reflect.Float64:
addFloat(uintptr(math.Float64bits(v.Float())))
case reflect.Struct:
keepAlive = addStruct(v, &numInts, &numFloats, &numStack, addInt, addFloat, addStack, keepAlive)
default:
panic("purego: unsupported kind: " + v.Kind().String())
}
}
if runtime.GOARCH == "arm64" || runtime.GOOS != "windows" {
// Use the normal arm64 calling convention even on Windows
syscall = syscall15Args{
cfn,
sysargs[0], sysargs[1], sysargs[2], sysargs[3], sysargs[4], sysargs[5],
sysargs[6], sysargs[7], sysargs[8], sysargs[9], sysargs[10], sysargs[11],
sysargs[12], sysargs[13], sysargs[14],
floats[0], floats[1], floats[2], floats[3], floats[4], floats[5], floats[6], floats[7],
syscall.arm64_r8,
}
runtime_cgocall(syscall15XABI0, unsafe.Pointer(&syscall))
} else {
// This is a fallback for Windows amd64, 386, and arm. Note this may not support floats
syscall.a1, syscall.a2, _ = syscall_syscall15X(cfn, sysargs[0], sysargs[1], sysargs[2], sysargs[3], sysargs[4],
sysargs[5], sysargs[6], sysargs[7], sysargs[8], sysargs[9], sysargs[10], sysargs[11],
sysargs[12], sysargs[13], sysargs[14])
syscall.f1 = syscall.a2 // on amd64 a2 stores the float return. On 32bit platforms floats aren't support
}
if ty.NumOut() == 0 {
return nil
}
outType := ty.Out(0)
v := reflect.New(outType).Elem()
switch outType.Kind() {
case reflect.Uintptr, reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
v.SetUint(uint64(syscall.a1))
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
v.SetInt(int64(syscall.a1))
case reflect.Bool:
v.SetBool(byte(syscall.a1) != 0)
case reflect.UnsafePointer:
// We take the address and then dereference it to trick go vet from creating a possible miss-use of unsafe.Pointer
v.SetPointer(*(*unsafe.Pointer)(unsafe.Pointer(&syscall.a1)))
case reflect.Ptr:
// It is safe to have the address of syscall.r1 not escape because it is immediately dereferenced with .Elem()
v = reflect.NewAt(outType, runtime_noescape(unsafe.Pointer(&syscall.a1))).Elem()
case reflect.Func:
// wrap this C function in a nicely typed Go function
v = reflect.New(outType)
RegisterFunc(v.Interface(), syscall.a1)
case reflect.String:
v.SetString(strings.GoString(syscall.a1))
case reflect.Float32:
// NOTE: syscall.r2 is only the floating return value on 64bit platforms.
// On 32bit platforms syscall.r2 is the upper part of a 64bit return.
v.SetFloat(float64(math.Float32frombits(uint32(syscall.f1))))
case reflect.Float64:
// NOTE: syscall.r2 is only the floating return value on 64bit platforms.
// On 32bit platforms syscall.r2 is the upper part of a 64bit return.
v.SetFloat(math.Float64frombits(uint64(syscall.f1)))
case reflect.Struct:
v = getStruct(outType, syscall)
default:
panic("purego: unsupported return kind: " + outType.Kind().String())
}
return []reflect.Value{v}
})
fn.Set(v)
}
// maxRegAllocStructSize is the biggest a struct can be while still fitting in registers.
// if it is bigger than this than enough space must be allocated on the heap and then passed into
// the function as the first parameter on amd64 or in R8 on arm64.
const maxRegAllocStructSize = 16
func isAllSameFloat(ty reflect.Type) bool {
first := ty.Field(0).Type.Kind()
if first != reflect.Float32 && first != reflect.Float64 {
return false
}
for i := 0; i < ty.NumField(); i++ {
f := ty.Field(i)
if f.Type.Kind() != first {
return false
}
}
return true
}
func checkStructFieldsSupported(ty reflect.Type) {
for i := 0; i < ty.NumField(); i++ {
f := ty.Field(i).Type
if f.Kind() == reflect.Array {
f = f.Elem()
} else if f.Kind() == reflect.Struct {
checkStructFieldsSupported(f)
continue
}
switch f.Kind() {
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64,
reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64,
reflect.Uintptr, reflect.Ptr, reflect.UnsafePointer, reflect.Float64, reflect.Float32:
default:
panic(fmt.Sprintf("purego: struct field type %s is not supported", f))
}
}
}
const is32bit = unsafe.Sizeof(uintptr(0)) == 4
func roundUpTo8(val uintptr) uintptr {
return (val + 7) &^ 7
}
func numOfIntegerRegisters() int {
switch runtime.GOARCH {
case "arm64":
return 8
case "amd64":
return 6
// TODO: figure out why 386 tests are not working
/*case "386":
return 0
case "arm":
return 4*/
default:
panic("purego: unknown GOARCH (" + runtime.GOARCH + ")")
}
}
+17
View File
@@ -0,0 +1,17 @@
// SPDX-License-Identifier: Apache-2.0
// SPDX-FileCopyrightText: 2022 The Ebitengine Authors
//go:build darwin || freebsd || linux || windows
package purego
import (
"unsafe"
)
//go:linkname runtime_cgocall runtime.cgocall
func runtime_cgocall(fn uintptr, arg unsafe.Pointer) int32 // from runtime/sys_libc.go
//go:linkname runtime_noescape runtime.noescape
//go:noescape
func runtime_noescape(p unsafe.Pointer) unsafe.Pointer // from runtime/stubs.go
+22
View File
@@ -0,0 +1,22 @@
// SPDX-License-Identifier: Apache-2.0
// SPDX-FileCopyrightText: 2024 The Ebitengine Authors
//go:build freebsd || linux
package cgo
/*
#cgo LDFLAGS: -ldl
#include <dlfcn.h>
*/
import "C"
// all that is needed is to assign each dl function because then its
// symbol will then be made available to the linker and linked to inside dlfcn.go
var (
_ = C.dlopen
_ = C.dlsym
_ = C.dlerror
_ = C.dlclose
)
+6
View File
@@ -0,0 +1,6 @@
// SPDX-License-Identifier: Apache-2.0
// SPDX-FileCopyrightText: 2024 The Ebitengine Authors
package cgo
// Empty so that importing this package doesn't cause issue for certain platforms.
+55
View File
@@ -0,0 +1,55 @@
// SPDX-License-Identifier: Apache-2.0
// SPDX-FileCopyrightText: 2022 The Ebitengine Authors
//go:build freebsd || (linux && !(arm64 || amd64))
package cgo
// this file is placed inside internal/cgo and not package purego
// because Cgo and assembly files can't be in the same package.
/*
#cgo LDFLAGS: -ldl
#include <stdint.h>
#include <dlfcn.h>
#include <errno.h>
#include <assert.h>
typedef struct syscall15Args {
uintptr_t fn;
uintptr_t a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15;
uintptr_t f1, f2, f3, f4, f5, f6, f7, f8;
uintptr_t r1, r2, err;
} syscall15Args;
void syscall15(struct syscall15Args *args) {
assert((args->f1|args->f2|args->f3|args->f4|args->f5|args->f6|args->f7|args->f8) == 0);
uintptr_t (*func_name)(uintptr_t a1, uintptr_t a2, uintptr_t a3, uintptr_t a4, uintptr_t a5, uintptr_t a6,
uintptr_t a7, uintptr_t a8, uintptr_t a9, uintptr_t a10, uintptr_t a11, uintptr_t a12,
uintptr_t a13, uintptr_t a14, uintptr_t a15);
*(void**)(&func_name) = (void*)(args->fn);
uintptr_t r1 = func_name(args->a1,args->a2,args->a3,args->a4,args->a5,args->a6,args->a7,args->a8,args->a9,
args->a10,args->a11,args->a12,args->a13,args->a14,args->a15);
args->r1 = r1;
args->err = errno;
}
*/
import "C"
import "unsafe"
// assign purego.syscall15XABI0 to the C version of this function.
var Syscall15XABI0 = unsafe.Pointer(C.syscall15)
//go:nosplit
func Syscall15X(fn, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15 uintptr) (r1, r2, err uintptr) {
args := C.syscall15Args{
C.uintptr_t(fn), C.uintptr_t(a1), C.uintptr_t(a2), C.uintptr_t(a3),
C.uintptr_t(a4), C.uintptr_t(a5), C.uintptr_t(a6),
C.uintptr_t(a7), C.uintptr_t(a8), C.uintptr_t(a9), C.uintptr_t(a10), C.uintptr_t(a11), C.uintptr_t(a12),
C.uintptr_t(a13), C.uintptr_t(a14), C.uintptr_t(a15), 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
}
C.syscall15(&args)
return uintptr(args.r1), uintptr(args.r2), uintptr(args.err)
}
+99
View File
@@ -0,0 +1,99 @@
// Copyright 2021 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.
// Macros for transitioning from the host ABI to Go ABI0.
//
// These save the frame pointer, so in general, functions that use
// these should have zero frame size to suppress the automatic frame
// pointer, though it's harmless to not do this.
#ifdef GOOS_windows
// REGS_HOST_TO_ABI0_STACK is the stack bytes used by
// PUSH_REGS_HOST_TO_ABI0.
#define REGS_HOST_TO_ABI0_STACK (28*8 + 8)
// PUSH_REGS_HOST_TO_ABI0 prepares for transitioning from
// the host ABI to Go ABI0 code. It saves all registers that are
// callee-save in the host ABI and caller-save in Go ABI0 and prepares
// for entry to Go.
//
// Save DI SI BP BX R12 R13 R14 R15 X6-X15 registers and the DF flag.
// Clear the DF flag for the Go ABI.
// MXCSR matches the Go ABI, so we don't have to set that,
// and Go doesn't modify it, so we don't have to save it.
#define PUSH_REGS_HOST_TO_ABI0() \
PUSHFQ \
CLD \
ADJSP $(REGS_HOST_TO_ABI0_STACK - 8) \
MOVQ DI, (0*0)(SP) \
MOVQ SI, (1*8)(SP) \
MOVQ BP, (2*8)(SP) \
MOVQ BX, (3*8)(SP) \
MOVQ R12, (4*8)(SP) \
MOVQ R13, (5*8)(SP) \
MOVQ R14, (6*8)(SP) \
MOVQ R15, (7*8)(SP) \
MOVUPS X6, (8*8)(SP) \
MOVUPS X7, (10*8)(SP) \
MOVUPS X8, (12*8)(SP) \
MOVUPS X9, (14*8)(SP) \
MOVUPS X10, (16*8)(SP) \
MOVUPS X11, (18*8)(SP) \
MOVUPS X12, (20*8)(SP) \
MOVUPS X13, (22*8)(SP) \
MOVUPS X14, (24*8)(SP) \
MOVUPS X15, (26*8)(SP)
#define POP_REGS_HOST_TO_ABI0() \
MOVQ (0*0)(SP), DI \
MOVQ (1*8)(SP), SI \
MOVQ (2*8)(SP), BP \
MOVQ (3*8)(SP), BX \
MOVQ (4*8)(SP), R12 \
MOVQ (5*8)(SP), R13 \
MOVQ (6*8)(SP), R14 \
MOVQ (7*8)(SP), R15 \
MOVUPS (8*8)(SP), X6 \
MOVUPS (10*8)(SP), X7 \
MOVUPS (12*8)(SP), X8 \
MOVUPS (14*8)(SP), X9 \
MOVUPS (16*8)(SP), X10 \
MOVUPS (18*8)(SP), X11 \
MOVUPS (20*8)(SP), X12 \
MOVUPS (22*8)(SP), X13 \
MOVUPS (24*8)(SP), X14 \
MOVUPS (26*8)(SP), X15 \
ADJSP $-(REGS_HOST_TO_ABI0_STACK - 8) \
POPFQ
#else
// SysV ABI
#define REGS_HOST_TO_ABI0_STACK (6*8)
// SysV MXCSR matches the Go ABI, so we don't have to set that,
// and Go doesn't modify it, so we don't have to save it.
// Both SysV and Go require DF to be cleared, so that's already clear.
// The SysV and Go frame pointer conventions are compatible.
#define PUSH_REGS_HOST_TO_ABI0() \
ADJSP $(REGS_HOST_TO_ABI0_STACK) \
MOVQ BP, (5*8)(SP) \
LEAQ (5*8)(SP), BP \
MOVQ BX, (0*8)(SP) \
MOVQ R12, (1*8)(SP) \
MOVQ R13, (2*8)(SP) \
MOVQ R14, (3*8)(SP) \
MOVQ R15, (4*8)(SP)
#define POP_REGS_HOST_TO_ABI0() \
MOVQ (0*8)(SP), BX \
MOVQ (1*8)(SP), R12 \
MOVQ (2*8)(SP), R13 \
MOVQ (3*8)(SP), R14 \
MOVQ (4*8)(SP), R15 \
MOVQ (5*8)(SP), BP \
ADJSP $-(REGS_HOST_TO_ABI0_STACK)
#endif
+39
View File
@@ -0,0 +1,39 @@
// Copyright 2021 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.
// Macros for transitioning from the host ABI to Go ABI0.
//
// These macros save and restore the callee-saved registers
// from the stack, but they don't adjust stack pointer, so
// the user should prepare stack space in advance.
// SAVE_R19_TO_R28(offset) saves R19 ~ R28 to the stack space
// of ((offset)+0*8)(RSP) ~ ((offset)+9*8)(RSP).
//
// SAVE_F8_TO_F15(offset) saves F8 ~ F15 to the stack space
// of ((offset)+0*8)(RSP) ~ ((offset)+7*8)(RSP).
//
// R29 is not saved because Go will save and restore it.
#define SAVE_R19_TO_R28(offset) \
STP (R19, R20), ((offset)+0*8)(RSP) \
STP (R21, R22), ((offset)+2*8)(RSP) \
STP (R23, R24), ((offset)+4*8)(RSP) \
STP (R25, R26), ((offset)+6*8)(RSP) \
STP (R27, g), ((offset)+8*8)(RSP)
#define RESTORE_R19_TO_R28(offset) \
LDP ((offset)+0*8)(RSP), (R19, R20) \
LDP ((offset)+2*8)(RSP), (R21, R22) \
LDP ((offset)+4*8)(RSP), (R23, R24) \
LDP ((offset)+6*8)(RSP), (R25, R26) \
LDP ((offset)+8*8)(RSP), (R27, g) /* R28 */
#define SAVE_F8_TO_F15(offset) \
FSTPD (F8, F9), ((offset)+0*8)(RSP) \
FSTPD (F10, F11), ((offset)+2*8)(RSP) \
FSTPD (F12, F13), ((offset)+4*8)(RSP) \
FSTPD (F14, F15), ((offset)+6*8)(RSP)
#define RESTORE_F8_TO_F15(offset) \
FLDPD ((offset)+0*8)(RSP), (F8, F9) \
FLDPD ((offset)+2*8)(RSP), (F10, F11) \
FLDPD ((offset)+4*8)(RSP), (F12, F13) \
FLDPD ((offset)+6*8)(RSP), (F14, F15)
+39
View File
@@ -0,0 +1,39 @@
// Copyright 2009 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.
#include "textflag.h"
#include "abi_amd64.h"
// Called by C code generated by cmd/cgo.
// func crosscall2(fn, a unsafe.Pointer, n int32, ctxt uintptr)
// Saves C callee-saved registers and calls cgocallback with three arguments.
// fn is the PC of a func(a unsafe.Pointer) function.
// This signature is known to SWIG, so we can't change it.
TEXT crosscall2(SB), NOSPLIT, $0-0
PUSH_REGS_HOST_TO_ABI0()
// Make room for arguments to cgocallback.
ADJSP $0x18
#ifndef GOOS_windows
MOVQ DI, 0x0(SP) // fn
MOVQ SI, 0x8(SP) // arg
// Skip n in DX.
MOVQ CX, 0x10(SP) // ctxt
#else
MOVQ CX, 0x0(SP) // fn
MOVQ DX, 0x8(SP) // arg
// Skip n in R8.
MOVQ R9, 0x10(SP) // ctxt
#endif
CALL runtime·cgocallback(SB)
ADJSP $-0x18
POP_REGS_HOST_TO_ABI0()
RET
+36
View File
@@ -0,0 +1,36 @@
// 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.
#include "textflag.h"
#include "abi_arm64.h"
// Called by C code generated by cmd/cgo.
// func crosscall2(fn, a unsafe.Pointer, n int32, ctxt uintptr)
// Saves C callee-saved registers and calls cgocallback with three arguments.
// fn is the PC of a func(a unsafe.Pointer) function.
TEXT crosscall2(SB), NOSPLIT|NOFRAME, $0
/*
* We still need to save all callee save register as before, and then
* push 3 args for fn (R0, R1, R3), skipping R2.
* Also note that at procedure entry in gc world, 8(RSP) will be the
* first arg.
*/
SUB $(8*24), RSP
STP (R0, R1), (8*1)(RSP)
MOVD R3, (8*3)(RSP)
SAVE_R19_TO_R28(8*4)
SAVE_F8_TO_F15(8*14)
STP (R29, R30), (8*22)(RSP)
// Initialize Go ABI environment
BL runtime·load_g(SB)
BL runtime·cgocallback(SB)
RESTORE_R19_TO_R28(8*4)
RESTORE_F8_TO_F15(8*14)
LDP (8*22)(RSP), (R29, R30)
ADD $(8*24), RSP
RET
+93
View File
@@ -0,0 +1,93 @@
// Copyright 2011 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.
//go:build darwin || freebsd || linux
package fakecgo
import (
_ "unsafe"
)
// TODO: decide if we need _runtime_cgo_panic_internal
//go:linkname x_cgo_init_trampoline x_cgo_init_trampoline
//go:linkname _cgo_init _cgo_init
var x_cgo_init_trampoline byte
var _cgo_init = &x_cgo_init_trampoline
// Creates a new system thread without updating any Go state.
//
// This method is invoked during shared library loading to create a new OS
// thread to perform the runtime initialization. This method is similar to
// _cgo_sys_thread_start except that it doesn't update any Go state.
//go:linkname x_cgo_thread_start_trampoline x_cgo_thread_start_trampoline
//go:linkname _cgo_thread_start _cgo_thread_start
var x_cgo_thread_start_trampoline byte
var _cgo_thread_start = &x_cgo_thread_start_trampoline
// Notifies that the runtime has been initialized.
//
// We currently block at every CGO entry point (via _cgo_wait_runtime_init_done)
// to ensure that the runtime has been initialized before the CGO call is
// executed. This is necessary for shared libraries where we kickoff runtime
// initialization in a separate thread and return without waiting for this
// thread to complete the init.
//go:linkname x_cgo_notify_runtime_init_done_trampoline x_cgo_notify_runtime_init_done_trampoline
//go:linkname _cgo_notify_runtime_init_done _cgo_notify_runtime_init_done
var x_cgo_notify_runtime_init_done_trampoline byte
var _cgo_notify_runtime_init_done = &x_cgo_notify_runtime_init_done_trampoline
// Indicates whether a dummy thread key has been created or not.
//
// When calling go exported function from C, we register a destructor
// callback, for a dummy thread key, by using pthread_key_create.
//go:linkname _cgo_pthread_key_created _cgo_pthread_key_created
var x_cgo_pthread_key_created uintptr
var _cgo_pthread_key_created = &x_cgo_pthread_key_created
// Set the x_crosscall2_ptr C function pointer variable point to crosscall2.
// It's for the runtime package to call at init time.
func set_crosscall2() {
// nothing needs to be done here for fakecgo
// because it's possible to just call cgocallback directly
}
//go:linkname _set_crosscall2 runtime.set_crosscall2
var _set_crosscall2 = set_crosscall2
// Store the g into the thread-specific value.
// So that pthread_key_destructor will dropm when the thread is exiting.
//go:linkname x_cgo_bindm_trampoline x_cgo_bindm_trampoline
//go:linkname _cgo_bindm _cgo_bindm
var x_cgo_bindm_trampoline byte
var _cgo_bindm = &x_cgo_bindm_trampoline
// TODO: decide if we need x_cgo_set_context_function
// TODO: decide if we need _cgo_yield
var (
// In Go 1.20 the race detector was rewritten to pure Go
// on darwin. This means that when CGO_ENABLED=0 is set
// fakecgo is built with race detector code. This is not
// good since this code is pretending to be C. The go:norace
// pragma is not enough, since it only applies to the native
// ABIInternal function. The ABIO wrapper (which is necessary,
// since all references to text symbols from assembly will use it)
// does not inherit the go:norace pragma, so it will still be
// instrumented by the race detector.
//
// To circumvent this issue, using closure calls in the
// assembly, which forces the compiler to use the ABIInternal
// native implementation (which has go:norace) instead.
threadentry_call = threadentry
x_cgo_init_call = x_cgo_init
x_cgo_setenv_call = x_cgo_setenv
x_cgo_unsetenv_call = x_cgo_unsetenv
x_cgo_thread_start_call = x_cgo_thread_start
)
+33
View File
@@ -0,0 +1,33 @@
// SPDX-License-Identifier: Apache-2.0
// SPDX-FileCopyrightText: 2022 The Ebitengine Authors
//go:build darwin || freebsd
// Package fakecgo implements the Cgo runtime (runtime/cgo) entirely in Go.
// This allows code that calls into C to function properly when CGO_ENABLED=0.
//
// # Goals
//
// fakecgo attempts to replicate the same naming structure as in the runtime.
// For example, functions that have the prefix "gcc_*" are named "go_*".
// This makes it easier to port other GOOSs and GOARCHs as well as to keep
// it in sync with runtime/cgo.
//
// # Support
//
// Currently, fakecgo only supports macOS on amd64 & arm64. It also cannot
// be used with -buildmode=c-archive because that requires special initialization
// that fakecgo does not implement at the moment.
//
// # Usage
//
// Using fakecgo is easy just import _ "github.com/ebitengine/purego" and then
// set the environment variable CGO_ENABLED=0.
// The recommended usage for fakecgo is to prefer using runtime/cgo if possible
// but if cross-compiling or fast build times are important fakecgo is available.
// Purego will pick which ever Cgo runtime is available and prefer the one that
// comes with Go (runtime/cgo).
package fakecgo
//go:generate go run gen.go
//go:generate gofmt -s -w symbols.go
+27
View File
@@ -0,0 +1,27 @@
// Copyright 2010 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.
//go:build freebsd && !cgo
package fakecgo
import _ "unsafe" // for go:linkname
// Supply environ and __progname, because we don't
// link against the standard FreeBSD crt0.o and the
// libc dynamic library needs them.
// Note: when building with cross-compiling or CGO_ENABLED=0, add
// the following argument to `go` so that these symbols are defined by
// making fakecgo the Cgo.
// -gcflags="github.com/ebitengine/purego/internal/fakecgo=-std"
//go:linkname _environ environ
//go:linkname _progname __progname
//go:cgo_export_dynamic environ
//go:cgo_export_dynamic __progname
var _environ uintptr
var _progname uintptr
@@ -0,0 +1,71 @@
// Copyright 2011 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.
package fakecgo
import "unsafe"
//go:nosplit
//go:norace
func _cgo_sys_thread_start(ts *ThreadStart) {
var attr pthread_attr_t
var ign, oset sigset_t
var p pthread_t
var size size_t
var err int
sigfillset(&ign)
pthread_sigmask(SIG_SETMASK, &ign, &oset)
size = pthread_get_stacksize_np(pthread_self())
pthread_attr_init(&attr)
pthread_attr_setstacksize(&attr, size)
// Leave stacklo=0 and set stackhi=size; mstart will do the rest.
ts.g.stackhi = uintptr(size)
err = _cgo_try_pthread_create(&p, &attr, unsafe.Pointer(threadentry_trampolineABI0), ts)
pthread_sigmask(SIG_SETMASK, &oset, nil)
if err != 0 {
print("fakecgo: pthread_create failed: ")
println(err)
abort()
}
}
// threadentry_trampolineABI0 maps the C ABI to Go ABI then calls the Go function
//
//go:linkname x_threadentry_trampoline threadentry_trampoline
var x_threadentry_trampoline byte
var threadentry_trampolineABI0 = &x_threadentry_trampoline
//go:nosplit
//go:norace
func threadentry(v unsafe.Pointer) unsafe.Pointer {
ts := *(*ThreadStart)(v)
free(v)
setg_trampoline(setg_func, uintptr(unsafe.Pointer(ts.g)))
// faking funcs in go is a bit a... involved - but the following works :)
fn := uintptr(unsafe.Pointer(&ts.fn))
(*(*func())(unsafe.Pointer(&fn)))()
return nil
}
// here we will store a pointer to the provided setg func
var setg_func uintptr
//go:nosplit
//go:norace
func x_cgo_init(g *G, setg uintptr) {
var size size_t
setg_func = setg
size = pthread_get_stacksize_np(pthread_self())
g.stacklo = uintptr(unsafe.Add(unsafe.Pointer(&size), -size+4096))
}
@@ -0,0 +1,86 @@
// Copyright 2011 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.
package fakecgo
import "unsafe"
//go:nosplit
//go:norace
func _cgo_sys_thread_start(ts *ThreadStart) {
var attr pthread_attr_t
var ign, oset sigset_t
var p pthread_t
var size size_t
var err int
sigfillset(&ign)
pthread_sigmask(SIG_SETMASK, &ign, &oset)
size = pthread_get_stacksize_np(pthread_self())
pthread_attr_init(&attr)
pthread_attr_setstacksize(&attr, size)
// Leave stacklo=0 and set stackhi=size; mstart will do the rest.
ts.g.stackhi = uintptr(size)
err = _cgo_try_pthread_create(&p, &attr, unsafe.Pointer(threadentry_trampolineABI0), ts)
pthread_sigmask(SIG_SETMASK, &oset, nil)
if err != 0 {
print("fakecgo: pthread_create failed: ")
println(err)
abort()
}
}
// threadentry_trampolineABI0 maps the C ABI to Go ABI then calls the Go function
//
//go:linkname x_threadentry_trampoline threadentry_trampoline
var x_threadentry_trampoline byte
var threadentry_trampolineABI0 = &x_threadentry_trampoline
//go:nosplit
//go:norace
func threadentry(v unsafe.Pointer) unsafe.Pointer {
ts := *(*ThreadStart)(v)
free(v)
// TODO: support ios
//#if TARGET_OS_IPHONE
// darwin_arm_init_thread_exception_port();
//#endif
setg_trampoline(setg_func, uintptr(unsafe.Pointer(ts.g)))
// faking funcs in go is a bit a... involved - but the following works :)
fn := uintptr(unsafe.Pointer(&ts.fn))
(*(*func())(unsafe.Pointer(&fn)))()
return nil
}
// here we will store a pointer to the provided setg func
var setg_func uintptr
// x_cgo_init(G *g, void (*setg)(void*)) (runtime/cgo/gcc_linux_amd64.c)
// This get's called during startup, adjusts stacklo, and provides a pointer to setg_gcc for us
// Additionally, if we set _cgo_init to non-null, go won't do it's own TLS setup
// This function can't be go:systemstack since go is not in a state where the systemcheck would work.
//
//go:nosplit
//go:norace
func x_cgo_init(g *G, setg uintptr) {
var size size_t
setg_func = setg
size = pthread_get_stacksize_np(pthread_self())
g.stacklo = uintptr(unsafe.Add(unsafe.Pointer(&size), -size+4096))
//TODO: support ios
//#if TARGET_OS_IPHONE
// darwin_arm_init_mach_exception_handler();
// darwin_arm_init_thread_exception_port();
// init_working_dir();
//#endif
}
@@ -0,0 +1,93 @@
// Copyright 2011 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.
package fakecgo
import "unsafe"
//go:nosplit
func _cgo_sys_thread_start(ts *ThreadStart) {
var attr pthread_attr_t
var ign, oset sigset_t
var p pthread_t
var size size_t
var err int
//fprintf(stderr, "runtime/cgo: _cgo_sys_thread_start: fn=%p, g=%p\n", ts->fn, ts->g); // debug
sigfillset(&ign)
pthread_sigmask(SIG_SETMASK, &ign, &oset)
pthread_attr_init(&attr)
pthread_attr_getstacksize(&attr, &size)
// Leave stacklo=0 and set stackhi=size; mstart will do the rest.
ts.g.stackhi = uintptr(size)
err = _cgo_try_pthread_create(&p, &attr, unsafe.Pointer(threadentry_trampolineABI0), ts)
pthread_sigmask(SIG_SETMASK, &oset, nil)
if err != 0 {
print("fakecgo: pthread_create failed: ")
println(err)
abort()
}
}
// threadentry_trampolineABI0 maps the C ABI to Go ABI then calls the Go function
//
//go:linkname x_threadentry_trampoline threadentry_trampoline
var x_threadentry_trampoline byte
var threadentry_trampolineABI0 = &x_threadentry_trampoline
//go:nosplit
func threadentry(v unsafe.Pointer) unsafe.Pointer {
ts := *(*ThreadStart)(v)
free(v)
setg_trampoline(setg_func, uintptr(unsafe.Pointer(ts.g)))
// faking funcs in go is a bit a... involved - but the following works :)
fn := uintptr(unsafe.Pointer(&ts.fn))
(*(*func())(unsafe.Pointer(&fn)))()
return nil
}
// here we will store a pointer to the provided setg func
var setg_func uintptr
//go:nosplit
func x_cgo_init(g *G, setg uintptr) {
var size size_t
var attr *pthread_attr_t
/* The memory sanitizer distributed with versions of clang
before 3.8 has a bug: if you call mmap before malloc, mmap
may return an address that is later overwritten by the msan
library. Avoid this problem by forcing a call to malloc
here, before we ever call malloc.
This is only required for the memory sanitizer, so it's
unfortunate that we always run it. It should be possible
to remove this when we no longer care about versions of
clang before 3.8. The test for this is
misc/cgo/testsanitizers.
GCC works hard to eliminate a seemingly unnecessary call to
malloc, so we actually use the memory we allocate. */
setg_func = setg
attr = (*pthread_attr_t)(malloc(unsafe.Sizeof(*attr)))
if attr == nil {
println("fakecgo: malloc failed")
abort()
}
pthread_attr_init(attr)
pthread_attr_getstacksize(attr, &size)
// runtime/cgo uses __builtin_frame_address(0) instead of `uintptr(unsafe.Pointer(&size))`
// but this should be OK since we are taking the address of the first variable in this function.
g.stacklo = uintptr(unsafe.Pointer(&size)) - uintptr(size) + 4096
pthread_attr_destroy(attr)
free(unsafe.Pointer(attr))
}
@@ -0,0 +1,96 @@
// Copyright 2011 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.
package fakecgo
import "unsafe"
//go:nosplit
func _cgo_sys_thread_start(ts *ThreadStart) {
var attr pthread_attr_t
var ign, oset sigset_t
var p pthread_t
var size size_t
var err int
//fprintf(stderr, "runtime/cgo: _cgo_sys_thread_start: fn=%p, g=%p\n", ts->fn, ts->g); // debug
sigfillset(&ign)
pthread_sigmask(SIG_SETMASK, &ign, &oset)
pthread_attr_init(&attr)
pthread_attr_getstacksize(&attr, &size)
// Leave stacklo=0 and set stackhi=size; mstart will do the rest.
ts.g.stackhi = uintptr(size)
err = _cgo_try_pthread_create(&p, &attr, unsafe.Pointer(threadentry_trampolineABI0), ts)
pthread_sigmask(SIG_SETMASK, &oset, nil)
if err != 0 {
print("fakecgo: pthread_create failed: ")
println(err)
abort()
}
}
// threadentry_trampolineABI0 maps the C ABI to Go ABI then calls the Go function
//
//go:linkname x_threadentry_trampoline threadentry_trampoline
var x_threadentry_trampoline byte
var threadentry_trampolineABI0 = &x_threadentry_trampoline
//go:nosplit
func threadentry(v unsafe.Pointer) unsafe.Pointer {
ts := *(*ThreadStart)(v)
free(v)
setg_trampoline(setg_func, uintptr(unsafe.Pointer(ts.g)))
// faking funcs in go is a bit a... involved - but the following works :)
fn := uintptr(unsafe.Pointer(&ts.fn))
(*(*func())(unsafe.Pointer(&fn)))()
return nil
}
// here we will store a pointer to the provided setg func
var setg_func uintptr
// x_cgo_init(G *g, void (*setg)(void*)) (runtime/cgo/gcc_linux_amd64.c)
// This get's called during startup, adjusts stacklo, and provides a pointer to setg_gcc for us
// Additionally, if we set _cgo_init to non-null, go won't do it's own TLS setup
// This function can't be go:systemstack since go is not in a state where the systemcheck would work.
//
//go:nosplit
func x_cgo_init(g *G, setg uintptr) {
var size size_t
var attr *pthread_attr_t
/* The memory sanitizer distributed with versions of clang
before 3.8 has a bug: if you call mmap before malloc, mmap
may return an address that is later overwritten by the msan
library. Avoid this problem by forcing a call to malloc
here, before we ever call malloc.
This is only required for the memory sanitizer, so it's
unfortunate that we always run it. It should be possible
to remove this when we no longer care about versions of
clang before 3.8. The test for this is
misc/cgo/testsanitizers.
GCC works hard to eliminate a seemingly unnecessary call to
malloc, so we actually use the memory we allocate. */
setg_func = setg
attr = (*pthread_attr_t)(malloc(unsafe.Sizeof(*attr)))
if attr == nil {
println("fakecgo: malloc failed")
abort()
}
pthread_attr_init(attr)
pthread_attr_getstacksize(attr, &size)
g.stacklo = uintptr(unsafe.Pointer(&size)) - uintptr(size) + 4096
pthread_attr_destroy(attr)
free(unsafe.Pointer(attr))
}
+66
View File
@@ -0,0 +1,66 @@
// SPDX-License-Identifier: Apache-2.0
// SPDX-FileCopyrightText: 2022 The Ebitengine Authors
//go:build darwin || freebsd || linux
package fakecgo
import (
"syscall"
"unsafe"
)
var (
pthread_g pthread_key_t
runtime_init_cond = PTHREAD_COND_INITIALIZER
runtime_init_mu = PTHREAD_MUTEX_INITIALIZER
runtime_init_done int
)
//go:nosplit
func x_cgo_notify_runtime_init_done() {
pthread_mutex_lock(&runtime_init_mu)
runtime_init_done = 1
pthread_cond_broadcast(&runtime_init_cond)
pthread_mutex_unlock(&runtime_init_mu)
}
// Store the g into a thread-specific value associated with the pthread key pthread_g.
// And pthread_key_destructor will dropm when the thread is exiting.
func x_cgo_bindm(g unsafe.Pointer) {
// We assume this will always succeed, otherwise, there might be extra M leaking,
// when a C thread exits after a cgo call.
// We only invoke this function once per thread in runtime.needAndBindM,
// and the next calls just reuse the bound m.
pthread_setspecific(pthread_g, g)
}
// _cgo_try_pthread_create retries pthread_create if it fails with
// EAGAIN.
//
//go:nosplit
//go:norace
func _cgo_try_pthread_create(thread *pthread_t, attr *pthread_attr_t, pfn unsafe.Pointer, arg *ThreadStart) int {
var ts syscall.Timespec
// tries needs to be the same type as syscall.Timespec.Nsec
// but the fields are int32 on 32bit and int64 on 64bit.
// tries is assigned to syscall.Timespec.Nsec in order to match its type.
tries := ts.Nsec
var err int
for tries = 0; tries < 20; tries++ {
err = int(pthread_create(thread, attr, pfn, unsafe.Pointer(arg)))
if err == 0 {
pthread_detach(*thread)
return 0
}
if err != int(syscall.EAGAIN) {
return err
}
ts.Sec = 0
ts.Nsec = (tries + 1) * 1000 * 1000 // Milliseconds.
nanosleep(&ts, nil)
}
return int(syscall.EAGAIN)
}
+93
View File
@@ -0,0 +1,93 @@
// Copyright 2011 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.
package fakecgo
import "unsafe"
//go:nosplit
func _cgo_sys_thread_start(ts *ThreadStart) {
var attr pthread_attr_t
var ign, oset sigset_t
var p pthread_t
var size size_t
var err int
//fprintf(stderr, "runtime/cgo: _cgo_sys_thread_start: fn=%p, g=%p\n", ts->fn, ts->g); // debug
sigfillset(&ign)
pthread_sigmask(SIG_SETMASK, &ign, &oset)
pthread_attr_init(&attr)
pthread_attr_getstacksize(&attr, &size)
// Leave stacklo=0 and set stackhi=size; mstart will do the rest.
ts.g.stackhi = uintptr(size)
err = _cgo_try_pthread_create(&p, &attr, unsafe.Pointer(threadentry_trampolineABI0), ts)
pthread_sigmask(SIG_SETMASK, &oset, nil)
if err != 0 {
print("fakecgo: pthread_create failed: ")
println(err)
abort()
}
}
// threadentry_trampolineABI0 maps the C ABI to Go ABI then calls the Go function
//
//go:linkname x_threadentry_trampoline threadentry_trampoline
var x_threadentry_trampoline byte
var threadentry_trampolineABI0 = &x_threadentry_trampoline
//go:nosplit
func threadentry(v unsafe.Pointer) unsafe.Pointer {
ts := *(*ThreadStart)(v)
free(v)
setg_trampoline(setg_func, uintptr(unsafe.Pointer(ts.g)))
// faking funcs in go is a bit a... involved - but the following works :)
fn := uintptr(unsafe.Pointer(&ts.fn))
(*(*func())(unsafe.Pointer(&fn)))()
return nil
}
// here we will store a pointer to the provided setg func
var setg_func uintptr
//go:nosplit
func x_cgo_init(g *G, setg uintptr) {
var size size_t
var attr *pthread_attr_t
/* The memory sanitizer distributed with versions of clang
before 3.8 has a bug: if you call mmap before malloc, mmap
may return an address that is later overwritten by the msan
library. Avoid this problem by forcing a call to malloc
here, before we ever call malloc.
This is only required for the memory sanitizer, so it's
unfortunate that we always run it. It should be possible
to remove this when we no longer care about versions of
clang before 3.8. The test for this is
misc/cgo/testsanitizers.
GCC works hard to eliminate a seemingly unnecessary call to
malloc, so we actually use the memory we allocate. */
setg_func = setg
attr = (*pthread_attr_t)(malloc(unsafe.Sizeof(*attr)))
if attr == nil {
println("fakecgo: malloc failed")
abort()
}
pthread_attr_init(attr)
pthread_attr_getstacksize(attr, &size)
// runtime/cgo uses __builtin_frame_address(0) instead of `uintptr(unsafe.Pointer(&size))`
// but this should be OK since we are taking the address of the first variable in this function.
g.stacklo = uintptr(unsafe.Pointer(&size)) - uintptr(size) + 4096
pthread_attr_destroy(attr)
free(unsafe.Pointer(attr))
}
+96
View File
@@ -0,0 +1,96 @@
// Copyright 2011 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.
package fakecgo
import "unsafe"
//go:nosplit
func _cgo_sys_thread_start(ts *ThreadStart) {
var attr pthread_attr_t
var ign, oset sigset_t
var p pthread_t
var size size_t
var err int
//fprintf(stderr, "runtime/cgo: _cgo_sys_thread_start: fn=%p, g=%p\n", ts->fn, ts->g); // debug
sigfillset(&ign)
pthread_sigmask(SIG_SETMASK, &ign, &oset)
pthread_attr_init(&attr)
pthread_attr_getstacksize(&attr, &size)
// Leave stacklo=0 and set stackhi=size; mstart will do the rest.
ts.g.stackhi = uintptr(size)
err = _cgo_try_pthread_create(&p, &attr, unsafe.Pointer(threadentry_trampolineABI0), ts)
pthread_sigmask(SIG_SETMASK, &oset, nil)
if err != 0 {
print("fakecgo: pthread_create failed: ")
println(err)
abort()
}
}
// threadentry_trampolineABI0 maps the C ABI to Go ABI then calls the Go function
//
//go:linkname x_threadentry_trampoline threadentry_trampoline
var x_threadentry_trampoline byte
var threadentry_trampolineABI0 = &x_threadentry_trampoline
//go:nosplit
func threadentry(v unsafe.Pointer) unsafe.Pointer {
ts := *(*ThreadStart)(v)
free(v)
setg_trampoline(setg_func, uintptr(unsafe.Pointer(ts.g)))
// faking funcs in go is a bit a... involved - but the following works :)
fn := uintptr(unsafe.Pointer(&ts.fn))
(*(*func())(unsafe.Pointer(&fn)))()
return nil
}
// here we will store a pointer to the provided setg func
var setg_func uintptr
// x_cgo_init(G *g, void (*setg)(void*)) (runtime/cgo/gcc_linux_amd64.c)
// This get's called during startup, adjusts stacklo, and provides a pointer to setg_gcc for us
// Additionally, if we set _cgo_init to non-null, go won't do it's own TLS setup
// This function can't be go:systemstack since go is not in a state where the systemcheck would work.
//
//go:nosplit
func x_cgo_init(g *G, setg uintptr) {
var size size_t
var attr *pthread_attr_t
/* The memory sanitizer distributed with versions of clang
before 3.8 has a bug: if you call mmap before malloc, mmap
may return an address that is later overwritten by the msan
library. Avoid this problem by forcing a call to malloc
here, before we ever call malloc.
This is only required for the memory sanitizer, so it's
unfortunate that we always run it. It should be possible
to remove this when we no longer care about versions of
clang before 3.8. The test for this is
misc/cgo/testsanitizers.
GCC works hard to eliminate a seemingly unnecessary call to
malloc, so we actually use the memory we allocate. */
setg_func = setg
attr = (*pthread_attr_t)(malloc(unsafe.Sizeof(*attr)))
if attr == nil {
println("fakecgo: malloc failed")
abort()
}
pthread_attr_init(attr)
pthread_attr_getstacksize(attr, &size)
g.stacklo = uintptr(unsafe.Pointer(&size)) - uintptr(size) + 4096
pthread_attr_destroy(attr)
free(unsafe.Pointer(attr))
}
+18
View File
@@ -0,0 +1,18 @@
// SPDX-License-Identifier: Apache-2.0
// SPDX-FileCopyrightText: 2022 The Ebitengine Authors
//go:build darwin || freebsd || linux
package fakecgo
//go:nosplit
//go:norace
func x_cgo_setenv(arg *[2]*byte) {
setenv(arg[0], arg[1], 1)
}
//go:nosplit
//go:norace
func x_cgo_unsetenv(arg *[1]*byte) {
unsetenv(arg[0])
}
+33
View File
@@ -0,0 +1,33 @@
// SPDX-License-Identifier: Apache-2.0
// SPDX-FileCopyrightText: 2022 The Ebitengine Authors
//go:build darwin || freebsd || linux
package fakecgo
import "unsafe"
// _cgo_thread_start is split into three parts in cgo since only one part is system dependent (keep it here for easier handling)
// _cgo_thread_start(ThreadStart *arg) (runtime/cgo/gcc_util.c)
// This get's called instead of the go code for creating new threads
// -> pthread_* stuff is used, so threads are setup correctly for C
// If this is missing, TLS is only setup correctly on thread 1!
// This function should be go:systemstack instead of go:nosplit (but that requires runtime)
//
//go:nosplit
//go:norace
func x_cgo_thread_start(arg *ThreadStart) {
var ts *ThreadStart
// Make our own copy that can persist after we return.
// _cgo_tsan_acquire();
ts = (*ThreadStart)(malloc(unsafe.Sizeof(*ts)))
// _cgo_tsan_release();
if ts == nil {
println("fakecgo: out of memory in thread_start")
abort()
}
// *ts = *arg would cause a writebarrier so use memmove instead
memmove(unsafe.Pointer(ts), unsafe.Pointer(arg), unsafe.Sizeof(*ts))
_cgo_sys_thread_start(ts) // OS-dependent half
}
+19
View File
@@ -0,0 +1,19 @@
// Copyright 2010 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.
//go:build darwin || freebsd || linux
// The runtime package contains an uninitialized definition
// for runtime·iscgo. Override it to tell the runtime we're here.
// There are various function pointers that should be set too,
// but those depend on dynamic linker magic to get initialized
// correctly, and sometimes they break. This variable is a
// backup: it depends only on old C style static linking rules.
package fakecgo
import _ "unsafe" // for go:linkname
//go:linkname _iscgo runtime.iscgo
var _iscgo bool = true
+35
View File
@@ -0,0 +1,35 @@
// SPDX-License-Identifier: Apache-2.0
// SPDX-FileCopyrightText: 2022 The Ebitengine Authors
//go:build darwin || freebsd || linux
package fakecgo
type (
size_t uintptr
sigset_t [128]byte
pthread_attr_t [64]byte
pthread_t int
pthread_key_t uint64
)
// for pthread_sigmask:
type sighow int32
const (
SIG_BLOCK sighow = 0
SIG_UNBLOCK sighow = 1
SIG_SETMASK sighow = 2
)
type G struct {
stacklo uintptr
stackhi uintptr
}
type ThreadStart struct {
g *G
tls *uintptr
fn uintptr
}
+20
View File
@@ -0,0 +1,20 @@
// SPDX-License-Identifier: Apache-2.0
// SPDX-FileCopyrightText: 2022 The Ebitengine Authors
package fakecgo
type (
pthread_mutex_t struct {
sig int64
opaque [56]byte
}
pthread_cond_t struct {
sig int64
opaque [40]byte
}
)
var (
PTHREAD_COND_INITIALIZER = pthread_cond_t{sig: 0x3CB0B1BB}
PTHREAD_MUTEX_INITIALIZER = pthread_mutex_t{sig: 0x32AAABA7}
)
+14
View File
@@ -0,0 +1,14 @@
// SPDX-License-Identifier: Apache-2.0
// SPDX-FileCopyrightText: 2022 The Ebitengine Authors
package fakecgo
type (
pthread_cond_t uintptr
pthread_mutex_t uintptr
)
var (
PTHREAD_COND_INITIALIZER = pthread_cond_t(0)
PTHREAD_MUTEX_INITIALIZER = pthread_mutex_t(0)
)
+14
View File
@@ -0,0 +1,14 @@
// SPDX-License-Identifier: Apache-2.0
// SPDX-FileCopyrightText: 2022 The Ebitengine Authors
package fakecgo
type (
pthread_cond_t [48]byte
pthread_mutex_t [48]byte
)
var (
PTHREAD_COND_INITIALIZER = pthread_cond_t{}
PTHREAD_MUTEX_INITIALIZER = pthread_mutex_t{}
)
+19
View File
@@ -0,0 +1,19 @@
// Copyright 2011 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.
//go:build darwin || freebsd || linux
package fakecgo
import _ "unsafe" // for go:linkname
//go:linkname x_cgo_setenv_trampoline x_cgo_setenv_trampoline
//go:linkname _cgo_setenv runtime._cgo_setenv
var x_cgo_setenv_trampoline byte
var _cgo_setenv = &x_cgo_setenv_trampoline
//go:linkname x_cgo_unsetenv_trampoline x_cgo_unsetenv_trampoline
//go:linkname _cgo_unsetenv runtime._cgo_unsetenv
var x_cgo_unsetenv_trampoline byte
var _cgo_unsetenv = &x_cgo_unsetenv_trampoline
+184
View File
@@ -0,0 +1,184 @@
// Code generated by 'go generate' with gen.go. DO NOT EDIT.
// SPDX-License-Identifier: Apache-2.0
// SPDX-FileCopyrightText: 2022 The Ebitengine Authors
//go:build darwin || freebsd || linux
package fakecgo
import (
"syscall"
"unsafe"
)
// setg_trampoline calls setg with the G provided
func setg_trampoline(setg uintptr, G uintptr)
//go:linkname memmove runtime.memmove
func memmove(to, from unsafe.Pointer, n uintptr)
// call5 takes fn the C function and 5 arguments and calls the function with those arguments
func call5(fn, a1, a2, a3, a4, a5 uintptr) uintptr
func malloc(size uintptr) unsafe.Pointer {
ret := call5(mallocABI0, uintptr(size), 0, 0, 0, 0)
// this indirection is to avoid go vet complaining about possible misuse of unsafe.Pointer
return *(*unsafe.Pointer)(unsafe.Pointer(&ret))
}
func free(ptr unsafe.Pointer) {
call5(freeABI0, uintptr(ptr), 0, 0, 0, 0)
}
func setenv(name *byte, value *byte, overwrite int32) int32 {
return int32(call5(setenvABI0, uintptr(unsafe.Pointer(name)), uintptr(unsafe.Pointer(value)), uintptr(overwrite), 0, 0))
}
func unsetenv(name *byte) int32 {
return int32(call5(unsetenvABI0, uintptr(unsafe.Pointer(name)), 0, 0, 0, 0))
}
func sigfillset(set *sigset_t) int32 {
return int32(call5(sigfillsetABI0, uintptr(unsafe.Pointer(set)), 0, 0, 0, 0))
}
func nanosleep(ts *syscall.Timespec, rem *syscall.Timespec) int32 {
return int32(call5(nanosleepABI0, uintptr(unsafe.Pointer(ts)), uintptr(unsafe.Pointer(rem)), 0, 0, 0))
}
func abort() {
call5(abortABI0, 0, 0, 0, 0, 0)
}
func pthread_attr_init(attr *pthread_attr_t) int32 {
return int32(call5(pthread_attr_initABI0, uintptr(unsafe.Pointer(attr)), 0, 0, 0, 0))
}
func pthread_create(thread *pthread_t, attr *pthread_attr_t, start unsafe.Pointer, arg unsafe.Pointer) int32 {
return int32(call5(pthread_createABI0, uintptr(unsafe.Pointer(thread)), uintptr(unsafe.Pointer(attr)), uintptr(start), uintptr(arg), 0))
}
func pthread_detach(thread pthread_t) int32 {
return int32(call5(pthread_detachABI0, uintptr(thread), 0, 0, 0, 0))
}
func pthread_sigmask(how sighow, ign *sigset_t, oset *sigset_t) int32 {
return int32(call5(pthread_sigmaskABI0, uintptr(how), uintptr(unsafe.Pointer(ign)), uintptr(unsafe.Pointer(oset)), 0, 0))
}
func pthread_self() pthread_t {
return pthread_t(call5(pthread_selfABI0, 0, 0, 0, 0, 0))
}
func pthread_get_stacksize_np(thread pthread_t) size_t {
return size_t(call5(pthread_get_stacksize_npABI0, uintptr(thread), 0, 0, 0, 0))
}
func pthread_attr_getstacksize(attr *pthread_attr_t, stacksize *size_t) int32 {
return int32(call5(pthread_attr_getstacksizeABI0, uintptr(unsafe.Pointer(attr)), uintptr(unsafe.Pointer(stacksize)), 0, 0, 0))
}
func pthread_attr_setstacksize(attr *pthread_attr_t, size size_t) int32 {
return int32(call5(pthread_attr_setstacksizeABI0, uintptr(unsafe.Pointer(attr)), uintptr(size), 0, 0, 0))
}
func pthread_attr_destroy(attr *pthread_attr_t) int32 {
return int32(call5(pthread_attr_destroyABI0, uintptr(unsafe.Pointer(attr)), 0, 0, 0, 0))
}
func pthread_mutex_lock(mutex *pthread_mutex_t) int32 {
return int32(call5(pthread_mutex_lockABI0, uintptr(unsafe.Pointer(mutex)), 0, 0, 0, 0))
}
func pthread_mutex_unlock(mutex *pthread_mutex_t) int32 {
return int32(call5(pthread_mutex_unlockABI0, uintptr(unsafe.Pointer(mutex)), 0, 0, 0, 0))
}
func pthread_cond_broadcast(cond *pthread_cond_t) int32 {
return int32(call5(pthread_cond_broadcastABI0, uintptr(unsafe.Pointer(cond)), 0, 0, 0, 0))
}
func pthread_setspecific(key pthread_key_t, value unsafe.Pointer) int32 {
return int32(call5(pthread_setspecificABI0, uintptr(key), uintptr(value), 0, 0, 0))
}
//go:linkname _malloc _malloc
var _malloc uintptr
var mallocABI0 = uintptr(unsafe.Pointer(&_malloc))
//go:linkname _free _free
var _free uintptr
var freeABI0 = uintptr(unsafe.Pointer(&_free))
//go:linkname _setenv _setenv
var _setenv uintptr
var setenvABI0 = uintptr(unsafe.Pointer(&_setenv))
//go:linkname _unsetenv _unsetenv
var _unsetenv uintptr
var unsetenvABI0 = uintptr(unsafe.Pointer(&_unsetenv))
//go:linkname _sigfillset _sigfillset
var _sigfillset uintptr
var sigfillsetABI0 = uintptr(unsafe.Pointer(&_sigfillset))
//go:linkname _nanosleep _nanosleep
var _nanosleep uintptr
var nanosleepABI0 = uintptr(unsafe.Pointer(&_nanosleep))
//go:linkname _abort _abort
var _abort uintptr
var abortABI0 = uintptr(unsafe.Pointer(&_abort))
//go:linkname _pthread_attr_init _pthread_attr_init
var _pthread_attr_init uintptr
var pthread_attr_initABI0 = uintptr(unsafe.Pointer(&_pthread_attr_init))
//go:linkname _pthread_create _pthread_create
var _pthread_create uintptr
var pthread_createABI0 = uintptr(unsafe.Pointer(&_pthread_create))
//go:linkname _pthread_detach _pthread_detach
var _pthread_detach uintptr
var pthread_detachABI0 = uintptr(unsafe.Pointer(&_pthread_detach))
//go:linkname _pthread_sigmask _pthread_sigmask
var _pthread_sigmask uintptr
var pthread_sigmaskABI0 = uintptr(unsafe.Pointer(&_pthread_sigmask))
//go:linkname _pthread_self _pthread_self
var _pthread_self uintptr
var pthread_selfABI0 = uintptr(unsafe.Pointer(&_pthread_self))
//go:linkname _pthread_get_stacksize_np _pthread_get_stacksize_np
var _pthread_get_stacksize_np uintptr
var pthread_get_stacksize_npABI0 = uintptr(unsafe.Pointer(&_pthread_get_stacksize_np))
//go:linkname _pthread_attr_getstacksize _pthread_attr_getstacksize
var _pthread_attr_getstacksize uintptr
var pthread_attr_getstacksizeABI0 = uintptr(unsafe.Pointer(&_pthread_attr_getstacksize))
//go:linkname _pthread_attr_setstacksize _pthread_attr_setstacksize
var _pthread_attr_setstacksize uintptr
var pthread_attr_setstacksizeABI0 = uintptr(unsafe.Pointer(&_pthread_attr_setstacksize))
//go:linkname _pthread_attr_destroy _pthread_attr_destroy
var _pthread_attr_destroy uintptr
var pthread_attr_destroyABI0 = uintptr(unsafe.Pointer(&_pthread_attr_destroy))
//go:linkname _pthread_mutex_lock _pthread_mutex_lock
var _pthread_mutex_lock uintptr
var pthread_mutex_lockABI0 = uintptr(unsafe.Pointer(&_pthread_mutex_lock))
//go:linkname _pthread_mutex_unlock _pthread_mutex_unlock
var _pthread_mutex_unlock uintptr
var pthread_mutex_unlockABI0 = uintptr(unsafe.Pointer(&_pthread_mutex_unlock))
//go:linkname _pthread_cond_broadcast _pthread_cond_broadcast
var _pthread_cond_broadcast uintptr
var pthread_cond_broadcastABI0 = uintptr(unsafe.Pointer(&_pthread_cond_broadcast))
//go:linkname _pthread_setspecific _pthread_setspecific
var _pthread_setspecific uintptr
var pthread_setspecificABI0 = uintptr(unsafe.Pointer(&_pthread_setspecific))
+27
View File
@@ -0,0 +1,27 @@
// Code generated by 'go generate' with gen.go. DO NOT EDIT.
// SPDX-License-Identifier: Apache-2.0
// SPDX-FileCopyrightText: 2022 The Ebitengine Authors
package fakecgo
//go:cgo_import_dynamic purego_malloc malloc "/usr/lib/libSystem.B.dylib"
//go:cgo_import_dynamic purego_free free "/usr/lib/libSystem.B.dylib"
//go:cgo_import_dynamic purego_setenv setenv "/usr/lib/libSystem.B.dylib"
//go:cgo_import_dynamic purego_unsetenv unsetenv "/usr/lib/libSystem.B.dylib"
//go:cgo_import_dynamic purego_sigfillset sigfillset "/usr/lib/libSystem.B.dylib"
//go:cgo_import_dynamic purego_nanosleep nanosleep "/usr/lib/libSystem.B.dylib"
//go:cgo_import_dynamic purego_abort abort "/usr/lib/libSystem.B.dylib"
//go:cgo_import_dynamic purego_pthread_attr_init pthread_attr_init "/usr/lib/libSystem.B.dylib"
//go:cgo_import_dynamic purego_pthread_create pthread_create "/usr/lib/libSystem.B.dylib"
//go:cgo_import_dynamic purego_pthread_detach pthread_detach "/usr/lib/libSystem.B.dylib"
//go:cgo_import_dynamic purego_pthread_sigmask pthread_sigmask "/usr/lib/libSystem.B.dylib"
//go:cgo_import_dynamic purego_pthread_self pthread_self "/usr/lib/libSystem.B.dylib"
//go:cgo_import_dynamic purego_pthread_get_stacksize_np pthread_get_stacksize_np "/usr/lib/libSystem.B.dylib"
//go:cgo_import_dynamic purego_pthread_attr_getstacksize pthread_attr_getstacksize "/usr/lib/libSystem.B.dylib"
//go:cgo_import_dynamic purego_pthread_attr_setstacksize pthread_attr_setstacksize "/usr/lib/libSystem.B.dylib"
//go:cgo_import_dynamic purego_pthread_attr_destroy pthread_attr_destroy "/usr/lib/libSystem.B.dylib"
//go:cgo_import_dynamic purego_pthread_mutex_lock pthread_mutex_lock "/usr/lib/libSystem.B.dylib"
//go:cgo_import_dynamic purego_pthread_mutex_unlock pthread_mutex_unlock "/usr/lib/libSystem.B.dylib"
//go:cgo_import_dynamic purego_pthread_cond_broadcast pthread_cond_broadcast "/usr/lib/libSystem.B.dylib"
//go:cgo_import_dynamic purego_pthread_setspecific pthread_setspecific "/usr/lib/libSystem.B.dylib"
@@ -0,0 +1,27 @@
// Code generated by 'go generate' with gen.go. DO NOT EDIT.
// SPDX-License-Identifier: Apache-2.0
// SPDX-FileCopyrightText: 2022 The Ebitengine Authors
package fakecgo
//go:cgo_import_dynamic purego_malloc malloc "libc.so.7"
//go:cgo_import_dynamic purego_free free "libc.so.7"
//go:cgo_import_dynamic purego_setenv setenv "libc.so.7"
//go:cgo_import_dynamic purego_unsetenv unsetenv "libc.so.7"
//go:cgo_import_dynamic purego_sigfillset sigfillset "libc.so.7"
//go:cgo_import_dynamic purego_nanosleep nanosleep "libc.so.7"
//go:cgo_import_dynamic purego_abort abort "libc.so.7"
//go:cgo_import_dynamic purego_pthread_attr_init pthread_attr_init "libpthread.so"
//go:cgo_import_dynamic purego_pthread_create pthread_create "libpthread.so"
//go:cgo_import_dynamic purego_pthread_detach pthread_detach "libpthread.so"
//go:cgo_import_dynamic purego_pthread_sigmask pthread_sigmask "libpthread.so"
//go:cgo_import_dynamic purego_pthread_self pthread_self "libpthread.so"
//go:cgo_import_dynamic purego_pthread_get_stacksize_np pthread_get_stacksize_np "libpthread.so"
//go:cgo_import_dynamic purego_pthread_attr_getstacksize pthread_attr_getstacksize "libpthread.so"
//go:cgo_import_dynamic purego_pthread_attr_setstacksize pthread_attr_setstacksize "libpthread.so"
//go:cgo_import_dynamic purego_pthread_attr_destroy pthread_attr_destroy "libpthread.so"
//go:cgo_import_dynamic purego_pthread_mutex_lock pthread_mutex_lock "libpthread.so"
//go:cgo_import_dynamic purego_pthread_mutex_unlock pthread_mutex_unlock "libpthread.so"
//go:cgo_import_dynamic purego_pthread_cond_broadcast pthread_cond_broadcast "libpthread.so"
//go:cgo_import_dynamic purego_pthread_setspecific pthread_setspecific "libpthread.so"
+27
View File
@@ -0,0 +1,27 @@
// Code generated by 'go generate' with gen.go. DO NOT EDIT.
// SPDX-License-Identifier: Apache-2.0
// SPDX-FileCopyrightText: 2022 The Ebitengine Authors
package fakecgo
//go:cgo_import_dynamic purego_malloc malloc "libc.so.6"
//go:cgo_import_dynamic purego_free free "libc.so.6"
//go:cgo_import_dynamic purego_setenv setenv "libc.so.6"
//go:cgo_import_dynamic purego_unsetenv unsetenv "libc.so.6"
//go:cgo_import_dynamic purego_sigfillset sigfillset "libc.so.6"
//go:cgo_import_dynamic purego_nanosleep nanosleep "libc.so.6"
//go:cgo_import_dynamic purego_abort abort "libc.so.6"
//go:cgo_import_dynamic purego_pthread_attr_init pthread_attr_init "libpthread.so.0"
//go:cgo_import_dynamic purego_pthread_create pthread_create "libpthread.so.0"
//go:cgo_import_dynamic purego_pthread_detach pthread_detach "libpthread.so.0"
//go:cgo_import_dynamic purego_pthread_sigmask pthread_sigmask "libpthread.so.0"
//go:cgo_import_dynamic purego_pthread_self pthread_self "libpthread.so.0"
//go:cgo_import_dynamic purego_pthread_get_stacksize_np pthread_get_stacksize_np "libpthread.so.0"
//go:cgo_import_dynamic purego_pthread_attr_getstacksize pthread_attr_getstacksize "libpthread.so.0"
//go:cgo_import_dynamic purego_pthread_attr_setstacksize pthread_attr_setstacksize "libpthread.so.0"
//go:cgo_import_dynamic purego_pthread_attr_destroy pthread_attr_destroy "libpthread.so.0"
//go:cgo_import_dynamic purego_pthread_mutex_lock pthread_mutex_lock "libpthread.so.0"
//go:cgo_import_dynamic purego_pthread_mutex_unlock pthread_mutex_unlock "libpthread.so.0"
//go:cgo_import_dynamic purego_pthread_cond_broadcast pthread_cond_broadcast "libpthread.so.0"
//go:cgo_import_dynamic purego_pthread_setspecific pthread_setspecific "libpthread.so.0"
@@ -0,0 +1,104 @@
// SPDX-License-Identifier: Apache-2.0
// SPDX-FileCopyrightText: 2022 The Ebitengine Authors
//go:build darwin || linux || freebsd
/*
trampoline for emulating required C functions for cgo in go (see cgo.go)
(we convert cdecl calling convention to go and vice-versa)
Since we're called from go and call into C we can cheat a bit with the calling conventions:
- in go all the registers are caller saved
- in C we have a couple of callee saved registers
=> we can use BX, R12, R13, R14, R15 instead of the stack
C Calling convention cdecl used here (we only need integer args):
1. arg: DI
2. arg: SI
3. arg: DX
4. arg: CX
5. arg: R8
6. arg: R9
We don't need floats with these functions -> AX=0
return value will be in AX
*/
#include "textflag.h"
#include "go_asm.h"
// these trampolines map the gcc ABI to Go ABI and then calls into the Go equivalent functions.
TEXT x_cgo_init_trampoline(SB), NOSPLIT, $16
MOVQ DI, AX
MOVQ SI, BX
MOVQ ·x_cgo_init_call(SB), DX
MOVQ (DX), CX
CALL CX
RET
TEXT x_cgo_thread_start_trampoline(SB), NOSPLIT, $8
MOVQ DI, AX
MOVQ ·x_cgo_thread_start_call(SB), DX
MOVQ (DX), CX
CALL CX
RET
TEXT x_cgo_setenv_trampoline(SB), NOSPLIT, $8
MOVQ DI, AX
MOVQ ·x_cgo_setenv_call(SB), DX
MOVQ (DX), CX
CALL CX
RET
TEXT x_cgo_unsetenv_trampoline(SB), NOSPLIT, $8
MOVQ DI, AX
MOVQ ·x_cgo_unsetenv_call(SB), DX
MOVQ (DX), CX
CALL CX
RET
TEXT x_cgo_notify_runtime_init_done_trampoline(SB), NOSPLIT, $0
CALL ·x_cgo_notify_runtime_init_done(SB)
RET
TEXT x_cgo_bindm_trampoline(SB), NOSPLIT, $0
CALL ·x_cgo_bindm(SB)
RET
// func setg_trampoline(setg uintptr, g uintptr)
TEXT ·setg_trampoline(SB), NOSPLIT, $0-16
MOVQ G+8(FP), DI
MOVQ setg+0(FP), BX
XORL AX, AX
CALL BX
RET
TEXT threadentry_trampoline(SB), NOSPLIT, $16
MOVQ DI, AX
MOVQ ·threadentry_call(SB), DX
MOVQ (DX), CX
CALL CX
RET
TEXT ·call5(SB), NOSPLIT, $0-56
MOVQ fn+0(FP), BX
MOVQ a1+8(FP), DI
MOVQ a2+16(FP), SI
MOVQ a3+24(FP), DX
MOVQ a4+32(FP), CX
MOVQ a5+40(FP), R8
XORL AX, AX // no floats
PUSHQ BP // save BP
MOVQ SP, BP // save SP inside BP bc BP is callee-saved
SUBQ $16, SP // allocate space for alignment
ANDQ $-16, SP // align on 16 bytes for SSE
CALL BX
MOVQ BP, SP // get SP back
POPQ BP // restore BP
MOVQ AX, ret+48(FP)
RET

Some files were not shown because too many files have changed in this diff Show More