// SPDX-License-Identifier: Apache-2.0 // SPDX-FileCopyrightText: 2012 The glfw3-go Authors // SPDX-FileCopyrightText: 2023 The Ebitengine Authors //go:build darwin || freebsd || linux || netbsd || openbsd package cglfw // #define GLFW_INCLUDE_NONE // #include "glfw3.h" // // void goErrorCB(int code, char* desc); // // static void glfwSetErrorCallbackCB() { // glfwSetErrorCallback((GLFWerrorfun)goErrorCB); // } import "C" import ( "fmt" "log" ) // ErrorCode corresponds to an error code. type ErrorCode int // Error codes that are translated to panics and the programmer should not // expect to handle. const ( notInitialized ErrorCode = C.GLFW_NOT_INITIALIZED // GLFW has not been initialized. noCurrentContext ErrorCode = C.GLFW_NO_CURRENT_CONTEXT // No context is current. invalidEnum ErrorCode = C.GLFW_INVALID_ENUM // One of the enum parameters for the function was given an invalid enum. invalidValue ErrorCode = C.GLFW_INVALID_VALUE // One of the parameters for the function was given an invalid value. outOfMemory ErrorCode = C.GLFW_OUT_OF_MEMORY // A memory allocation failed. platformError ErrorCode = C.GLFW_PLATFORM_ERROR // A platform-specific error occurred that does not match any of the more specific categories. ) const ( // APIUnavailable is the error code used when GLFW could not find support // for the requested client API on the system. // // The installed graphics driver does not support the requested client API, // or does not support it via the chosen context creation backend. Below // are a few examples. // // Some pre-installed Windows graphics drivers do not support OpenGL. AMD // only supports OpenGL ES via EGL, while Nvidia and Intel only supports it // via a WGL or GLX extension. OS X does not provide OpenGL ES at all. The // Mesa EGL, OpenGL and OpenGL ES libraries do not interface with the // Nvidia binary driver. APIUnavailable ErrorCode = C.GLFW_API_UNAVAILABLE // VersionUnavailable is the error code used when the requested OpenGL or // OpenGL ES (including any requested profile or context option) is not // available on this machine. // // The machine does not support your requirements. If your application is // sufficiently flexible, downgrade your requirements and try again. // Otherwise, inform the user that their machine does not match your // requirements. // // Future invalid OpenGL and OpenGL ES versions, for example OpenGL 4.8 if // 5.0 comes out before the 4.x series gets that far, also fail with this // error and not GLFW_INVALID_VALUE, because GLFW cannot know what future // versions will exist. VersionUnavailable ErrorCode = C.GLFW_VERSION_UNAVAILABLE // FormatUnavailable is the error code used for both window creation and // clipboard querying format errors. // // If emitted during window creation, the requested pixel format is not // supported. This means one or more hard constraints did not match any of // the available pixel formats. If your application is sufficiently // flexible, downgrade your requirements and try again. Otherwise, inform // the user that their machine does not match your requirements. // // If emitted when querying the clipboard, the contents of the clipboard // could not be converted to the requested format. You should ignore the // error or report it to the user, as appropriate. FormatUnavailable ErrorCode = C.GLFW_FORMAT_UNAVAILABLE ) func (e ErrorCode) String() string { switch e { case notInitialized: return "NotInitialized" case noCurrentContext: return "NoCurrentContext" case invalidEnum: return "InvalidEnum" case invalidValue: return "InvalidValue" case outOfMemory: return "OutOfMemory" case platformError: return "PlatformError" case APIUnavailable: return "APIUnavailable" case VersionUnavailable: return "VersionUnavailable" case FormatUnavailable: return "FormatUnavailable" default: return fmt.Sprintf("ErrorCode(%d)", e) } } // Error holds error code and description. type Error struct { Code ErrorCode Desc string } // Error prints the error code and description in a readable format. func (e *Error) Error() string { return fmt.Sprintf("%s: %s", e.Code.String(), e.Desc) } // Note: There are many cryptic caveats to proper error handling here. // See: https://github.com/go-gl/glfw3/pull/86 // Holds the value of the last error. var lastError = make(chan *Error, 1) //export goErrorCB func goErrorCB(code C.int, desc *C.char) { flushErrors() err := &Error{ErrorCode(code), C.GoString(desc)} select { case lastError <- err: default: fmt.Println("GLFW: An uncaught error has occurred:", err) fmt.Println("GLFW: Please report this bug in the Go package immediately.") } } // Set the glfw callback internally func init() { C.glfwSetErrorCallbackCB() } // flushErrors is called by Terminate before it actually calls C.glfwTerminate, // this ensures that any uncaught errors buffered in lastError are printed // before the program exits. func flushErrors() { err := fetchError() if err != nil { fmt.Println("GLFW: An uncaught error has occurred:", err) fmt.Println("GLFW: Please report this bug in the Go package immediately.") } } // acceptError fetches the next error from the error channel, it accepts only // errors with one of the given error codes. If any other error is encountered, // a panic will occur. // // Platform errors are always printed, for information why please see: // // https://github.com/go-gl/glfw/issues/127 func acceptError(codes ...ErrorCode) error { // Grab the next error, if there is one. err := fetchError() if err == nil { return nil } // Only if the error has the specific error code accepted by the caller, do // we return the error. for _, code := range codes { if err.Code == code { return err } } // The error isn't accepted by the caller. If the error code is not a code // defined in the GLFW C documentation as a programmer error, then the // caller should have accepted it. This is effectively a bug in this // package. switch err.Code { case platformError: log.Println(err) return nil case notInitialized, noCurrentContext, invalidEnum, invalidValue, outOfMemory: panic(err) default: fmt.Println("GLFW: An invalid error was not accepted by the caller:", err) fmt.Println("GLFW: Please report this bug in the Go package immediately.") panic(err) } } // panicError is a helper used by functions which expect no errors (except // programmer errors) to occur. It will panic if it finds any such error. func panicError() { err := acceptError() if err != nil { panic(err) } } // fetchError fetches the next error from the error channel, it does not block // and returns nil if there is no error present. func fetchError() *Error { select { case err := <-lastError: return err default: return nil } }