diff options
101 files changed, 5471 insertions, 1278 deletions
@@ -7,6 +7,8 @@ CMakeFiles /install_manifest.txt /CMakeCache.txt +/CPackConfig.cmake +/CPackSourceConfig.cmake # ---------------------------------------------- # Ninja @@ -30,6 +32,9 @@ Makefile *.a *.so +/bin/gl_basic +/bin/gl_basic_test +/bin/simple-x11-egl /bin/wflinfo /doc/html/man/waffle.7.html /doc/html/man/waffle_attrib_list.3.html @@ -4,6 +4,7 @@ Arun Sl <arun.sl@tcs.com> Ben Widawsky <ben@bwidawsk.net> Brian Paul <brianp@vmware.com> Chad Versace <chad.versace@linux.intel.com> <chad@chad-versace.us> +Chad Versace <chad.versace@linux.intel.com> <chad@kiwitree.net> Emil Velikov <emil.l.velikov@gmail.com> Jeff Bland <jksb@linux.com> <jksb@member.fsf.org> Jordan Justen <jordan.l.justen@intel.com> <jljusten@gmail.com> @@ -7,9 +7,9 @@ waffle_top := $(LOCAL_PATH) # !!! The version must be updated in tandem with the CMakeLists !!! # waffle_major_version := 1 -waffle_minor_version := 4 -waffle_patch_version := 4 -waffle_api_version := 0x0104 +waffle_minor_version := 5 +waffle_patch_version := 0 +waffle_api_version := 0x0105 waffle_android_major_version := $(word 1, $(subst ., , $(PLATFORM_VERSION))) waffle_android_minor_version := $(word 2, $(subst ., , $(PLATFORM_VERSION))) @@ -27,8 +27,6 @@ $(waffle_top)/include/waffle/waffle_version.h: \ LOCAL_MODULE_TAGS := eng LOCAL_MODULE := libwaffle-$(waffle_major_version) -LOCAL_CC := $(TARGET_CC) -std=c99 - LOCAL_CFLAGS := \ -DANDROID_STUB \ -DWAFFLE_API_VERSION=$(waffle_api_version) \ @@ -38,7 +36,10 @@ LOCAL_CFLAGS := \ -DWAFFLE_ANDROID_MINOR_VERSION=$(waffle_android_minor_version) \ -Wno-pointer-arith +LOCAL_CFLAGS += -std=c99 + LOCAL_C_INCLUDES := \ + $(LOCAL_PATH)/include \ $(LOCAL_PATH)/include/waffle \ $(LOCAL_PATH)/src/ \ $(LOCAL_PATH)/src/waffle/api/ \ @@ -47,6 +48,7 @@ LOCAL_C_INCLUDES := \ $(LOCAL_PATH)/src/waffle/linux/ \ $(LOCAL_PATH)/src/waffle/droid/ \ $(LOCAL_PATH)/third_party/khronos/ \ + $(LOCAL_PATH)/third_party/threads/ LOCAL_SRC_FILES := \ src/waffle/core/wcore_tinfo.c \ @@ -71,18 +73,19 @@ LOCAL_SRC_FILES := \ src/waffle/egl/wegl_config.c \ src/waffle/egl/wegl_context.c \ src/waffle/egl/wegl_display.c \ + src/waffle/egl/wegl_platform.c \ src/waffle/egl/wegl_util.c \ src/waffle/egl/wegl_window.c \ src/waffle/android/droid_platform.c \ src/waffle/android/droid_display.c \ src/waffle/android/droid_window.c \ src/waffle/android/droid_surfaceflingerlink.cpp \ + third_party/threads/threads_posix.c LOCAL_SHARED_LIBRARIES := \ - libEGL \ libdl \ libutils \ - libgui \ + libgui LOCAL_GENERATED_SOURCES := \ $(LOCAL_PATH)/include/waffle/waffle_version.h @@ -93,7 +96,7 @@ LOCAL_COPY_HEADERS := \ include/waffle/waffle_glx.h \ include/waffle/waffle_version.h \ include/waffle/waffle_wayland.h \ - include/waffle/waffle_x11_egl.h \ + include/waffle/waffle_x11_egl.h LOCAL_COPY_HEADERS_TO := waffle-$(waffle_major_version) diff --git a/CMakeLists.txt b/CMakeLists.txt index 470cd67..6fabb54 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -29,6 +29,13 @@ cmake_minimum_required(VERSION 2.8.11) list(APPEND CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/cmake/Modules") +# Set the default location for all build artifacts to traditionally named +# top-level directories. CMake's default location for build artifacts varies +# per artifact and is hard-to-guess. +set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin") +set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/lib") +set(CMAKE_LIBRARY_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/lib") + include(WaffleDefineOS) include(WaffleFindDependencies) include(Options.cmake) @@ -93,6 +100,20 @@ include_directories( src ) +add_subdirectory(third_party/threads) +include_directories( + third_party/threads + ) +set(THREADS_LIBRARIES threads_bundled) + +if(MSVC) + add_subdirectory(third_party/getopt) + include_directories( + third_party/getopt + ) + set(GETOPT_LIBRARIES getopt_bundled) +endif() + add_subdirectory(doc) add_subdirectory(src) add_subdirectory(include) @@ -165,3 +186,25 @@ install( # ------------------------------------------------------------------------------ include(WafflePrintConfigurationSummary) + +set (CPACK_SET_DESTDIR ON) +set (CPACK_PACKAGE_VERSION_MAJOR ${waffle_major_version}) +set (CPACK_PACKAGE_VERSION_MINOR ${waffle_minor_version}) +set (CPACK_PACKAGE_VERSION_PATCH ${waffle_patch_version}) + +# cpack detects win64 vs win32 only when msvc is available +# reported upstream, fix (likely post cmake 3.0) pending +if (MINGW) + if (CMAKE_SIZEOF_VOID_P EQUAL 8) + set (CPACK_SYSTEM_NAME win64) + endif () +endif () + +# See http://www.vtk.org/Wiki/CMake:CPackPackageGenerators +if (WIN32) + set (CPACK_GENERATOR "ZIP") +else () + set (CPACK_GENERATOR "TBZ2") +endif () + +include(CPack) @@ -50,10 +50,12 @@ Waffle, see the following: Build Requirements ================== +Waffle uses CMake for its build system. + Linux ----- - -Waffle uses CMake for it build system. +On Linux it's recommended to install the cmake package using your +distribution package manager. Archlinux: pacman -S cmake Fedora 17: yum install cmake @@ -97,6 +99,53 @@ a comman-separated list of any combination of "x11", "wayland", and "drm". - Debian: apt-get install libgbm-dev libudev-dev +Windows - cross-building under Linux +------------------------------------ +Make sure that CMake is installed on your system. + + Archlinux: pacman -S cmake + Fedora 17: yum install cmake + Debian: apt-get install cmake + +The MinGW-W64 cross-build toolchain is recommended and its CMake wrapper. + + Archlinux: pacman -S mingw-w64-gcc mingw-w64-cmake (latter is in AUR) + Fedora 17: yum install FINISHME + Debian: apt-get install FINISHME + + +Windows - native builds +----------------------- +Download and install the latest version CMake from the official website: + + http://cmake.org/ + +Install Microsoft Visual Studio 2013* or later. +Install 'Visual C++' feature. + +Download OpenGL Core API and Extension Header Files. + + http://www.opengl.org/registry/#headers + +Copy the header files to MSVC. + + C:\Program Files\Microsoft Visual Studio 12.0\VC\include\GL + + +[*] Waffle heavily requires on features introduced by the C99 standard. As +such only reasonable compiler (at the time of writing) from the Microsoft +Visual Compiler series is MSVC 2013. Building with older versions is likely +to be broken. + +Windows - CYGWIN +---------------- +Waffle is not tested to build under CYGWIN and is likely to be broken. +Patches addressing it are more than welcome. + +For build requirements, build and installation instructions, refer to the +Linux notes in the relevant sections. + + Build and Installation ====================== @@ -114,6 +163,9 @@ or 1. Configure pkg-config ----------------------- +Compiling for Windows does require any additional dependencies, as such +this step can be omitted. + If any of Waffle's dependencies are installed in custom locations, you must set the PKG_CONFIG_PATH environment variable. For example, if you installed a dependeny into /usr/local, then: @@ -123,23 +175,26 @@ a dependeny into /usr/local, then: 2. Configure CMake ------------------ + +2.1 Linux and Mac +----------------- On Linux and Mac, running CMake with no arguments as below will configure Waffle for a release build (optimized compiler flags and basic debug symbols) and will auto-enable support for features whose dependencies are installed: cmake . -To manually control the configuration process, or to later modify the an already-configured source tree, -run one of the following: +To manually control the configuration process, or to later modify the an +already-configured source tree, run one of the following: # An ncurses interface to CMake configuration. - ccamke $BUILD_DIR + ccmake . # A graphical Qt interface to CMake configuration. - cmake-gui $BUILD_DIR + cmake-gui . # Edit the raw configuration file. - vim $BUILD_DIR/CMakeCache.txt + vim CMakeCache.txt All configuration options can also be set on the command line during the *initial* invocation of cmake. For example, to configure Waffle for a debug @@ -147,17 +202,117 @@ build, require support for Wayland, and install into '/usr' instead of '/usr/local', run the following: cmake -DCMAKE_BUILD_TYPE=Debug \ - -DCMAKE_INSTALL_PREFIX=/usr/local \ + -DCMAKE_INSTALL_PREFIX=/usr \ -Dwaffle_has_wayland=1 \ . +2.2 Windows - cross-building under Linux +---------------------------------------- +The following sh snippet can be used to ease the configuration process. + + _architectures="i686-w64-mingw32 x86_64-w64-mingw32" + unset LDFLAGS + for _arch in ${_architectures}; do + _install_prefix=/usr/${_arch} + mkdir -p build-${_arch} && pushd build-${_arch} + ${_arch}-cmake .. \ + -DCMAKE_INSTALL_PREFIX=${_install_prefix} \ + -DCMAKE_INSTALL_LIBDIR=${_install_prefix}/lib \ + \ + -Dwaffle_build_tests=0 \ + -Dwaffle_build_examples=1 + make + popd + done + +Make sure to adjust _install_prefix to "" if the resulting library will +not be used for further cross-building. + + +2.3 Windows - native builds +--------------------------- + +For native Windows builds, one must provide a generator argument and +optionally a toolset if the resulting library must be compatible with +Windows XP. When the resulting library is to be 64bit "Win64" needs to be +appended to the generator argument. + + @echo Configuring Waffle as Windows Vista compatible 32bit library + cmake -G "Visual Studio 12" -H%CD% -B%CD%\build\msvc32 -DCMAKE_INSTALL_PREFIX="" + + @echo Configuring Waffle as Windows Vista compatible 64bit library + cmake -G "Visual Studio 12 Win64" -H%CD% -B%CD%\build\msvc64 -DCMAKE_INSTALL_PREFIX="" + + @echo Configuring Waffle as Windows XP compatible 32bit library + cmake -G "Visual Studio 12" -T "v120_xp" -H%CD% -B%CD%\build\msvc32 -DCMAKE_INSTALL_PREFIX="" + +For alternative control of the configuration process, or to later modify an +already-configured source tree, run the graphical Qt interface via: + + cmake-gui + 3. Build and Install -------------------- +The following commands build Waffle, run its tests, installs the project and +creates a binary archive in a platform agnostic way. + +Note that not all steps may be required in your case and the configuration +settings (cache) are located in the current directory as indicated by ".". + + cmake --build . + cmake --build . --target check + cmake --build . --target check-func + cmake --build . --target install + cpack + +Calling `cmake ... check` only runs unittests that do not access the native +OpenGL platform. To run additional functional tests, which do access the +native OpenGL platform, call `cmake ... check-func`. + +3.1 Linux and Mac +----------------- +On Linux and Mac the default CMake generator is Unix Makefiles, as such we +can use an alternative version of the above commands: make make check + make check-func make install + make package + + +3.2 Windows - cross-building under Linux +---------------------------------------- + + _architectures="i686-w64-mingw32 x86_64-w64-mingw32" + unset LDFLAGS + for _arch in ${_architectures}; do + pushd build-${_arch} + make + make install + make package + popd + done + +Note: Running the tests (`make check` and/or `make check-func`) is not tested +but may work if the appropriate environment is setup via wine. + + +3.3 Windows - native builds +--------------------------- +One can manage the build/install/package process via Visual Studio's GUI +or via the command line. + +When using the GUI open .\build\msvc*\waffle-VERSION.sln, where * can be +either 32 or 64 depending on your requirements. + +If building via the command line, navigate to the correct folder and invoke +the desired command as outlined in `Section 3. Build and Install` + +For example the following will build 32bit Waffle and will package/archive +it into a file called waffle1-VERSION-win32.zip. -Calling `make check` only runs unittests that do not access the native OpenGL -platform. To run additional functional tests, which do access the native -OpenGL platform, call `make check-func`. + @echo Preparing to build 32 bit version of waffle + cd .\build\msvc32 + cmake --build . + cpack diff --git a/cmake/Modules/WaffleDefineCompilerFlags.cmake b/cmake/Modules/WaffleDefineCompilerFlags.cmake index 4d149c8..710b7e0 100644 --- a/cmake/Modules/WaffleDefineCompilerFlags.cmake +++ b/cmake/Modules/WaffleDefineCompilerFlags.cmake @@ -33,49 +33,98 @@ function(waffle_add_c_flag flag var) endif() endfunction() -set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} --std=c99") -set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall") -set(CMAKE_C_FLAGS_DEBUG "-g3 -O0 -DDEBUG") +if (NOT MSVC) + # + # compiler flags + # + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} --std=c99") + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall") + set(CMAKE_C_FLAGS_DEBUG "-g3 -O0 -DDEBUG") -# Use '-g1' to produce enough debug info for generating backtraces, but not -# enough for single-stepping. -set(CMAKE_C_FLAGS_RELEASE "-g1 -O2 -DNDEBUG") + # Use '-g1' to produce enough debug info for generating backtraces, but not + # enough for single-stepping. + set(CMAKE_C_FLAGS_RELEASE "-g1 -O2 -DNDEBUG") -waffle_add_c_flag("-Werror=implicit-function-declaration" WERROR_IMPLICIT_FUNCTION_DECLARATION) -waffle_add_c_flag("-Werror=incompatible-pointer-types" WERROR_INCOMPATIBLE_POINTER_TYPES) -waffle_add_c_flag("-Werror=int-conversion" WERROR_INT_CONVERSION) + waffle_add_c_flag("-Werror=implicit-function-declaration" WERROR_IMPLICIT_FUNCTION_DECLARATION) + waffle_add_c_flag("-Werror=incompatible-pointer-types" WERROR_INCOMPATIBLE_POINTER_TYPES) + waffle_add_c_flag("-Werror=int-conversion" WERROR_INT_CONVERSION) + waffle_add_c_flag("-fvisibility=hidden" WITH_VISIBILITY_HIDDEN) -if(waffle_on_linux) - # On MacOS, the SSE2 headers trigger this error. - waffle_add_c_flag("-Werror=missing-prototypes" WERROR_MISSING_PROTOTYPES) -endif() + if(waffle_on_linux) + # On MacOS, the SSE2 headers trigger this error. + waffle_add_c_flag("-Werror=missing-prototypes" WERROR_MISSING_PROTOTYPES) + endif() + + if(MINGW) + # Avoid depending on MinGW runtime DLLs + check_c_compiler_flag(-static-libgcc HAVE_STATIC_LIBGCC_FLAG) + if (HAVE_STATIC_LIBGCC_FLAG) + set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -static-libgcc") + set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -static-libgcc") + set(CMAKE_MODULE_LINKER_FLAGS "${CMAKE_MODULE_LINKER_FLAGS} -static-libgcc") + endif () + else() + # Avoid using TLS with MinGW builds. + waffle_check_thread_local_storage() + endif() +else() + # XXX: and update the threads code + # http://msdn.microsoft.com/en-us/library/aa383745.aspx + if(CMAKE_GENERATOR_TOOLSET MATCHES "_xp$") + # Windows XP + add_definitions(-D_WIN32_WINNT=0x0501 -DWINVER=0x0501) + else() + # Windows 7 + add_definitions(-D_WIN32_WINNT=0x0601 -DWINVER=0x0601) + endif() -waffle_check_thread_local_storage() + # Adjust warnings + add_definitions(-D_CRT_NONSTDC_NO_WARNINGS -D_CRT_SECURE_NO_WARNINGS) -if(waffle_has_tls) - add_definitions(-DWAFFLE_HAS_TLS) -endif() + # Use static runtime + # http://www.cmake.org/Wiki/CMake_FAQ#How_can_I_build_my_MSVC_application_with_a_static_runtime.3F + foreach(flag_var + CMAKE_C_FLAGS CMAKE_C_FLAGS_DEBUG CMAKE_C_FLAGS_RELEASE CMAKE_C_FLAGS_MINSIZEREL CMAKE_C_FLAGS_RELWITHDEBINFO + ) + if(${flag_var} MATCHES "/MD") + string (REGEX REPLACE "/MD" "/MT" ${flag_var} "${${flag_var}}") + endif() + endforeach(flag_var) -if(waffle_has_tls_model_initial_exec) - add_definitions(-DWAFFLE_HAS_TLS_MODEL_INITIAL_EXEC) endif() if(waffle_on_mac) add_definitions(-DWAFFLE_HAS_CGL) endif() -if(waffle_has_glx) - add_definitions(-DWAFFLE_HAS_GLX) -endif() +if(waffle_on_linux) + if(waffle_has_glx) + add_definitions(-DWAFFLE_HAS_GLX) + endif() -if(waffle_has_wayland) - add_definitions(-DWAFFLE_HAS_WAYLAND) -endif() + if(waffle_has_wayland) + add_definitions(-DWAFFLE_HAS_WAYLAND) + endif() + + if(waffle_has_x11_egl) + add_definitions(-DWAFFLE_HAS_X11_EGL) + endif() + + if(waffle_has_gbm) + add_definitions(-DWAFFLE_HAS_GBM) + endif() + + if(waffle_has_tls) + add_definitions(-DWAFFLE_HAS_TLS) + endif() + + if(waffle_has_tls_model_initial_exec) + add_definitions(-DWAFFLE_HAS_TLS_MODEL_INITIAL_EXEC) + endif() -if(waffle_has_x11_egl) - add_definitions(-DWAFFLE_HAS_X11_EGL) + add_definitions(-D_XOPEN_SOURCE=600) endif() -if(waffle_has_gbm) - add_definitions(-DWAFFLE_HAS_GBM) +if(waffle_on_windows) + add_definitions(-DWAFFLE_HAS_WGL) endif() diff --git a/cmake/Modules/WaffleDefineOS.cmake b/cmake/Modules/WaffleDefineOS.cmake index c3ec3bb..38013a1 100644 --- a/cmake/Modules/WaffleDefineOS.cmake +++ b/cmake/Modules/WaffleDefineOS.cmake @@ -27,6 +27,8 @@ if("${CMAKE_SYSTEM_NAME}" STREQUAL "Linux") set(waffle_on_linux true) elseif("${CMAKE_SYSTEM_NAME}" STREQUAL "Darwin") set(waffle_on_mac true) +elseif("${CMAKE_SYSTEM_NAME}" STREQUAL "Windows") + set(waffle_on_windows true) else() message(FATAL_ERROR "Unrecognized CMAKE_SYSTEM_NAME=\"${CMAKE_SYSTEM_NAME}\"") endif() diff --git a/cmake/Modules/WaffleDefineVersion.cmake b/cmake/Modules/WaffleDefineVersion.cmake index c665b93..4965c0a 100644 --- a/cmake/Modules/WaffleDefineVersion.cmake +++ b/cmake/Modules/WaffleDefineVersion.cmake @@ -39,8 +39,8 @@ # Bump this to x.y.90 immediately after each waffle-x.y.0 release. # set(waffle_major_version "1") -set(waffle_minor_version "4") -set(waffle_patch_version "4") +set(waffle_minor_version "5") +set(waffle_patch_version "0") set(waffle_version "${waffle_major_version}.${waffle_minor_version}.${waffle_patch_version}") diff --git a/cmake/Modules/WaffleFindDependencies.cmake b/cmake/Modules/WaffleFindDependencies.cmake index 9245772..3fd7338 100644 --- a/cmake/Modules/WaffleFindDependencies.cmake +++ b/cmake/Modules/WaffleFindDependencies.cmake @@ -80,3 +80,8 @@ if(waffle_on_linux) waffle_pkg_config(gbm gbm) waffle_pkg_config(libudev libudev) endif() + + +if(waffle_on_windows) + find_path(GLEXT_INCLUDE_DIR NAMES GL/wglext.h DOC "Include for GL/wglext.h") +endif() diff --git a/cmake/Modules/WafflePrintConfigurationSummary.cmake b/cmake/Modules/WafflePrintConfigurationSummary.cmake index 2b66fae..1f38db5 100644 --- a/cmake/Modules/WafflePrintConfigurationSummary.cmake +++ b/cmake/Modules/WafflePrintConfigurationSummary.cmake @@ -32,6 +32,9 @@ message("Configuration summary") message("-----------------------------------------------") message("") message("Supported platforms: ") +if(waffle_on_mac) + message(" cgl") +endif() if(waffle_has_glx) message(" glx") endif() @@ -44,15 +47,16 @@ endif() if(waffle_has_gbm) message(" gbm") endif() +if(waffle_on_windows) + message(" wgl") +endif() message("") message("Dependencies:") if(waffle_has_egl) message(" egl_INCLUDE_DIRS: ${egl_INCLUDE_DIRS}") - message(" egl_LDFLAGS: ${egl_LDFLAGS}") endif() if(waffle_has_glx) message(" gl_INCLUDE_DIRS: ${gl_INCLUDE_DIRS}") - message(" gl_LDFLAGS: ${gl_LDFLAGS}") endif() if(waffle_has_wayland) message(" wayland-client_INCLUDE_DIRS: ${wayland-client_INCLUDE_DIRS}") @@ -66,7 +70,6 @@ if(waffle_has_x11) endif() if(waffle_has_gbm) message(" gbm_INCLUDE_DIRS: ${gbm_INCLUDE_DIRS}") - message(" gbm_LDFLAGS: ${gbm_LDFLAGS}") endif() message("") message("Build type:") diff --git a/cmake/Modules/WaffleValidateOptions.cmake b/cmake/Modules/WaffleValidateOptions.cmake index d9fc299..ea60b0e 100644 --- a/cmake/Modules/WaffleValidateOptions.cmake +++ b/cmake/Modules/WaffleValidateOptions.cmake @@ -135,4 +135,17 @@ elseif(waffle_on_mac) if(waffle_has_x11_egl) message(FATAL_ERROR "Option is not supported on Darwin: waffle_has_x11_egl.") endif() +elseif(waffle_on_windows) + if(waffle_has_gbm) + message(FATAL_ERROR "Option is not supported on Windows: waffle_has_gbm.") + endif() + if(waffle_has_glx) + message(FATAL_ERROR "Option is not supported on Windows: waffle_has_glx.") + endif() + if(waffle_has_wayland) + message(FATAL_ERROR "Option is not supported on Windows: waffle_has_wayland.") + endif() + if(waffle_has_x11_egl) + message(FATAL_ERROR "Option is not supported on Windows: waffle_has_x11_egl.") + endif() endif() diff --git a/debian/changelog b/debian/changelog index 04dc41e..361f2d9 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,3 +1,15 @@ +waffle (1.5.0-1) unstable; urgency=low + + * New upstream release + + -- Jordan Justen <jordan.l.justen@intel.com> Tue, 16 Dec 2014 11:06:19 -0800 + +waffle (1.5.0~rc1-1) unstable; urgency=low + + * New upstream release candidate + + -- Jordan Justen <jordan.l.justen@intel.com> Thu, 04 Dec 2014 23:18:00 -0800 + waffle (1.4.3-1) unstable; urgency=low * New upstream release diff --git a/doc/release-notes/waffle-1.5.0.txt b/doc/release-notes/waffle-1.5.0.txt new file mode 100644 index 0000000..8ca5028 --- /dev/null +++ b/doc/release-notes/waffle-1.5.0.txt @@ -0,0 +1,50 @@ +Waffle 1.5.0 Release Notes +========================== + +Backwards compatibility notes +----------------------------- +Waffle 1.5.0 is backwards-compatible with Waffle 1.4.0, in ABI and API. + + +New Features +------------ +* Windows: Experimental support for WGL [Emil Velikov] + + Emil graciously added support for the Windows WGL API this summer, + represented by the new enum 'WAFFLE_PLATFORM_WGL'. The plan is to convert + Piglit's Windows build to use this rather than GLUT. + +* wflinfo now prints the OpenGL shading language version. [Dylan Baker] + +* Linux: Some library dependencies converted from static to dynamic [Emil Velikov] + + libwaffle on Linux no longer statically links to, and instead dynamically + opens, the libraries below. This change allows a single build of Waffle to be + deployed onto systems with and without these libraries. + + libEGL + libGL + libgbm + +* Android: Experimental support for Android Lollipop. (Waffle builds on + Lollipop but may have critical bugs). [Yongqin Liu] + + +Bugfixes since 1.4.0 +-------------------- +* cmake: Fix building with CMake 2.8.11 +* glx: Fix requirements for creation of ES2 context +* wflinfo used glGetStringi incorrectly on Mali drivers. +* gbm: Workaround Mesa linkage issue +* gbm: Remove Mesa warning evoked by waffle_window_swap_buffers(). + + +Contributors since Waffle 1.4.0 +------------------------------- + 80 Emil Velikov <emil.l.velikov@gmail.com> + 39 Chad Versace <chad.versace@linux.intel.com> + 9 Jordan Justen <jordan.l.justen@intel.com> + 2 Frank Henigman <fjhenigman@chromium.org> + 2 Yongqin Liu <yongqin.liu@linaro.org> + 1 Dylan Baker <dylanx.c.baker@intel.com> + 1 José Fonseca <jfonseca@vmware.com> diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt index 8392ecb..281ef47 100644 --- a/examples/CMakeLists.txt +++ b/examples/CMakeLists.txt @@ -21,7 +21,7 @@ endif() # ---------------------------------------------------------------------------- add_executable(gl_basic gl_basic.c) -target_link_libraries(gl_basic ${waffle_libname}) +target_link_libraries(gl_basic ${waffle_libname} ${GETOPT_LIBRARIES}) if(waffle_on_mac) set_target_properties(gl_basic diff --git a/examples/gl_basic.c b/examples/gl_basic.c index 8e1a28c..69418c8 100644 --- a/examples/gl_basic.c +++ b/examples/gl_basic.c @@ -43,7 +43,11 @@ #include <stdio.h> #include <stdlib.h> #include <string.h> +#if !defined(_WIN32) #include <time.h> +#else +#include <windows.h> +#endif #ifdef __APPLE__ # import <Foundation/NSAutoreleasePool.h> @@ -57,13 +61,14 @@ removeXcodeArgs(int *argc, char **argv); static const char *usage_message = "usage:\n" - " gl_basic --platform=android|cgl|gbm|glx|wayland|x11_egl\n" + " gl_basic --platform=android|cgl|gbm|glx|wayland|wgl|x11_egl\n" " --api=gl|gles1|gles2|gles3\n" " [--version=MAJOR.MINOR]\n" " [--profile=core|compat|none]\n" " [--forward-compatible]\n" " [--debug]\n" " [--resize-window]\n" + " [--window-size=WIDTHxHEIGHT]\n" "\n" "examples:\n" " gl_basic --platform=glx --api=gl\n" @@ -92,6 +97,7 @@ enum { OPT_DEBUG, OPT_FORWARD_COMPATIBLE, OPT_RESIZE_WINDOW, + OPT_WINDOW_SIZE, }; static const struct option get_opts[] = { @@ -102,10 +108,19 @@ static const struct option get_opts[] = { { .name = "debug", .has_arg = no_argument, .val = OPT_DEBUG }, { .name = "forward-compatible", .has_arg = no_argument, .val = OPT_FORWARD_COMPATIBLE }, { .name = "resize-window", .has_arg = no_argument, .val = OPT_RESIZE_WINDOW }, + { .name = "window-size", .has_arg = required_argument, .val = OPT_WINDOW_SIZE }, { 0 }, }; -static void __attribute__((noreturn)) +#if defined(__GNUC__) +#define NORETURN __attribute__((noreturn)) +#elif defined(_MSC_VER) +#define NORETURN __declspec(noreturn) +#else +#define NORETURN +#endif + +static void NORETURN error_printf(const char *fmt, ...) { va_list ap; @@ -120,7 +135,8 @@ error_printf(const char *fmt, ...) exit(EXIT_FAILURE); } -static void __attribute__((noreturn)) + +static void NORETURN usage_error_printf(const char *fmt, ...) { fflush(stdout); @@ -177,15 +193,23 @@ enum { GL_CONTEXT_FLAG_DEBUG_BIT = 0x00000002, }; -#define WINDOW_WIDTH 320 -#define WINDOW_HEIGHT 240 +static int window_width = 320; +static int window_height = 240; -static void (*glClearColor)(GLclampf red, GLclampf green, GLclampf blue, GLclampf alpha); -static void (*glClear)(GLbitfield mask); -static void (*glGetIntegerv)(GLenum pname, GLint *params); -static void (*glReadPixels)(GLint x, GLint y, GLsizei width, GLsizei height, - GLenum format, GLenum type, GLvoid* data); -static void (*glViewport)(GLint x, GLint y, GLsizei width, GLsizei height); +#ifndef _WIN32 +#define APIENTRY +#else +#ifndef APIENTRY +#define APIENTRY __stdcall +#endif +#endif + +static void (APIENTRY *glClearColor)(GLclampf red, GLclampf green, GLclampf blue, GLclampf alpha); +static void (APIENTRY *glClear)(GLbitfield mask); +static void (APIENTRY *glGetIntegerv)(GLenum pname, GLint *params); +static void (APIENTRY *glReadPixels)(GLint x, GLint y, GLsizei width, GLsizei height, + GLenum format, GLenum type, GLvoid* data); +static void (APIENTRY *glViewport)(GLint x, GLint y, GLsizei width, GLsizei height); /// @brief Command line options. struct options { @@ -220,6 +244,7 @@ static const struct enum_map platform_map[] = { {WAFFLE_PLATFORM_GBM, "gbm" }, {WAFFLE_PLATFORM_GLX, "glx" }, {WAFFLE_PLATFORM_WAYLAND, "wayland" }, + {WAFFLE_PLATFORM_WGL, "wgl" }, {WAFFLE_PLATFORM_X11_EGL, "x11_egl" }, {0, 0 }, }; @@ -326,6 +351,15 @@ parse_args(int argc, char *argv[], struct options *opts) case OPT_RESIZE_WINDOW: opts->resize_window = true; break; + case OPT_WINDOW_SIZE: { + int match_count; + match_count = sscanf(optarg, "%dx%d", &window_width, &window_height); + if (match_count != 2) { + usage_error_printf("'%s' is not a valid window geometry", + optarg); + } + break; + } default: abort(); loop_get_opt = false; @@ -367,14 +401,16 @@ draw(struct waffle_window *window, bool resize) { bool ok; unsigned char *colors; - int width = WINDOW_WIDTH; - int height = WINDOW_HEIGHT; + int width = window_width; + int height = window_height; +#if !defined(_WIN32) static const struct timespec sleep_time = { // 0.5 sec .tv_sec = 0, .tv_nsec = 500000000, }; +#endif for (int i = 0; i < 3; ++i) { switch (i) { @@ -419,7 +455,11 @@ draw(struct waffle_window *window, bool resize) if (!ok) return false; +#if !defined(_WIN32) nanosleep(&sleep_time, NULL); +#else + Sleep(500); +#endif } return true; @@ -496,6 +536,8 @@ main(int argc, char **argv) struct waffle_context *ctx; struct waffle_window *window; + GLint context_flags = 0; + #ifdef __APPLE__ cocoa_init(); #endif @@ -586,7 +628,7 @@ main(int argc, char **argv) if (!ctx) error_waffle(); - window = waffle_window_create(config, WINDOW_WIDTH, WINDOW_HEIGHT); + window = waffle_window_create(config, window_width, window_height); if (!window) error_waffle(); @@ -594,7 +636,6 @@ main(int argc, char **argv) if (!ok) error_waffle(); - GLint context_flags = 0; if (opts.context_forward_compatible || opts.context_debug) { glGetIntegerv(GL_CONTEXT_FLAGS, &context_flags); } diff --git a/examples/simple-x11-egl.c b/examples/simple-x11-egl.c index ed26eaa..9e218ac 100644 --- a/examples/simple-x11-egl.c +++ b/examples/simple-x11-egl.c @@ -49,8 +49,16 @@ enum { GL_COLOR_BUFFER_BIT = 0x00004000, }; -static void (*glClearColor)(GLclampf red, GLclampf green, GLclampf blue, GLclampf alpha); -static void (*glClear)(GLbitfield mask); +#ifndef _WIN32 +#define APIENTRY +#else +#ifndef APIENTRY +#define APIENTRY __stdcall +#endif +#endif + +static void (APIENTRY *glClearColor)(GLclampf red, GLclampf green, GLclampf blue, GLclampf alpha); +static void (APIENTRY *glClear)(GLbitfield mask); // --------------------------------------------- diff --git a/include/c99_compat.h b/include/c99_compat.h new file mode 100644 index 0000000..297df31 --- /dev/null +++ b/include/c99_compat.h @@ -0,0 +1,113 @@ +// Copyright 2012 Intel Corporation +// +// 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. +// +// 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 HOLDER 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. + +#pragma once + +/* + * C99 restrict keyword + */ +#ifndef restrict +# if defined(__GNUC__) +# define restrict __restrict__ +# elif defined(_MSC_VER) + /* leave empty to prevent compiler wows - decl vs. def diff */ +# define restrict +# else +# define restrict +# endif +#endif + +/* + * C99 inline keyword + */ +#ifndef inline +# if defined(__GNUC__) +# define inline __inline__ +# elif defined(_MSC_VER) +# define inline __inline +# elif defined(__ICL) +# define inline __inline +# else +# define inline +# endif +#endif + +/* + * C99 string manipulation functions - snprintf, strcasecmp + * + * http://stackoverflow.com/questions/2915672/snprintf-and-visual-studio-2010 + */ +#if defined(_MSC_VER) +#include <stdarg.h> // for va_start, va_end +#include <stdio.h> // for _vscprintf, _vscprintf_s +#include <string.h> + +#define strcasecmp _stricmp +#define snprintf c99_snprintf +#define vsnprintf c99_vsnprintf + +static inline int +c99_vsnprintf(char* str, size_t size, const char* format, va_list ap) +{ + int count = -1; + + if (size != 0) + count = _vsnprintf_s(str, size, _TRUNCATE, format, ap); + if (count == -1) + count = _vscprintf(format, ap); + + return count; +} + +static inline int +c99_snprintf(char* str, size_t size, const char* format, ...) +{ + int count; + va_list ap; + + va_start(ap, format); + count = c99_vsnprintf(str, size, format, ap); + va_end(ap); + + return count; +} + +#else +#include <strings.h> + +#endif + +/* + * strerror_r - strictly speaking not C99 function, but it's been around of + * ages in one shape or another. + * + * Under *NIX there are three flavours - XSI-compliant (POSIX.1-2001) + * or not, vs GNU-specific one. The POSIX one is available only under *NIX + * and is guaranteed to be thread-safe (as used in waffle). + * Under Windows the function is not thread-safe and thus it's depreciated. + */ +#if defined(_WIN32) +#define strerror_r(errno,buf,len) strerror_s(buf,len,errno) +#endif diff --git a/include/waffle/waffle.h b/include/waffle/waffle.h index 91f3434..e04b23f 100644 --- a/include/waffle/waffle.h +++ b/include/waffle/waffle.h @@ -103,6 +103,7 @@ enum waffle_enum { WAFFLE_PLATFORM_WAYLAND = 0x0014, WAFFLE_PLATFORM_X11_EGL = 0x0015, WAFFLE_PLATFORM_GBM = 0x0016, + WAFFLE_PLATFORM_WGL = 0x0017, // ------------------------------------------------------------------ // For waffle_config_choose() diff --git a/include/waffle_test/waffle_test.h b/include/waffle_test/waffle_test.h index c7517e8..32cd2b6 100644 --- a/include/waffle_test/waffle_test.h +++ b/include/waffle_test/waffle_test.h @@ -47,10 +47,22 @@ testgroup_##group##_setup, \ testgroup_##group##_teardown) +/// This must be called only from test suite s passed to wt_main(). +#define TEST_RUN2(group, displayname, testname) \ + wt_runner_run_test( \ + #group, #displayname, \ + test_##group##_##testname, \ + testgroup_##group##_setup, \ + testgroup_##group##_teardown) + /// @param test_runners is a list of functions that call TEST_RUN(). The list /// is a null-terminated. /// @return number of failed tests. +#ifdef _WIN32 +int wt_main(int *argc, char **argv, void (__stdcall *test_suites[])(void)); +#else int wt_main(int *argc, char **argv, void (*test_suites[])(void)); +#endif // _WIN32 #define TEST_PASS() wt_test_pass() #define TEST_SKIP() wt_test_skip() diff --git a/man/waffle_display.3.xml b/man/waffle_display.3.xml index 755c632..824a01a 100644 --- a/man/waffle_display.3.xml +++ b/man/waffle_display.3.xml @@ -93,7 +93,7 @@ struct waffle_display; given to <citerefentry><refentrytitle><function>waffle_init</function></refentrytitle><manvolnum>3</manvolnum></citerefentry>. </para> <para> - On Android and CGL, <parameter>name</parameter> is ignored. + On Android, CGL and WGL <parameter>name</parameter> is ignored. </para> <para> On the X11 platforms, GLX and X11/EGL, the function connects to the X11 display with the given diff --git a/man/waffle_dl.3.xml b/man/waffle_dl.3.xml index b089b93..463081f 100644 --- a/man/waffle_dl.3.xml +++ b/man/waffle_dl.3.xml @@ -74,7 +74,7 @@ <filename>libGL.so.1</filename>, <filename>libGLESv1_CM.so</filename>, <filename>libGLESv2.so</filename>, and - <filename>libGLESv2.so</filename>, repectively. + <filename>libGLESv2.so</filename>, respectively. </para> <variablelist> diff --git a/man/waffle_enum.3.xml b/man/waffle_enum.3.xml index c6ad548..a96bd20 100644 --- a/man/waffle_enum.3.xml +++ b/man/waffle_enum.3.xml @@ -100,6 +100,7 @@ enum waffle_enum { WAFFLE_PLATFORM_WAYLAND = 0x0014, WAFFLE_PLATFORM_X11_EGL = 0x0015, WAFFLE_PLATFORM_GBM = 0x0016, + WAFFLE_PLATFORM_WGL = 0x0017, // ------------------------------------------------------------------ // For waffle_config_choose() diff --git a/man/waffle_get_proc_address.3.xml b/man/waffle_get_proc_address.3.xml index 36b9ea2..333e127 100644 --- a/man/waffle_get_proc_address.3.xml +++ b/man/waffle_get_proc_address.3.xml @@ -66,10 +66,13 @@ On CGL, this function returns <constant>NULL</constant> because there exists no <function>CGLGetProcAdress()</function>. + + On WGL, this redirects to + <citerefentry><refentrytitle><function>wglGetProcAddress</function></refentrytitle><manvolnum>3</manvolnum></citerefentry>. </para> <para> - Some aspects of this function's behavior is platform-specific and non-intuitive. + Some aspects of this function's behavior are platform-specific and non-intuitive. For example, @@ -89,6 +92,14 @@ then <function>waffle_get_proc_address()</function> may return a <constant>NULL</constant>. </para> </listitem> + + <listitem> + <para> + Under Windows (WGL) a current context must be available before executing the function. + + Otherwise <function>waffle_get_proc_address()</function> may return a <constant>NULL</constant>. + </para> + </listitem> </itemizedlist> </para> @@ -99,7 +110,9 @@ the <ulink url="http://www.opengl.org/registry/doc/glx1.4.pdf">GLX 1.4 Specification</ulink> - or the <ulink url="http://www.khronos.org/registry/egl/specs/eglspec.1.4.20110406.pdf">EGL 1.4 Specification</ulink>. + the <ulink url="http://www.khronos.org/registry/egl/specs/eglspec.1.4.20110406.pdf">EGL 1.4 Specification</ulink> + + or the <ulink url="http://msdn.microsoft.com/en-gb/library/windows/desktop/dd374386(v=vs.85).aspx">MSDN article</ulink>. </para> </listitem> </varlistentry> diff --git a/man/waffle_init.3.xml b/man/waffle_init.3.xml index a1b9cd3..a22723d 100644 --- a/man/waffle_init.3.xml +++ b/man/waffle_init.3.xml @@ -129,6 +129,14 @@ </listitem> </varlistentry> <varlistentry> + <term><constant>WAFFLE_PLATFORM_WGL</constant></term> + <listitem> + <para> + [Windows] Use WGL on Windows. + </para> + </listitem> + </varlistentry> + <varlistentry> <term><constant>WAFFLE_PLATFORM_X11_EGL</constant></term> <listitem> <para> diff --git a/man/waffle_is_extension_in_string.3.xml b/man/waffle_is_extension_in_string.3.xml index 2734ea2..2808639 100644 --- a/man/waffle_is_extension_in_string.3.xml +++ b/man/waffle_is_extension_in_string.3.xml @@ -69,7 +69,7 @@ Note that this function is not restricted to the OpenGL extension string; - it can also be used on the GLX and EGL extension strings. + it can also be used on the WGL, GLX and EGL extension strings. </para> <para> diff --git a/man/wflinfo.1.xml b/man/wflinfo.1.xml index 4060f38..c75dcf7 100644 --- a/man/wflinfo.1.xml +++ b/man/wflinfo.1.xml @@ -76,6 +76,7 @@ <member>gbm</member> <member>glx</member> <member>wayland</member> + <member>wgl</member> <member>x11_egl</member> </simplelist> </para> diff --git a/pkg/archlinux/mingw-w64-waffle/PKGBUILD b/pkg/archlinux/mingw-w64-waffle/PKGBUILD new file mode 100644 index 0000000..c563948 --- /dev/null +++ b/pkg/archlinux/mingw-w64-waffle/PKGBUILD @@ -0,0 +1,86 @@ +# Maintainer: Chad Versace <chad.versace@linux.intel.com> + +pkgname='mingw-w64-waffle' +pkgver='1.3.0' +pkgrel=1 +pkgdesc='a library for choosing window system and OpenGL API at runtime (mingw-w64)' +arch=('any') +url='http://waffle-gl.github.io' +license=('BSD') + +depends=( + 'mingw-w64-crt>=3.1.0-3' + ) +makedepends=( + 'mingw-w64-cmake' + + # For building the docs. +# XXX: Add as soon as we enable docs/manpages +# 'libxslt' +# 'docbook-xsl' + + ) + +options=('!strip' '!buildflags' 'staticlibs') +_architectures="i686-w64-mingw32 x86_64-w64-mingw32" + +if [[ ! -v _srcroot ]]; then + msg "Environment variable _srcroot is unset" + msg "Fall back to using git worktree for _srcroot ..." + _srcroot="$(git rev-parse --show-toplevel)" || exit + msg "Using _srcroot=$_srcroot" +fi + +build() { + unset LDFLAGS + cd "${_srcroot}" + msg "Building mingw-w64-waffle for cross-building" + for _arch in ${_architectures}; do + mkdir -p build-${_arch} && pushd build-${_arch} + ${_arch}-cmake .. \ + -DCMAKE_INSTALL_PREFIX=/usr/${_arch} \ + -DCMAKE_INSTALL_LIBDIR=/usr/${_arch}/lib \ + -DCMAKE_BUILD_TYPE=Release \ + \ + -Dwaffle_build_tests=0 \ + -Dwaffle_build_manpages=0 \ + -Dwaffle_build_htmldocs=0 \ + -Dwaffle_build_examples=1 + make + popd + done + + # There should be a better way to do this + msg "Building mingw-w64-waffle for native builds" + for _arch in ${_architectures}; do + mkdir -p "build-${_arch}-win" && pushd "build-${_arch}-win" + ${_arch}-cmake .. \ + -DCMAKE_INSTALL_PREFIX="" \ + -DCMAKE_INSTALL_LIBDIR="lib" \ + -DCMAKE_BUILD_TYPE=Release \ + \ + -Dwaffle_build_tests=0 \ + -Dwaffle_build_manpages=0 \ + -Dwaffle_build_htmldocs=0 \ + -Dwaffle_build_examples=1 + make + popd + done +} + +package() { + for _arch in ${_architectures}; do + cd "${_srcroot}/build-${_arch}" + make DESTDIR="${pkgdir}" install +# ${_arch}-strip --strip-unneeded "$pkgdir"/usr/${_arch}/bin/*.dll +# ${_arch}-strip -g "$pkgdir"/usr/${_arch}/lib/*.a + done + + for _arch in ${_architectures}; do + cd "${_srcroot}/build-${_arch}-win" + # Create Windows zip archives + make package + done +} + +# vim:set ts=2 sw=2 et: diff --git a/pkg/archlinux/waffle-1.4.0/.gitignore b/pkg/archlinux/waffle-1.4.0/.gitignore new file mode 100644 index 0000000..f69628a --- /dev/null +++ b/pkg/archlinux/waffle-1.4.0/.gitignore @@ -0,0 +1,7 @@ +pkg/ +src/ +waffle/ + +*.gz +*.sig +*.xz diff --git a/pkg/archlinux/waffle-1.4.0/PKGBUILD b/pkg/archlinux/waffle-1.4.0/PKGBUILD new file mode 100644 index 0000000..61cd7ab --- /dev/null +++ b/pkg/archlinux/waffle-1.4.0/PKGBUILD @@ -0,0 +1,56 @@ +# maintainer: chad versace <chad.versace@linux.intel.com> + +pkgname='waffle' +pkgver='1.4.0' +pkgrel=1 +pkgdesc='a library for choosing window system and OpenGL API at runtime' +arch=('i686' 'x86_64') +url='http://www.waffle-gl.org' +license=('BSD') + +depends=( + 'libgl' # for glx + 'libegl' + 'libgbm' + 'libx11' + 'libxcb' + 'wayland' + ) +makedepends=( + 'cmake' + 'xcb-proto' + + # for building the docs. + 'libxslt' + 'docbook-xsl' + ) +source=("http://www.waffle-gl.org/files/release/${pkgname}-${pkgver}/${pkgname}-${pkgver}.tar.xz") +sha256sums=('3238b3da5d066750084c7cdd7b3e185bd8dce3a974cb1f804ccf0a8c87600923') + +_unpackdir="${pkgname}-${pkgver}" + +build() { + cd "$srcdir/$_unpackdir" + cmake \ + -DCMAKE_INSTALL_PREFIX=/usr \ + -DCMAKE_INSTALL_LIBDIR=/usr/lib \ + -DCMAKE_BUILD_TYPE=Release \ + -Dwaffle_has_glx=1 \ + -Dwaffle_has_x11_egl=1 \ + -Dwaffle_has_wayland=1 \ + -Dwaffle_has_gbm=1 \ + -Dwaffle_build_manpages=1 \ + -Dwaffle_build_htmldocs=1 \ + -Dwaffle_build_examples=0 + make +} + +package() { + cd "$srcdir/${_unpackdir}" + make DESTDIR="$pkgdir/" install + install -m755 -d "$pkgdir/usr/share/licenses/$pkgname" + install -m644 "$pkgdir/usr/share/doc/waffle1/LICENSE.txt" \ + "$pkgdir/usr/share/licenses/$pkgname/LICENSE.txt" +} + +# vim:set ts=2 sw=2 et: diff --git a/pkg/archlinux/waffle-1.4.1/PKGBUILD b/pkg/archlinux/waffle-1.4.1/PKGBUILD index e877ddc..5426e4b 100644 --- a/pkg/archlinux/waffle-1.4.1/PKGBUILD +++ b/pkg/archlinux/waffle-1.4.1/PKGBUILD @@ -2,7 +2,7 @@ pkgname='waffle' pkgver='1.4.1' -pkgrel=1 +pkgrel=2 pkgdesc='a library for choosing window system and OpenGL API at runtime' arch=('i686' 'x86_64') url='http://www.waffle-gl.org' @@ -34,10 +34,6 @@ build() { -DCMAKE_INSTALL_PREFIX=/usr \ -DCMAKE_INSTALL_LIBDIR=/usr/lib \ -DCMAKE_BUILD_TYPE=Release \ - -Dwaffle_has_gbm=1 \ - -Dwaffle_has_glx=1 \ - -Dwaffle_has_x11_egl=1 \ - -Dwaffle_has_wayland=1 \ -Dwaffle_build_manpages=1 \ -Dwaffle_build_htmldocs=1 \ -Dwaffle_build_examples=0 diff --git a/pkg/archlinux/waffle-git/PKGBUILD b/pkg/archlinux/waffle-git/PKGBUILD index b5a6d96..932595c 100644 --- a/pkg/archlinux/waffle-git/PKGBUILD +++ b/pkg/archlinux/waffle-git/PKGBUILD @@ -12,8 +12,6 @@ provides=(waffle) conflicts=(waffle) depends=( 'libgl' # for glx - 'libegl' - 'libgbm' 'libx11' 'libxcb' 'wayland' @@ -22,6 +20,9 @@ makedepends=( 'cmake' 'xcb-proto' + 'libegl' + 'libgbm' + # for building the docs. 'libxslt' 'docbook-xsl' @@ -43,10 +44,6 @@ build() { -DCMAKE_INSTALL_PREFIX=/usr \ -DCMAKE_INSTALL_LIBDIR=/usr/lib \ -DCMAKE_BUILD_TYPE=Release \ - -Dwaffle_has_glx=1 \ - -Dwaffle_has_x11_egl=1 \ - -Dwaffle_has_wayland=1 \ - -Dwaffle_has_gbm=1 \ -Dwaffle_build_manpages=1 \ -Dwaffle_build_htmldocs=1 \ -Dwaffle_build_examples=0 @@ -54,6 +51,9 @@ build() { } package() { + optdepends=('libegl: for x11_egl, gbm or wayland support') + optdepends=('libgbm: for gbm support') + cd "$srcdir/${_unpackdir}" make DESTDIR="$pkgdir/" install install -m755 -d "$pkgdir/usr/share/licenses/$pkgname" diff --git a/src/utils/CMakeLists.txt b/src/utils/CMakeLists.txt index 848702c..9cb6cc7 100644 --- a/src/utils/CMakeLists.txt +++ b/src/utils/CMakeLists.txt @@ -3,11 +3,7 @@ # ---------------------------------------------------------------------------- add_executable(wflinfo wflinfo.c) -target_link_libraries(wflinfo ${waffle_libname}) -set_target_properties(wflinfo - PROPERTIES - RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin" - ) +target_link_libraries(wflinfo ${waffle_libname} ${GETOPT_LIBRARIES}) if(waffle_on_mac) set_target_properties(wflinfo diff --git a/src/utils/wflinfo.c b/src/utils/wflinfo.c index 0907b4b..5a9195c 100644 --- a/src/utils/wflinfo.c +++ b/src/utils/wflinfo.c @@ -62,7 +62,7 @@ static const char *usage_message = "\n" "Required Parameters:\n" " -p, --platform\n" - " One of: android, cgl, gbm, glx, wayland or x11_egl\n" + " One of: android, cgl, gbm, glx, wayland, wgl or x11_egl\n" "\n" " -a, --api\n" " One of: gl, gles1, gles2 or gles3\n" @@ -123,7 +123,15 @@ strneq(const char *a, const char *b, size_t n) return strncmp(a, b, n) == 0; } -static void __attribute__((noreturn)) +#if defined(__GNUC__) +#define NORETURN __attribute__((noreturn)) +#elif defined(_MSC_VER) +#define NORETURN __declspec(noreturn) +#else +#define NORETURN +#endif + +static void NORETURN error_printf(const char *module, const char *fmt, ...) { va_list ap; @@ -137,14 +145,14 @@ error_printf(const char *module, const char *fmt, ...) exit(EXIT_FAILURE); } -static void __attribute__((noreturn)) +static void NORETURN write_usage_and_exit(FILE *f, int exit_code) { fprintf(f, "%s", usage_message); exit(exit_code); } -static void __attribute__((noreturn)) +static void NORETURN usage_error_printf(const char *fmt, ...) { fprintf(stderr, "Wflinfo usage error: "); @@ -201,6 +209,7 @@ enum { GL_VERSION = 0x1F02, GL_EXTENSIONS = 0x1F03, GL_NUM_EXTENSIONS = 0x821D, + GL_SHADING_LANGUAGE_VERSION = 0x8B8C, }; #define WINDOW_WIDTH 320 @@ -212,10 +221,18 @@ enum { #define GL_CONTEXT_CORE_PROFILE_BIT 0x00000001 #define GL_CONTEXT_COMPATIBILITY_PROFILE_BIT 0x00000002 -static GLenum (*glGetError)(void); -static void (*glGetIntegerv)(GLenum pname, GLint *params); -static const GLubyte * (*glGetString)(GLenum name); -static const GLubyte * (*glGetStringi)(GLenum name, GLint i); +#ifndef _WIN32 +#define APIENTRY +#else +#ifndef APIENTRY +#define APIENTRY __stdcall +#endif +#endif + +static GLenum (APIENTRY *glGetError)(void); +static void (APIENTRY *glGetIntegerv)(GLenum pname, GLint *params); +static const GLubyte * (APIENTRY *glGetString)(GLenum name); +static const GLubyte * (APIENTRY *glGetStringi)(GLenum name, GLint i); /// @brief Command line options. struct options { @@ -251,6 +268,7 @@ static const struct enum_map platform_map[] = { {WAFFLE_PLATFORM_GBM, "gbm" }, {WAFFLE_PLATFORM_GLX, "glx" }, {WAFFLE_PLATFORM_WAYLAND, "wayland" }, + {WAFFLE_PLATFORM_WGL, "wgl" }, {WAFFLE_PLATFORM_X11_EGL, "x11_egl" }, {0, 0 }, }; @@ -519,16 +537,19 @@ print_wflinfo(const struct options *opts) } const char *vendor = (const char *) glGetString(GL_VENDOR); - if (glGetError() != GL_NO_ERROR || vendor == NULL) + if (glGetError() != GL_NO_ERROR || vendor == NULL) { vendor = "WFLINFO_GL_ERROR"; + } const char *renderer = (const char *) glGetString(GL_RENDERER); - if (glGetError() != GL_NO_ERROR || renderer == NULL) + if (glGetError() != GL_NO_ERROR || renderer == NULL) { renderer = "WFLINFO_GL_ERROR"; + } const char *version_str = (const char *) glGetString(GL_VERSION); - if (glGetError() != GL_NO_ERROR || version_str == NULL) + if (glGetError() != GL_NO_ERROR || version_str == NULL) { version_str = "WFLINFO_GL_ERROR"; + } const char *platform = enum_map_to_str(platform_map, opts->platform); assert(platform != NULL); @@ -554,8 +575,25 @@ print_wflinfo(const struct options *opts) if (!glGetStringi && use_getstringi) error_get_gl_symbol("glGetStringi"); - if (opts->verbose) + if (opts->verbose) { + // There are two exceptional cases where wflinfo may not get a + // version (or a valid version): one is in gles1 and the other + // is GL < 2.0. In these cases do not return WFLINFO_GL_ERROR, + // return None. This is preferable to returning WFLINFO_GL_ERROR + // because it creates a consistant interface for parsers + const char *language_str = "None"; + if ((opts->context_api == WAFFLE_CONTEXT_OPENGL && version >= 20) || + opts->context_api == WAFFLE_CONTEXT_OPENGL_ES2 || + opts->context_api == WAFFLE_CONTEXT_OPENGL_ES3) { + language_str = (const char *) glGetString(GL_SHADING_LANGUAGE_VERSION); + if (glGetError() != GL_NO_ERROR || language_str == NULL) { + language_str = "WFLINFO_GL_ERROR"; + } + } + + printf("OpenGL shading language version string: %s\n", language_str); print_extensions(use_getstringi); + } return true; } @@ -744,26 +782,27 @@ gl_get_version(void) static bool gl_has_extension_GetString(const char *name) { - const size_t buf_len = 4096; - char exts[buf_len]; +#define BUF_LEN 4096 + char exts[BUF_LEN]; const uint8_t *exts_orig = glGetString(GL_EXTENSIONS); if (glGetError()) { error_printf("Wflinfo", "glGetInteger(GL_EXTENSIONS) failed"); } - memcpy(exts, exts_orig, buf_len); - exts[buf_len - 1] = 0; + memcpy(exts, exts_orig, BUF_LEN); + exts[BUF_LEN - 1] = 0; char *ext = strtok(exts, " "); do { - if (strneq(ext, name, buf_len)) { + if (strneq(ext, name, BUF_LEN)) { return true; } ext = strtok(NULL, " "); } while (ext); return false; +#undef BUF_LEN } /// @brief Check if current context has an extension using glGetStringi(). @@ -1079,6 +1118,8 @@ main(int argc, char **argv) if (!ok) error_waffle(); + glGetStringi = waffle_get_proc_address("glGetStringi"); + ok = print_wflinfo(&opts); if (!ok) error_waffle(); diff --git a/src/waffle/CMakeLists.txt b/src/waffle/CMakeLists.txt index ab10498..d76e029 100644 --- a/src/waffle/CMakeLists.txt +++ b/src/waffle/CMakeLists.txt @@ -16,12 +16,14 @@ include_directories( glx linux wayland + wgl x11 xegl ${egl_INCLUDE_DIRS} ${gbm_INCLUDE_DIRS} ${gl_INCLUDE_DIRS} + ${GLEXT_INCLUDE_DIR} ${libudev_INCLUDE_DIRS} ${wayland-client_INCLUDE_DIRS} ${wayland-egl_INCLUDE_DIRS} @@ -32,17 +34,11 @@ include_directories( # Target: waffle (shared library) # ---------------------------------------------------------------------------- +list(APPEND waffle_libdeps + ${THREADS_LIBRARIES} + ) + if(waffle_on_linux) - if(waffle_has_egl) - list(APPEND waffle_libdeps - ${egl_LDFLAGS} - ) - endif() - if(waffle_has_glx) - list(APPEND waffle_libdeps - ${gl_LDFLAGS} - ) - endif() if(waffle_has_wayland) list(APPEND waffle_libdeps ${wayland-client_LDFLAGS} @@ -56,7 +52,6 @@ if(waffle_on_linux) endif() if(waffle_has_gbm) list(APPEND waffle_libdeps - ${gbm_LDFLAGS} ${libudev_LDFLAGS} ) endif() @@ -103,6 +98,7 @@ if(waffle_has_egl) egl/wegl_config.c egl/wegl_context.c egl/wegl_display.c + egl/wegl_platform.c egl/wegl_util.c egl/wegl_window.c ) @@ -161,6 +157,22 @@ if(waffle_has_gbm) ) endif() +if(waffle_on_windows) + list(APPEND waffle_sources + wgl/wgl_config.c + wgl/wgl_context.c + wgl/wgl_display.c + wgl/wgl_dl.c + wgl/wgl_error.c + wgl/wgl_platform.c + wgl/wgl_window.c + ) + + list(APPEND waffle_libdeps + opengl32.lib + ) +endif() + # CMake will pass to the C compiler only C sources. CMake does not recognize the # .m extension and ignores any such files in the source lists. To coerce CMake # to pass .m files to the compiler, we must lie and claim that they are @@ -169,9 +181,20 @@ set_source_files_properties( ${waffle_sources} PROPERTIES LANGUAGE C - COMPILE_FLAGS "-fvisibility=hidden" ) +if(waffle_on_windows) + configure_file( + waffle.def.in + ${CMAKE_CURRENT_SOURCE_DIR}/${waffle_libname}.def + @ONLY + ) + + list(APPEND waffle_sources + ${waffle_libname}.def + ) +endif() + include_directories( ${GLX_INLCLUDE_DIRS} ${X11_INCLUDE_DIRS} @@ -204,14 +227,22 @@ target_link_libraries(${waffle_libname} ${waffle_libdeps}) set_target_properties(${waffle_libname} PROPERTIES - LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib SOVERSION ${waffle_soversion} VERSION ${waffle_soversion}.${waffle_minor_version}.${waffle_patch_version} ) +if(waffle_on_windows) + set_target_properties(${waffle_libname} + PROPERTIES + PREFIX "" + ) +endif() + install( TARGETS ${waffle_libname} + RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} + ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} COMPONENT libraries ) @@ -235,8 +266,7 @@ target_link_libraries(waffle_static ${waffle_libdeps}) set_target_properties(waffle_static PROPERTIES - OUTPUT_NAME "waffle-${waffle_major_version}" - ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/lib" + OUTPUT_NAME "waffle-static-${waffle_major_version}" ) # ---------------------------------------------------------------------------- @@ -258,8 +288,7 @@ function(add_unittest unittest_name) waffle_static ) add_custom_target(${unittest_name}_run - DEPENDS ${unittest_name} - COMMAND "${CMAKE_BINARY_DIR}/tests/${unittest_name}" + COMMAND "${unittest_name}" ) add_dependencies(check ${unittest_name}_run) diff --git a/src/waffle/android/droid_surfaceflingerlink.cpp b/src/waffle/android/droid_surfaceflingerlink.cpp index 765f70f..397c86c 100644 --- a/src/waffle/android/droid_surfaceflingerlink.cpp +++ b/src/waffle/android/droid_surfaceflingerlink.cpp @@ -23,8 +23,9 @@ // 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. -#if WAFFLE_ANDROID_MAJOR_VERSION >= 4 && \ - WAFFLE_ANDROID_MINOR_VERSION >= 1 +#if WAFFLE_ANDROID_MAJOR_VERSION == 4 && \ + WAFFLE_ANDROID_MINOR_VERSION >= 1 || \ + WAFFLE_ANDROID_MAJOR_VERSION >= 5 # include <gui/Surface.h> # include <gui/SurfaceComposerClient.h> #elif WAFFLE_ANROID_MAJOR_VERSION >= 4 && \ @@ -107,8 +108,9 @@ droid_setup_surface( // The signature of SurfaceComposerClient::createSurface() differs across // Android versions. -#if WAFFLE_ANDROID_MAJOR_VERSION >= 4 && \ - WAFFLE_ANDROID_MINOR_VERSION >= 2 +#if WAFFLE_ANDROID_MAJOR_VERSION == 4 && \ + WAFFLE_ANDROID_MINOR_VERSION >= 2 || \ + WAFFLE_ANDROID_MAJOR_VERSION >= 5 pANWContainer->surface_control = pSFContainer->composer_client->createSurface( String8("Waffle Surface"), diff --git a/src/waffle/api/api_priv.h b/src/waffle/api/api_priv.h index 1fec575..51cf53c 100644 --- a/src/waffle/api/api_priv.h +++ b/src/waffle/api/api_priv.h @@ -39,7 +39,8 @@ // TODO: Implement WAFFLE_API for Apple. // #if defined(_WIN32) -# define WAFFLE_API __declspec(dllexport) +// Use module-definition file to restrict the exported symbols under windows. +# define WAFFLE_API #elif defined(__GNUC__) && __GNUC__ >= 4 # define WAFFLE_API __attribute__ ((visibility("default"))) #else diff --git a/src/waffle/api/waffle_gl_misc.c b/src/waffle/api/waffle_gl_misc.c index c746f52..138974d 100644 --- a/src/waffle/api/waffle_gl_misc.c +++ b/src/waffle/api/waffle_gl_misc.c @@ -25,6 +25,7 @@ #include <stddef.h> #include <string.h> +#include "c99_compat.h" #include "api_priv.h" @@ -34,10 +35,6 @@ #include "wcore_platform.h" #include "wcore_window.h" -#if __STDC_VERSION__ < 199901L -# define restrict -#endif - WAFFLE_API bool waffle_is_extension_in_string( const char *restrict extension_string, diff --git a/src/waffle/api/waffle_init.c b/src/waffle/api/waffle_init.c index b45c3ba..ebc6cd5 100644 --- a/src/waffle/api/waffle_init.c +++ b/src/waffle/api/waffle_init.c @@ -34,6 +34,7 @@ struct wcore_platform* glx_platform_create(void); struct wcore_platform* wayland_platform_create(void); struct wcore_platform* xegl_platform_create(void); struct wcore_platform* wgbm_platform_create(void); +struct wcore_platform* wgl_platform_create(void); static bool waffle_init_parse_attrib_list( @@ -97,6 +98,12 @@ waffle_init_parse_attrib_list( CASE_UNDEFINED_PLATFORM(GBM) #endif +#ifdef WAFFLE_HAS_WGL + CASE_DEFINED_PLATFORM(WGL) +#else + CASE_UNDEFINED_PLATFORM(WGL) +#endif + default: wcore_errorf(WAFFLE_ERROR_BAD_ATTRIBUTE, "WAFFLE_PLATFORM has bad value 0x%x", @@ -153,6 +160,10 @@ waffle_init_create_platform(int32_t waffle_platform) case WAFFLE_PLATFORM_GBM: return wgbm_platform_create(); #endif +#ifdef WAFFLE_HAS_WGL + case WAFFLE_PLATFORM_WGL: + return wgl_platform_create(); +#endif default: assert(false); return NULL; diff --git a/src/waffle/core/wcore_attrib_list.c b/src/waffle/core/wcore_attrib_list.c index ba380b8..48d4b33 100644 --- a/src/waffle/core/wcore_attrib_list.c +++ b/src/waffle/core/wcore_attrib_list.c @@ -40,7 +40,7 @@ wcore_attrib_list_length(const int32_t attrib_list[]) while (*i != 0) i += 2; - return (i - attrib_list) / 2; + return (int32_t) (i - attrib_list) / 2; } bool diff --git a/src/waffle/core/wcore_display.c b/src/waffle/core/wcore_display.c index 43e9e6a..a47e2c3 100644 --- a/src/waffle/core/wcore_display.c +++ b/src/waffle/core/wcore_display.c @@ -24,8 +24,8 @@ // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include <assert.h> -#include <pthread.h> #include <stdio.h> +#include "threads.h" #include "wcore_display.h" @@ -34,14 +34,14 @@ wcore_display_init(struct wcore_display *self, struct wcore_platform *platform) { static size_t id_counter = 0; - static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; + static mtx_t mutex = _MTX_INITIALIZER_NP; assert(self); assert(platform); - pthread_mutex_lock(&mutex); + mtx_lock(&mutex); self->api.display_id = ++id_counter; - pthread_mutex_unlock(&mutex); + mtx_unlock(&mutex); self->platform = platform; diff --git a/src/waffle/core/wcore_display.h b/src/waffle/core/wcore_display.h index 0b95729..9a0357d 100644 --- a/src/waffle/core/wcore_display.h +++ b/src/waffle/core/wcore_display.h @@ -25,6 +25,8 @@ #pragma once +#include "c99_compat.h" + #include "api_object.h" #include "wcore_util.h" diff --git a/src/waffle/core/wcore_error.c b/src/waffle/core/wcore_error.c index e03642d..fdf70f8 100644 --- a/src/waffle/core/wcore_error.c +++ b/src/waffle/core/wcore_error.c @@ -23,14 +23,13 @@ // 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. -#define _XOPEN_SOURCE 600 // for strerror_r - #include <errno.h> #include <stdarg.h> #include <stdbool.h> #include <stdio.h> #include <stdlib.h> #include <string.h> +#include "c99_compat.h" #include "wcore_error.h" #include "wcore_tinfo.h" diff --git a/src/waffle/core/wcore_error_unittest.c b/src/waffle/core/wcore_error_unittest.c index e5d6cba..0ac667b 100644 --- a/src/waffle/core/wcore_error_unittest.c +++ b/src/waffle/core/wcore_error_unittest.c @@ -23,17 +23,13 @@ // 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. -#ifdef __linux__ -# define _XOPEN_SOURCE 600 -#endif - #include <setjmp.h> #include <stdarg.h> #include <stdio.h> #include <stdlib.h> #include <string.h> - -#include <pthread.h> +#include "c99_compat.h" +#include "threads.h" #include <cmocka.h> @@ -131,7 +127,7 @@ enum { NUM_THREADS = 3, }; -/// Given to pthread_create() in test wcore_error.thread_local. +/// Given to thrd_create() in test wcore_error.thread_local. /// /// A sub-thread, after calling wcore_error(), locks the mutex, increments /// `num_threads_waiting`, and waits on `cond`. When all sub-threads are @@ -142,10 +138,10 @@ struct thread_arg { int thread_id; /// Protects `num_threads_waiting` and `cond`. - pthread_mutex_t *mutex; + mtx_t *mutex; /// Satisfied when `num_threads_waiting == TOTAL_THREADS`. - pthread_cond_t *cond; + cnd_t *cond; /// Number of threads waiting on `cond`. volatile int *num_threads_waiting; @@ -172,10 +168,10 @@ thread_start(struct thread_arg *a) // Wait for all threads to set their error codes, thus giving // the threads opportunity to clobber each other's codes. - pthread_mutex_lock(a->mutex); { + mtx_lock(a->mutex); { *a->num_threads_waiting += 1; - pthread_cond_wait(a->cond, a->mutex); - pthread_mutex_unlock(a->mutex); + cnd_wait(a->cond, a->mutex); + mtx_unlock(a->mutex); } // Verify that the threads did not clobber each other's @@ -188,26 +184,26 @@ thread_start(struct thread_arg *a) // Test that threads do not clobber each other's error codes. static void test_wcore_error_thread_local(void **state) { - pthread_mutex_t mutex; - pthread_cond_t cond; + mtx_t mutex; + cnd_t cond; volatile int num_threads_waiting = 0; - pthread_t threads[NUM_THREADS]; + thrd_t threads[NUM_THREADS]; struct thread_arg thread_args[NUM_THREADS]; - bool exit_codes[NUM_THREADS]; + int exit_codes[NUM_THREADS]; - pthread_mutex_init(&mutex, NULL); - pthread_cond_init(&cond, NULL); + mtx_init(&mutex, mtx_plain); + cnd_init(&cond); - for (intptr_t i = 0; i < NUM_THREADS; ++i) { + for (int i = 0; i < NUM_THREADS; ++i) { struct thread_arg *a = &thread_args[i]; a->thread_id = i; a->mutex = &mutex; a->cond = &cond; a->num_threads_waiting = &num_threads_waiting; - pthread_create(&threads[i], NULL, - (void* (*)(void*)) thread_start, + thrd_create(&threads[i], + (thrd_start_t) thread_start, (void*) a); } @@ -215,18 +211,18 @@ test_wcore_error_thread_local(void **state) { // the threads opportunity to clobber each other's codes. while (num_threads_waiting < NUM_THREADS) ;; - pthread_mutex_lock(&mutex); { - pthread_cond_broadcast(&cond); - pthread_mutex_unlock(&mutex); + mtx_lock(&mutex); { + cnd_broadcast(&cond); + mtx_unlock(&mutex); } for (int i = 0; i < NUM_THREADS; ++i) { - pthread_join(threads[i], (void**) &exit_codes[i]); + thrd_join(threads[i], &exit_codes[i]); assert_true(exit_codes[i]); } - pthread_cond_destroy(&cond); - pthread_mutex_destroy(&mutex); + cnd_destroy(&cond); + mtx_destroy(&mutex); } int diff --git a/src/waffle/core/wcore_platform.h b/src/waffle/core/wcore_platform.h index 65de879..77943e4 100644 --- a/src/waffle/core/wcore_platform.h +++ b/src/waffle/core/wcore_platform.h @@ -28,6 +28,7 @@ #include <assert.h> #include <stdbool.h> #include <stdint.h> +#include "c99_compat.h" struct wcore_config; struct wcore_config_attrs; diff --git a/src/waffle/core/wcore_tinfo.c b/src/waffle/core/wcore_tinfo.c index df31085..9a190f9 100644 --- a/src/waffle/core/wcore_tinfo.c +++ b/src/waffle/core/wcore_tinfo.c @@ -28,13 +28,13 @@ #include <stdio.h> #include <stdlib.h> -#include <pthread.h> +#include "threads.h" #include "wcore_error.h" #include "wcore_tinfo.h" -static pthread_once_t wcore_tinfo_once = PTHREAD_ONCE_INIT; -static pthread_key_t wcore_tinfo_key; +static once_flag wcore_tinfo_once = ONCE_FLAG_INIT; +static tss_t wcore_tinfo_key; #ifdef WAFFLE_HAS_TLS /// @brief Thread-local storage for all of Waffle. @@ -54,7 +54,15 @@ static __thread struct wcore_tinfo wcore_tinfo ; #endif // WAFFLE_HAS_TLS -static void __attribute__((noreturn)) +#if defined(__GNUC__) +#define NORETURN __attribute__((noreturn)) +#elif defined(_MSC_VER) +#define NORETURN __declspec(noreturn) +#else +#define NORETURN +#endif + +static void NORETURN wcore_tinfo_abort_init(void) { printf("waffle: fatal-error: failed to initialize thread local info\n"); @@ -80,7 +88,7 @@ wcore_tinfo_key_create(void) { int err; - err = pthread_key_create(&wcore_tinfo_key, wcore_tinfo_key_dtor); + err = tss_create(&wcore_tinfo_key, wcore_tinfo_key_dtor); if (err) wcore_tinfo_abort_init(); } @@ -105,12 +113,13 @@ wcore_tinfo_init(struct wcore_tinfo *tinfo) // each instance of tinfo must be registered individually. The key's data // is never retrieved because use the key only to register tinfo for // destruction. - err = pthread_once(&wcore_tinfo_once, wcore_tinfo_key_create); - if (err) - wcore_tinfo_abort_init(); + + // With C11 threads call_once "can never fail"... + // http://open-std.org/twiki/pub/WG14/DefectReports/n1654.htm + call_once(&wcore_tinfo_once, wcore_tinfo_key_create); #endif - err = pthread_setspecific(wcore_tinfo_key, tinfo); + err = tss_set(wcore_tinfo_key, tinfo); if (err) wcore_tinfo_abort_init(); } @@ -122,14 +131,13 @@ wcore_tinfo_get(void) wcore_tinfo_init(&wcore_tinfo); return &wcore_tinfo; #else - int err; struct wcore_tinfo *tinfo; - err = pthread_once(&wcore_tinfo_once, wcore_tinfo_key_create); - if (err) - wcore_tinfo_abort_init(); + // With C11 threads call_once "can never fail"... + // http://open-std.org/twiki/pub/WG14/DefectReports/n1654.htm + call_once(&wcore_tinfo_once, wcore_tinfo_key_create); - tinfo = pthread_getspecific(wcore_tinfo_key); + tinfo = tss_get(wcore_tinfo_key); if (tinfo) return tinfo; diff --git a/src/waffle/core/wcore_util.c b/src/waffle/core/wcore_util.c index 2276dde..deee1bf 100644 --- a/src/waffle/core/wcore_util.c +++ b/src/waffle/core/wcore_util.c @@ -61,6 +61,7 @@ wcore_enum_to_string(int32_t e) CASE(WAFFLE_PLATFORM_WAYLAND); CASE(WAFFLE_PLATFORM_X11_EGL); CASE(WAFFLE_PLATFORM_GBM); + CASE(WAFFLE_PLATFORM_WGL); CASE(WAFFLE_CONTEXT_API); CASE(WAFFLE_CONTEXT_OPENGL); CASE(WAFFLE_CONTEXT_OPENGL_ES1); diff --git a/src/waffle/egl/wegl_config.c b/src/waffle/egl/wegl_config.c index 5dbe1b6..db3d3c9 100644 --- a/src/waffle/egl/wegl_config.c +++ b/src/waffle/egl/wegl_config.c @@ -33,6 +33,7 @@ #include "wegl_config.h" #include "wegl_display.h" #include "wegl_imports.h" +#include "wegl_platform.h" #include "wegl_util.h" /// @brief Check the WAFFLE_CONTEXT_* attributes. @@ -129,6 +130,7 @@ static EGLConfig choose_real_config(struct wegl_display *dpy, const struct wcore_config_attrs *attrs) { + struct wegl_platform *plat = wegl_platform(dpy->wcore.platform); EGLConfig config = NULL; bool ok = true; @@ -189,10 +191,10 @@ choose_real_config(struct wegl_display *dpy, } EGLint num_configs = 0; - ok &= eglChooseConfig(dpy->egl, - attrib_list, &config, 1, &num_configs); + ok &= plat->eglChooseConfig(dpy->egl, + attrib_list, &config, 1, &num_configs); if (!ok) { - wegl_emit_error("eglChooseConfig"); + wegl_emit_error(plat, "eglChooseConfig"); return NULL; } else if (num_configs == 0) { diff --git a/src/waffle/egl/wegl_context.c b/src/waffle/egl/wegl_context.c index abd6129..ba7d426 100644 --- a/src/waffle/egl/wegl_context.c +++ b/src/waffle/egl/wegl_context.c @@ -31,21 +31,22 @@ #include "wegl_config.h" #include "wegl_context.h" #include "wegl_imports.h" +#include "wegl_platform.h" #include "wegl_util.h" static bool -bind_api(int32_t waffle_context_api) +bind_api(struct wegl_platform *plat, int32_t waffle_context_api) { bool ok = true; switch (waffle_context_api) { case WAFFLE_CONTEXT_OPENGL: - ok &= eglBindAPI(EGL_OPENGL_API); + ok &= plat->eglBindAPI(EGL_OPENGL_API); break; case WAFFLE_CONTEXT_OPENGL_ES1: case WAFFLE_CONTEXT_OPENGL_ES2: case WAFFLE_CONTEXT_OPENGL_ES3: - ok &= eglBindAPI(EGL_OPENGL_ES_API); + ok &= plat->eglBindAPI(EGL_OPENGL_ES_API); break; default: wcore_error_internal("waffle_context_api has bad value #x%x", @@ -54,7 +55,7 @@ bind_api(int32_t waffle_context_api) } if (!ok) - wegl_emit_error("eglBindAPI"); + wegl_emit_error(plat, "eglBindAPI"); return ok; } @@ -65,6 +66,7 @@ create_real_context(struct wegl_config *config, { struct wegl_display *dpy = wegl_display(config->wcore.display); + struct wegl_platform *plat = wegl_platform(dpy->wcore.platform); struct wcore_config_attrs *attrs = &config->wcore.attrs; bool ok = true; int32_t waffle_context_api = attrs->context_api; @@ -142,14 +144,14 @@ create_real_context(struct wegl_config *config, attrib_list[i++] = EGL_NONE; - ok = bind_api(waffle_context_api); + ok = bind_api(plat, waffle_context_api); if (!ok) return false; - EGLContext ctx = eglCreateContext(dpy->egl, config->egl, - share_ctx, attrib_list); + EGLContext ctx = plat->eglCreateContext(dpy->egl, config->egl, + share_ctx, attrib_list); if (!ctx) - wegl_emit_error("eglCreateContext"); + wegl_emit_error(plat, "eglCreateContext"); return ctx; } @@ -189,6 +191,8 @@ fail: bool wegl_context_destroy(struct wcore_context *wc_ctx) { + struct wegl_display *dpy = wegl_display(wc_ctx->display); + struct wegl_platform *plat = wegl_platform(dpy->wcore.platform); struct wegl_context *ctx; bool result = true; @@ -198,10 +202,10 @@ wegl_context_destroy(struct wcore_context *wc_ctx) ctx = wegl_context(wc_ctx); if (ctx->egl) { - bool ok = eglDestroyContext(wegl_display(wc_ctx->display)->egl, - ctx->egl); + bool ok = plat->eglDestroyContext(wegl_display(wc_ctx->display)->egl, + ctx->egl); if (!ok) { - wegl_emit_error("eglDestroyContext"); + wegl_emit_error(plat, "eglDestroyContext"); result = false; } } diff --git a/src/waffle/egl/wegl_display.c b/src/waffle/egl/wegl_display.c index 0716a32..88fce7a 100644 --- a/src/waffle/egl/wegl_display.c +++ b/src/waffle/egl/wegl_display.c @@ -31,15 +31,17 @@ #include "wegl_display.h" #include "wegl_imports.h" #include "wegl_util.h" +#include "wegl_platform.h" static bool get_extensions(struct wegl_display *dpy) { - const char *extensions = eglQueryString(dpy->egl, EGL_EXTENSIONS); + struct wegl_platform *plat = wegl_platform(dpy->wcore.platform); + const char *extensions = plat->eglQueryString(dpy->egl, EGL_EXTENSIONS); if (!extensions) { - wegl_emit_error("eglQueryString(EGL_EXTENSIONS"); - return false; + wegl_emit_error(plat, "eglQueryString(EGL_EXTENSIONS"); + return false; } // waffle_is_extension_in_string() resets the error state. That's ok, @@ -59,6 +61,7 @@ wegl_display_init(struct wegl_display *dpy, struct wcore_platform *wc_plat, intptr_t native_display) { + struct wegl_platform *plat = wegl_platform(wc_plat); bool ok; EGLint major, minor; @@ -66,21 +69,21 @@ wegl_display_init(struct wegl_display *dpy, if (!ok) goto fail; - dpy->egl = eglGetDisplay((EGLNativeDisplayType) native_display); + dpy->egl = plat->eglGetDisplay((EGLNativeDisplayType) native_display); if (!dpy->egl) { - wegl_emit_error("eglGetDisplay"); + wegl_emit_error(plat, "eglGetDisplay"); goto fail; } - ok = eglInitialize(dpy->egl, &major, &minor); + ok = plat->eglInitialize(dpy->egl, &major, &minor); if (!ok) { - wegl_emit_error("eglInitialize"); + wegl_emit_error(plat, "eglInitialize"); goto fail; } ok = get_extensions(dpy); if (!ok) - goto fail; + goto fail; return true; @@ -92,12 +95,13 @@ fail: bool wegl_display_teardown(struct wegl_display *dpy) { + struct wegl_platform *plat = wegl_platform(dpy->wcore.platform); bool ok = true; if (dpy->egl) { - ok = eglTerminate(dpy->egl); + ok = plat->eglTerminate(dpy->egl); if (!ok) - wegl_emit_error("eglTerminate"); + wegl_emit_error(plat, "eglTerminate"); } return ok; diff --git a/src/waffle/egl/wegl_platform.c b/src/waffle/egl/wegl_platform.c new file mode 100644 index 0000000..196b14a --- /dev/null +++ b/src/waffle/egl/wegl_platform.c @@ -0,0 +1,113 @@ +// Copyright 2014 Emil Velikov +// +// 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. +// +// 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 HOLDER 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. + +#include <dlfcn.h> + +#include "wcore_error.h" +#include "wegl_platform.h" + + +#ifdef WAFFLE_HAS_ANDROID +static const char *libEGL_filename = "libEGL.so"; +#else +static const char *libEGL_filename = "libEGL.so.1"; +#endif + +bool +wegl_platform_teardown(struct wegl_platform *self) +{ + bool ok = true; + int error = 0; + + if (self->eglHandle) { + error = dlclose(self->eglHandle); + if (error) { + ok = false; + wcore_errorf(WAFFLE_ERROR_UNKNOWN, + "dlclose(\"%s\") failed: %s", + libEGL_filename, dlerror()); + } + } + + ok &= wcore_platform_teardown(&self->wcore); + return ok; +} +bool +wegl_platform_init(struct wegl_platform *self) +{ + bool ok; + + ok = wcore_platform_init(&self->wcore); + if (!ok) + goto error; + + self->eglHandle = dlopen(libEGL_filename, RTLD_LAZY | RTLD_LOCAL); + if (!self->eglHandle) { + wcore_errorf(WAFFLE_ERROR_FATAL, + "dlopen(\"%s\") failed: %s", + libEGL_filename, dlerror()); + goto error; + } + +#define RETRIEVE_EGL_SYMBOL(function) \ + self->function = dlsym(self->eglHandle, #function); \ + if (!self->function) { \ + wcore_errorf(WAFFLE_ERROR_FATAL, \ + "dlsym(\"%s\", \"" #function "\") failed: %s", \ + libEGL_filename, dlerror()); \ + goto error; \ + } + + RETRIEVE_EGL_SYMBOL(eglMakeCurrent); + RETRIEVE_EGL_SYMBOL(eglGetProcAddress); + + // display + RETRIEVE_EGL_SYMBOL(eglGetDisplay); + RETRIEVE_EGL_SYMBOL(eglInitialize); + RETRIEVE_EGL_SYMBOL(eglQueryString); + RETRIEVE_EGL_SYMBOL(eglGetError); + RETRIEVE_EGL_SYMBOL(eglTerminate); + + // config + RETRIEVE_EGL_SYMBOL(eglChooseConfig); + + // context + RETRIEVE_EGL_SYMBOL(eglBindAPI); + RETRIEVE_EGL_SYMBOL(eglCreateContext); + RETRIEVE_EGL_SYMBOL(eglDestroyContext); + + // window + RETRIEVE_EGL_SYMBOL(eglGetConfigAttrib); + RETRIEVE_EGL_SYMBOL(eglCreateWindowSurface); + RETRIEVE_EGL_SYMBOL(eglDestroySurface); + RETRIEVE_EGL_SYMBOL(eglSwapBuffers); + +#undef RETRIEVE_EGL_SYMBOL + +error: + // On failure the caller of wegl_platform_init will trigger it's own + // destruction which will execute wegl_platform_teardown. + return ok; +} diff --git a/src/waffle/egl/wegl_platform.h b/src/waffle/egl/wegl_platform.h new file mode 100644 index 0000000..645c3f8 --- /dev/null +++ b/src/waffle/egl/wegl_platform.h @@ -0,0 +1,82 @@ +// Copyright 2014 Emil Velikov +// +// 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. +// +// 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 HOLDER 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. + +#pragma once + +#include <EGL/egl.h> + +#include "wcore_platform.h" +#include "wcore_util.h" + +struct wegl_platform { + struct wcore_platform wcore; + + // EGL function pointers + void *eglHandle; + + EGLBoolean (*eglMakeCurrent)(EGLDisplay dpy, EGLSurface draw, + EGLSurface read, EGLContext ctx); + __eglMustCastToProperFunctionPointerType + (*eglGetProcAddress)(const char *procname); + + // display + EGLDisplay (*eglGetDisplay)(EGLNativeDisplayType display_id); + EGLBoolean (*eglInitialize)(EGLDisplay dpy, EGLint *major, EGLint *minor); + const char * (*eglQueryString)(EGLDisplay dpy, EGLint name); + EGLint (*eglGetError)(void); + EGLBoolean (*eglTerminate)(EGLDisplay dpy); + + // config + EGLBoolean (*eglChooseConfig)(EGLDisplay dpy, const EGLint *attrib_list, + EGLConfig *configs, EGLint config_size, + EGLint *num_config); + + // context + EGLBoolean (*eglBindAPI)(EGLenum api); + EGLContext (*eglCreateContext)(EGLDisplay dpy, EGLConfig config, + EGLContext share_context, + const EGLint *attrib_list); + EGLBoolean (*eglDestroyContext)(EGLDisplay dpy, EGLContext ctx); + + // window + EGLBoolean (*eglGetConfigAttrib)(EGLDisplay dpy, EGLConfig config, + EGLint attribute, EGLint *value); + EGLSurface (*eglCreateWindowSurface)(EGLDisplay dpy, EGLConfig config, + EGLNativeWindowType win, + const EGLint *attrib_list); + EGLBoolean (*eglDestroySurface)(EGLDisplay dpy, EGLSurface surface); + EGLBoolean (*eglSwapBuffers)(EGLDisplay dpy, EGLSurface surface); +}; + +DEFINE_CONTAINER_CAST_FUNC(wegl_platform, + struct wegl_platform, + struct wcore_platform, + wcore) + +bool +wegl_platform_teardown(struct wegl_platform *self); + +bool +wegl_platform_init(struct wegl_platform *self); diff --git a/src/waffle/egl/wegl_util.c b/src/waffle/egl/wegl_util.c index eb2415b..81fdbd9 100644 --- a/src/waffle/egl/wegl_util.c +++ b/src/waffle/egl/wegl_util.c @@ -28,13 +28,14 @@ #include "wegl_context.h" #include "wegl_display.h" #include "wegl_imports.h" +#include "wegl_platform.h" #include "wegl_util.h" #include "wegl_window.h" void -wegl_emit_error(const char *egl_func_call) +wegl_emit_error(struct wegl_platform *plat, const char *egl_func_call) { - EGLint egl_error_code = eglGetError(); + EGLint egl_error_code = plat->eglGetError(); const char *egl_error_name; switch (egl_error_code) { @@ -74,17 +75,18 @@ wegl_make_current(struct wcore_platform *wc_plat, struct wcore_window *wc_window, struct wcore_context *wc_ctx) { - bool ok; + struct wegl_platform *plat = wegl_platform(wc_plat); EGLSurface surface = wc_window ? wegl_window(wc_window)->egl : NULL; + bool ok; - ok = eglMakeCurrent(wegl_display(wc_dpy)->egl, - surface, - surface, - wc_ctx - ? wegl_context(wc_ctx)->egl - : NULL); + ok = plat->eglMakeCurrent(wegl_display(wc_dpy)->egl, + surface, + surface, + wc_ctx + ? wegl_context(wc_ctx)->egl + : NULL); if (!ok) - wegl_emit_error("eglMakeCurrent"); + wegl_emit_error(plat, "eglMakeCurrent"); return ok; } @@ -92,5 +94,6 @@ wegl_make_current(struct wcore_platform *wc_plat, void* wegl_get_proc_address(struct wcore_platform *wc_self, const char *name) { - return eglGetProcAddress(name); + struct wegl_platform *self = wegl_platform(wc_self); + return self->eglGetProcAddress(name); } diff --git a/src/waffle/egl/wegl_util.h b/src/waffle/egl/wegl_util.h index 772f71d..bb1692f 100644 --- a/src/waffle/egl/wegl_util.h +++ b/src/waffle/egl/wegl_util.h @@ -33,12 +33,13 @@ struct wcore_context; struct wcore_display; struct wcore_window; +struct wegl_platform; /// @brief Sets the waffle error with info from eglGetError(). /// @param egl_func_call Examples are "eglMakeCurrent()" and /// "eglBindAPI(EGL_OPENGL_API)". void -wegl_emit_error(const char *egl_func_call); +wegl_emit_error(struct wegl_platform *plat, const char *egl_func_call); bool wegl_make_current(struct wcore_platform *wc_plat, diff --git a/src/waffle/egl/wegl_window.c b/src/waffle/egl/wegl_window.c index e16b61d..753fd2f 100644 --- a/src/waffle/egl/wegl_window.c +++ b/src/waffle/egl/wegl_window.c @@ -26,6 +26,7 @@ #include "wegl_config.h" #include "wegl_display.h" #include "wegl_imports.h" +#include "wegl_platform.h" #include "wegl_util.h" #include "wegl_window.h" @@ -38,6 +39,7 @@ wegl_window_init(struct wegl_window *window, { struct wegl_config *config = wegl_config(wc_config); struct wegl_display *dpy = wegl_display(wc_config->display); + struct wegl_platform *plat = wegl_platform(dpy->wcore.platform); EGLint egl_render_buffer; bool ok; @@ -55,12 +57,13 @@ wegl_window_init(struct wegl_window *window, EGL_NONE, }; - window->egl = eglCreateWindowSurface(dpy->egl, - config->egl, - (EGLNativeWindowType) native_window, - attrib_list); + window->egl = plat->eglCreateWindowSurface(dpy->egl, + config->egl, + (EGLNativeWindowType) + native_window, + attrib_list); if (!window->egl) { - wegl_emit_error("eglCreateWindowSurface"); + wegl_emit_error(plat, "eglCreateWindowSurface"); goto fail; } @@ -75,12 +78,13 @@ bool wegl_window_teardown(struct wegl_window *window) { struct wegl_display *dpy = wegl_display(window->wcore.display); + struct wegl_platform *plat = wegl_platform(dpy->wcore.platform); bool result = true; if (window->egl) { - bool ok = eglDestroySurface(dpy->egl, window->egl); + bool ok = plat->eglDestroySurface(dpy->egl, window->egl); if (!ok) { - wegl_emit_error("eglDestroySurface"); + wegl_emit_error(plat, "eglDestroySurface"); result = false; } } @@ -94,10 +98,11 @@ wegl_window_swap_buffers(struct wcore_window *wc_window) { struct wegl_window *window = wegl_window(wc_window); struct wegl_display *dpy = wegl_display(window->wcore.display); + struct wegl_platform *plat = wegl_platform(dpy->wcore.platform); - bool ok = eglSwapBuffers(dpy->egl, window->egl); + bool ok = plat->eglSwapBuffers(dpy->egl, window->egl); if (!ok) - wegl_emit_error("eglSwapBuffers"); + wegl_emit_error(plat, "eglSwapBuffers"); return ok; } diff --git a/src/waffle/gbm/wgbm_display.c b/src/waffle/gbm/wgbm_display.c index 22a8e8a..76e6c32 100644 --- a/src/waffle/gbm/wgbm_display.c +++ b/src/waffle/gbm/wgbm_display.c @@ -23,14 +23,12 @@ // 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. -#define __GBM__ 1 #define _GNU_SOURCE #include <dlfcn.h> #include <stdlib.h> #include <unistd.h> -#include <gbm.h> #include <libudev.h> #include <fcntl.h> #include <sys/types.h> @@ -46,6 +44,8 @@ bool wgbm_display_destroy(struct wcore_display *wc_self) { struct wgbm_display *self = wgbm_display(wc_self); + struct wcore_platform *wc_plat = wc_self->platform; + struct wgbm_platform *plat = wgbm_platform(wegl_platform(wc_plat)); bool ok = true; int fd; @@ -56,8 +56,8 @@ wgbm_display_destroy(struct wcore_display *wc_self) ok &= wegl_display_teardown(&self->wegl); if (self->gbm_device) { - fd = gbm_device_get_fd(self->gbm_device); - gbm_device_destroy(self->gbm_device); + fd = plat->gbm_device_get_fd(self->gbm_device); + plat->gbm_device_destroy(self->gbm_device); close(fd); } @@ -120,6 +120,7 @@ wgbm_display_connect(struct wcore_platform *wc_plat, const char *name) { struct wgbm_display *self; + struct wgbm_platform *plat = wgbm_platform(wegl_platform(wc_plat)); bool ok = true; int fd; @@ -139,7 +140,7 @@ wgbm_display_connect(struct wcore_platform *wc_plat, } dlopen("libglapi.so.0", RTLD_LAZY | RTLD_GLOBAL); - self->gbm_device = gbm_create_device(fd); + self->gbm_device = plat->gbm_create_device(fd); if (!self->gbm_device) { wcore_errorf(WAFFLE_ERROR_UNKNOWN, "gbm_create_device failed"); goto error; diff --git a/src/waffle/gbm/wgbm_platform.c b/src/waffle/gbm/wgbm_platform.c index d556145..981c366 100644 --- a/src/waffle/gbm/wgbm_platform.c +++ b/src/waffle/gbm/wgbm_platform.c @@ -23,10 +23,10 @@ // 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. -#define __GBM__ 1 #define _POSIX_C_SOURCE 200112 // glib feature macro for unsetenv() #include <stdlib.h> +#include <dlfcn.h> #include "wcore_error.h" @@ -34,6 +34,7 @@ #include "wegl_config.h" #include "wegl_context.h" +#include "wegl_platform.h" #include "wegl_util.h" #include "wgbm_config.h" @@ -41,13 +42,16 @@ #include "wgbm_platform.h" #include "wgbm_window.h" +static const char *libgbm_filename = "libgbm.so.1"; + static const struct wcore_platform_vtbl wgbm_platform_vtbl; static bool wgbm_platform_destroy(struct wcore_platform *wc_self) { - struct wgbm_platform *self = wgbm_platform(wc_self); + struct wgbm_platform *self = wgbm_platform(wegl_platform(wc_self)); bool ok = true; + int error = 0; if (!self) return true; @@ -57,7 +61,17 @@ wgbm_platform_destroy(struct wcore_platform *wc_self) if (self->linux) ok &= linux_platform_destroy(self->linux); - ok &= wcore_platform_teardown(wc_self); + if (self->gbmHandle) { + error = dlclose(self->gbmHandle); + if (error) { + ok &= false; + wcore_errorf(WAFFLE_ERROR_UNKNOWN, + "dlclose(\"%s\") failed: %s", + libgbm_filename, dlerror()); + } + } + + ok &= wegl_platform_teardown(&self->wegl); free(self); return ok; } @@ -72,21 +86,49 @@ wgbm_platform_create(void) if (self == NULL) return NULL; - ok = wcore_platform_init(&self->wcore); + ok = wegl_platform_init(&self->wegl); if (!ok) goto error; + self->gbmHandle = dlopen(libgbm_filename, RTLD_LAZY | RTLD_LOCAL); + if (!self->gbmHandle) { + wcore_errorf(WAFFLE_ERROR_FATAL, + "dlopen(\"%s\") failed: %s", + libgbm_filename, dlerror()); + goto error; + } + +#define RETRIEVE_GBM_SYMBOL(function) \ + self->function = dlsym(self->gbmHandle, #function); \ + if (!self->function) { \ + wcore_errorf(WAFFLE_ERROR_FATAL, \ + "dlsym(\"%s\", \"" #function "\") failed: %s", \ + libgbm_filename, dlerror()); \ + goto error; \ + } + + RETRIEVE_GBM_SYMBOL(gbm_create_device); + RETRIEVE_GBM_SYMBOL(gbm_device_get_fd); + RETRIEVE_GBM_SYMBOL(gbm_device_destroy); + + RETRIEVE_GBM_SYMBOL(gbm_surface_create); + RETRIEVE_GBM_SYMBOL(gbm_surface_destroy); + + RETRIEVE_GBM_SYMBOL(gbm_surface_lock_front_buffer); + RETRIEVE_GBM_SYMBOL(gbm_surface_release_buffer); +#undef RETRIEVE_GBM_SYMBOL + self->linux = linux_platform_create(); if (!self->linux) goto error; setenv("EGL_PLATFORM", "drm", true); - self->wcore.vtbl = &wgbm_platform_vtbl; - return &self->wcore; + self->wegl.wcore.vtbl = &wgbm_platform_vtbl; + return &self->wegl.wcore; error: - wgbm_platform_destroy(&self->wcore); + wgbm_platform_destroy(&self->wegl.wcore); return NULL; } @@ -94,8 +136,8 @@ static bool wgbm_dl_can_open(struct wcore_platform *wc_self, int32_t waffle_dl) { - return linux_platform_dl_can_open(wgbm_platform(wc_self)->linux, - waffle_dl); + struct wgbm_platform *self = wgbm_platform(wegl_platform(wc_self)); + return linux_platform_dl_can_open(self->linux, waffle_dl); } static void* @@ -103,9 +145,8 @@ wgbm_dl_sym(struct wcore_platform *wc_self, int32_t waffle_dl, const char *name) { - return linux_platform_dl_sym(wgbm_platform(wc_self)->linux, - waffle_dl, - name); + struct wgbm_platform *self = wgbm_platform(wegl_platform(wc_self)); + return linux_platform_dl_sym(self->linux, waffle_dl, name); } static union waffle_native_context* diff --git a/src/waffle/gbm/wgbm_platform.h b/src/waffle/gbm/wgbm_platform.h index c833780..259eb19 100644 --- a/src/waffle/gbm/wgbm_platform.h +++ b/src/waffle/gbm/wgbm_platform.h @@ -27,23 +27,40 @@ #include <stdbool.h> #include <stdlib.h> +#include <gbm.h> #undef linux -#include "wcore_platform.h" +#include "wegl_platform.h" #include "wcore_util.h" struct linux_platform; struct wgbm_platform { - struct wcore_platform wcore; + struct wegl_platform wegl; struct linux_platform *linux; + + // GBM function pointers + void *gbmHandle; + + struct gbm_device *(*gbm_create_device)(int fd); + int (*gbm_device_get_fd)(struct gbm_device *gbm); + void (*gbm_device_destroy)(struct gbm_device *gbm); + + struct gbm_surface *(*gbm_surface_create)(struct gbm_device *gbm, + uint32_t width, uint32_t height, + uint32_t format, uint32_t flags); + void (*gbm_surface_destroy)(struct gbm_surface *surface); + + struct gbm_bo *(*gbm_surface_lock_front_buffer)(struct gbm_surface *surface); + void (*gbm_surface_release_buffer)(struct gbm_surface *surface, + struct gbm_bo *bo); }; DEFINE_CONTAINER_CAST_FUNC(wgbm_platform, struct wgbm_platform, - struct wcore_platform, - wcore) + struct wegl_platform, + wegl) struct wcore_platform* wgbm_platform_create(void); diff --git a/src/waffle/gbm/wgbm_window.c b/src/waffle/gbm/wgbm_window.c index bce32a4..edac449 100644 --- a/src/waffle/gbm/wgbm_window.c +++ b/src/waffle/gbm/wgbm_window.c @@ -23,8 +23,6 @@ // 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. -#define __GBM__ 1 - #include <stdlib.h> #include <string.h> @@ -38,11 +36,14 @@ #include "wgbm_config.h" #include "wgbm_display.h" +#include "wgbm_platform.h" #include "wgbm_window.h" bool wgbm_window_destroy(struct wcore_window *wc_self) { + struct wcore_platform *wc_plat = wc_self->display->platform; + struct wgbm_platform *plat = wgbm_platform(wegl_platform(wc_plat)); struct wgbm_window *self = wgbm_window(wc_self); bool ok = true; @@ -50,7 +51,7 @@ wgbm_window_destroy(struct wcore_window *wc_self) return ok; ok &= wegl_window_teardown(&self->wegl); - gbm_surface_destroy(self->gbm_surface); + plat->gbm_surface_destroy(self->gbm_surface); free(self); return ok; } @@ -61,8 +62,9 @@ wgbm_window_create(struct wcore_platform *wc_plat, int width, int height) { - struct wgbm_window *self; struct wgbm_display *dpy = wgbm_display(wc_config->display); + struct wgbm_platform *plat = wgbm_platform(wegl_platform(wc_plat)); + struct wgbm_window *self; uint32_t gbm_format; bool ok = true; @@ -72,9 +74,9 @@ wgbm_window_create(struct wcore_platform *wc_plat, gbm_format = wgbm_config_get_gbm_format(&wc_config->attrs); assert(gbm_format != 0); - self->gbm_surface = gbm_surface_create(dpy->gbm_device, width, height, - gbm_format, - GBM_BO_USE_RENDERING); + self->gbm_surface = plat->gbm_surface_create(dpy->gbm_device, + width, height, gbm_format, + GBM_BO_USE_RENDERING); if (!self->gbm_surface) { wcore_errorf(WAFFLE_ERROR_UNKNOWN, "gbm_surface_create failed"); @@ -104,15 +106,18 @@ wgbm_window_show(struct wcore_window *wc_self) bool wgbm_window_swap_buffers(struct wcore_window *wc_self) { + struct wcore_platform *wc_plat = wc_self->display->platform; + struct wgbm_platform *plat = wgbm_platform(wegl_platform(wc_plat)); + if (!wegl_window_swap_buffers(wc_self)) return false; struct wgbm_window *self = wgbm_window(wc_self); - struct gbm_bo *bo = gbm_surface_lock_front_buffer(self->gbm_surface); + struct gbm_bo *bo = plat->gbm_surface_lock_front_buffer(self->gbm_surface); if (!bo) return false; - gbm_surface_release_buffer(self->gbm_surface, bo); + plat->gbm_surface_release_buffer(self->gbm_surface, bo); return true; } diff --git a/src/waffle/gbm/wgbm_window.h b/src/waffle/gbm/wgbm_window.h index 0a895db..729612b 100644 --- a/src/waffle/gbm/wgbm_window.h +++ b/src/waffle/gbm/wgbm_window.h @@ -30,6 +30,7 @@ #include "wegl_window.h" struct wcore_platform; +struct gbm_surface; struct wgbm_window { struct gbm_surface *gbm_surface; diff --git a/src/waffle/glx/glx_config.c b/src/waffle/glx/glx_config.c index e077b07..7792aa0 100644 --- a/src/waffle/glx/glx_config.c +++ b/src/waffle/glx/glx_config.c @@ -170,6 +170,7 @@ glx_config_choose(struct wcore_platform *wc_plat, { struct glx_config *self; struct glx_display *dpy = glx_display(wc_dpy); + struct glx_platform *plat = glx_platform(wc_plat); GLXFBConfig *configs = NULL; int num_configs = 0; @@ -222,7 +223,7 @@ glx_config_choose(struct wcore_platform *wc_plat, }; // Set glx_fbconfig. - configs = wrapped_glXChooseFBConfig(dpy->x11.xlib, + configs = wrapped_glXChooseFBConfig(plat, dpy->x11.xlib, dpy->x11.screen, attrib_list, &num_configs); @@ -235,7 +236,7 @@ glx_config_choose(struct wcore_platform *wc_plat, self->glx_fbconfig = configs[0]; // Set glx_fbconfig_id. - ok = !wrapped_glXGetFBConfigAttrib(dpy->x11.xlib, + ok = !wrapped_glXGetFBConfigAttrib(plat, dpy->x11.xlib, self->glx_fbconfig, GLX_FBCONFIG_ID, &self->glx_fbconfig_id); @@ -245,7 +246,7 @@ glx_config_choose(struct wcore_platform *wc_plat, } // Set xcb_visual_id. - vi = wrapped_glXGetVisualFromFBConfig(dpy->x11.xlib, + vi = wrapped_glXGetVisualFromFBConfig(plat, dpy->x11.xlib, self->glx_fbconfig); if (!vi) { wcore_errorf(WAFFLE_ERROR_UNKNOWN, diff --git a/src/waffle/glx/glx_context.c b/src/waffle/glx/glx_context.c index 62573dc..57db2ba 100644 --- a/src/waffle/glx/glx_context.c +++ b/src/waffle/glx/glx_context.c @@ -45,6 +45,7 @@ glx_context_destroy(struct wcore_context *wc_self) { struct glx_context *self; struct glx_display *dpy; + struct glx_platform *platform; bool ok = true; if (!wc_self) @@ -52,9 +53,10 @@ glx_context_destroy(struct wcore_context *wc_self) self = glx_context(wc_self); dpy = glx_display(wc_self->display); + platform = glx_platform(wc_self->display->platform); if (self->glx) - wrapped_glXDestroyContext(dpy->x11.xlib, self->glx); + wrapped_glXDestroyContext(platform, dpy->x11.xlib, self->glx); ok &= wcore_context_teardown(wc_self); free(self); @@ -182,7 +184,8 @@ glx_context_create_native(struct glx_config *config, } } else { - ctx = wrapped_glXCreateNewContext(dpy->x11.xlib, + ctx = wrapped_glXCreateNewContext(platform, + dpy->x11.xlib, config->glx_fbconfig, GLX_RGBA_TYPE, real_share_ctx, diff --git a/src/waffle/glx/glx_display.c b/src/waffle/glx/glx_display.c index 184438f..567d442 100644 --- a/src/waffle/glx/glx_display.c +++ b/src/waffle/glx/glx_display.c @@ -51,8 +51,9 @@ glx_display_destroy(struct wcore_display *wc_self) static bool glx_display_set_extensions(struct glx_display *self) { - - const char *s = wrapped_glXQueryExtensionsString(self->x11.xlib, + struct glx_platform *platform = glx_platform(self->wcore.platform); + const char *s = wrapped_glXQueryExtensionsString(platform, + self->x11.xlib, self->x11.screen); if (!s) { wcore_errorf(WAFFLE_ERROR_UNKNOWN, diff --git a/src/waffle/glx/glx_platform.c b/src/waffle/glx/glx_platform.c index 804c275..4fb2eed 100644 --- a/src/waffle/glx/glx_platform.c +++ b/src/waffle/glx/glx_platform.c @@ -24,6 +24,7 @@ // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include <stdlib.h> +#include <dlfcn.h> #include "wcore_error.h" @@ -36,6 +37,8 @@ #include "glx_window.h" #include "glx_wrappers.h" +static const char *libGL_filename = "libGL.so.1"; + static const struct wcore_platform_vtbl glx_platform_vtbl; static bool @@ -43,6 +46,7 @@ glx_platform_destroy(struct wcore_platform *wc_self) { struct glx_platform *self = glx_platform(wc_self); bool ok = true; + int error = 0; if (!self) return true; @@ -50,6 +54,16 @@ glx_platform_destroy(struct wcore_platform *wc_self) if (self->linux) ok &= linux_platform_destroy(self->linux); + if (self->glxHandle) { + error = dlclose(self->glxHandle); + if (error) { + ok &= false; + wcore_errorf(WAFFLE_ERROR_UNKNOWN, + "dlclose(\"%s\") failed: %s", + libGL_filename, dlerror()); + } + } + ok &= wcore_platform_teardown(wc_self); free(self); return ok; @@ -69,11 +83,42 @@ glx_platform_create(void) if (!ok) goto error; + self->glxHandle = dlopen(libGL_filename, RTLD_LAZY | RTLD_LOCAL); + if (!self->glxHandle) { + wcore_errorf(WAFFLE_ERROR_FATAL, + "dlopen(\"%s\") failed: %s", + libGL_filename, dlerror()); + goto error; + } + +#define RETRIEVE_GLX_SYMBOL(function) \ + self->function = dlsym(self->glxHandle, #function); \ + if (!self->function) { \ + wcore_errorf(WAFFLE_ERROR_FATAL, \ + "dlsym(\"%s\", \"" #function "\") failed: %s", \ + libGL_filename, dlerror()); \ + goto error; \ + } + + RETRIEVE_GLX_SYMBOL(glXCreateNewContext); + RETRIEVE_GLX_SYMBOL(glXDestroyContext); + RETRIEVE_GLX_SYMBOL(glXMakeCurrent); + + RETRIEVE_GLX_SYMBOL(glXQueryExtensionsString); + RETRIEVE_GLX_SYMBOL(glXGetProcAddress); + + RETRIEVE_GLX_SYMBOL(glXGetVisualFromFBConfig); + RETRIEVE_GLX_SYMBOL(glXGetFBConfigAttrib); + RETRIEVE_GLX_SYMBOL(glXChooseFBConfig); + + RETRIEVE_GLX_SYMBOL(glXSwapBuffers); +#undef RETRIEVE_GLX_SYMBOL + self->linux = linux_platform_create(); if (!self->linux) goto error; - self->glXCreateContextAttribsARB = (PFNGLXCREATECONTEXTATTRIBSARBPROC) glXGetProcAddress((const uint8_t*) "glXCreateContextAttribsARB"); + self->glXCreateContextAttribsARB = (PFNGLXCREATECONTEXTATTRIBSARBPROC) self->glXGetProcAddress((const uint8_t*) "glXCreateContextAttribsARB"); self->wcore.vtbl = &glx_platform_vtbl; return &self->wcore; @@ -89,12 +134,13 @@ glx_platform_make_current(struct wcore_platform *wc_self, struct wcore_window *wc_window, struct wcore_context *wc_ctx) { - bool ok; + struct glx_platform *self = glx_platform(wc_self); Display *dpy = glx_display(wc_dpy)->x11.xlib; GLXDrawable win = wc_window ? glx_window(wc_window)->x11.xcb : 0; GLXContext ctx = wc_ctx ? glx_context(wc_ctx)->glx : NULL; - - ok = wrapped_glXMakeCurrent(dpy, win, ctx); + bool ok; + + ok = wrapped_glXMakeCurrent(self, dpy, win, ctx); if (!ok) { wcore_errorf(WAFFLE_ERROR_UNKNOWN, "glXMakeCurrent failed"); } @@ -106,7 +152,8 @@ static void* glx_platform_get_proc_address(struct wcore_platform *wc_self, const char *name) { - return glXGetProcAddress((const GLubyte*) name); + struct glx_platform *self = glx_platform(wc_self); + return self->glXGetProcAddress((const GLubyte*) name); } static bool diff --git a/src/waffle/glx/glx_platform.h b/src/waffle/glx/glx_platform.h index 58519e8..36ddff6 100644 --- a/src/waffle/glx/glx_platform.h +++ b/src/waffle/glx/glx_platform.h @@ -26,12 +26,8 @@ #pragma once #include <GL/glx.h> -#include <X11/Xlib.h> -#include <xcb/xcb.h> #undef linux -#include "waffle_glx.h" - #include "wcore_platform.h" #include "wcore_util.h" @@ -41,6 +37,27 @@ struct glx_platform { struct wcore_platform wcore; struct linux_platform *linux; + // glX function pointers + void *glxHandle; + + GLXContext (*glXCreateNewContext)(Display *dpy, GLXFBConfig config, + int renderType, GLXContext shareList, + Bool direct); + void (*glXDestroyContext)(Display *dpy, GLXContext ctx); + Bool (*glXMakeCurrent)(Display *dpy, GLXDrawable drawable, GLXContext ctx); + + const char *(*glXQueryExtensionsString)(Display *dpy, int screen); + void *(*glXGetProcAddress)(const GLubyte *procname); + + XVisualInfo *(*glXGetVisualFromFBConfig)(Display *dpy, GLXFBConfig config); + int (*glXGetFBConfigAttrib)(Display *dpy, GLXFBConfig config, + int attribute, int *value); + GLXFBConfig *(*glXChooseFBConfig)(Display *dpy, int screen, + const int *attribList, int *nitems); + + void (*glXSwapBuffers)(Display *dpy, GLXDrawable drawable); + + PFNGLXCREATECONTEXTATTRIBSARBPROC glXCreateContextAttribsARB; }; diff --git a/src/waffle/glx/glx_window.c b/src/waffle/glx/glx_window.c index 702ed3f..34fa784 100644 --- a/src/waffle/glx/glx_window.c +++ b/src/waffle/glx/glx_window.c @@ -25,6 +25,7 @@ #include <stdlib.h> #include <string.h> +#include <xcb/xcb.h> #include "wcore_error.h" @@ -100,8 +101,9 @@ glx_window_swap_buffers(struct wcore_window *wc_self) { struct glx_window *self = glx_window(wc_self); struct glx_display *dpy = glx_display(wc_self->display); + struct glx_platform *plat = glx_platform(wc_self->display->platform); - wrapped_glXSwapBuffers(dpy->x11.xlib, self->x11.xcb); + wrapped_glXSwapBuffers(plat, dpy->x11.xlib, self->x11.xcb); return true; } diff --git a/src/waffle/glx/glx_window.h b/src/waffle/glx/glx_window.h index 4cf44e3..5aed497 100644 --- a/src/waffle/glx/glx_window.h +++ b/src/waffle/glx/glx_window.h @@ -27,8 +27,6 @@ #include <stdbool.h> -#include <xcb/xcb.h> - #include "wcore_window.h" #include "wcore_util.h" diff --git a/src/waffle/glx/glx_wrappers.h b/src/waffle/glx/glx_wrappers.h index 5f53332..1411c86 100644 --- a/src/waffle/glx/glx_wrappers.h +++ b/src/waffle/glx/glx_wrappers.h @@ -45,19 +45,22 @@ #include "x11_wrappers.h" static inline GLXFBConfig* -wrapped_glXChooseFBConfig(Display *dpy, int screen, +wrapped_glXChooseFBConfig(struct glx_platform *platform, + Display *dpy, int screen, const int *attribList, int *nitems) { X11_SAVE_ERROR_HANDLER - GLXFBConfig *configs = glXChooseFBConfig(dpy, screen, attribList, nitems); + GLXFBConfig *configs = platform->glXChooseFBConfig(dpy, screen, + attribList, nitems); X11_RESTORE_ERROR_HANDLER return configs; } static inline GLXContext -wrapped_glXCreateContextAttribsARB( - struct glx_platform *platform, Display *dpy, GLXFBConfig config, - GLXContext share_context, Bool direct, const int *attrib_list) +wrapped_glXCreateContextAttribsARB(struct glx_platform *platform, + Display *dpy, GLXFBConfig config, + GLXContext share_context, Bool direct, + const int *attrib_list) { X11_SAVE_ERROR_HANDLER GLXContext ctx = platform->glXCreateContextAttribsARB( @@ -67,65 +70,72 @@ wrapped_glXCreateContextAttribsARB( } static inline GLXContext -wrapped_glXCreateNewContext(Display *dpy, GLXFBConfig config, int renderType, +wrapped_glXCreateNewContext(struct glx_platform *platform, + Display *dpy, GLXFBConfig config, int renderType, GLXContext shareList, Bool direct) { X11_SAVE_ERROR_HANDLER - GLXContext ctx = glXCreateNewContext(dpy, config, renderType, shareList, - direct); + GLXContext ctx = platform->glXCreateNewContext(dpy, config, renderType, + shareList, direct); X11_RESTORE_ERROR_HANDLER return ctx; } static inline int -wrapped_glXGetFBConfigAttrib(Display *dpy, GLXFBConfig config, +wrapped_glXGetFBConfigAttrib(struct glx_platform *platform, + Display *dpy, GLXFBConfig config, int attribute, int *value) { X11_SAVE_ERROR_HANDLER - int error = glXGetFBConfigAttrib(dpy, config, attribute, value); + int error = platform->glXGetFBConfigAttrib(dpy, config, attribute, value); X11_RESTORE_ERROR_HANDLER return error; } static inline XVisualInfo* -wrapped_glXGetVisualFromFBConfig(Display *dpy, GLXFBConfig config) +wrapped_glXGetVisualFromFBConfig(struct glx_platform *platform, + Display *dpy, GLXFBConfig config) { X11_SAVE_ERROR_HANDLER - XVisualInfo *vi = glXGetVisualFromFBConfig(dpy, config); + XVisualInfo *vi = platform->glXGetVisualFromFBConfig(dpy, config); X11_RESTORE_ERROR_HANDLER return vi; } static inline void -wrapped_glXDestroyContext(Display *dpy, GLXContext ctx) +wrapped_glXDestroyContext(struct glx_platform *platform, + Display *dpy, GLXContext ctx) { X11_SAVE_ERROR_HANDLER - glXDestroyContext(dpy, ctx); + platform->glXDestroyContext(dpy, ctx); X11_RESTORE_ERROR_HANDLER } static inline Bool -wrapped_glXMakeCurrent(Display *dpy, GLXDrawable drawable, GLXContext ctx) +wrapped_glXMakeCurrent(struct glx_platform *platform, + Display *dpy, GLXDrawable drawable, GLXContext ctx) { X11_SAVE_ERROR_HANDLER - Bool ok = glXMakeCurrent(dpy, drawable, ctx); + Bool ok = platform->glXMakeCurrent(dpy, drawable, ctx); X11_RESTORE_ERROR_HANDLER return ok; } static inline const char* -wrapped_glXQueryExtensionsString(Display *dpy, int screen) +wrapped_glXQueryExtensionsString(struct glx_platform *platform, + Display *dpy, int screen) { X11_SAVE_ERROR_HANDLER - const char *s = glXQueryExtensionsString(dpy, screen); + const char *s = platform->glXQueryExtensionsString(dpy, screen); X11_RESTORE_ERROR_HANDLER return s; } static inline void -wrapped_glXSwapBuffers(Display *dpy, GLXDrawable drawable) +wrapped_glXSwapBuffers(struct glx_platform *platform, + Display *dpy, GLXDrawable drawable) { X11_SAVE_ERROR_HANDLER - glXSwapBuffers(dpy, drawable); + platform->glXSwapBuffers(dpy, drawable); X11_RESTORE_ERROR_HANDLER } diff --git a/src/waffle/waffle.def.in b/src/waffle/waffle.def.in new file mode 100644 index 0000000..db8464f --- /dev/null +++ b/src/waffle/waffle.def.in @@ -0,0 +1,33 @@ +LIBRARY @waffle_libname@ + +EXPORTS + waffle_error_get_code + waffle_error_get_info + waffle_error_to_string + waffle_enum_to_string + waffle_init + waffle_make_current + waffle_get_proc_address + waffle_is_extension_in_string + waffle_display_connect + waffle_display_disconnect + waffle_display_supports_context_api + waffle_display_get_native + waffle_config_choose + waffle_config_destroy + waffle_config_get_native + waffle_context_create + waffle_context_destroy + waffle_context_get_native + waffle_window_create + waffle_window_destroy + waffle_window_show + waffle_window_swap_buffers + waffle_window_get_native + waffle_window_resize + waffle_dl_can_open + waffle_dl_sym + waffle_attrib_list_length + waffle_attrib_list_get + waffle_attrib_list_get_with_default + waffle_attrib_list_update
\ No newline at end of file diff --git a/src/waffle/wayland/wayland_platform.c b/src/waffle/wayland/wayland_platform.c index 8ca30d4..63cfdc2 100644 --- a/src/waffle/wayland/wayland_platform.c +++ b/src/waffle/wayland/wayland_platform.c @@ -36,6 +36,7 @@ #include "wegl_config.h" #include "wegl_context.h" +#include "wegl_platform.h" #include "wegl_util.h" #include "wayland_display.h" @@ -47,7 +48,7 @@ static const struct wcore_platform_vtbl wayland_platform_vtbl; static bool wayland_platform_destroy(struct wcore_platform *wc_self) { - struct wayland_platform *self = wayland_platform(wc_self); + struct wayland_platform *self = wayland_platform(wegl_platform(wc_self)); bool ok = true; if (!self) @@ -58,7 +59,7 @@ wayland_platform_destroy(struct wcore_platform *wc_self) if (self->linux) ok &= linux_platform_destroy(self->linux); - ok &= wcore_platform_teardown(wc_self); + ok &= wegl_platform_teardown(&self->wegl); free(self); return ok; } @@ -73,7 +74,7 @@ wayland_platform_create(void) if (self == NULL) return NULL; - ok = wcore_platform_init(&self->wcore); + ok = wegl_platform_init(&self->wegl); if (!ok) goto error; @@ -83,11 +84,11 @@ wayland_platform_create(void) setenv("EGL_PLATFORM", "wayland", true); - self->wcore.vtbl = &wayland_platform_vtbl; - return &self->wcore; + self->wegl.wcore.vtbl = &wayland_platform_vtbl; + return &self->wegl.wcore; error: - wayland_platform_destroy(&self->wcore); + wayland_platform_destroy(&self->wegl.wcore); return NULL; } @@ -95,8 +96,8 @@ static bool wayland_dl_can_open(struct wcore_platform *wc_self, int32_t waffle_dl) { - return linux_platform_dl_can_open(wayland_platform(wc_self)->linux, - waffle_dl); + struct wayland_platform *self = wayland_platform(wegl_platform(wc_self)); + return linux_platform_dl_can_open(self->linux, waffle_dl); } static void* @@ -104,9 +105,8 @@ wayland_dl_sym(struct wcore_platform *wc_self, int32_t waffle_dl, const char *name) { - return linux_platform_dl_sym(wayland_platform(wc_self)->linux, - waffle_dl, - name); + struct wayland_platform *self = wayland_platform(wegl_platform(wc_self)); + return linux_platform_dl_sym(self->linux, waffle_dl, name); } static union waffle_native_config* diff --git a/src/waffle/wayland/wayland_platform.h b/src/waffle/wayland/wayland_platform.h index 41ca8f5..c4e870f 100644 --- a/src/waffle/wayland/wayland_platform.h +++ b/src/waffle/wayland/wayland_platform.h @@ -32,20 +32,20 @@ #include "waffle_wayland.h" -#include "wcore_platform.h" +#include "wegl_platform.h" #include "wcore_util.h" struct linux_platform; struct wayland_platform { - struct wcore_platform wcore; + struct wegl_platform wegl; struct linux_platform *linux; }; DEFINE_CONTAINER_CAST_FUNC(wayland_platform, struct wayland_platform, - struct wcore_platform, - wcore) + struct wegl_platform, + wegl) struct wcore_platform* wayland_platform_create(void); diff --git a/src/waffle/wgl/wgl_config.c b/src/waffle/wgl/wgl_config.c new file mode 100644 index 0000000..59a70a6 --- /dev/null +++ b/src/waffle/wgl/wgl_config.c @@ -0,0 +1,302 @@ +// Copyright 2014 Emil Velikov +// +// 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. +// +// 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 HOLDER 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. + +#include <assert.h> +#include <stdlib.h> +#include <string.h> +#include <windows.h> + +#include "wcore_config_attrs.h" +#include "wcore_error.h" + +#include "wgl_config.h" +#include "wgl_display.h" +#include "wgl_error.h" +#include "wgl_platform.h" +#include "wgl_window.h" + +bool +wgl_config_destroy(struct wcore_config *wc_self) +{ + struct wgl_config *self = wgl_config(wc_self); + bool ok = true; + + if (!self) + return true; + + if (self->window) + ok &= wgl_window_priv_destroy(&self->window->wcore); + + ok &= wcore_config_teardown(wc_self); + free(self); + return ok; +} + +/// @brief Check the values of `attrs->context_*`. +static bool +wgl_config_check_context_attrs(struct wgl_display *dpy, + const struct wcore_config_attrs *attrs) +{ + if (attrs->context_forward_compatible) { + assert(attrs->context_api == WAFFLE_CONTEXT_OPENGL); + assert(wcore_config_attrs_version_ge(attrs, 30)); + } + + if (attrs->context_debug && !dpy->ARB_create_context) { + wcore_errorf(WAFFLE_ERROR_UNSUPPORTED_ON_PLATFORM, + "WGL_ARB_create_context is required in order to " + "request a debug context"); + return false; + } + + switch (attrs->context_api) { + case WAFFLE_CONTEXT_OPENGL: + if (!wcore_config_attrs_version_eq(attrs, 10) && !dpy->ARB_create_context) { + wcore_errorf(WAFFLE_ERROR_UNSUPPORTED_ON_PLATFORM, + "WGL_ARB_create_context is required in order to " + "request an OpenGL version not equal to the default " + "value 1.0"); + return false; + } + else if (wcore_config_attrs_version_ge(attrs, 32) && !dpy->ARB_create_context_profile) { + wcore_errorf(WAFFLE_ERROR_UNSUPPORTED_ON_PLATFORM, + "WGL_ARB_create_context_profile is required " + "to create a context with version >= 3.2"); + return false; + } + else if (wcore_config_attrs_version_ge(attrs, 32)) { + assert(attrs->context_profile == WAFFLE_CONTEXT_CORE_PROFILE || + attrs->context_profile == WAFFLE_CONTEXT_COMPATIBILITY_PROFILE); + } + + if (attrs->context_forward_compatible && !dpy->ARB_create_context) { + wcore_errorf(WAFFLE_ERROR_UNSUPPORTED_ON_PLATFORM, + "WGL_ARB_create_context is required in order to " + "request a forward-compatible context"); + return false; + } + + return true; + + case WAFFLE_CONTEXT_OPENGL_ES1: + assert(wcore_config_attrs_version_eq(attrs, 10) || + wcore_config_attrs_version_eq(attrs, 11)); + assert(!attrs->context_forward_compatible); + + if (!dpy->EXT_create_context_es_profile) { + wcore_errorf(WAFFLE_ERROR_UNSUPPORTED_ON_PLATFORM, + "WGL_EXT_create_context_es_profile is required " + "to create an OpenGL ES1 context"); + return false; + } + + return true; + + case WAFFLE_CONTEXT_OPENGL_ES2: + assert(attrs->context_major_version == 2); + assert(!attrs->context_forward_compatible); + + if (!dpy->EXT_create_context_es2_profile + && !dpy->EXT_create_context_es_profile) { + wcore_errorf(WAFFLE_ERROR_UNSUPPORTED_ON_PLATFORM, + "WGL_EXT_create_context_es_profile or " + "WGL_EXT_create_context_es2_profile is required " + "to create an OpenGL ES2 context"); + return false; + } + + return true; + + case WAFFLE_CONTEXT_OPENGL_ES3: + assert(attrs->context_major_version == 3); + + if (!dpy->EXT_create_context_es_profile) { + wcore_errorf(WAFFLE_ERROR_UNSUPPORTED_ON_PLATFORM, + "WGL_EXT_create_context_es_profile is required " + "to create an OpenGL ES3 context"); + return false; + } + + return true; + + default: + wcore_error_internal("context_api has bad value %#x", + attrs->context_api); + return false; + } +} + +static void +wgl_config_set_pixeldescriptor(struct wgl_config *config, + const struct wcore_config_attrs *attrs) +{ + PIXELFORMATDESCRIPTOR *pfd = &config->pfd; + + pfd->nSize = sizeof(PIXELFORMATDESCRIPTOR); + pfd->nVersion = 1; + + pfd->dwFlags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL; + if (attrs->double_buffered) + pfd->dwFlags |= PFD_DOUBLEBUFFER; + + pfd->iPixelType = PFD_TYPE_RGBA; + + pfd->cColorBits = attrs->rgba_size; + pfd->cRedBits = attrs->red_size; + pfd->cGreenBits = attrs->green_size; + pfd->cBlueBits = attrs->blue_size; + pfd->cAlphaBits = attrs->alpha_size; + + pfd->cDepthBits = attrs->depth_size; + pfd->cStencilBits = attrs->stencil_size; + + // XXX: Double check these + pfd->cAccumRedBits = attrs->accum_buffer; + pfd->cAccumGreenBits = attrs->accum_buffer; + pfd->cAccumBlueBits = attrs->accum_buffer; + pfd->cAccumAlphaBits = attrs->accum_buffer; + pfd->cAccumBits = pfd->cAccumRedBits + + pfd->cAccumGreenBits + + pfd->cAccumBlueBits + + pfd->cAccumAlphaBits; + + pfd->iLayerType = PFD_MAIN_PLANE; +} + +static bool +wgl_config_choose_native(struct wgl_config *config, + struct wgl_display *dpy, + const struct wcore_config_attrs *attrs) +{ + + // Use wglChoosePixelFormatARB if available. + if (dpy->ARB_pixel_format) { + float fAttribs[1] = { 0 }; + int iAttribs[] = { + WGL_COLOR_BITS_ARB, attrs->rgba_size, + WGL_RED_BITS_ARB, attrs->red_size, + WGL_GREEN_BITS_ARB, attrs->green_size, + WGL_BLUE_BITS_ARB, attrs->blue_size, + WGL_ALPHA_BITS_ARB, attrs->alpha_size, + + WGL_DEPTH_BITS_ARB, attrs->depth_size, + WGL_STENCIL_BITS_ARB, attrs->stencil_size, + + WGL_SAMPLE_BUFFERS_ARB, attrs->sample_buffers, + WGL_STEREO_ARB, attrs->samples, + + WGL_DOUBLE_BUFFER_ARB, attrs->double_buffered, + + WGL_ACCUM_RED_BITS_ARB, attrs->accum_buffer, + WGL_ACCUM_GREEN_BITS_ARB, attrs->accum_buffer, + WGL_ACCUM_BLUE_BITS_ARB, attrs->accum_buffer, + WGL_ACCUM_ALPHA_BITS_ARB, attrs->accum_buffer, + + WGL_DRAW_TO_WINDOW_ARB, GL_TRUE, + WGL_ACCELERATION_ARB, WGL_FULL_ACCELERATION_ARB, + + 0, + }; + unsigned int num_formats; + bool ok; + + // But first we need a current context to use it... + ok = wglMakeCurrent(dpy->hDC, dpy->hglrc); + if (!ok) + return false; + + ok = dpy->wglChoosePixelFormatARB(dpy->hDC, iAttribs, fAttribs, 1, + &config->pixel_format, &num_formats); + + wglMakeCurrent(NULL, NULL); + + if (!ok || !num_formats) { + wcore_errorf(WAFFLE_ERROR_UNKNOWN, + "wglChoosePixelFormatARB failed"); + return false; + } + + } + else { + config->pixel_format = ChoosePixelFormat(dpy->hDC, &config->pfd); + if (!config->pixel_format) { + wcore_errorf(WAFFLE_ERROR_UNKNOWN, + "ChoosePixelFormat failed to find a matching format"); + return false; + } + } + + return true; +} + + +struct wcore_config* +wgl_config_choose(struct wcore_platform *wc_plat, + struct wcore_display *wc_dpy, + const struct wcore_config_attrs *attrs) +{ + struct wgl_config *self; + struct wgl_display *dpy = wgl_display(wc_dpy); + struct wcore_window *wc_window; + bool ok; + + ok = wgl_config_check_context_attrs(dpy, attrs); + if (!ok) + return NULL; + + self = wcore_calloc(sizeof(*self)); + if (!self) + return NULL; + + ok = wcore_config_init(&self->wcore, wc_dpy, attrs); + if (!ok) + goto error; + + wgl_config_set_pixeldescriptor(self, attrs); + + ok = wgl_config_choose_native(self, dpy, attrs); + if (!ok) + goto error; + + // Hurray, we've got the pixel format. + + wc_window = wgl_window_priv_create(wc_plat, &self->wcore, 10, 10); + if (!wc_window) + goto error; + + self->window = wgl_window(wc_window); + + // Now let's pray that the root window's hDC is compatible with the + // new window hDC. + ok = SetPixelFormat(self->window->hDC, self->pixel_format, &self->pfd); + if (!ok) + goto error; + + return &self->wcore; + +error: + wgl_config_destroy(&self->wcore); + return NULL; +} diff --git a/src/waffle/wgl/wgl_config.h b/src/waffle/wgl/wgl_config.h new file mode 100644 index 0000000..77f8905 --- /dev/null +++ b/src/waffle/wgl/wgl_config.h @@ -0,0 +1,61 @@ +// Copyright 2014 Emil Velikov +// +// 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. +// +// 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 HOLDER 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. + +#pragma once + +#include <stdbool.h> +#include <stdint.h> +#include <GL/gl.h> +#include <GL/wglext.h> + +#include "wcore_config.h" +#include "wcore_util.h" + +struct wcore_config_attrs; +struct wcore_platform; +struct wgl_window; + +struct wgl_config { + struct wcore_config wcore; + PIXELFORMATDESCRIPTOR pfd; + int pixel_format; + + // XXX: Currently we manage only one window per config. + struct wgl_window *window; +}; + +static inline struct wgl_config* +wgl_config(struct wcore_config *wcore) +{ + return (struct wgl_config *)wcore; +} + +struct wcore_config* +wgl_config_choose(struct wcore_platform *wc_plat, + struct wcore_display *wc_dpy, + const struct wcore_config_attrs *attrs); + +bool +wgl_config_destroy(struct wcore_config *wc_self); diff --git a/src/waffle/wgl/wgl_context.c b/src/waffle/wgl/wgl_context.c new file mode 100644 index 0000000..dd45f81 --- /dev/null +++ b/src/waffle/wgl/wgl_context.c @@ -0,0 +1,212 @@ +// Copyright 2014 Emil Velikov +// +// 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. +// +// 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 HOLDER 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. + +#include <assert.h> +#include <stdlib.h> +#include <windows.h> + +#include "wcore_error.h" + +#include "wgl_config.h" +#include "wgl_context.h" +#include "wgl_display.h" +#include "wgl_error.h" +#include "wgl_window.h" + +bool +wgl_context_destroy(struct wcore_context *wc_self) +{ + struct wgl_context *self = wgl_context(wc_self); + bool ok = true; + + if (!self) + return true; + + if (self->hglrc) + ok &= wglDeleteContext(self->hglrc); + + ok &= wcore_context_teardown(wc_self); + free(self); + return ok; +} + + +/// @brief Fill @a attrib_list, which will be given to wglCreateContextAttribsARB(). +/// +/// This does not validate the `config->context_*` attributes. That validation +/// occurred during waffle_config_choose(). +static bool +wgl_context_fill_attrib_list(struct wgl_config *config, + int attrib_list[]) +{ + struct wcore_config_attrs *attrs = &config->wcore.attrs; + int i = 0; + int context_flags = 0; + + // XXX: Check if the following workaround is relevant under Windows. + + // As a workaround for NVidia, do not specify + // WGL_CONTEXT_MAJOR_VERSION_ARB and WGL_CONTEXT_MINOR_VERSION_ARB in the + // call to wglCreateContextAttribsARB if the user requested an OpenGL + // context of unspecified version or if the user explicitly requested an + // OpenGL 1.0 context. + // + // Calling wglCreateContextAttribARB with MAJOR=1 and MINOR=0, according + // to the spec, is equivalent to calling it with MAJOR and MINOR + // unspecified. From the WGL_ARB_create_context spec: + // + // If an attribute is not specified in <attrib_list>, + // then the default value specified below is used instead. + // + // The default values for WGL_CONTEXT_MAJOR_VERSION_ARB and + // WGL_CONTEXT_MINOR_VERSION_ARB are 1 and 0 respectively. In this + // case, implementations will typically return the most recent version + // of OpenGL they support which is backwards compatible with OpenGL 1.0 + // (e.g. 3.0, 3.1 + GL_ARB_compatibility, or 3.2 compatibility profile) + // + // However, NVidia's libGL, circa 2012-12-19, is not compliant. Calling + // wglCreateContextAttribsARB with MAJOR=1 and MINOR=0 returns an OpenGL + // 2.1 context. Calling it with MAJOR and MINOR unspecified returns + // a context of the latest supported OpenGL version. + if (!(wcore_config_attrs_version_eq(attrs, 10) && + attrs->context_api == WAFFLE_CONTEXT_OPENGL)) + { + attrib_list[i++] = WGL_CONTEXT_MAJOR_VERSION_ARB; + attrib_list[i++] = attrs->context_major_version; + + attrib_list[i++] = WGL_CONTEXT_MINOR_VERSION_ARB; + attrib_list[i++] = attrs->context_minor_version; + } + + switch (attrs->context_api) { + case WAFFLE_CONTEXT_OPENGL: + if (wcore_config_attrs_version_ge(attrs, 32)) { + switch (attrs->context_profile) { + case WAFFLE_CONTEXT_CORE_PROFILE: + attrib_list[i++] = WGL_CONTEXT_PROFILE_MASK_ARB; + attrib_list[i++] = WGL_CONTEXT_CORE_PROFILE_BIT_ARB; + break; + case WAFFLE_CONTEXT_COMPATIBILITY_PROFILE: + attrib_list[i++] = WGL_CONTEXT_PROFILE_MASK_ARB; + attrib_list[i++] = WGL_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB; + break; + default: + assert(false); + break; + } + } + + if (attrs->context_forward_compatible) { + context_flags |= WGL_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB; + } + + break; + case WAFFLE_CONTEXT_OPENGL_ES1: + case WAFFLE_CONTEXT_OPENGL_ES2: + case WAFFLE_CONTEXT_OPENGL_ES3: + attrib_list[i++] = WGL_CONTEXT_PROFILE_MASK_ARB; + attrib_list[i++] = WGL_CONTEXT_ES_PROFILE_BIT_EXT; + break; + } + + if (attrs->context_debug) { + context_flags |= WGL_CONTEXT_DEBUG_BIT_ARB; + } + + if (context_flags != 0) { + attrib_list[i++] = WGL_CONTEXT_FLAGS_ARB; + attrib_list[i++] = context_flags; + } + + attrib_list[i++] = 0; + return true; +} + +static HGLRC +wgl_context_create_native(struct wgl_config *config, + struct wgl_context *share_ctx) +{ + struct wgl_display *dpy = wgl_display(config->wcore.display); + HGLRC real_share_ctx = share_ctx ? share_ctx->hglrc : NULL; + HGLRC hglrc; + + if (dpy->ARB_create_context) { + bool ok; + + // Choose a large size to prevent accidental overflow. + int attrib_list[64]; + + ok = wgl_context_fill_attrib_list(config, attrib_list); + if (!ok) + return NULL; + + hglrc = dpy->wglCreateContextAttribsARB(config->window->hDC, + real_share_ctx, + attrib_list); + if (!hglrc) { + wcore_errorf(WAFFLE_ERROR_UNKNOWN, + "wglCreateContextAttribsARB failed"); + return NULL; + } + } + else { + hglrc = wglCreateContext(config->window->hDC); + if (!hglrc) { + wcore_errorf(WAFFLE_ERROR_UNKNOWN, "wglCreateContext failed"); + return NULL; + } + } + + return hglrc; +} + +struct wcore_context* +wgl_context_create(struct wcore_platform *wc_plat, + struct wcore_config *wc_config, + struct wcore_context *wc_share_ctx) +{ + struct wgl_config *config = wgl_config(wc_config); + struct wgl_context *share_ctx = wgl_context(wc_share_ctx); + struct wgl_context *self; + int error; + + self = wcore_calloc(sizeof(*self)); + if (!self) + return NULL; + + error = !wcore_context_init(&self->wcore, wc_config); + if (error) + goto fail; + + self->hglrc = wgl_context_create_native(config, share_ctx); + if (!self->hglrc) + goto fail; + + return &self->wcore; + +fail: + wgl_context_destroy(&self->wcore); + return NULL; +} diff --git a/src/waffle/wgl/wgl_context.h b/src/waffle/wgl/wgl_context.h new file mode 100644 index 0000000..c55ad58 --- /dev/null +++ b/src/waffle/wgl/wgl_context.h @@ -0,0 +1,53 @@ +// Copyright 2014 Emil Velikov +// +// 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. +// +// 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 HOLDER 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. + +#pragma once + +#include <stdbool.h> + +#include "wcore_context.h" +#include "wcore_util.h" + +struct wcore_config; +struct wcore_platform; + +struct wgl_context { + struct wcore_context wcore; + HGLRC hglrc; +}; + +static inline struct wgl_context* +wgl_context(struct wcore_context *wcore) +{ + return (struct wgl_context *)wcore; +} + +struct wcore_context* +wgl_context_create(struct wcore_platform *wc_plat, + struct wcore_config *wc_config, + struct wcore_context *wc_share_ctx); + +bool +wgl_context_destroy(struct wcore_context *wc_self); diff --git a/src/waffle/wgl/wgl_display.c b/src/waffle/wgl/wgl_display.c new file mode 100644 index 0000000..9b3b38e --- /dev/null +++ b/src/waffle/wgl/wgl_display.c @@ -0,0 +1,275 @@ +// Copyright 2014 Emil Velikov +// +// 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. +// +// 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 HOLDER 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. + + +#include <stdlib.h> +#include <windows.h> +#include "c99_compat.h" + +#include "wcore_error.h" + +#include "wgl_display.h" +#include "wgl_dl.h" +#include "wgl_platform.h" + +bool +wgl_display_destroy(struct wcore_display *wc_self) +{ + struct wgl_display *self = wgl_display(wc_self); + bool ok = true; + + if (!self) + return true; + + if (self->hWnd) { + if (self->hglrc) { + ok &= wglDeleteContext(self->hglrc); + } + + if (self->hDC) + ok &= ReleaseDC(self->hWnd, self->hDC); + + ok &= DestroyWindow(self->hWnd); + } + + ok &= wcore_display_teardown(wc_self); + free(self); + return ok; +} + +static bool +wgl_display_create_window(struct wgl_platform *plat, struct wgl_display *dpy) +{ + dpy->hWnd = CreateWindow(plat->class_name, NULL, + WS_POPUPWINDOW|WS_DISABLED, + 0, 0, 0, 0, NULL, NULL, NULL, NULL); + if (!dpy->hWnd) + return false; + + dpy->hDC = GetDC(dpy->hWnd); + if (!dpy->hDC) + return false; + + return true; +} + +static bool +wgl_display_choose_config(struct wgl_display *dpy) +{ + // XXX: Is there a move common/appropriate pixelformat ? + PIXELFORMATDESCRIPTOR pfd = {0}; + bool ok; + + pfd.nSize = sizeof(PIXELFORMATDESCRIPTOR); + pfd.nVersion = 1; + + pfd.dwFlags = PFD_SUPPORT_OPENGL | PFD_DRAW_TO_WINDOW | PFD_DOUBLEBUFFER; + + pfd.iPixelType = PFD_TYPE_RGBA; + pfd.iLayerType = PFD_MAIN_PLANE; + pfd.cColorBits = 32; + pfd.cDepthBits = 16; + + dpy->pixel_format = ChoosePixelFormat(dpy->hDC, &pfd); + if (!dpy->pixel_format) + return false; + + ok = SetPixelFormat(dpy->hDC, dpy->pixel_format, &pfd); + if (!ok) + return false; + + return true; +} + +static bool +wgl_display_hardware_render(struct wgl_display *dpy) +{ +#ifndef GL_RENDERER +#define GL_RENDERER 0x1F01 +#endif + typedef unsigned int GLenum; + typedef unsigned char GLubyte; + typedef const GLubyte * (__stdcall *PFNGLGETSTRINGPROC)(GLenum name); + + PFNGLGETSTRINGPROC glGetString_func; + const GLubyte *gl_renderer; + + glGetString_func = wgl_dl_sym(dpy->wcore.platform, WAFFLE_DL_OPENGL, "glGetString"); + if (!glGetString_func) + return false; + + gl_renderer = glGetString_func(GL_RENDERER); + + // Bail out if we cannot retrieve the renderer string or if we're using GDI + if (!gl_renderer || strcasecmp((const char *)gl_renderer, "GDI Generic") == 0) + return false; + + return true; +} + +static bool +wgl_display_set_extensions(struct wgl_display *dpy) +{ + typedef const char * (__stdcall *PFNWGLGETEXTENSIONSSTRINGARBPROC)(HDC hdc); + PFNWGLGETEXTENSIONSSTRINGARBPROC wglGetExtensionsStringARB_func; + const char *extensions; + + wglGetExtensionsStringARB_func = (void *)wglGetProcAddress("wglGetExtensionsStringARB"); + if (!wglGetExtensionsStringARB_func) { + wcore_errorf(WAFFLE_ERROR_UNKNOWN, + "wglGetProcAddress(\"wglGetExtensionsStringARB\") failed"); + return false; + } + + extensions = wglGetExtensionsStringARB_func(dpy->hDC); + + if (!extensions) { + wcore_errorf(WAFFLE_ERROR_UNKNOWN, + "wglGetExtensionsStringARB failed"); + return false; + } + + dpy->ARB_create_context = waffle_is_extension_in_string(extensions, "WGL_ARB_create_context"); + dpy->ARB_create_context_profile = waffle_is_extension_in_string(extensions, "WGL_ARB_create_context_profile"); + dpy->EXT_create_context_es_profile = waffle_is_extension_in_string(extensions, "WGL_EXT_create_context_es_profile"); + + // The WGL_EXT_create_context_es2_profile spec, version 5 2012/04/06, + // states that WGL_EXT_create_context_es_profile is an alias of + // WGL_EXT_create_context_es2_profile and requires that both names must be + // exported together for backwards compatibility with clients that expect + // the es2_profile name. + if (dpy->EXT_create_context_es_profile) { + dpy->EXT_create_context_es2_profile = true; + } + else { + // Assume that WGL does not implement version 3 of the extension, in + // which case the ES contexts WGL is capable of creating is ES2. + dpy->EXT_create_context_es2_profile = waffle_is_extension_in_string(extensions, "WGL_EXT_create_context_es2_profile"); + } + + dpy->ARB_pixel_format = waffle_is_extension_in_string(extensions, "WGL_ARB_pixel_format"); + + return true; +} + +static bool +wgl_display_set_func_ptrs(struct wgl_display *dpy) +{ + if (dpy->ARB_create_context) { + dpy->wglCreateContextAttribsARB = (void *)wglGetProcAddress("wglCreateContextAttribsARB"); + if (!dpy->wglCreateContextAttribsARB) { + wcore_errorf(WAFFLE_ERROR_UNKNOWN, + "wglGetProcAddress(\"wglCreateContextAttribsARB\") failed"); + return false; + } + } + + if (dpy->ARB_pixel_format) { + dpy->wglChoosePixelFormatARB = (void *)wglGetProcAddress("wglChoosePixelFormatARB"); + if (!dpy->wglChoosePixelFormatARB) { + wcore_errorf(WAFFLE_ERROR_UNKNOWN, + "wglGetProcAddress(\"wglChoosePixelFormatARB\") failed"); + return false; + } + } + + return true; +} + +struct wcore_display* +wgl_display_connect(struct wcore_platform *wc_plat, + const char *name) +{ + struct wgl_display *self; + bool ok; + + self = wcore_calloc(sizeof(*self)); + if (!self) + return NULL; + + ok = wcore_display_init(&self->wcore, wc_plat); + if (!ok) + goto error; + + ok = wgl_display_create_window(wgl_platform(wc_plat), self); + if (!ok) + goto error; + + ok = wgl_display_choose_config(self); + if (!ok) + goto error; + + self->hglrc = wglCreateContext(self->hDC); + if (!self->hglrc) + goto error; + + ok = wglMakeCurrent(self->hDC, self->hglrc); + if (!ok) + goto error; + + ok = wgl_display_hardware_render(self); + if (!ok) + goto error; + + ok = wgl_display_set_extensions(self); + if (!ok) + goto error; + + ok = wgl_display_set_func_ptrs(self); + if (!ok) + goto error; + + ok = wglMakeCurrent(NULL, NULL); + if (!ok) + goto error; + + return &self->wcore; + +error: + wgl_display_destroy(&self->wcore); + return NULL; +} + +bool +wgl_display_supports_context_api(struct wcore_display *wc_self, + int32_t context_api) +{ + struct wgl_display *self = wgl_display(wc_self); + + switch (context_api) { + case WAFFLE_CONTEXT_OPENGL: + return true; + case WAFFLE_CONTEXT_OPENGL_ES1: + return self->EXT_create_context_es_profile; + case WAFFLE_CONTEXT_OPENGL_ES2: + return self->EXT_create_context_es2_profile; + case WAFFLE_CONTEXT_OPENGL_ES3: + return self->EXT_create_context_es_profile; + default: + wcore_error_internal("waffle_context_api has bad value %#x", + context_api); + return false; + } +} diff --git a/src/waffle/wgl/wgl_display.h b/src/waffle/wgl/wgl_display.h new file mode 100644 index 0000000..d0d94fe --- /dev/null +++ b/src/waffle/wgl/wgl_display.h @@ -0,0 +1,81 @@ +// Copyright 2014 Emil Velikov +// +// 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. +// +// 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 HOLDER 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. + +#pragma once + +#include <stdbool.h> +#include <stdint.h> + +#include "wcore_display.h" +#include "wcore_util.h" + +struct wcore_platform; + +// XXX: Move the typedefs ? +typedef HGLRC (__stdcall *PFNWGLCREATECONTEXTATTRIBSARBPROC)(HDC hDC, + HGLRC hShareContext, + const int *attribList); + +typedef BOOL (__stdcall *PFNWGLCHOOSEPIXELFORMATARBPROC )(HDC hdc, + const int * piAttribIList, + const float * pfAttribFList, + unsigned int nMaxFormats, + int * piFormats, + unsigned int * nNumFormats); + +struct wgl_display { + struct wcore_display wcore; + + HWND hWnd; + HDC hDC; + int pixel_format; + HGLRC hglrc; + + bool ARB_create_context; + bool ARB_create_context_profile; + bool EXT_create_context_es_profile; + bool EXT_create_context_es2_profile; + bool ARB_pixel_format; + + PFNWGLCREATECONTEXTATTRIBSARBPROC wglCreateContextAttribsARB; + PFNWGLCHOOSEPIXELFORMATARBPROC wglChoosePixelFormatARB; +}; + +static inline struct wgl_display* +wgl_display(struct wcore_display *wcore) +{ + return (struct wgl_display*)wcore; +} + +struct wcore_display* +wgl_display_connect(struct wcore_platform *wc_plat, + const char *name); + +bool +wgl_display_destroy(struct wcore_display *wc_self); + +bool +wgl_display_supports_context_api(struct wcore_display *wc_self, + int32_t context_api); diff --git a/src/waffle/wgl/wgl_dl.c b/src/waffle/wgl/wgl_dl.c new file mode 100644 index 0000000..9d05cb9 --- /dev/null +++ b/src/waffle/wgl/wgl_dl.c @@ -0,0 +1,143 @@ +// Copyright 2014 Emil Velikov +// +// 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. +// +// 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 HOLDER 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. + + +#include <assert.h> +#include <windows.h> + +#include "wcore_error.h" + +#include "wgl_dl.h" +#include "wgl_platform.h" + +static const char *wgl_gl_path = "OPENGL32"; + +static bool +wgl_dl_check_enum(int32_t waffle_dl) +{ + switch (waffle_dl) { + case WAFFLE_DL_OPENGL: + case WAFFLE_DL_OPENGL_ES1: + case WAFFLE_DL_OPENGL_ES2: + case WAFFLE_DL_OPENGL_ES3: + // OPENGL32.DLL provides GL and GLES* symbols. + return true; + default: + assert(false); + return false; + } +} + +static bool +wgl_dl_open(struct wgl_platform *plat) +{ + plat->dl_gl = LoadLibraryA(wgl_gl_path); + + if (plat->dl_gl) + return true; + + int error = GetLastError(); + + if (error) { + wcore_errorf(WAFFLE_ERROR_UNKNOWN, + "LoadLibraryA(\"%s\") failed: %d", + wgl_gl_path, error); + } + + return false; +} + +bool +wgl_dl_can_open(struct wcore_platform *wc_plat, + int32_t waffle_dl) +{ + struct wgl_platform *plat = wgl_platform(wc_plat); + + if (!wgl_dl_check_enum(waffle_dl)) + return false; + + if (plat->dl_gl) + return true; + + WCORE_ERROR_DISABLED({ + wgl_dl_open(plat); + }); + + return plat->dl_gl != NULL; +} + +void* +wgl_dl_sym(struct wcore_platform *wc_plat, + int32_t waffle_dl, + const char *name) +{ + struct wgl_platform *plat = wgl_platform(wc_plat); + + if (!wgl_dl_check_enum(waffle_dl)) + return NULL; + + if (!plat->dl_gl) + wgl_dl_open(plat); + + if (!plat->dl_gl) + return NULL; + + void *sym = GetProcAddress(plat->dl_gl, name); + + if (sym) + return sym; + + int error = GetLastError(); + + if (error) { + wcore_errorf(WAFFLE_ERROR_UNKNOWN, + "GetProcAddress(libname=\"%s\", symbol=\"%s\") failed: %d", + wgl_gl_path, name, error); + } + + return NULL; +} + +bool +wgl_dl_close(struct wcore_platform *wc_plat) +{ + struct wgl_platform *plat = wgl_platform(wc_plat); + + if (!plat->dl_gl) + return true; + + if (FreeLibrary(plat->dl_gl)) + return true; + + int error = GetLastError(); + + if (error) { + wcore_errorf(WAFFLE_ERROR_UNKNOWN, + "FreeLibrary(libname=\"%s\") failed: %d", + wgl_gl_path, error); + } + + return false; +} diff --git a/src/waffle/wgl/wgl_dl.h b/src/waffle/wgl/wgl_dl.h new file mode 100644 index 0000000..6ccd170 --- /dev/null +++ b/src/waffle/wgl/wgl_dl.h @@ -0,0 +1,43 @@ +// Copyright 2014 Emil Velikov +// +// 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. +// +// 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 HOLDER 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. + +#pragma once + +#include <stdbool.h> +#include <stdint.h> + +struct wcore_platform; + +bool +wgl_dl_can_open(struct wcore_platform *wc_plat, + int32_t waffle_dl); + +void* +wgl_dl_sym(struct wcore_platform *wc_plat, + int32_t waffle_dl, + const char *name); + +bool +wgl_dl_close(struct wcore_platform *wc_plat); diff --git a/src/waffle/wgl/wgl_error.c b/src/waffle/wgl/wgl_error.c new file mode 100644 index 0000000..679ff43 --- /dev/null +++ b/src/waffle/wgl/wgl_error.c @@ -0,0 +1,48 @@ +// Copyright 2014 Emil Velikov +// +// 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. +// +// 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 HOLDER 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. + +#include <assert.h> +#include <stdbool.h> +#include <stdlib.h> + +#include "wcore_error.h" + +#include "wgl_error.h" + +const char* +wgl_error_to_string(int error_code) +{ + return NULL; +} + +void +wgl_error_failed_func(const char *func_name, int error_code) +{ + wcore_errorf(WAFFLE_ERROR_UNKNOWN, + "%s failed with %s: %s", + func_name, + wgl_error_to_string(error_code), + NULL); +} diff --git a/src/waffle/wgl/wgl_error.h b/src/waffle/wgl/wgl_error.h new file mode 100644 index 0000000..aca0769 --- /dev/null +++ b/src/waffle/wgl/wgl_error.h @@ -0,0 +1,32 @@ +// Copyright 2014 Emil Velikov +// +// 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. +// +// 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 HOLDER 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. + +#pragma once + +const char* +wgl_error_to_string(int error_code); + +void +wgl_error_failed_func(const char *func_name, int error_code); diff --git a/src/waffle/wgl/wgl_platform.c b/src/waffle/wgl/wgl_platform.c new file mode 100644 index 0000000..f4649b4 --- /dev/null +++ b/src/waffle/wgl/wgl_platform.c @@ -0,0 +1,173 @@ +// Copyright 2014 Emil Velikov +// +// 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. +// +// 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 HOLDER 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. + +#include <stdlib.h> +#include <windows.h> + +#include "wcore_error.h" + +#include "wgl_config.h" +#include "wgl_context.h" +#include "wgl_display.h" +#include "wgl_dl.h" +#include "wgl_platform.h" +#include "wgl_window.h" + +static const struct wcore_platform_vtbl wgl_platform_vtbl; + +const char* wfl_class_name = "waffle"; + +static bool +wgl_platform_destroy(struct wcore_platform *wc_self) +{ + struct wgl_platform *self = wgl_platform(wc_self); + bool ok = true; + + if (!self) + return true; + + if (self->dl_gl) + ok &= wgl_dl_close(wc_self); + + if (self->class_name) + ok &= UnregisterClass(self->class_name, GetModuleHandle(NULL)); + + ok &= wcore_platform_teardown(wc_self); + free(self); + return ok; +} + +static bool +wgl_platform_register_class(const char* class_name) +{ + WNDCLASS wc; + bool ok; + + memset(&wc, 0, sizeof(wc)); + wc.style = CS_OWNDC | CS_HREDRAW | CS_VREDRAW; + // XXX: Use a non-default window_proc ? + wc.lpfnWndProc = DefWindowProc; + wc.hIcon = LoadIcon(NULL, IDI_APPLICATION); + wc.hInstance = GetModuleHandle(NULL); + wc.hCursor = LoadCursor(NULL, IDC_ARROW); + wc.hbrBackground = (HBRUSH) (COLOR_BTNFACE + 1);; + wc.lpszClassName = class_name; + + ok = !!RegisterClass(&wc); + + if (!ok) { + int error = GetLastError(); + + if (error) { + wcore_errorf(WAFFLE_ERROR_UNKNOWN, + "RegisterClass() failed: %d", + error); + } + } + + return ok; +} + +struct wcore_platform* +wgl_platform_create(void) +{ + struct wgl_platform *self; + bool ok; + + self = wcore_calloc(sizeof(*self)); + if (!self) + return NULL; + + ok = wcore_platform_init(&self->wcore); + if (!ok) + goto error; + + ok = wgl_platform_register_class(wfl_class_name); + if (!ok) + goto error; + self->class_name = wfl_class_name; + + self->wcore.vtbl = &wgl_platform_vtbl; + return &self->wcore; + +error: + wgl_platform_destroy(&self->wcore); + return NULL; +} + +static bool +wgl_make_current(struct wcore_platform *wc_self, + struct wcore_display *wc_dpy, + struct wcore_window *wc_window, + struct wcore_context *wc_ctx) +{ + HDC hDC = wc_window ? wgl_window(wc_window)->hDC : NULL; + HGLRC hglrc = wc_ctx ? wgl_context(wc_ctx)->hglrc : NULL; + + return wglMakeCurrent(hDC, hglrc); +} + +static void* +wgl_get_proc_address(struct wcore_platform *wc_self, const char *name) +{ + return wglGetProcAddress(name); +} + +static const struct wcore_platform_vtbl wgl_platform_vtbl = { + .destroy = wgl_platform_destroy, + + .make_current = wgl_make_current, + .get_proc_address = wgl_get_proc_address, + .dl_can_open = wgl_dl_can_open, + .dl_sym = wgl_dl_sym, + + .display = { + .connect = wgl_display_connect, + .destroy = wgl_display_destroy, + .supports_context_api = wgl_display_supports_context_api, + .get_native = NULL, + }, + + .config = { + .choose = wgl_config_choose, + .destroy = wgl_config_destroy, + .get_native = NULL, + }, + + .context = { + .create = wgl_context_create, + .destroy = wgl_context_destroy, + .get_native = NULL, + }, + + .window = { + .create = wgl_window_create, + .destroy = wgl_window_destroy, + .show = wgl_window_show, + .resize = wgl_window_resize, + .swap_buffers = wgl_window_swap_buffers, + .get_native = NULL, + }, +}; diff --git a/src/waffle/wgl/wgl_platform.h b/src/waffle/wgl/wgl_platform.h new file mode 100644 index 0000000..a25d2f4 --- /dev/null +++ b/src/waffle/wgl/wgl_platform.h @@ -0,0 +1,53 @@ +// Copyright 2014 Emil Velikov +// +// 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. +// +// 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 HOLDER 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. + +#pragma once + +#include "wcore_platform.h" +#include "wcore_util.h" + +struct wgl_platform { + struct wcore_platform wcore; + + /// @brief The full system version number - (major << 8) | minor. + /// + /// For example, 0x0501 indicates Windows XP. + int32_t system_version_full; + + /// @brief The OpenGL library obtained with LoadLibraryA(). + void *dl_gl; + + /// @brief The class name of the waffle windows. + const char *class_name; +}; + +static inline struct wgl_platform* +wgl_platform(struct wcore_platform *wcore) +{ + return (struct wgl_platform*)wcore; +} + +struct wcore_platform* +wgl_platform_create(void); diff --git a/src/waffle/wgl/wgl_window.c b/src/waffle/wgl/wgl_window.c new file mode 100644 index 0000000..7c3932f --- /dev/null +++ b/src/waffle/wgl/wgl_window.c @@ -0,0 +1,212 @@ +// Copyright 2014 Emil Velikov +// +// 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. +// +// 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 HOLDER 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. + +#include <windows.h> + +#include "wcore_error.h" + +#include "wgl_config.h" +#include "wgl_platform.h" +#include "wgl_window.h" + +bool +wgl_window_destroy(struct wcore_window *wc_self) +{ + struct wgl_window *self = wgl_window(wc_self); + + assert(self); + assert(self->hWnd); + + self->created = false; + ShowWindow(self->hWnd, SW_HIDE); + return true; +} + +bool +wgl_window_priv_destroy(struct wcore_window *wc_self) +{ + struct wgl_window *self = wgl_window(wc_self); + bool ok = true; + + if (!self) + return true; + + if (self->hWnd) { + if (self->hDC) { + ok &= ReleaseDC(self->hWnd, self->hDC); + } + ok &= DestroyWindow(self->hWnd); + } + + ok &= wcore_window_teardown(wc_self); + free(self); + return ok; +} + +struct wcore_window* +wgl_window_create(struct wcore_platform *wc_plat, + struct wcore_config *wc_config, + int width, + int height) +{ + struct wgl_config *config = wgl_config(wc_config); + bool ok; + + assert(config->window); + + // Currently we do not allow multiple windows per config. + // Neither piglit nor the waffle examples do that yet, so just + // return NULL in case that ever changes. + assert(!config->window->created); + if (config->window->created) + return NULL; + + config->window->created = true; + + ok = wgl_window_resize(&config->window->wcore, width, height); + if (!ok) + return NULL; + + return &config->window->wcore; +} + +struct wcore_window* +wgl_window_priv_create(struct wcore_platform *wc_plat, + struct wcore_config *wc_config, + int width, + int height) +{ + struct wgl_platform *plat = wgl_platform(wc_plat); + struct wgl_window *self; + bool ok; + RECT rect; + + self = wcore_calloc(sizeof(*self)); + if (!self) + return NULL; + + ok = wcore_window_init(&self->wcore, wc_config); + if (!ok) + goto error; + + rect.left = 0; + rect.top = 0; + rect.right = rect.left + width; + rect.bottom = rect.top + height; + + ok = AdjustWindowRect(&rect, WS_POPUPWINDOW, FALSE); + if (!ok) + goto error; + + self->hWnd = CreateWindow(plat->class_name, NULL, WS_POPUPWINDOW, + 0, 0, + rect.right - rect.left, rect.bottom - rect.top, + NULL, NULL, NULL, NULL); + if (!self->hWnd) + goto error; + +#ifndef NDEBUG + // Verify the client area size matches the required size. + + GetClientRect(self->hWnd, &rect); + assert(rect.left == 0); + assert(rect.top == 0); + assert(rect.right - rect.left == width); + assert(rect.bottom - rect.top == height); +#endif + + self->hDC = GetDC(self->hWnd); + if (!self->hDC) + goto error; + + return &self->wcore; + +error: + wgl_window_priv_destroy(&self->wcore); + return NULL; +} + +bool +wgl_window_show(struct wcore_window *wc_self) +{ + struct wgl_window *self = wgl_window(wc_self); + + assert(self); + assert(self->hWnd); + + // If the window was previously hidden the function returns zero, + // and non-zero otherwise. + // XXX: Use SW_SHOW or SW_SHOWDEFAULT, SW_SHOWNORMAL ? + ShowWindow(self->hWnd, SW_SHOW); + return true; +} + +bool +wgl_window_resize(struct wcore_window *wc_self, + int32_t width, int32_t height) +{ + struct wgl_window *self = wgl_window(wc_self); + RECT rect; + bool ok; + + assert(self); + assert(self->hWnd); + + rect.left = 0; + rect.top = 0; + rect.right = rect.left + width; + rect.bottom = rect.top + height; + + ok = AdjustWindowRect(&rect, WS_POPUPWINDOW, FALSE); + if (!ok) + return false; + + ok = SetWindowPos(self->hWnd, 0, 0, 0, + rect.right - rect.left, + rect.bottom - rect.top, + SWP_NOMOVE|SWP_NOZORDER|SWP_NOACTIVATE); + +#ifndef NDEBUG + // Verify the client area size matches the required size. + + GetClientRect(self->hWnd, &rect); + assert(rect.left == 0); + assert(rect.top == 0); + assert(rect.right - rect.left == width); + assert(rect.bottom - rect.top == height); +#endif + return ok; +} + +bool +wgl_window_swap_buffers(struct wcore_window *wc_self) +{ + struct wgl_window *self = wgl_window(wc_self); + + assert(self); + assert(self->hDC); + + return SwapBuffers(self->hDC); +} diff --git a/src/waffle/wgl/wgl_window.h b/src/waffle/wgl/wgl_window.h new file mode 100644 index 0000000..a60205d --- /dev/null +++ b/src/waffle/wgl/wgl_window.h @@ -0,0 +1,77 @@ +// Copyright 2014 Emil Velikov +// +// 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. +// +// 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 HOLDER 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. + +#pragma once + +#include <stdbool.h> + +#include "wcore_util.h" +#include "wcore_window.h" + +struct wcore_platform; + +struct wgl_window { + struct wcore_window wcore; + HWND hWnd; + HDC hDC; + bool created; +}; + +static inline struct wgl_window* +wgl_window(struct wcore_window *wcore) +{ + return (struct wgl_window*)wcore; +} + +struct wcore_window* +wgl_window_priv_create(struct wcore_platform *wc_plat, + struct wcore_config *wc_config, + int width, + int height); + +bool +wgl_window_priv_destroy(struct wcore_window *wc_self); + +struct wcore_window* +wgl_window_create(struct wcore_platform *wc_plat, + struct wcore_config *wc_config, + int width, + int height); + +bool +wgl_window_destroy(struct wcore_window *wc_self); + +bool +wgl_window_show(struct wcore_window *wc_self); + +bool +wgl_window_resize(struct wcore_window *wc_self, + int32_t width, int32_t height); + +bool +wgl_window_swap_buffers(struct wcore_window *wc_self); + +union waffle_native_window* +wgl_window_get_native(struct wcore_window *wc_self); diff --git a/src/waffle/xegl/xegl_platform.c b/src/waffle/xegl/xegl_platform.c index 71bf23c..e36a41b 100644 --- a/src/waffle/xegl/xegl_platform.c +++ b/src/waffle/xegl/xegl_platform.c @@ -31,6 +31,7 @@ #include "wegl_config.h" #include "wegl_context.h" +#include "wegl_platform.h" #include "wegl_util.h" #include "linux_platform.h" @@ -44,7 +45,7 @@ static const struct wcore_platform_vtbl xegl_platform_vtbl; static bool xegl_platform_destroy(struct wcore_platform *wc_self) { - struct xegl_platform *self = xegl_platform(wc_self); + struct xegl_platform *self = xegl_platform(wegl_platform(wc_self)); bool ok = true; if (!self) @@ -55,7 +56,7 @@ xegl_platform_destroy(struct wcore_platform *wc_self) if (self->linux) ok &= linux_platform_destroy(self->linux); - ok &= wcore_platform_teardown(wc_self); + ok &= wegl_platform_teardown(&self->wegl); free(self); return ok; } @@ -70,7 +71,7 @@ xegl_platform_create(void) if (self == NULL) return NULL; - ok = wcore_platform_init(&self->wcore); + ok = wegl_platform_init(&self->wegl); if (!ok) goto error; @@ -80,11 +81,11 @@ xegl_platform_create(void) setenv("EGL_PLATFORM", "x11", true); - self->wcore.vtbl = &xegl_platform_vtbl; - return &self->wcore; + self->wegl.wcore.vtbl = &xegl_platform_vtbl; + return &self->wegl.wcore; error: - xegl_platform_destroy(&self->wcore); + xegl_platform_destroy(&self->wegl.wcore); return NULL; } @@ -92,8 +93,8 @@ static bool xegl_dl_can_open(struct wcore_platform *wc_self, int32_t waffle_dl) { - return linux_platform_dl_can_open(xegl_platform(wc_self)->linux, - waffle_dl); + struct xegl_platform *self = xegl_platform(wegl_platform(wc_self)); + return linux_platform_dl_can_open(self->linux, waffle_dl); } static void* @@ -101,9 +102,8 @@ xegl_dl_sym(struct wcore_platform *wc_self, int32_t waffle_dl, const char *name) { - return linux_platform_dl_sym(xegl_platform(wc_self)->linux, - waffle_dl, - name); + struct xegl_platform *self = xegl_platform(wegl_platform(wc_self)); + return linux_platform_dl_sym(self->linux, waffle_dl, name); } static union waffle_native_config* diff --git a/src/waffle/xegl/xegl_platform.h b/src/waffle/xegl/xegl_platform.h index 835f360..003343f 100644 --- a/src/waffle/xegl/xegl_platform.h +++ b/src/waffle/xegl/xegl_platform.h @@ -31,20 +31,20 @@ #include "waffle_x11_egl.h" -#include "wcore_platform.h" +#include "wegl_platform.h" #include "wcore_util.h" struct linux_platform; struct xegl_platform { - struct wcore_platform wcore; + struct wegl_platform wegl; struct linux_platform *linux; }; DEFINE_CONTAINER_CAST_FUNC(xegl_platform, struct xegl_platform, - struct wcore_platform, - wcore) + struct wegl_platform, + wegl) struct wcore_platform* xegl_platform_create(void); diff --git a/src/waffle/xegl/xegl_window.c b/src/waffle/xegl/xegl_window.c index 5fcadda..ce638b4 100644 --- a/src/waffle/xegl/xegl_window.c +++ b/src/waffle/xegl/xegl_window.c @@ -31,6 +31,7 @@ #include "wcore_error.h" #include "wegl_config.h" +#include "wegl_platform.h" #include "wegl_util.h" #include "xegl_display.h" @@ -60,6 +61,7 @@ xegl_window_create(struct wcore_platform *wc_plat, struct xegl_window *self; struct xegl_display *dpy = xegl_display(wc_config->display); struct wegl_config *config = wegl_config(wc_config); + struct wegl_platform *plat = wegl_platform(wc_plat); xcb_visualid_t visual; bool ok = true; @@ -67,12 +69,12 @@ xegl_window_create(struct wcore_platform *wc_plat, if (self == NULL) return NULL; - ok = eglGetConfigAttrib(dpy->wegl.egl, - config->egl, - EGL_NATIVE_VISUAL_ID, - (EGLint*) &visual); + ok = plat->eglGetConfigAttrib(dpy->wegl.egl, + config->egl, + EGL_NATIVE_VISUAL_ID, + (EGLint*) &visual); if (!ok) { - wegl_emit_error("eglGetConfigAttrib(EGL_NATIVE_VISUAL_ID)"); + wegl_emit_error(plat, "eglGetConfigAttrib(EGL_NATIVE_VISUAL_ID)"); goto error; } diff --git a/src/waffle_test/CMakeLists.txt b/src/waffle_test/CMakeLists.txt index 40d2aba..8ed4ea2 100644 --- a/src/waffle_test/CMakeLists.txt +++ b/src/waffle_test/CMakeLists.txt @@ -3,7 +3,7 @@ include_directories( "${CMAKE_SOURCE_DIR}/include/waffle_test/priv" ) -add_library(waffle_test SHARED +add_library(waffle_test STATIC wt_main.c wt_runner.c wt_test.c diff --git a/src/waffle_test/wt_main.c b/src/waffle_test/wt_main.c index 205c83c..b3e6580 100644 --- a/src/waffle_test/wt_main.c +++ b/src/waffle_test/wt_main.c @@ -27,7 +27,11 @@ #include "priv/wt_runner.h" int +#ifdef _WIN32 +wt_main(int *argc, char **argv, void (__stdcall *test_suites[])(void)) +#else wt_main(int *argc, char **argv, void (*test_suites[])(void)) +#endif // _WIN32 { int num_fail; diff --git a/src/waffle_test/wt_runner.c b/src/waffle_test/wt_runner.c index 729c033..bd1e561 100644 --- a/src/waffle_test/wt_runner.c +++ b/src/waffle_test/wt_runner.c @@ -23,10 +23,6 @@ // 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. -#ifdef __linux__ -# define _XOPEN_SOURCE 500 // for strdup() -#endif - #include "wt_runner.h" #include <stdbool.h> diff --git a/tests/functional/CMakeLists.txt b/tests/functional/CMakeLists.txt index febdf5f..f1388b2 100644 --- a/tests/functional/CMakeLists.txt +++ b/tests/functional/CMakeLists.txt @@ -1,8 +1,3 @@ -link_libraries( - ${waffle_libname} - waffle_test - ) - set(gl_basic_test_sources gl_basic_test.c ) @@ -13,11 +8,13 @@ if(waffle_on_mac) ) endif() -set_source_files_properties( - ${gl_basic_test_sources} - PROPERTIES - COMPILE_FLAGS "-Wno-initializer-overrides" -) +if(NOT MSVC) + set_source_files_properties( + ${gl_basic_test_sources} + PROPERTIES + COMPILE_FLAGS "-Wno-initializer-overrides" + ) +endif() # CMake will pass to the C compiler only C sources. CMake does not recognize the # .m extension and ignores any such files in the source lists. To coerce CMake @@ -32,9 +29,13 @@ add_executable(gl_basic_test ${gl_basic_test_sources} ) +target_link_libraries(gl_basic_test + ${waffle_libname} + waffle_test + ) + add_custom_target(gl_basic_test_run - DEPENDS gl_basic_test - COMMAND ${CMAKE_BINARY_DIR}/tests/functional/gl_basic_test + COMMAND gl_basic_test ) add_dependencies(check-func gl_basic_test_run) diff --git a/tests/functional/gl_basic_test.c b/tests/functional/gl_basic_test.c index 035b221..0e932e3 100644 --- a/tests/functional/gl_basic_test.c +++ b/tests/functional/gl_basic_test.c @@ -37,10 +37,16 @@ #include <stdio.h> #include <stdlib.h> #include <string.h> +#if !defined(_WIN32) #include <unistd.h> +#else +#include <windows.h> +#endif #include <sys/types.h> +#if !defined(_WIN32) #include <sys/wait.h> +#endif #include "waffle.h" #include "waffle_test/waffle_test.h" @@ -108,17 +114,25 @@ typedef double GLclampd; /* double precision float in [0,1] */ #define GL_CONTEXT_FLAG_FORWARD_COMPATIBLE_BIT 0x00000001 #define GL_CONTEXT_FLAG_DEBUG_BIT 0x00000002 -static GLenum (*glGetError)(void); -static void (*glGetIntegerv)(GLenum pname, GLint *params); -static void (*glClearColor)(GLclampf red, - GLclampf green, - GLclampf blue, - GLclampf alpha); -static void (*glClear)(GLbitfield mask); -static void (*glReadPixels)(GLint x, GLint y, - GLsizei width, GLsizei height, - GLenum format, GLenum type, - GLvoid *pixels ); +#ifndef _WIN32 +#define APIENTRY +#else +#ifndef APIENTRY +#define APIENTRY __stdcall +#endif +#endif + +static GLenum (APIENTRY *glGetError)(void); +static void (APIENTRY *glGetIntegerv)(GLenum pname, GLint *params); +static void (APIENTRY *glClearColor)(GLclampf red, + GLclampf green, + GLclampf blue, + GLclampf alpha); +static void (APIENTRY *glClear)(GLbitfield mask); +static void (APIENTRY *glReadPixels)(GLint x, GLint y, + GLsizei width, GLsizei height, + GLenum format, GLenum type, + GLvoid *pixels ); static void testgroup_gl_basic_setup(void) @@ -235,15 +249,6 @@ gl_basic_draw__(struct gl_basic_draw_args__ args) config_attrib_list[i++] = alpha; config_attrib_list[i++] = 0; - // Check that we've set the EGL_PLATFORM environment variable for Mesa. - // - // If Mesa's libEGL is built with support for multiple platforms, then the - // environment variable EGL_PLATFORM must be set before the first EGL - // call. Otherwise, libEGL may initialize itself with the incorrect - // platform. In my experiments, first calling eglGetProcAddress will - // produce a segfault in eglInitialize. - waffle_get_proc_address("glClear"); - // Create objects. ASSERT_TRUE(dpy = waffle_display_connect(NULL)); @@ -337,366 +342,129 @@ gl_basic_draw__(struct gl_basic_draw_args__ args) ASSERT_TRUE(waffle_display_disconnect(dpy)); } -#ifdef WAFFLE_HAS_CGL -TEST(gl_basic, cgl_init) -{ - gl_basic_init(WAFFLE_PLATFORM_CGL); -} - -TEST(gl_basic, cgl_gles1_unsupported) -{ - gl_basic_draw(.api=WAFFLE_CONTEXT_OPENGL_ES1, - .expect_error=WAFFLE_ERROR_UNSUPPORTED_ON_PLATFORM); -} - -TEST(gl_basic, cgl_gles2_unsupported) -{ - gl_basic_draw(.api=WAFFLE_CONTEXT_OPENGL_ES2, - .expect_error=WAFFLE_ERROR_UNSUPPORTED_ON_PLATFORM); -} +// +// List of tests common to all platforms. +// -TEST(gl_basic, cgl_gl_rgb) +TEST(gl_basic, all_gl_rgb) { gl_basic_draw(.api=WAFFLE_CONTEXT_OPENGL); } -TEST(gl_basic, cgl_gl_rgba) +TEST(gl_basic, all_gl_rgba) { gl_basic_draw(.api=WAFFLE_CONTEXT_OPENGL, .alpha=true); } -TEST(gl_basic, cgl_gl_debug_is_unsupported) -{ - gl_basic_draw(.api=WAFFLE_CONTEXT_OPENGL, - .debug=true, - .expect_error=WAFFLE_ERROR_UNSUPPORTED_ON_PLATFORM); -} - -TEST(gl_basic, cgl_gl_fwdcompat_bad_attribute) -{ - gl_basic_draw(.api=WAFFLE_CONTEXT_OPENGL, - .forward_compatible=true, - .expect_error=WAFFLE_ERROR_BAD_ATTRIBUTE); -} - -TEST(gl_basic, cgl_gl10) +TEST(gl_basic, all_gl10) { gl_basic_draw(.api=WAFFLE_CONTEXT_OPENGL, .version=10); } -TEST(gl_basic, cgl_gl11) +TEST(gl_basic, all_gl11) { gl_basic_draw(.api=WAFFLE_CONTEXT_OPENGL, .version=11); } -TEST(gl_basic, cgl_gl12) +TEST(gl_basic, all_gl12) { gl_basic_draw(.api=WAFFLE_CONTEXT_OPENGL, .version=12); } -TEST(gl_basic, cgl_gl13) +TEST(gl_basic, all_gl13) { gl_basic_draw(.api=WAFFLE_CONTEXT_OPENGL, .version=13); } -TEST(gl_basic, cgl_gl14) +TEST(gl_basic, all_gl14) { gl_basic_draw(.api=WAFFLE_CONTEXT_OPENGL, .version=14); } -TEST(gl_basic, cgl_gl15) +TEST(gl_basic, all_gl15) { gl_basic_draw(.api=WAFFLE_CONTEXT_OPENGL, .version=15); } -TEST(gl_basic, cgl_gl20) +TEST(gl_basic, all_gl20) { gl_basic_draw(.api=WAFFLE_CONTEXT_OPENGL, .version=20); } -TEST(gl_basic, cgl_gl21) +TEST(gl_basic, all_gl21) { gl_basic_draw(.api=WAFFLE_CONTEXT_OPENGL, .version=21); } -TEST(gl_basic, cgl_gl30) -{ - gl_basic_draw(.api=WAFFLE_CONTEXT_OPENGL, - .version=30, - .expect_error=WAFFLE_ERROR_UNSUPPORTED_ON_PLATFORM); -} - -TEST(gl_basic, cgl_gl31) -{ - gl_basic_draw(.api=WAFFLE_CONTEXT_OPENGL, - .version=31); -} - -TEST(gl_basic, cgl_gl32_core) -{ - gl_basic_draw(.api=WAFFLE_CONTEXT_OPENGL, - .version=32, - .profile=WAFFLE_CONTEXT_CORE_PROFILE); -} - -TEST(gl_basic, cgl_gl33_core) -{ - gl_basic_draw(.api=WAFFLE_CONTEXT_OPENGL, - .version=33, - .profile=WAFFLE_CONTEXT_CORE_PROFILE, - .expect_error=WAFFLE_ERROR_UNSUPPORTED_ON_PLATFORM); -} - -TEST(gl_basic, cgl_gl40_core) -{ - gl_basic_draw(.api=WAFFLE_CONTEXT_OPENGL, - .version=40, - .profile=WAFFLE_CONTEXT_CORE_PROFILE, - .expect_error=WAFFLE_ERROR_UNSUPPORTED_ON_PLATFORM); -} - -TEST(gl_basic, cgl_gl41_core) -{ - gl_basic_draw(.api=WAFFLE_CONTEXT_OPENGL, - .version=41, - .profile=WAFFLE_CONTEXT_CORE_PROFILE, - .expect_error=WAFFLE_ERROR_UNSUPPORTED_ON_PLATFORM); -} - -TEST(gl_basic, cgl_gl42_core) -{ - gl_basic_draw(.api=WAFFLE_CONTEXT_OPENGL, - .version=42, - .profile=WAFFLE_CONTEXT_CORE_PROFILE, - .expect_error=WAFFLE_ERROR_UNSUPPORTED_ON_PLATFORM); -} - -TEST(gl_basic, cgl_gl43_core) -{ - gl_basic_draw(.api=WAFFLE_CONTEXT_OPENGL, - .version=43, - .profile=WAFFLE_CONTEXT_CORE_PROFILE, - .expect_error=WAFFLE_ERROR_UNSUPPORTED_ON_PLATFORM); -} - -TEST(gl_basic, cgl_gl32_compat) -{ - gl_basic_draw(.api=WAFFLE_CONTEXT_OPENGL, - .version=32, - .profile=WAFFLE_CONTEXT_COMPATIBILITY_PROFILE, - .expect_error=WAFFLE_ERROR_UNSUPPORTED_ON_PLATFORM); -} - -TEST(gl_basic, cgl_gl33_compat) -{ - gl_basic_draw(.api=WAFFLE_CONTEXT_OPENGL, - .version=33, - .profile=WAFFLE_CONTEXT_COMPATIBILITY_PROFILE, - .expect_error=WAFFLE_ERROR_UNSUPPORTED_ON_PLATFORM); -} - -TEST(gl_basic, cgl_gl40_compat) -{ - gl_basic_draw(.api=WAFFLE_CONTEXT_OPENGL, - .version=40, - .profile=WAFFLE_CONTEXT_COMPATIBILITY_PROFILE, - .expect_error=WAFFLE_ERROR_UNSUPPORTED_ON_PLATFORM); -} - -TEST(gl_basic, cgl_gl41_compat) -{ - gl_basic_draw(.api=WAFFLE_CONTEXT_OPENGL, - .version=41, - .profile=WAFFLE_CONTEXT_COMPATIBILITY_PROFILE, - .expect_error=WAFFLE_ERROR_UNSUPPORTED_ON_PLATFORM); -} - -TEST(gl_basic, cgl_gl42_compat) -{ - gl_basic_draw(.api=WAFFLE_CONTEXT_OPENGL, - .version=42, - .profile=WAFFLE_CONTEXT_COMPATIBILITY_PROFILE, - .expect_error=WAFFLE_ERROR_UNSUPPORTED_ON_PLATFORM); -} - -TEST(gl_basic, cgl_gl43_compat) -{ - gl_basic_draw(.api=WAFFLE_CONTEXT_OPENGL, - .version=43, - .profile=WAFFLE_CONTEXT_COMPATIBILITY_PROFILE, - .expect_error=WAFFLE_ERROR_UNSUPPORTED_ON_PLATFORM); -} - -static void -testsuite_cgl(void) -{ - TEST_RUN(gl_basic, cgl_init); - - TEST_RUN(gl_basic, cgl_gles1_unsupported); - TEST_RUN(gl_basic, cgl_gles2_unsupported); - - TEST_RUN(gl_basic, cgl_gl_rgb); - TEST_RUN(gl_basic, cgl_gl_rgba); - - TEST_RUN(gl_basic, cgl_gl_debug_is_unsupported); - TEST_RUN(gl_basic, cgl_gl_fwdcompat_bad_attribute); - - TEST_RUN(gl_basic, cgl_gl10); - TEST_RUN(gl_basic, cgl_gl11); - TEST_RUN(gl_basic, cgl_gl12); - TEST_RUN(gl_basic, cgl_gl13); - TEST_RUN(gl_basic, cgl_gl14); - TEST_RUN(gl_basic, cgl_gl15); - TEST_RUN(gl_basic, cgl_gl20); - TEST_RUN(gl_basic, cgl_gl21); - TEST_RUN(gl_basic, cgl_gl30); - TEST_RUN(gl_basic, cgl_gl31); - - TEST_RUN(gl_basic, cgl_gl32_core); - TEST_RUN(gl_basic, cgl_gl33_core); - TEST_RUN(gl_basic, cgl_gl40_core); - TEST_RUN(gl_basic, cgl_gl41_core); - TEST_RUN(gl_basic, cgl_gl42_core); - TEST_RUN(gl_basic, cgl_gl43_core); - - TEST_RUN(gl_basic, cgl_gl32_compat); - TEST_RUN(gl_basic, cgl_gl33_compat); - TEST_RUN(gl_basic, cgl_gl40_compat); - TEST_RUN(gl_basic, cgl_gl41_compat); - TEST_RUN(gl_basic, cgl_gl42_compat); - TEST_RUN(gl_basic, cgl_gl43_compat); -} -#endif // WAFFLE_HAS_CGL - -#ifdef WAFFLE_HAS_GLX -TEST(gl_basic, glx_init) -{ - gl_basic_init(WAFFLE_PLATFORM_GLX); -} - -TEST(gl_basic, glx_gl_rgb) -{ - gl_basic_draw(.api=WAFFLE_CONTEXT_OPENGL); -} - -TEST(gl_basic, glx_gl_rgba) -{ - gl_basic_draw(.api=WAFFLE_CONTEXT_OPENGL, - .alpha=true); -} - -TEST(gl_basic, glx_gl_debug) -{ - gl_basic_draw(.api=WAFFLE_CONTEXT_OPENGL, - .debug=true); -} - -TEST(gl_basic, glx_gl_fwdcompat_bad_attribute) +TEST(gl_basic, all_gl21_fwdcompat_bad_attribute) { gl_basic_draw(.api=WAFFLE_CONTEXT_OPENGL, + .version=21, .forward_compatible=true, .expect_error=WAFFLE_ERROR_BAD_ATTRIBUTE); } -TEST(gl_basic, glx_gl10) -{ - gl_basic_draw(.api=WAFFLE_CONTEXT_OPENGL, - .version=10); -} - -TEST(gl_basic, glx_gl11) -{ - gl_basic_draw(.api=WAFFLE_CONTEXT_OPENGL, - .version=11); -} - -TEST(gl_basic, glx_gl12) -{ - gl_basic_draw(.api=WAFFLE_CONTEXT_OPENGL, - .version=12); -} -TEST(gl_basic, glx_gl13) -{ - gl_basic_draw(.api=WAFFLE_CONTEXT_OPENGL, - .version=13); -} - -TEST(gl_basic, glx_gl14) -{ - gl_basic_draw(.api=WAFFLE_CONTEXT_OPENGL, - .version=14); -} - -TEST(gl_basic, glx_gl15) -{ - gl_basic_draw(.api=WAFFLE_CONTEXT_OPENGL, - .version=15); -} - -TEST(gl_basic, glx_gl20) -{ - gl_basic_draw(.api=WAFFLE_CONTEXT_OPENGL, - .version=20); -} - -TEST(gl_basic, glx_gl21) +// +// List of linux (glx, wayland and x11_egl) and windows (wgl) specific tests. +// +#if defined(WAFFLE_HAS_GLX) || defined(WAFFLE_HAS_WAYLAND) || defined(WAFFLE_HAS_X11_EGL) || defined(WAFFLE_HAS_WGL) +TEST(gl_basic, all_but_cgl_gl_debug) { gl_basic_draw(.api=WAFFLE_CONTEXT_OPENGL, - .version=21); + .debug=true); } -TEST(gl_basic, glx_gl21_fwdcompat_bad_attribute) +TEST(gl_basic, all_but_cgl_gl_fwdcompat_bad_attribute) { gl_basic_draw(.api=WAFFLE_CONTEXT_OPENGL, - .version=21, .forward_compatible=true, .expect_error=WAFFLE_ERROR_BAD_ATTRIBUTE); } -TEST(gl_basic, glx_gl30) +TEST(gl_basic, all_but_cgl_gl30) { gl_basic_draw(.api=WAFFLE_CONTEXT_OPENGL, .version=30); } -TEST(gl_basic, glx_gl30_fwdcompat) +TEST(gl_basic, all_but_cgl_gl30_fwdcompat) { gl_basic_draw(.api=WAFFLE_CONTEXT_OPENGL, .version=30, .forward_compatible=true); } -TEST(gl_basic, glx_gl31) +TEST(gl_basic, all_but_cgl_gl31) { gl_basic_draw(.api=WAFFLE_CONTEXT_OPENGL, .version=31); } -TEST(gl_basic, glx_gl31_fwdcompat) +TEST(gl_basic, all_but_cgl_gl31_fwdcompat) { gl_basic_draw(.api=WAFFLE_CONTEXT_OPENGL, .version=31, .forward_compatible=true); } -TEST(gl_basic, glx_gl32_core) +TEST(gl_basic, all_but_cgl_gl32_core) { gl_basic_draw(.api=WAFFLE_CONTEXT_OPENGL, .version=32, .profile=WAFFLE_CONTEXT_CORE_PROFILE); } -TEST(gl_basic, glx_gl32_core_fwdcompat) +TEST(gl_basic, all_but_cgl_gl32_core_fwdcompat) { gl_basic_draw(.api=WAFFLE_CONTEXT_OPENGL, .version=32, @@ -704,505 +472,417 @@ TEST(gl_basic, glx_gl32_core_fwdcompat) .forward_compatible=true); } -TEST(gl_basic, glx_gl33_core) +TEST(gl_basic, all_but_cgl_gl33_core) { gl_basic_draw(.api=WAFFLE_CONTEXT_OPENGL, .version=33, .profile=WAFFLE_CONTEXT_CORE_PROFILE); } -TEST(gl_basic, glx_gl40_core) +TEST(gl_basic, all_but_cgl_gl40_core) { gl_basic_draw(.api=WAFFLE_CONTEXT_OPENGL, .version=40, .profile=WAFFLE_CONTEXT_CORE_PROFILE); } -TEST(gl_basic, glx_gl41_core) +TEST(gl_basic, all_but_cgl_gl41_core) { gl_basic_draw(.api=WAFFLE_CONTEXT_OPENGL, .version=41, .profile=WAFFLE_CONTEXT_CORE_PROFILE); } -TEST(gl_basic, glx_gl42_core) +TEST(gl_basic, all_but_cgl_gl42_core) { gl_basic_draw(.api=WAFFLE_CONTEXT_OPENGL, .version=42, .profile=WAFFLE_CONTEXT_CORE_PROFILE); } -TEST(gl_basic, glx_gl43_core) +TEST(gl_basic, all_but_cgl_gl43_core) { gl_basic_draw(.api=WAFFLE_CONTEXT_OPENGL, .version=43, .profile=WAFFLE_CONTEXT_CORE_PROFILE); } -TEST(gl_basic, glx_gl32_compat) +TEST(gl_basic, all_but_cgl_gl32_compat) { gl_basic_draw(.api=WAFFLE_CONTEXT_OPENGL, .version=32, .profile=WAFFLE_CONTEXT_COMPATIBILITY_PROFILE); } -TEST(gl_basic, glx_gl33_compat) +TEST(gl_basic, all_but_cgl_gl33_compat) { gl_basic_draw(.api=WAFFLE_CONTEXT_OPENGL, .version=33, .profile=WAFFLE_CONTEXT_COMPATIBILITY_PROFILE); } -TEST(gl_basic, glx_gl40_compat) +TEST(gl_basic, all_but_cgl_gl40_compat) { gl_basic_draw(.api=WAFFLE_CONTEXT_OPENGL, .version=40, .profile=WAFFLE_CONTEXT_COMPATIBILITY_PROFILE); } -TEST(gl_basic, glx_gl41_compat) +TEST(gl_basic, all_but_cgl_gl41_compat) { gl_basic_draw(.api=WAFFLE_CONTEXT_OPENGL, .version=41, .profile=WAFFLE_CONTEXT_COMPATIBILITY_PROFILE); } -TEST(gl_basic, glx_gl42_compat) +TEST(gl_basic, all_but_cgl_gl42_compat) { gl_basic_draw(.api=WAFFLE_CONTEXT_OPENGL, .version=42, .profile=WAFFLE_CONTEXT_COMPATIBILITY_PROFILE); } -TEST(gl_basic, glx_gl43_compat) +TEST(gl_basic, all_but_cgl_gl43_compat) { gl_basic_draw(.api=WAFFLE_CONTEXT_OPENGL, .version=43, .profile=WAFFLE_CONTEXT_COMPATIBILITY_PROFILE); } -TEST(gl_basic, glx_gles1_rgb) +TEST(gl_basic, all_but_cgl_gles1_rgb) { gl_basic_draw(.api=WAFFLE_CONTEXT_OPENGL_ES1); } -TEST(gl_basic, glx_gles1_rgba) +TEST(gl_basic, all_but_cgl_gles1_rgba) { gl_basic_draw(.api=WAFFLE_CONTEXT_OPENGL_ES1, .alpha=true); } -TEST(gl_basic, glx_gles10) +TEST(gl_basic, all_but_cgl_gles10) { - gl_basic_draw(.api=WAFFLE_CONTEXT_OPENGL_ES1, - .version=10, - .alpha=true); + gl_basic_draw(.api=WAFFLE_CONTEXT_OPENGL_ES1, + .version=10, + .alpha=true); } -TEST(gl_basic, glx_gles1_fwdcompat_bad_attribute) +TEST(gl_basic, all_but_cgl_gles11) { gl_basic_draw(.api=WAFFLE_CONTEXT_OPENGL_ES1, - .forward_compatible=true, - .expect_error=WAFFLE_ERROR_BAD_ATTRIBUTE); + .version=11, + .alpha=true); } -TEST(gl_basic, glx_gles11) +TEST(gl_basic, all_but_cgl_gles1_fwdcompat_bad_attribute) { - gl_basic_draw(.api=WAFFLE_CONTEXT_OPENGL_ES1, - .version=11, - .alpha=true); + gl_basic_draw(.api=WAFFLE_CONTEXT_OPENGL_ES1, + .forward_compatible=true, + .expect_error=WAFFLE_ERROR_BAD_ATTRIBUTE); } -TEST(gl_basic, glx_gles2_rgb) +TEST(gl_basic, all_but_cgl_gles2_rgb) { gl_basic_draw(.api=WAFFLE_CONTEXT_OPENGL_ES2); } -TEST(gl_basic, glx_gles2_rgba) +TEST(gl_basic, all_but_cgl_gles2_rgba) { gl_basic_draw(.api=WAFFLE_CONTEXT_OPENGL_ES2, .alpha=true); } -TEST(gl_basic, glx_gles20) +TEST(gl_basic, all_but_cgl_gles20) { gl_basic_draw(.api=WAFFLE_CONTEXT_OPENGL_ES2, .version=20); } -TEST(gl_basic, glx_gles2_fwdcompat_bad_attribute) +TEST(gl_basic, all_but_cgl_gles2_fwdcompat_bad_attribute) { gl_basic_draw(.api=WAFFLE_CONTEXT_OPENGL_ES2, .forward_compatible=true, .expect_error=WAFFLE_ERROR_BAD_ATTRIBUTE); } -TEST(gl_basic, glx_gles3_rgb) +TEST(gl_basic, all_but_cgl_gles3_rgb) { gl_basic_draw(.api=WAFFLE_CONTEXT_OPENGL_ES3); } -TEST(gl_basic, glx_gles3_rgba) +TEST(gl_basic, all_but_cgl_gles3_rgba) { gl_basic_draw(.api=WAFFLE_CONTEXT_OPENGL_ES3, .alpha=true); } -TEST(gl_basic, glx_gles30) +TEST(gl_basic, all_but_cgl_gles30) { gl_basic_draw(.api=WAFFLE_CONTEXT_OPENGL_ES3, .version=30); } -TEST(gl_basic, glx_gles3_fwdcompat_bad_attribute) +TEST(gl_basic, all_but_cgl_gles3_fwdcompat_bad_attribute) { gl_basic_draw(.api=WAFFLE_CONTEXT_OPENGL_ES3, .forward_compatible=true, .expect_error=WAFFLE_ERROR_BAD_ATTRIBUTE); } +#endif -static void -testsuite_glx(void) -{ - TEST_RUN(gl_basic, glx_init); - - TEST_RUN(gl_basic, glx_gl_rgb); - TEST_RUN(gl_basic, glx_gl_rgba); - TEST_RUN(gl_basic, glx_gl_debug); - TEST_RUN(gl_basic, glx_gl_fwdcompat_bad_attribute); - - TEST_RUN(gl_basic, glx_gl10); - TEST_RUN(gl_basic, glx_gl11); - TEST_RUN(gl_basic, glx_gl12); - TEST_RUN(gl_basic, glx_gl13); - TEST_RUN(gl_basic, glx_gl14); - TEST_RUN(gl_basic, glx_gl15); - TEST_RUN(gl_basic, glx_gl20); - TEST_RUN(gl_basic, glx_gl21); - TEST_RUN(gl_basic, glx_gl21_fwdcompat_bad_attribute); - TEST_RUN(gl_basic, glx_gl30); - TEST_RUN(gl_basic, glx_gl30_fwdcompat); - TEST_RUN(gl_basic, glx_gl31); - TEST_RUN(gl_basic, glx_gl31_fwdcompat); - - TEST_RUN(gl_basic, glx_gl32_core); - TEST_RUN(gl_basic, glx_gl32_core_fwdcompat); - TEST_RUN(gl_basic, glx_gl33_core); - TEST_RUN(gl_basic, glx_gl40_core); - TEST_RUN(gl_basic, glx_gl41_core); - TEST_RUN(gl_basic, glx_gl42_core); - TEST_RUN(gl_basic, glx_gl43_core); - - TEST_RUN(gl_basic, glx_gl32_compat); - TEST_RUN(gl_basic, glx_gl33_compat); - TEST_RUN(gl_basic, glx_gl40_compat); - TEST_RUN(gl_basic, glx_gl41_compat); - TEST_RUN(gl_basic, glx_gl42_compat); - TEST_RUN(gl_basic, glx_gl43_compat); - - TEST_RUN(gl_basic, glx_gles1_rgb); - TEST_RUN(gl_basic, glx_gles1_rgba); - TEST_RUN(gl_basic, glx_gles1_fwdcompat_bad_attribute); - - TEST_RUN(gl_basic, glx_gles10); - TEST_RUN(gl_basic, glx_gles11); - - TEST_RUN(gl_basic, glx_gles2_rgb); - TEST_RUN(gl_basic, glx_gles2_rgba); - TEST_RUN(gl_basic, glx_gles2_fwdcompat_bad_attribute); - - TEST_RUN(gl_basic, glx_gles20); - - TEST_RUN(gl_basic, glx_gles3_rgb); - TEST_RUN(gl_basic, glx_gles3_rgba); - TEST_RUN(gl_basic, glx_gles3_fwdcompat_bad_attribute); - - TEST_RUN(gl_basic, glx_gles30); -} -#endif // WAFFLE_HAS_GLX - -#ifdef WAFFLE_HAS_WAYLAND -TEST(gl_basic, wayland_init) -{ - gl_basic_init(WAFFLE_PLATFORM_WAYLAND); -} - -TEST(gl_basic, wayland_gl_rgb) -{ - gl_basic_draw(.api=WAFFLE_CONTEXT_OPENGL); -} - -TEST(gl_basic, wayland_gl_rgba) -{ - gl_basic_draw(.api=WAFFLE_CONTEXT_OPENGL, - .alpha=true); -} - -TEST(gl_basic, wayland_gl_debug) -{ - gl_basic_draw(.api=WAFFLE_CONTEXT_OPENGL, - .debug=true); -} - -TEST(gl_basic, wayland_gl_fwdcompat_bad_attribute) -{ - gl_basic_draw(.api=WAFFLE_CONTEXT_OPENGL, - .forward_compatible=true, - .expect_error=WAFFLE_ERROR_BAD_ATTRIBUTE); -} - -TEST(gl_basic, wayland_gl10) -{ - gl_basic_draw(.api=WAFFLE_CONTEXT_OPENGL, - .version=10); -} - -TEST(gl_basic, wayland_gl11) -{ - gl_basic_draw(.api=WAFFLE_CONTEXT_OPENGL, - .version=11); -} - -TEST(gl_basic, wayland_gl12) -{ - gl_basic_draw(.api=WAFFLE_CONTEXT_OPENGL, - .version=12); -} - -TEST(gl_basic, wayland_gl13) -{ - gl_basic_draw(.api=WAFFLE_CONTEXT_OPENGL, - .version=13); -} - -TEST(gl_basic, wayland_gl14) +#ifdef WAFFLE_HAS_CGL +TEST(gl_basic, cgl_init) { - gl_basic_draw(.api=WAFFLE_CONTEXT_OPENGL, - .version=14); + gl_basic_init(WAFFLE_PLATFORM_CGL); } -TEST(gl_basic, wayland_gl15) +TEST(gl_basic, cgl_gles1_unsupported) { - gl_basic_draw(.api=WAFFLE_CONTEXT_OPENGL, - .version=15); + gl_basic_draw(.api=WAFFLE_CONTEXT_OPENGL_ES1, + .expect_error=WAFFLE_ERROR_UNSUPPORTED_ON_PLATFORM); } -TEST(gl_basic, wayland_gl20) +TEST(gl_basic, cgl_gles2_unsupported) { - gl_basic_draw(.api=WAFFLE_CONTEXT_OPENGL, - .version=20); + gl_basic_draw(.api=WAFFLE_CONTEXT_OPENGL_ES2, + .expect_error=WAFFLE_ERROR_UNSUPPORTED_ON_PLATFORM); } -TEST(gl_basic, wayland_gl21) +TEST(gl_basic, cgl_gl_debug_is_unsupported) { gl_basic_draw(.api=WAFFLE_CONTEXT_OPENGL, - .version=21); + .debug=true, + .expect_error=WAFFLE_ERROR_UNSUPPORTED_ON_PLATFORM); } -TEST(gl_basic, wayland_gl21_fwdcompat_bad_attribute) +TEST(gl_basic, cgl_gl_fwdcompat_bad_attribute) { gl_basic_draw(.api=WAFFLE_CONTEXT_OPENGL, - .version=21, .forward_compatible=true, .expect_error=WAFFLE_ERROR_BAD_ATTRIBUTE); } -TEST(gl_basic, wayland_gl30) -{ - gl_basic_draw(.api=WAFFLE_CONTEXT_OPENGL, - .version=30); -} - -TEST(gl_basic, wayland_gl30_fwdcompat) +TEST(gl_basic, cgl_gl30) { gl_basic_draw(.api=WAFFLE_CONTEXT_OPENGL, .version=30, - .forward_compatible=true); + .expect_error=WAFFLE_ERROR_UNSUPPORTED_ON_PLATFORM); } -TEST(gl_basic, wayland_gl31) +TEST(gl_basic, cgl_gl31) { gl_basic_draw(.api=WAFFLE_CONTEXT_OPENGL, .version=31); } -TEST(gl_basic, wayland_gl31_fwdcompat) -{ - gl_basic_draw(.api=WAFFLE_CONTEXT_OPENGL, - .version=31, - .forward_compatible=true); -} - -TEST(gl_basic, wayland_gl32_core) +TEST(gl_basic, cgl_gl32_core) { gl_basic_draw(.api=WAFFLE_CONTEXT_OPENGL, .version=32, .profile=WAFFLE_CONTEXT_CORE_PROFILE); } -TEST(gl_basic, wayland_gl32_core_fwdcompat) -{ - gl_basic_draw(.api=WAFFLE_CONTEXT_OPENGL, - .version=32, - .profile=WAFFLE_CONTEXT_CORE_PROFILE, - .forward_compatible=true); -} - -TEST(gl_basic, wayland_gl33_core) +TEST(gl_basic, cgl_gl33_core) { gl_basic_draw(.api=WAFFLE_CONTEXT_OPENGL, .version=33, - .profile=WAFFLE_CONTEXT_CORE_PROFILE); + .profile=WAFFLE_CONTEXT_CORE_PROFILE, + .expect_error=WAFFLE_ERROR_UNSUPPORTED_ON_PLATFORM); } -TEST(gl_basic, wayland_gl40_core) +TEST(gl_basic, cgl_gl40_core) { gl_basic_draw(.api=WAFFLE_CONTEXT_OPENGL, .version=40, - .profile=WAFFLE_CONTEXT_CORE_PROFILE); + .profile=WAFFLE_CONTEXT_CORE_PROFILE, + .expect_error=WAFFLE_ERROR_UNSUPPORTED_ON_PLATFORM); } -TEST(gl_basic, wayland_gl41_core) +TEST(gl_basic, cgl_gl41_core) { gl_basic_draw(.api=WAFFLE_CONTEXT_OPENGL, .version=41, - .profile=WAFFLE_CONTEXT_CORE_PROFILE); + .profile=WAFFLE_CONTEXT_CORE_PROFILE, + .expect_error=WAFFLE_ERROR_UNSUPPORTED_ON_PLATFORM); } -TEST(gl_basic, wayland_gl42_core) +TEST(gl_basic, cgl_gl42_core) { gl_basic_draw(.api=WAFFLE_CONTEXT_OPENGL, .version=42, - .profile=WAFFLE_CONTEXT_CORE_PROFILE); + .profile=WAFFLE_CONTEXT_CORE_PROFILE, + .expect_error=WAFFLE_ERROR_UNSUPPORTED_ON_PLATFORM); } -TEST(gl_basic, wayland_gl43_core) +TEST(gl_basic, cgl_gl43_core) { gl_basic_draw(.api=WAFFLE_CONTEXT_OPENGL, .version=43, - .profile=WAFFLE_CONTEXT_CORE_PROFILE); + .profile=WAFFLE_CONTEXT_CORE_PROFILE, + .expect_error=WAFFLE_ERROR_UNSUPPORTED_ON_PLATFORM); } -TEST(gl_basic, wayland_gl32_compat) +TEST(gl_basic, cgl_gl32_compat) { gl_basic_draw(.api=WAFFLE_CONTEXT_OPENGL, .version=32, - .profile=WAFFLE_CONTEXT_COMPATIBILITY_PROFILE); + .profile=WAFFLE_CONTEXT_COMPATIBILITY_PROFILE, + .expect_error=WAFFLE_ERROR_UNSUPPORTED_ON_PLATFORM); } -TEST(gl_basic, wayland_gl33_compat) +TEST(gl_basic, cgl_gl33_compat) { gl_basic_draw(.api=WAFFLE_CONTEXT_OPENGL, .version=33, - .profile=WAFFLE_CONTEXT_COMPATIBILITY_PROFILE); + .profile=WAFFLE_CONTEXT_COMPATIBILITY_PROFILE, + .expect_error=WAFFLE_ERROR_UNSUPPORTED_ON_PLATFORM); } -TEST(gl_basic, wayland_gl40_compat) +TEST(gl_basic, cgl_gl40_compat) { gl_basic_draw(.api=WAFFLE_CONTEXT_OPENGL, .version=40, - .profile=WAFFLE_CONTEXT_COMPATIBILITY_PROFILE); + .profile=WAFFLE_CONTEXT_COMPATIBILITY_PROFILE, + .expect_error=WAFFLE_ERROR_UNSUPPORTED_ON_PLATFORM); } -TEST(gl_basic, wayland_gl41_compat) +TEST(gl_basic, cgl_gl41_compat) { gl_basic_draw(.api=WAFFLE_CONTEXT_OPENGL, .version=41, - .profile=WAFFLE_CONTEXT_COMPATIBILITY_PROFILE); + .profile=WAFFLE_CONTEXT_COMPATIBILITY_PROFILE, + .expect_error=WAFFLE_ERROR_UNSUPPORTED_ON_PLATFORM); } -TEST(gl_basic, wayland_gl42_compat) +TEST(gl_basic, cgl_gl42_compat) { gl_basic_draw(.api=WAFFLE_CONTEXT_OPENGL, .version=42, - .profile=WAFFLE_CONTEXT_COMPATIBILITY_PROFILE); + .profile=WAFFLE_CONTEXT_COMPATIBILITY_PROFILE, + .expect_error=WAFFLE_ERROR_UNSUPPORTED_ON_PLATFORM); } -TEST(gl_basic, wayland_gl43_compat) +TEST(gl_basic, cgl_gl43_compat) { gl_basic_draw(.api=WAFFLE_CONTEXT_OPENGL, .version=43, - .profile=WAFFLE_CONTEXT_COMPATIBILITY_PROFILE); -} - -TEST(gl_basic, wayland_gles1_rgb) -{ - gl_basic_draw(.api=WAFFLE_CONTEXT_OPENGL_ES1); -} - -TEST(gl_basic, wayland_gles1_rgba) -{ - gl_basic_draw(.api=WAFFLE_CONTEXT_OPENGL_ES1, - .alpha=true); + .profile=WAFFLE_CONTEXT_COMPATIBILITY_PROFILE, + .expect_error=WAFFLE_ERROR_UNSUPPORTED_ON_PLATFORM); } -TEST(gl_basic, wayland_gles10) +static void +testsuite_cgl(void) { - gl_basic_draw(.api=WAFFLE_CONTEXT_OPENGL_ES1, - .version=10, - .alpha=true); -} + TEST_RUN(gl_basic, cgl_init); -TEST(gl_basic, wayland_gles1_fwdcompat_bad_attribute) -{ - gl_basic_draw(.api=WAFFLE_CONTEXT_OPENGL_ES1, - .forward_compatible=true, - .expect_error=WAFFLE_ERROR_BAD_ATTRIBUTE); -} + TEST_RUN(gl_basic, cgl_gles1_unsupported); + TEST_RUN(gl_basic, cgl_gles2_unsupported); -TEST(gl_basic, wayland_gles11) -{ - gl_basic_draw(.api=WAFFLE_CONTEXT_OPENGL_ES1, - .version=11, - .alpha=true); -} + TEST_RUN2(gl_basic, cgl_gl_rgb, all_gl_rgb); + TEST_RUN2(gl_basic, cgl_gl_rgba, all_gl_rgba); -TEST(gl_basic, wayland_gles2_rgb) -{ - gl_basic_draw(.api=WAFFLE_CONTEXT_OPENGL_ES2); -} + TEST_RUN(gl_basic, cgl_gl_debug_is_unsupported); + TEST_RUN(gl_basic, cgl_gl_fwdcompat_bad_attribute); -TEST(gl_basic, wayland_gles2_rgba) -{ - gl_basic_draw(.api=WAFFLE_CONTEXT_OPENGL_ES2, - .alpha=true); -} + TEST_RUN2(gl_basic, cgl_gl10, all_gl10); + TEST_RUN2(gl_basic, cgl_gl11, all_gl11); + TEST_RUN2(gl_basic, cgl_gl12, all_gl12); + TEST_RUN2(gl_basic, cgl_gl13, all_gl13); + TEST_RUN2(gl_basic, cgl_gl14, all_gl14); + TEST_RUN2(gl_basic, cgl_gl15, all_gl15); + TEST_RUN2(gl_basic, cgl_gl20, all_gl20); + TEST_RUN2(gl_basic, cgl_gl21, all_gl21); + TEST_RUN2(gl_basic, cgl_gl21_fwdcompat_bad_attribute, all_gl21_fwdcompat_bad_attribute); + TEST_RUN(gl_basic, cgl_gl30); + TEST_RUN(gl_basic, cgl_gl31); -TEST(gl_basic, wayland_gles20) -{ - gl_basic_draw(.api=WAFFLE_CONTEXT_OPENGL_ES2, - .version=20); -} + TEST_RUN(gl_basic, cgl_gl32_core); + TEST_RUN(gl_basic, cgl_gl33_core); + TEST_RUN(gl_basic, cgl_gl40_core); + TEST_RUN(gl_basic, cgl_gl41_core); + TEST_RUN(gl_basic, cgl_gl42_core); + TEST_RUN(gl_basic, cgl_gl43_core); -TEST(gl_basic, wayland_gles2_fwdcompat_bad_attribute) -{ - gl_basic_draw(.api=WAFFLE_CONTEXT_OPENGL_ES2, - .forward_compatible=true, - .expect_error=WAFFLE_ERROR_BAD_ATTRIBUTE); + TEST_RUN(gl_basic, cgl_gl32_compat); + TEST_RUN(gl_basic, cgl_gl33_compat); + TEST_RUN(gl_basic, cgl_gl40_compat); + TEST_RUN(gl_basic, cgl_gl41_compat); + TEST_RUN(gl_basic, cgl_gl42_compat); + TEST_RUN(gl_basic, cgl_gl43_compat); } +#endif // WAFFLE_HAS_CGL -TEST(gl_basic, wayland_gles3_rgb) +#ifdef WAFFLE_HAS_GLX +TEST(gl_basic, glx_init) { - gl_basic_draw(.api=WAFFLE_CONTEXT_OPENGL_ES3); + gl_basic_init(WAFFLE_PLATFORM_GLX); } -TEST(gl_basic, wayland_gles3_rgba) +static void +testsuite_glx(void) { - gl_basic_draw(.api=WAFFLE_CONTEXT_OPENGL_ES3, - .alpha=true); -} + TEST_RUN(gl_basic, glx_init); -TEST(gl_basic, wayland_gles30) -{ - gl_basic_draw(.api=WAFFLE_CONTEXT_OPENGL_ES3, - .version=30); + TEST_RUN2(gl_basic, glx_gl_rgb, all_gl_rgb); + TEST_RUN2(gl_basic, glx_gl_rgba, all_gl_rgb); + TEST_RUN2(gl_basic, glx_gl_debug, all_but_cgl_gl_debug); + TEST_RUN2(gl_basic, glx_gl_fwdcompat_bad_attribute, all_but_cgl_gl_fwdcompat_bad_attribute); + + TEST_RUN2(gl_basic, glx_gl10, all_gl10); + TEST_RUN2(gl_basic, glx_gl11, all_gl11); + TEST_RUN2(gl_basic, glx_gl12, all_gl12); + TEST_RUN2(gl_basic, glx_gl13, all_gl13); + TEST_RUN2(gl_basic, glx_gl14, all_gl14); + TEST_RUN2(gl_basic, glx_gl15, all_gl15); + TEST_RUN2(gl_basic, glx_gl20, all_gl20); + TEST_RUN2(gl_basic, glx_gl21, all_gl21); + TEST_RUN2(gl_basic, glx_gl21_fwdcompat_bad_attribute, all_gl21_fwdcompat_bad_attribute); + + TEST_RUN2(gl_basic, glx_gl30, all_but_cgl_gl30); + TEST_RUN2(gl_basic, glx_gl30_fwdcompat, all_but_cgl_gl30_fwdcompat); + TEST_RUN2(gl_basic, glx_gl31, all_but_cgl_gl31); + TEST_RUN2(gl_basic, glx_gl31_fwdcompat, all_but_cgl_gl31_fwdcompat); + + TEST_RUN2(gl_basic, glx_gl32_core, all_but_cgl_gl32_core); + TEST_RUN2(gl_basic, glx_gl32_core_fwdcompat, all_but_cgl_gl32_core_fwdcompat); + TEST_RUN2(gl_basic, glx_gl33_core, all_but_cgl_gl33_core); + TEST_RUN2(gl_basic, glx_gl40_core, all_but_cgl_gl40_core); + TEST_RUN2(gl_basic, glx_gl41_core, all_but_cgl_gl41_core); + TEST_RUN2(gl_basic, glx_gl42_core, all_but_cgl_gl42_core); + TEST_RUN2(gl_basic, glx_gl43_core, all_but_cgl_gl43_core); + + TEST_RUN2(gl_basic, glx_gl32_compat, all_but_cgl_gl32_compat); + TEST_RUN2(gl_basic, glx_gl33_compat, all_but_cgl_gl33_compat); + TEST_RUN2(gl_basic, glx_gl40_compat, all_but_cgl_gl40_compat); + TEST_RUN2(gl_basic, glx_gl41_compat, all_but_cgl_gl41_compat); + TEST_RUN2(gl_basic, glx_gl42_compat, all_but_cgl_gl42_compat); + TEST_RUN2(gl_basic, glx_gl43_compat, all_but_cgl_gl43_compat); + + TEST_RUN2(gl_basic, glx_gles1_rgb, all_but_cgl_gles1_rgb); + TEST_RUN2(gl_basic, glx_gles1_rgba, all_but_cgl_gles1_rgba); + TEST_RUN2(gl_basic, glx_gles1_fwdcompat_bad_attribute, all_but_cgl_gles1_fwdcompat_bad_attribute); + + TEST_RUN2(gl_basic, glx_gles10, all_but_cgl_gles10); + TEST_RUN2(gl_basic, glx_gles11, all_but_cgl_gles10); + + TEST_RUN2(gl_basic, glx_gles2_rgb, all_but_cgl_gles2_rgb); + TEST_RUN2(gl_basic, glx_gles2_rgba, all_but_cgl_gles2_rgba); + TEST_RUN2(gl_basic, glx_gles2_fwdcompat_bad_attribute, all_but_cgl_gles2_fwdcompat_bad_attribute); + + TEST_RUN2(gl_basic, glx_gles20, all_but_cgl_gles20); + + TEST_RUN2(gl_basic, glx_gles3_rgb, all_but_cgl_gles3_rgb); + TEST_RUN2(gl_basic, glx_gles3_rgba, all_but_cgl_gles3_rgba); + TEST_RUN2(gl_basic, glx_gles3_fwdcompat_bad_attribute, all_but_cgl_gles3_fwdcompat_bad_attribute); + + TEST_RUN2(gl_basic, glx_gles30, all_but_cgl_gles30); } +#endif // WAFFLE_HAS_GLX -TEST(gl_basic, wayland_gles3_fwdcompat_bad_attribute) +#ifdef WAFFLE_HAS_WAYLAND +TEST(gl_basic, wayland_init) { - gl_basic_draw(.api=WAFFLE_CONTEXT_OPENGL_ES3, - .forward_compatible=true, - .expect_error=WAFFLE_ERROR_BAD_ATTRIBUTE); + gl_basic_init(WAFFLE_PLATFORM_WAYLAND); } static void @@ -1210,58 +890,60 @@ testsuite_wayland(void) { TEST_RUN(gl_basic, wayland_init); - TEST_RUN(gl_basic, wayland_gl_rgb); - TEST_RUN(gl_basic, wayland_gl_rgba); - TEST_RUN(gl_basic, wayland_gl_debug); - TEST_RUN(gl_basic, wayland_gl_fwdcompat_bad_attribute); - - TEST_RUN(gl_basic, wayland_gl10); - TEST_RUN(gl_basic, wayland_gl11); - TEST_RUN(gl_basic, wayland_gl12); - TEST_RUN(gl_basic, wayland_gl13); - TEST_RUN(gl_basic, wayland_gl14); - TEST_RUN(gl_basic, wayland_gl15); - TEST_RUN(gl_basic, wayland_gl20); - TEST_RUN(gl_basic, wayland_gl21); - TEST_RUN(gl_basic, wayland_gl21_fwdcompat_bad_attribute); - TEST_RUN(gl_basic, wayland_gl30); - TEST_RUN(gl_basic, wayland_gl30_fwdcompat); - TEST_RUN(gl_basic, wayland_gl31); - TEST_RUN(gl_basic, wayland_gl31_fwdcompat); - - TEST_RUN(gl_basic, wayland_gl32_core); - TEST_RUN(gl_basic, wayland_gl32_core_fwdcompat); - TEST_RUN(gl_basic, wayland_gl33_core); - TEST_RUN(gl_basic, wayland_gl40_core); - TEST_RUN(gl_basic, wayland_gl41_core); - TEST_RUN(gl_basic, wayland_gl42_core); - TEST_RUN(gl_basic, wayland_gl43_core); - - TEST_RUN(gl_basic, wayland_gl32_compat); - TEST_RUN(gl_basic, wayland_gl33_compat); - TEST_RUN(gl_basic, wayland_gl40_compat); - TEST_RUN(gl_basic, wayland_gl41_compat); - TEST_RUN(gl_basic, wayland_gl42_compat); - TEST_RUN(gl_basic, wayland_gl43_compat); - - TEST_RUN(gl_basic, wayland_gles1_rgb); - TEST_RUN(gl_basic, wayland_gles1_rgba); - TEST_RUN(gl_basic, wayland_gles1_fwdcompat_bad_attribute); - - TEST_RUN(gl_basic, wayland_gles10); - TEST_RUN(gl_basic, wayland_gles11); - - TEST_RUN(gl_basic, wayland_gles2_rgb); - TEST_RUN(gl_basic, wayland_gles2_rgba); - TEST_RUN(gl_basic, wayland_gles2_fwdcompat_bad_attribute); - - TEST_RUN(gl_basic, wayland_gles20); - - TEST_RUN(gl_basic, wayland_gles3_rgb); - TEST_RUN(gl_basic, wayland_gles3_rgba); - TEST_RUN(gl_basic, wayland_gles3_fwdcompat_bad_attribute); - - TEST_RUN(gl_basic, wayland_gles30); + TEST_RUN2(gl_basic, wayland_gl_rgb, all_gl_rgb); + TEST_RUN2(gl_basic, wayland_gl_rgba, all_gl_rgba); + + TEST_RUN2(gl_basic, wayland_gl_debug, all_but_cgl_gl_debug); + TEST_RUN2(gl_basic, wayland_gl_fwdcompat_bad_attribute, all_but_cgl_gl_fwdcompat_bad_attribute); + + TEST_RUN2(gl_basic, wayland_gl10, all_gl10); + TEST_RUN2(gl_basic, wayland_gl11, all_gl11); + TEST_RUN2(gl_basic, wayland_gl12, all_gl12); + TEST_RUN2(gl_basic, wayland_gl13, all_gl13); + TEST_RUN2(gl_basic, wayland_gl14, all_gl14); + TEST_RUN2(gl_basic, wayland_gl15, all_gl15); + TEST_RUN2(gl_basic, wayland_gl20, all_gl20); + TEST_RUN2(gl_basic, wayland_gl21, all_gl21); + TEST_RUN2(gl_basic, wayland_gl21_fwdcompat_bad_attribute, all_gl21_fwdcompat_bad_attribute); + + TEST_RUN2(gl_basic, wayland_gl30, all_but_cgl_gl30); + TEST_RUN2(gl_basic, wayland_gl30_fwdcompat, all_but_cgl_gl30_fwdcompat); + TEST_RUN2(gl_basic, wayland_gl31, all_but_cgl_gl31); + TEST_RUN2(gl_basic, wayland_gl31_fwdcompat, all_but_cgl_gl31_fwdcompat); + + TEST_RUN2(gl_basic, wayland_gl32_core, all_but_cgl_gl32_core); + TEST_RUN2(gl_basic, wayland_gl32_core_fwdcompat, all_but_cgl_gl32_core_fwdcompat); + TEST_RUN2(gl_basic, wayland_gl33_core, all_but_cgl_gl33_core); + TEST_RUN2(gl_basic, wayland_gl40_core, all_but_cgl_gl40_core); + TEST_RUN2(gl_basic, wayland_gl41_core, all_but_cgl_gl41_core); + TEST_RUN2(gl_basic, wayland_gl42_core, all_but_cgl_gl42_core); + TEST_RUN2(gl_basic, wayland_gl43_core, all_but_cgl_gl43_core); + + TEST_RUN2(gl_basic, wayland_gl32_compat, all_but_cgl_gl32_compat); + TEST_RUN2(gl_basic, wayland_gl33_compat, all_but_cgl_gl33_compat); + TEST_RUN2(gl_basic, wayland_gl40_compat, all_but_cgl_gl40_compat); + TEST_RUN2(gl_basic, wayland_gl41_compat, all_but_cgl_gl41_compat); + TEST_RUN2(gl_basic, wayland_gl42_compat, all_but_cgl_gl42_compat); + TEST_RUN2(gl_basic, wayland_gl43_compat, all_but_cgl_gl43_compat); + + TEST_RUN2(gl_basic, wayland_gles1_rgb, all_but_cgl_gles1_rgb); + TEST_RUN2(gl_basic, wayland_gles1_rgba, all_but_cgl_gles1_rgba); + TEST_RUN2(gl_basic, wayland_gles1_fwdcompat_bad_attribute, all_but_cgl_gles1_fwdcompat_bad_attribute); + + TEST_RUN2(gl_basic, wayland_gles10, all_but_cgl_gles10); + TEST_RUN2(gl_basic, wayland_gles11, all_but_cgl_gles11); + + TEST_RUN2(gl_basic, wayland_gles1_rgb, all_but_cgl_gles1_rgb); + TEST_RUN2(gl_basic, wayland_gles1_rgba, all_but_cgl_gles1_rgba); + TEST_RUN2(gl_basic, wayland_gles1_fwdcompat_bad_attribute, all_but_cgl_gles1_fwdcompat_bad_attribute); + + TEST_RUN2(gl_basic, wayland_gles20, all_but_cgl_gles20); + + TEST_RUN2(gl_basic, wayland_gles3_rgb, all_but_cgl_gles3_rgb); + TEST_RUN2(gl_basic, wayland_gles3_rgba, all_but_cgl_gles3_rgba); + TEST_RUN2(gl_basic, wayland_gles3_fwdcompat_bad_attribute, all_but_cgl_gles3_fwdcompat_bad_attribute); + + TEST_RUN2(gl_basic, wayland_gles30, all_but_cgl_gles30); } #endif // WAFFLE_HAS_WAYLAND @@ -1271,350 +953,198 @@ TEST(gl_basic, x11_egl_init) gl_basic_init(WAFFLE_PLATFORM_X11_EGL); } -TEST(gl_basic, x11_egl_gl_rgb) -{ - gl_basic_draw(.api=WAFFLE_CONTEXT_OPENGL); -} - -TEST(gl_basic, x11_egl_gl_rgba) -{ - gl_basic_draw(.api=WAFFLE_CONTEXT_OPENGL, - .alpha=true); -} - -TEST(gl_basic, x11_egl_gl_debug) -{ - gl_basic_draw(.api=WAFFLE_CONTEXT_OPENGL, - .debug=true); -} - -TEST(gl_basic, x11_egl_gl_fwdcompat_bad_attribute) -{ - gl_basic_draw(.api=WAFFLE_CONTEXT_OPENGL, - .forward_compatible=true, - .expect_error=WAFFLE_ERROR_BAD_ATTRIBUTE); -} - -TEST(gl_basic, x11_egl_gl10) -{ - gl_basic_draw(.api=WAFFLE_CONTEXT_OPENGL, - .version=10); -} - -TEST(gl_basic, x11_egl_gl11) -{ - gl_basic_draw(.api=WAFFLE_CONTEXT_OPENGL, - .version=11); -} - -TEST(gl_basic, x11_egl_gl12) -{ - gl_basic_draw(.api=WAFFLE_CONTEXT_OPENGL, - .version=12); -} - -TEST(gl_basic, x11_egl_gl13) -{ - gl_basic_draw(.api=WAFFLE_CONTEXT_OPENGL, - .version=13); -} - -TEST(gl_basic, x11_egl_gl14) -{ - gl_basic_draw(.api=WAFFLE_CONTEXT_OPENGL, - .version=14); -} - -TEST(gl_basic, x11_egl_gl15) -{ - gl_basic_draw(.api=WAFFLE_CONTEXT_OPENGL, - .version=15); -} - -TEST(gl_basic, x11_egl_gl20) -{ - gl_basic_draw(.api=WAFFLE_CONTEXT_OPENGL, - .version=20); -} - -TEST(gl_basic, x11_egl_gl21) -{ - gl_basic_draw(.api=WAFFLE_CONTEXT_OPENGL, - .version=21); -} - -TEST(gl_basic, x11_egl_gl21_fwdcompat_bad_attribute) -{ - gl_basic_draw(.api=WAFFLE_CONTEXT_OPENGL, - .version=21, - .forward_compatible=true, - .expect_error=WAFFLE_ERROR_BAD_ATTRIBUTE); -} - -TEST(gl_basic, x11_egl_gl30) -{ - gl_basic_draw(.api=WAFFLE_CONTEXT_OPENGL, - .version=30); -} - -TEST(gl_basic, x11_egl_gl30_fwdcompat) -{ - gl_basic_draw(.api=WAFFLE_CONTEXT_OPENGL, - .version=30, - .forward_compatible=true); -} - -TEST(gl_basic, x11_egl_gl31) -{ - gl_basic_draw(.api=WAFFLE_CONTEXT_OPENGL, - .version=31); -} - -TEST(gl_basic, x11_egl_gl31_fwdcompat) -{ - gl_basic_draw(.api=WAFFLE_CONTEXT_OPENGL, - .version=31, - .forward_compatible=true); -} - -TEST(gl_basic, x11_egl_gl32_core) -{ - gl_basic_draw(.api=WAFFLE_CONTEXT_OPENGL, - .version=32, - .profile=WAFFLE_CONTEXT_CORE_PROFILE); -} - -TEST(gl_basic, x11_egl_gl32_core_fwdcompat) -{ - gl_basic_draw(.api=WAFFLE_CONTEXT_OPENGL, - .version=32, - .profile=WAFFLE_CONTEXT_CORE_PROFILE, - .forward_compatible=true); -} - -TEST(gl_basic, x11_egl_gl33_core) -{ - gl_basic_draw(.api=WAFFLE_CONTEXT_OPENGL, - .version=33, - .profile=WAFFLE_CONTEXT_CORE_PROFILE); -} - -TEST(gl_basic, x11_egl_gl40_core) -{ - gl_basic_draw(.api=WAFFLE_CONTEXT_OPENGL, - .version=40, - .profile=WAFFLE_CONTEXT_CORE_PROFILE); -} - -TEST(gl_basic, x11_egl_gl41_core) -{ - gl_basic_draw(.api=WAFFLE_CONTEXT_OPENGL, - .version=41, - .profile=WAFFLE_CONTEXT_CORE_PROFILE); -} - -TEST(gl_basic, x11_egl_gl42_core) -{ - gl_basic_draw(.api=WAFFLE_CONTEXT_OPENGL, - .version=42, - .profile=WAFFLE_CONTEXT_CORE_PROFILE); -} - -TEST(gl_basic, x11_egl_gl43_core) -{ - gl_basic_draw(.api=WAFFLE_CONTEXT_OPENGL, - .version=43, - .profile=WAFFLE_CONTEXT_CORE_PROFILE); -} - -TEST(gl_basic, x11_egl_gl32_compat) -{ - gl_basic_draw(.api=WAFFLE_CONTEXT_OPENGL, - .version=32, - .profile=WAFFLE_CONTEXT_COMPATIBILITY_PROFILE); -} - -TEST(gl_basic, x11_egl_gl33_compat) -{ - gl_basic_draw(.api=WAFFLE_CONTEXT_OPENGL, - .version=33, - .profile=WAFFLE_CONTEXT_COMPATIBILITY_PROFILE); -} - -TEST(gl_basic, x11_egl_gl40_compat) -{ - gl_basic_draw(.api=WAFFLE_CONTEXT_OPENGL, - .version=40, - .profile=WAFFLE_CONTEXT_COMPATIBILITY_PROFILE); -} - -TEST(gl_basic, x11_egl_gl41_compat) -{ - gl_basic_draw(.api=WAFFLE_CONTEXT_OPENGL, - .version=41, - .profile=WAFFLE_CONTEXT_COMPATIBILITY_PROFILE); -} - -TEST(gl_basic, x11_egl_gl42_compat) -{ - gl_basic_draw(.api=WAFFLE_CONTEXT_OPENGL, - .version=42, - .profile=WAFFLE_CONTEXT_COMPATIBILITY_PROFILE); -} - -TEST(gl_basic, x11_egl_gl43_compat) -{ - gl_basic_draw(.api=WAFFLE_CONTEXT_OPENGL, - .version=43, - .profile=WAFFLE_CONTEXT_COMPATIBILITY_PROFILE); -} - -TEST(gl_basic, x11_egl_gles1_rgb) -{ - gl_basic_draw(.api=WAFFLE_CONTEXT_OPENGL_ES1); -} - -TEST(gl_basic, x11_egl_gles1_rgba) -{ - gl_basic_draw(.api=WAFFLE_CONTEXT_OPENGL_ES1, - .alpha=true); -} - -TEST(gl_basic, x11_egl_gles10) -{ - gl_basic_draw(.api=WAFFLE_CONTEXT_OPENGL_ES1, - .version=10, - .alpha=true); -} - -TEST(gl_basic, x11_egl_gles11) -{ - gl_basic_draw(.api=WAFFLE_CONTEXT_OPENGL_ES1, - .version=11, - .alpha=true); -} - -TEST(gl_basic, x11_egl_gles1_fwdcompat_bad_attribute) -{ - gl_basic_draw(.api=WAFFLE_CONTEXT_OPENGL_ES1, - .forward_compatible=true, - .expect_error=WAFFLE_ERROR_BAD_ATTRIBUTE); -} - -TEST(gl_basic, x11_egl_gles2_rgb) -{ - gl_basic_draw(.api=WAFFLE_CONTEXT_OPENGL_ES2); -} - -TEST(gl_basic, x11_egl_gles2_rgba) +static void +testsuite_x11_egl(void) { - gl_basic_draw(.api=WAFFLE_CONTEXT_OPENGL_ES2, - .alpha=true); -} + TEST_RUN(gl_basic, x11_egl_init); -TEST(gl_basic, x11_egl_gles20) -{ - gl_basic_draw(.api=WAFFLE_CONTEXT_OPENGL_ES2, - .version=20); + TEST_RUN2(gl_basic, x11_egl_gl_rgb, all_gl_rgb); + TEST_RUN2(gl_basic, x11_egl_gl_rgba, all_gl_rgba); + TEST_RUN2(gl_basic, x11_egl_gl_debug, all_but_cgl_gl_debug); + TEST_RUN2(gl_basic, x11_egl_gl_fwdcompat_bad_attribute, all_but_cgl_gl_fwdcompat_bad_attribute); + + TEST_RUN2(gl_basic, x11_egl_gl10, all_gl10); + TEST_RUN2(gl_basic, x11_egl_gl11, all_gl11); + TEST_RUN2(gl_basic, x11_egl_gl12, all_gl12); + TEST_RUN2(gl_basic, x11_egl_gl13, all_gl13); + TEST_RUN2(gl_basic, x11_egl_gl14, all_gl14); + TEST_RUN2(gl_basic, x11_egl_gl15, all_gl15); + TEST_RUN2(gl_basic, x11_egl_gl20, all_gl20); + TEST_RUN2(gl_basic, x11_egl_gl21, all_gl21); + TEST_RUN2(gl_basic, x11_egl_gl21_fwdcompat_bad_attribute, all_gl21_fwdcompat_bad_attribute); + + TEST_RUN2(gl_basic, x11_egl_gl30, all_but_cgl_gl30); + TEST_RUN2(gl_basic, x11_egl_gl30_fwdcompat, all_but_cgl_gl30_fwdcompat); + TEST_RUN2(gl_basic, x11_egl_gl31, all_but_cgl_gl31); + TEST_RUN2(gl_basic, x11_egl_gl31_fwdcompat, all_but_cgl_gl31_fwdcompat); + + TEST_RUN2(gl_basic, x11_egl_gl32_core, all_but_cgl_gl32_core); + TEST_RUN2(gl_basic, x11_egl_gl32_core_fwdcompat, all_but_cgl_gl32_core_fwdcompat); + TEST_RUN2(gl_basic, x11_egl_gl33_core, all_but_cgl_gl33_core); + TEST_RUN2(gl_basic, x11_egl_gl40_core, all_but_cgl_gl40_core); + TEST_RUN2(gl_basic, x11_egl_gl41_core, all_but_cgl_gl41_core); + TEST_RUN2(gl_basic, x11_egl_gl42_core, all_but_cgl_gl42_core); + TEST_RUN2(gl_basic, x11_egl_gl43_core, all_but_cgl_gl43_core); + + TEST_RUN2(gl_basic, x11_egl_gl32_compat, all_but_cgl_gl32_compat); + TEST_RUN2(gl_basic, x11_egl_gl33_compat, all_but_cgl_gl33_compat); + TEST_RUN2(gl_basic, x11_egl_gl40_compat, all_but_cgl_gl40_compat); + TEST_RUN2(gl_basic, x11_egl_gl41_compat, all_but_cgl_gl41_compat); + TEST_RUN2(gl_basic, x11_egl_gl42_compat, all_but_cgl_gl42_compat); + TEST_RUN2(gl_basic, x11_egl_gl43_compat, all_but_cgl_gl43_compat); + + TEST_RUN2(gl_basic, x11_egl_gles1_rgb, all_but_cgl_gles1_rgb); + TEST_RUN2(gl_basic, x11_egl_gles1_rgba, all_but_cgl_gles1_rgba); + TEST_RUN2(gl_basic, x11_egl_gles1_fwdcompat_bad_attribute, all_but_cgl_gles1_fwdcompat_bad_attribute); + + TEST_RUN2(gl_basic, x11_egl_gles10, all_but_cgl_gles10); + TEST_RUN2(gl_basic, x11_egl_gles11, all_but_cgl_gles11); + + TEST_RUN2(gl_basic, x11_egl_gles2_rgb, all_but_cgl_gles2_rgb); + TEST_RUN2(gl_basic, x11_egl_gles2_rgba, all_but_cgl_gles2_rgba); + TEST_RUN2(gl_basic, x11_egl_gles2_fwdcompat_bad_attribute, all_but_cgl_gles2_fwdcompat_bad_attribute); + + TEST_RUN2(gl_basic, x11_egl_gles20, all_but_cgl_gles20); + + TEST_RUN2(gl_basic, x11_egl_gles3_rgb, all_but_cgl_gles3_rgb); + TEST_RUN2(gl_basic, x11_egl_gles3_rgba, all_but_cgl_gles3_rgba); + TEST_RUN2(gl_basic, x11_egl_gles3_fwdcompat_bad_attribute, all_but_cgl_gles3_fwdcompat_bad_attribute); + + TEST_RUN2(gl_basic, x11_egl_gles30, all_but_cgl_gles30); } +#endif // WAFFLE_HAS_X11_EGL -TEST(gl_basic, x11_egl_gles2_fwdcompat_bad_attribute) +#ifdef WAFFLE_HAS_WGL +TEST(gl_basic, wgl_init) { - gl_basic_draw(.api=WAFFLE_CONTEXT_OPENGL_ES2, - .forward_compatible=true, - .expect_error=WAFFLE_ERROR_BAD_ATTRIBUTE); + gl_basic_init(WAFFLE_PLATFORM_WGL); } -TEST(gl_basic, x11_egl_gles3_rgb) -{ - gl_basic_draw(.api=WAFFLE_CONTEXT_OPENGL_ES3); -} +static void +testsuite_wgl(void) +{ + TEST_RUN(gl_basic, wgl_init); + + TEST_RUN(gl_basic, all_gl_rgb); + TEST_RUN(gl_basic, all_gl_rgba); + TEST_RUN(gl_basic, all_but_cgl_gl_debug); + TEST_RUN(gl_basic, all_but_cgl_gl_fwdcompat_bad_attribute); + + TEST_RUN(gl_basic, all_gl10); + TEST_RUN(gl_basic, all_gl11); + TEST_RUN(gl_basic, all_gl12); + TEST_RUN(gl_basic, all_gl13); + TEST_RUN(gl_basic, all_gl14); + TEST_RUN(gl_basic, all_gl15); + TEST_RUN(gl_basic, all_gl20); + TEST_RUN(gl_basic, all_gl21); + TEST_RUN(gl_basic, all_gl21_fwdcompat_bad_attribute); + + TEST_RUN(gl_basic, all_but_cgl_gl30); + TEST_RUN(gl_basic, all_but_cgl_gl30_fwdcompat); + TEST_RUN(gl_basic, all_but_cgl_gl31); + TEST_RUN(gl_basic, all_but_cgl_gl31_fwdcompat); + + TEST_RUN(gl_basic, all_but_cgl_gl32_core); + TEST_RUN(gl_basic, all_but_cgl_gl32_core_fwdcompat); + TEST_RUN(gl_basic, all_but_cgl_gl33_core); + TEST_RUN(gl_basic, all_but_cgl_gl40_core); + TEST_RUN(gl_basic, all_but_cgl_gl41_core); + TEST_RUN(gl_basic, all_but_cgl_gl42_core); + TEST_RUN(gl_basic, all_but_cgl_gl43_core); + + TEST_RUN(gl_basic, all_but_cgl_gl32_compat); + TEST_RUN(gl_basic, all_but_cgl_gl33_compat); + TEST_RUN(gl_basic, all_but_cgl_gl40_compat); + TEST_RUN(gl_basic, all_but_cgl_gl41_compat); + TEST_RUN(gl_basic, all_but_cgl_gl42_compat); + TEST_RUN(gl_basic, all_but_cgl_gl43_compat); + + TEST_RUN(gl_basic, all_but_cgl_gles1_rgb); + TEST_RUN(gl_basic, all_but_cgl_gles1_rgba); + TEST_RUN(gl_basic, all_but_cgl_gles1_fwdcompat_bad_attribute); + + TEST_RUN(gl_basic, all_but_cgl_gles10); + TEST_RUN(gl_basic, all_but_cgl_gles11); + + TEST_RUN(gl_basic, all_but_cgl_gles2_rgb); + TEST_RUN(gl_basic, all_but_cgl_gles2_rgba); + TEST_RUN(gl_basic, all_but_cgl_gles2_fwdcompat_bad_attribute); + + TEST_RUN(gl_basic, all_but_cgl_gles20); + + TEST_RUN(gl_basic, all_but_cgl_gles3_rgb); + TEST_RUN(gl_basic, all_but_cgl_gles3_rgba); + TEST_RUN(gl_basic, all_but_cgl_gles3_fwdcompat_bad_attribute); + + TEST_RUN(gl_basic, all_but_cgl_gles30); +} +#endif // WAFFLE_HAS_WGL -TEST(gl_basic, x11_egl_gles3_rgba) +static void +usage_error(void) { - gl_basic_draw(.api=WAFFLE_CONTEXT_OPENGL_ES3, - .alpha=true); + fprintf(stderr, "gl_basic_test: usage error\n"); + exit(1); } -TEST(gl_basic, x11_egl_gles30) +#ifdef _WIN32 +static DWORD __stdcall +worker_thread(LPVOID lpParam) { - gl_basic_draw(.api=WAFFLE_CONTEXT_OPENGL_ES3, - .version=30); -} + void (__stdcall *testsuite)(void) = lpParam; + void (__stdcall *testsuites[])(void) = {testsuite, 0}; + int argc = 0; + DWORD num_failures = wt_main(&argc, NULL, testsuites); -TEST(gl_basic, x11_egl_gles3_fwdcompat_bad_attribute) -{ - gl_basic_draw(.api=WAFFLE_CONTEXT_OPENGL_ES3, - .forward_compatible=true, - .expect_error=WAFFLE_ERROR_BAD_ATTRIBUTE); + return num_failures; } +/// Run the testsuite in a separate thread. If the testsuite fails, then exit +/// immediately. static void -testsuite_x11_egl(void) +run_testsuite(void (*testsuite)(void)) { - TEST_RUN(gl_basic, x11_egl_init); - - TEST_RUN(gl_basic, x11_egl_gl_rgb); - TEST_RUN(gl_basic, x11_egl_gl_rgba); - TEST_RUN(gl_basic, x11_egl_gl_debug); - TEST_RUN(gl_basic, x11_egl_gl_fwdcompat_bad_attribute); - - TEST_RUN(gl_basic, x11_egl_gl10); - TEST_RUN(gl_basic, x11_egl_gl11); - TEST_RUN(gl_basic, x11_egl_gl12); - TEST_RUN(gl_basic, x11_egl_gl13); - TEST_RUN(gl_basic, x11_egl_gl14); - TEST_RUN(gl_basic, x11_egl_gl15); - TEST_RUN(gl_basic, x11_egl_gl20); - TEST_RUN(gl_basic, x11_egl_gl21); - TEST_RUN(gl_basic, x11_egl_gl21_fwdcompat_bad_attribute); - TEST_RUN(gl_basic, x11_egl_gl30); - TEST_RUN(gl_basic, x11_egl_gl30_fwdcompat); - TEST_RUN(gl_basic, x11_egl_gl31); - TEST_RUN(gl_basic, x11_egl_gl31_fwdcompat); - - TEST_RUN(gl_basic, x11_egl_gl32_core); - TEST_RUN(gl_basic, x11_egl_gl32_core_fwdcompat); - TEST_RUN(gl_basic, x11_egl_gl33_core); - TEST_RUN(gl_basic, x11_egl_gl40_core); - TEST_RUN(gl_basic, x11_egl_gl41_core); - TEST_RUN(gl_basic, x11_egl_gl42_core); - TEST_RUN(gl_basic, x11_egl_gl43_core); - - TEST_RUN(gl_basic, x11_egl_gl32_compat); - TEST_RUN(gl_basic, x11_egl_gl33_compat); - TEST_RUN(gl_basic, x11_egl_gl40_compat); - TEST_RUN(gl_basic, x11_egl_gl41_compat); - TEST_RUN(gl_basic, x11_egl_gl42_compat); - TEST_RUN(gl_basic, x11_egl_gl43_compat); - - TEST_RUN(gl_basic, x11_egl_gles1_rgb); - TEST_RUN(gl_basic, x11_egl_gles1_rgba); - TEST_RUN(gl_basic, x11_egl_gles1_fwdcompat_bad_attribute); - - TEST_RUN(gl_basic, x11_egl_gles10); - TEST_RUN(gl_basic, x11_egl_gles11); - - TEST_RUN(gl_basic, x11_egl_gles2_rgb); - TEST_RUN(gl_basic, x11_egl_gles2_rgba); - TEST_RUN(gl_basic, x11_egl_gles2_fwdcompat_bad_attribute); - - TEST_RUN(gl_basic, x11_egl_gles20); - - TEST_RUN(gl_basic, x11_egl_gles3_rgb); - TEST_RUN(gl_basic, x11_egl_gles3_rgba); - TEST_RUN(gl_basic, x11_egl_gles3_fwdcompat_bad_attribute); - - TEST_RUN(gl_basic, x11_egl_gles30); + HANDLE hThread; + DWORD dwThreadId; + + hThread = CreateThread(NULL, 0, worker_thread, testsuite, 0, &dwThreadId); + if (hThread != NULL) { + // XXX: Add a decent timeout interval + if (WaitForSingleObject(hThread, INFINITE) == WAIT_OBJECT_0) { + DWORD exit_status; + + if (GetExitCodeThread(hThread, &exit_status)) { + // exit_status is number of failures. + if (exit_status == 0) { + // All tests passed. + CloseHandle(hThread); + return; + } + else { + // Some tests failed. Don't run any more tests. + exit(exit_status); + // or exit process ? + } + } + else { + fprintf(stderr, "gl_basic_test: error: get thread exit status" + " failed (%lu)\n", GetLastError()); + abort(); + } + } + else { + fprintf(stderr, "gl_basic_test: error: wait for thread failed\n"); + abort(); + } + } + else { + fprintf(stderr, "gl_basic_test: error: CreateThread failed\n"); + abort(); + } } -#endif // WAFFLE_HAS_X11_EGL -static void -usage_error(void) -{ - fprintf(stderr, "gl_basic_test: usage error\n"); - exit(1); -} +#else /// Run the testsuite in a separate process. If the testsuite fails, then exit /// immediately. @@ -1662,6 +1192,8 @@ run_testsuite(void (*testsuite)(void)) } } +#endif // _WIN32 + int main(int argc, char *argv[]) { @@ -1680,6 +1212,9 @@ main(int argc, char *argv[]) #ifdef WAFFLE_HAS_X11_EGL run_testsuite(testsuite_x11_egl); #endif +#ifdef WAFFLE_HAS_WGL + run_testsuite(testsuite_wgl); +#endif return 0; } diff --git a/third_party/getopt/CMakeLists.txt b/third_party/getopt/CMakeLists.txt new file mode 100644 index 0000000..e1a2c1c --- /dev/null +++ b/third_party/getopt/CMakeLists.txt @@ -0,0 +1,10 @@ +include_directories(${CMAKE_CURRENT_SOURCE_DIR}) + +add_library(getopt_bundled STATIC EXCLUDE_FROM_ALL getopt_long.c) + +install( + FILES LICENSE + DESTINATION "${CMAKE_INSTALL_DOCDIR}" + RENAME LICENSE-getopt.txt + COMPONENT coredocs +) diff --git a/third_party/getopt/LICENSE b/third_party/getopt/LICENSE new file mode 100644 index 0000000..1a9141b --- /dev/null +++ b/third_party/getopt/LICENSE @@ -0,0 +1,45 @@ +Copyright (c) 2002 Todd C. Miller <Todd.Miller@courtesan.com> + +Permission to use, copy, modify, and distribute this software for any +purpose with or without fee is hereby granted, provided that the above +copyright notice and this permission notice appear in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +Sponsored in part by the Defense Advanced Research Projects +Agency (DARPA) and Air Force Research Laboratory, Air Force +Materiel Command, USAF, under agreement number F39502-99-1-0512. + + +Copyright (c) 2000 The NetBSD Foundation, Inc. +All rights reserved. + +This code is derived from software contributed to The NetBSD Foundation +by Dieter Baron and Thomas Klausner. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: +1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. +2. 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. + +THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. 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 FOUNDATION 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. diff --git a/third_party/getopt/getopt.h b/third_party/getopt/getopt.h new file mode 100644 index 0000000..b6da6df --- /dev/null +++ b/third_party/getopt/getopt.h @@ -0,0 +1,82 @@ +/* $OpenBSD: getopt.h,v 1.2 2008/06/26 05:42:04 ray Exp $ */ +/* $NetBSD: getopt.h,v 1.4 2000/07/07 10:43:54 ad Exp $ */ + +/*- + * Copyright (c) 2000 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Dieter Baron and Thomas Klausner. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. 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 FOUNDATION 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. + */ + +#ifndef _GETOPT_H_ +#define _GETOPT_H_ + +/* + * GNU-like getopt_long() and 4.4BSD getsubopt()/optreset extensions + */ +#define no_argument 0 +#define required_argument 1 +#define optional_argument 2 + +#ifdef __cplusplus +extern "C" { +#endif + +struct option { + /* name of long option */ + const char *name; + /* + * one of no_argument, required_argument, and optional_argument: + * whether option takes an argument + */ + int has_arg; + /* if not NULL, set *flag to val when option found */ + int *flag; + /* if flag not NULL, value to set *flag to; else return value */ + int val; +}; + +int getopt_long(int, char * const *, const char *, + const struct option *, int *); +int getopt_long_only(int, char * const *, const char *, + const struct option *, int *); +#ifndef _GETOPT_DEFINED_ +#define _GETOPT_DEFINED_ +int getopt(int, char * const *, const char *); +int getsubopt(char **, char * const *, char **); + +extern char *optarg; /* getopt(3) external variables */ +extern int opterr; +extern int optind; +extern int optopt; +extern int optreset; +extern char *suboptarg; /* getsubopt(3) external variable */ +#endif + +#ifdef __cplusplus +} +#endif + +#endif /* !_GETOPT_H_ */ diff --git a/third_party/getopt/getopt_long.c b/third_party/getopt/getopt_long.c new file mode 100644 index 0000000..81268b8 --- /dev/null +++ b/third_party/getopt/getopt_long.c @@ -0,0 +1,511 @@ +/* $OpenBSD: getopt_long.c,v 1.24 2010/07/22 19:31:53 blambert Exp $ */ +/* $NetBSD: getopt_long.c,v 1.15 2002/01/31 22:43:40 tv Exp $ */ + +/* + * Copyright (c) 2002 Todd C. Miller <Todd.Miller@courtesan.com> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + * Sponsored in part by the Defense Advanced Research Projects + * Agency (DARPA) and Air Force Research Laboratory, Air Force + * Materiel Command, USAF, under agreement number F39502-99-1-0512. + */ +/*- + * Copyright (c) 2000 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Dieter Baron and Thomas Klausner. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. 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 FOUNDATION 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. + */ + +#include <errno.h> +#include <getopt.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +int opterr = 1; /* if error message should be printed */ +int optind = 1; /* index into parent argv vector */ +int optopt = '?'; /* character checked for validity */ +int optreset; /* reset getopt */ +char *optarg; /* argument associated with option */ + +#define PRINT_ERROR ((opterr) && (*options != ':')) + +#define FLAG_PERMUTE 0x01 /* permute non-options to the end of argv */ +#define FLAG_ALLARGS 0x02 /* treat non-options as args to option "-1" */ +#define FLAG_LONGONLY 0x04 /* operate as getopt_long_only */ + +/* return values */ +#define BADCH (int)'?' +#define BADARG ((*options == ':') ? (int)':' : (int)'?') +#define INORDER (int)1 + +#define EMSG "" + +static int getopt_internal(int, char * const *, const char *, + const struct option *, int *, int); +static int parse_long_options(char * const *, const char *, + const struct option *, int *, int); +static int gcd(int, int); +static void permute_args(int, int, int, char * const *); + +static char *place = EMSG; /* option letter processing */ + +/* XXX: set optreset to 1 rather than these two */ +static int nonopt_start = -1; /* first non option argument (for permute) */ +static int nonopt_end = -1; /* first option after non options (for permute) */ + +/* Error messages */ +static const char recargchar[] = "option requires an argument -- %c"; +static const char recargstring[] = "option requires an argument -- %s"; +static const char ambig[] = "ambiguous option -- %.*s"; +static const char noarg[] = "option doesn't take an argument -- %.*s"; +static const char illoptchar[] = "unknown option -- %c"; +static const char illoptstring[] = "unknown option -- %s"; + +/* + * Compute the greatest common divisor of a and b. + */ +static int +gcd(int a, int b) +{ + int c; + + c = a % b; + while (c != 0) { + a = b; + b = c; + c = a % b; + } + + return (b); +} + +/* + * Exchange the block from nonopt_start to nonopt_end with the block + * from nonopt_end to opt_end (keeping the same order of arguments + * in each block). + */ +static void +permute_args(int panonopt_start, int panonopt_end, int opt_end, + char * const *nargv) +{ + int cstart, cyclelen, i, j, ncycle, nnonopts, nopts, pos; + char *swap; + + /* + * compute lengths of blocks and number and size of cycles + */ + nnonopts = panonopt_end - panonopt_start; + nopts = opt_end - panonopt_end; + ncycle = gcd(nnonopts, nopts); + cyclelen = (opt_end - panonopt_start) / ncycle; + + for (i = 0; i < ncycle; i++) { + cstart = panonopt_end+i; + pos = cstart; + for (j = 0; j < cyclelen; j++) { + if (pos >= panonopt_end) + pos -= nnonopts; + else + pos += nopts; + swap = nargv[pos]; + /* LINTED const cast */ + ((char **) nargv)[pos] = nargv[cstart]; + /* LINTED const cast */ + ((char **)nargv)[cstart] = swap; + } + } +} + +/* + * parse_long_options -- + * Parse long options in argc/argv argument vector. + * Returns -1 if short_too is set and the option does not match long_options. + */ +static int +parse_long_options(char * const *nargv, const char *options, + const struct option *long_options, int *idx, int short_too) +{ + char *current_argv, *has_equal; + size_t current_argv_len; + int i, match; + + current_argv = place; + match = -1; + + optind++; + + if ((has_equal = strchr(current_argv, '=')) != NULL) { + /* argument found (--option=arg) */ + current_argv_len = has_equal - current_argv; + has_equal++; + } else + current_argv_len = strlen(current_argv); + + for (i = 0; long_options[i].name; i++) { + /* find matching long option */ + if (strncmp(current_argv, long_options[i].name, + current_argv_len)) + continue; + + if (strlen(long_options[i].name) == current_argv_len) { + /* exact match */ + match = i; + break; + } + /* + * If this is a known short option, don't allow + * a partial match of a single character. + */ + if (short_too && current_argv_len == 1) + continue; + + if (match == -1) /* partial match */ + match = i; + else { + /* ambiguous abbreviation */ + if (PRINT_ERROR) + fprintf(stderr, ambig, (int)current_argv_len, + current_argv); + optopt = 0; + return (BADCH); + } + } + if (match != -1) { /* option found */ + if (long_options[match].has_arg == no_argument + && has_equal) { + if (PRINT_ERROR) + fprintf(stderr, noarg, (int)current_argv_len, + current_argv); + /* + * XXX: GNU sets optopt to val regardless of flag + */ + if (long_options[match].flag == NULL) + optopt = long_options[match].val; + else + optopt = 0; + return (BADARG); + } + if (long_options[match].has_arg == required_argument || + long_options[match].has_arg == optional_argument) { + if (has_equal) + optarg = has_equal; + else if (long_options[match].has_arg == + required_argument) { + /* + * optional argument doesn't use next nargv + */ + optarg = nargv[optind++]; + } + } + if ((long_options[match].has_arg == required_argument) + && (optarg == NULL)) { + /* + * Missing argument; leading ':' indicates no error + * should be generated. + */ + if (PRINT_ERROR) + fprintf(stderr, recargstring, + current_argv); + /* + * XXX: GNU sets optopt to val regardless of flag + */ + if (long_options[match].flag == NULL) + optopt = long_options[match].val; + else + optopt = 0; + --optind; + return (BADARG); + } + } else { /* unknown option */ + if (short_too) { + --optind; + return (-1); + } + if (PRINT_ERROR) + fprintf(stderr, illoptstring, current_argv); + optopt = 0; + return (BADCH); + } + if (idx) + *idx = match; + if (long_options[match].flag) { + *long_options[match].flag = long_options[match].val; + return (0); + } else + return (long_options[match].val); +} + +/* + * getopt_internal -- + * Parse argc/argv argument vector. Called by user level routines. + */ +static int +getopt_internal(int nargc, char * const *nargv, const char *options, + const struct option *long_options, int *idx, int flags) +{ + char *oli; /* option letter list index */ + int optchar, short_too; + static int posixly_correct = -1; + + if (options == NULL) + return (-1); + + /* + * Disable GNU extensions if POSIXLY_CORRECT is set or options + * string begins with a '+'. + */ + if (posixly_correct == -1) + posixly_correct = (getenv("POSIXLY_CORRECT") != NULL); + if (posixly_correct || *options == '+') + flags &= ~FLAG_PERMUTE; + else if (*options == '-') + flags |= FLAG_ALLARGS; + if (*options == '+' || *options == '-') + options++; + + /* + * XXX Some GNU programs (like cvs) set optind to 0 instead of + * XXX using optreset. Work around this braindamage. + */ + if (optind == 0) + optind = optreset = 1; + + optarg = NULL; + if (optreset) + nonopt_start = nonopt_end = -1; +start: + if (optreset || !*place) { /* update scanning pointer */ + optreset = 0; + if (optind >= nargc) { /* end of argument vector */ + place = EMSG; + if (nonopt_end != -1) { + /* do permutation, if we have to */ + permute_args(nonopt_start, nonopt_end, + optind, nargv); + optind -= nonopt_end - nonopt_start; + } + else if (nonopt_start != -1) { + /* + * If we skipped non-options, set optind + * to the first of them. + */ + optind = nonopt_start; + } + nonopt_start = nonopt_end = -1; + return (-1); + } + if (*(place = nargv[optind]) != '-' || + (place[1] == '\0' && strchr(options, '-') == NULL)) { + place = EMSG; /* found non-option */ + if (flags & FLAG_ALLARGS) { + /* + * GNU extension: + * return non-option as argument to option 1 + */ + optarg = nargv[optind++]; + return (INORDER); + } + if (!(flags & FLAG_PERMUTE)) { + /* + * If no permutation wanted, stop parsing + * at first non-option. + */ + return (-1); + } + /* do permutation */ + if (nonopt_start == -1) + nonopt_start = optind; + else if (nonopt_end != -1) { + permute_args(nonopt_start, nonopt_end, + optind, nargv); + nonopt_start = optind - + (nonopt_end - nonopt_start); + nonopt_end = -1; + } + optind++; + /* process next argument */ + goto start; + } + if (nonopt_start != -1 && nonopt_end == -1) + nonopt_end = optind; + + /* + * If we have "-" do nothing, if "--" we are done. + */ + if (place[1] != '\0' && *++place == '-' && place[1] == '\0') { + optind++; + place = EMSG; + /* + * We found an option (--), so if we skipped + * non-options, we have to permute. + */ + if (nonopt_end != -1) { + permute_args(nonopt_start, nonopt_end, + optind, nargv); + optind -= nonopt_end - nonopt_start; + } + nonopt_start = nonopt_end = -1; + return (-1); + } + } + + /* + * Check long options if: + * 1) we were passed some + * 2) the arg is not just "-" + * 3) either the arg starts with -- we are getopt_long_only() + */ + if (long_options != NULL && place != nargv[optind] && + (*place == '-' || (flags & FLAG_LONGONLY))) { + short_too = 0; + if (*place == '-') + place++; /* --foo long option */ + else if (*place != ':' && strchr(options, *place) != NULL) + short_too = 1; /* could be short option too */ + + optchar = parse_long_options(nargv, options, long_options, + idx, short_too); + if (optchar != -1) { + place = EMSG; + return (optchar); + } + } + + if ((optchar = (int)*place++) == (int)':' || + (optchar == (int)'-' && *place != '\0') || + (oli = strchr(options, optchar)) == NULL) { + /* + * If the user specified "-" and '-' isn't listed in + * options, return -1 (non-option) as per POSIX. + * Otherwise, it is an unknown option character (or ':'). + */ + if (optchar == (int)'-' && *place == '\0') + return (-1); + if (!*place) + ++optind; + if (PRINT_ERROR) + fprintf(stderr, illoptchar, optchar); + optopt = optchar; + return (BADCH); + } + if (long_options != NULL && optchar == 'W' && oli[1] == ';') { + /* -W long-option */ + if (*place) /* no space */ + /* NOTHING */; + else if (++optind >= nargc) { /* no arg */ + place = EMSG; + if (PRINT_ERROR) + fprintf(stderr, recargchar, optchar); + optopt = optchar; + return (BADARG); + } else /* white space */ + place = nargv[optind]; + optchar = parse_long_options(nargv, options, long_options, + idx, 0); + place = EMSG; + return (optchar); + } + if (*++oli != ':') { /* doesn't take argument */ + if (!*place) + ++optind; + } else { /* takes (optional) argument */ + optarg = NULL; + if (*place) /* no white space */ + optarg = place; + else if (oli[1] != ':') { /* arg not optional */ + if (++optind >= nargc) { /* no arg */ + place = EMSG; + if (PRINT_ERROR) + fprintf(stderr, recargchar, optchar); + optopt = optchar; + return (BADARG); + } else + optarg = nargv[optind]; + } + place = EMSG; + ++optind; + } + /* dump back option letter */ + return (optchar); +} + +/* + * getopt -- + * Parse argc/argv argument vector. + * + * [eventually this will replace the BSD getopt] + */ +int +getopt(int nargc, char * const *nargv, const char *options) +{ + + /* + * We don't pass FLAG_PERMUTE to getopt_internal() since + * the BSD getopt(3) (unlike GNU) has never done this. + * + * Furthermore, since many privileged programs call getopt() + * before dropping privileges it makes sense to keep things + * as simple (and bug-free) as possible. + */ + return (getopt_internal(nargc, nargv, options, NULL, NULL, 0)); +} + +/* + * getopt_long -- + * Parse argc/argv argument vector. + */ +int +getopt_long(int nargc, char * const *nargv, const char *options, + const struct option *long_options, int *idx) +{ + + return (getopt_internal(nargc, nargv, options, long_options, idx, + FLAG_PERMUTE)); +} + +/* + * getopt_long_only -- + * Parse argc/argv argument vector. + */ +int +getopt_long_only(int nargc, char * const *nargv, const char *options, + const struct option *long_options, int *idx) +{ + + return (getopt_internal(nargc, nargv, options, long_options, idx, + FLAG_PERMUTE|FLAG_LONGONLY)); +} diff --git a/third_party/threads/CMakeLists.txt b/third_party/threads/CMakeLists.txt new file mode 100644 index 0000000..1cc6092 --- /dev/null +++ b/third_party/threads/CMakeLists.txt @@ -0,0 +1,23 @@ +include_directories (${CMAKE_CURRENT_SOURCE_DIR}) + +if(WIN32) + set(threads_sources threads_win32.c) +else() + set(threads_sources threads_posix.c) +endif() + +add_library(threads_bundled STATIC EXCLUDE_FROM_ALL ${threads_sources}) + +set_target_properties(threads_bundled + PROPERTIES + # This static library is linked into the shared libwaffle and the latter + # is PIC enabled thus we'll need to enable it here as well. + POSITION_INDEPENDENT_CODE ON + ) + +install( + FILES LICENSE + DESTINATION "${CMAKE_INSTALL_DOCDIR}" + RENAME LICENSE-threads.txt + COMPONENT coredocs +) diff --git a/third_party/threads/LICENSE b/third_party/threads/LICENSE new file mode 100644 index 0000000..0606f1b --- /dev/null +++ b/third_party/threads/LICENSE @@ -0,0 +1,28 @@ +/* + * C11 <threads.h> emulation library + * + * (C) Copyright yohhoy 2012. + * Distributed under the Boost Software License, Version 1.0. + * + * Permission is hereby granted, free of charge, to any person or organization + * obtaining a copy of the software and accompanying documentation covered by + * this license (the "Software") to use, reproduce, display, distribute, + * execute, and transmit the Software, and to prepare [[derivative work]]s of the + * Software, and to permit third-parties to whom the Software is furnished to + * do so, all subject to the following: + * + * The copyright notices in the Software and this entire statement, including + * the above license grant, this restriction and the following disclaimer, + * must be included in all copies of the Software, in whole or in part, and + * all derivative works of the Software, unless such copies or derivative + * works are solely in the form of machine-executable object code generated by + * a source language processor. + * + * THE SOFTWARE IS 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, TITLE AND NON-INFRINGEMENT. IN NO EVENT + * SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE + * FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ diff --git a/third_party/threads/threads.h b/third_party/threads/threads.h new file mode 100644 index 0000000..4e7dba2 --- /dev/null +++ b/third_party/threads/threads.h @@ -0,0 +1,180 @@ +/* + * C11 <threads.h> emulation library + * + * (C) Copyright yohhoy 2012. + * Distributed under the Boost Software License, Version 1.0. + * + * Permission is hereby granted, free of charge, to any person or organization + * obtaining a copy of the software and accompanying documentation covered by + * this license (the "Software") to use, reproduce, display, distribute, + * execute, and transmit the Software, and to prepare [[derivative work]]s of the + * Software, and to permit third-parties to whom the Software is furnished to + * do so, all subject to the following: + * + * The copyright notices in the Software and this entire statement, including + * the above license grant, this restriction and the following disclaimer, + * must be included in all copies of the Software, in whole or in part, and + * all derivative works of the Software, unless such copies or derivative + * works are solely in the form of machine-executable object code generated by + * a source language processor. + * + * THE SOFTWARE IS 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, TITLE AND NON-INFRINGEMENT. IN NO EVENT + * SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE + * FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ +#ifndef EMULATED_THREADS_H_INCLUDED_ +#define EMULATED_THREADS_H_INCLUDED_ + +#include <time.h> + +#ifndef TIME_UTC +#define TIME_UTC 1 +#endif + +#if defined(_WIN32) +#include <windows.h> + +// check configuration +#if defined(EMULATED_THREADS_USE_NATIVE_CALL_ONCE) && (_WIN32_WINNT < 0x0600) +#error EMULATED_THREADS_USE_NATIVE_CALL_ONCE requires _WIN32_WINNT>=0x0600 +#endif + +#if defined(EMULATED_THREADS_USE_NATIVE_CV) && (_WIN32_WINNT < 0x0600) +#error EMULATED_THREADS_USE_NATIVE_CV requires _WIN32_WINNT>=0x0600 +#endif + + +/*---------------------------- macros ----------------------------*/ +#ifdef EMULATED_THREADS_USE_NATIVE_CALL_ONCE +#define ONCE_FLAG_INIT INIT_ONCE_STATIC_INIT +#else +#define ONCE_FLAG_INIT {0} +#endif +#define TSS_DTOR_ITERATIONS 1 + +// FIXME: temporary non-standard hack to ease transition +#define _MTX_INITIALIZER_NP {{(PCRITICAL_SECTION_DEBUG)-1, -1, 0, 0, 0, 0}} + +/*---------------------------- types ----------------------------*/ +typedef struct cnd_t { +#ifdef EMULATED_THREADS_USE_NATIVE_CV + CONDITION_VARIABLE condvar; +#else + int blocked; + int gone; + int to_unblock; + HANDLE sem_queue; + HANDLE sem_gate; + CRITICAL_SECTION monitor; +#endif +} cnd_t; + +typedef HANDLE thrd_t; + +typedef DWORD tss_t; + +typedef struct mtx_t { + CRITICAL_SECTION cs; +} mtx_t; + +#ifdef EMULATED_THREADS_USE_NATIVE_CALL_ONCE +typedef INIT_ONCE once_flag; +#else +typedef struct once_flag_t { + volatile LONG status; +} once_flag; +#endif + +#elif defined(__unix__) || defined(__unix) +#include <pthread.h> + +/*---------------------------- macros ----------------------------*/ +#define ONCE_FLAG_INIT PTHREAD_ONCE_INIT +#ifdef INIT_ONCE_STATIC_INIT +#define TSS_DTOR_ITERATIONS PTHREAD_DESTRUCTOR_ITERATIONS +#else +#define TSS_DTOR_ITERATIONS 1 // assume TSS dtor MAY be called at least once. +#endif + +// FIXME: temporary non-standard hack to ease transition +#define _MTX_INITIALIZER_NP PTHREAD_MUTEX_INITIALIZER + +/*---------------------------- types ----------------------------*/ +typedef pthread_cond_t cnd_t; +typedef pthread_t thrd_t; +typedef pthread_key_t tss_t; +typedef pthread_mutex_t mtx_t; +typedef pthread_once_t once_flag; + +#else +#error Not supported on this platform. +#endif + + +/*---------------------------- types ----------------------------*/ +typedef void (*tss_dtor_t)(void*); +typedef int (*thrd_start_t)(void*); + +struct xtime { + time_t sec; + long nsec; +}; +typedef struct xtime xtime; + + +/*-------------------- enumeration constants --------------------*/ +enum { + mtx_plain = 0, + mtx_try = 1, + mtx_timed = 2, + mtx_recursive = 4 +}; + +enum { + thrd_success = 0, // succeeded + thrd_timeout, // timeout + thrd_error, // failed + thrd_busy, // resource busy + thrd_nomem // out of memory +}; + + +/*-------------------------- functions --------------------------*/ +void call_once(once_flag *flag, void (*func)(void)); + +int cnd_broadcast(cnd_t *cond); +void cnd_destroy(cnd_t *cond); +int cnd_init(cnd_t *cond); +int cnd_signal(cnd_t *cond); +int cnd_timedwait(cnd_t *cond, mtx_t *mtx, const xtime *xt); +int cnd_wait(cnd_t *cond, mtx_t *mtx); + +void mtx_destroy(mtx_t *mtx); +int mtx_init(mtx_t *mtx, int type); +int mtx_lock(mtx_t *mtx); +int mtx_timedlock(mtx_t *mtx, const xtime *xt); +int mtx_trylock(mtx_t *mtx); +int mtx_unlock(mtx_t *mtx); + +int thrd_create(thrd_t *thr, thrd_start_t func, void *arg); +thrd_t thrd_current(void); +int thrd_detach(thrd_t thr); +int thrd_equal(thrd_t thr0, thrd_t thr1); +void thrd_exit(int res); +int thrd_join(thrd_t thr, int *res); +void thrd_sleep(const xtime *xt); +void thrd_yield(void); + +int tss_create(tss_t *key, tss_dtor_t dtor); +void tss_delete(tss_t key); +void *tss_get(tss_t key); +int tss_set(tss_t key, void *val); + +int xtime_get(xtime *xt, int base); + + +#endif /* EMULATED_THREADS_H_INCLUDED_ */ diff --git a/third_party/threads/threads_posix.c b/third_party/threads/threads_posix.c new file mode 100644 index 0000000..5835e43 --- /dev/null +++ b/third_party/threads/threads_posix.c @@ -0,0 +1,325 @@ +/* + * C11 <threads.h> emulation library + * + * (C) Copyright yohhoy 2012. + * Distributed under the Boost Software License, Version 1.0. + * + * Permission is hereby granted, free of charge, to any person or organization + * obtaining a copy of the software and accompanying documentation covered by + * this license (the "Software") to use, reproduce, display, distribute, + * execute, and transmit the Software, and to prepare [[derivative work]]s of the + * Software, and to permit third-parties to whom the Software is furnished to + * do so, all subject to the following: + * + * The copyright notices in the Software and this entire statement, including + * the above license grant, this restriction and the following disclaimer, + * must be included in all copies of the Software, in whole or in part, and + * all derivative works of the Software, unless such copies or derivative + * works are solely in the form of machine-executable object code generated by + * a source language processor. + * + * THE SOFTWARE IS 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, TITLE AND NON-INFRINGEMENT. IN NO EVENT + * SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE + * FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ +#include <stdlib.h> +#ifndef assert +#include <assert.h> +#endif +#include <limits.h> +#include <errno.h> +#include <unistd.h> +#include <sched.h> + +/* +Configuration macro: + + EMULATED_THREADS_USE_NATIVE_TIMEDLOCK + Use pthread_mutex_timedlock() for `mtx_timedlock()' + Otherwise use mtx_trylock() + *busy loop* emulation. +*/ +#if !defined(__CYGWIN__) && !defined(ANDROID) +#define EMULATED_THREADS_USE_NATIVE_TIMEDLOCK +#endif + +#include "threads.h" + + +/* +Implementation limits: + - Conditionally emulation for "mutex with timeout" + (see EMULATED_THREADS_USE_NATIVE_TIMEDLOCK macro) +*/ +int mtx_trylock(mtx_t *mtx); // forward decl. +void thrd_yield(void); // forward decl. + +struct impl_thrd_param { + thrd_start_t func; + void *arg; +}; + +static void *impl_thrd_routine(void *p) +{ + struct impl_thrd_param pack = *((struct impl_thrd_param *)p); + free(p); + return (void*)(intptr_t)pack.func(pack.arg); +} + + +/*--------------- 7.25.2 Initialization functions ---------------*/ +// 7.25.2.1 +void call_once(once_flag *flag, void (*func)(void)) +{ + pthread_once(flag, func); +} + + +/*------------- 7.25.3 Condition variable functions -------------*/ +// 7.25.3.1 +int cnd_broadcast(cnd_t *cond) +{ + if (!cond) return thrd_error; + pthread_cond_broadcast(cond); + return thrd_success; +} + +// 7.25.3.2 +void cnd_destroy(cnd_t *cond) +{ + assert(cond); + pthread_cond_destroy(cond); +} + +// 7.25.3.3 +int cnd_init(cnd_t *cond) +{ + if (!cond) return thrd_error; + pthread_cond_init(cond, NULL); + return thrd_success; +} + +// 7.25.3.4 +int cnd_signal(cnd_t *cond) +{ + if (!cond) return thrd_error; + pthread_cond_signal(cond); + return thrd_success; +} + +// 7.25.3.5 +int cnd_timedwait(cnd_t *cond, mtx_t *mtx, const xtime *xt) +{ + struct timespec abs_time; + int rt; + if (!cond || !mtx || !xt) return thrd_error; + rt = pthread_cond_timedwait(cond, mtx, &abs_time); + if (rt == ETIMEDOUT) + return thrd_busy; + return (rt == 0) ? thrd_success : thrd_error; +} + +// 7.25.3.6 +int cnd_wait(cnd_t *cond, mtx_t *mtx) +{ + if (!cond || !mtx) return thrd_error; + pthread_cond_wait(cond, mtx); + return thrd_success; +} + + +/*-------------------- 7.25.4 Mutex functions --------------------*/ +// 7.25.4.1 +void mtx_destroy(mtx_t *mtx) +{ + assert(mtx); + pthread_mutex_destroy(mtx); +} + +// 7.25.4.2 +int mtx_init(mtx_t *mtx, int type) +{ + pthread_mutexattr_t attr; + if (!mtx) return thrd_error; + if (type != mtx_plain && type != mtx_timed && type != mtx_try + && type != (mtx_plain|mtx_recursive) + && type != (mtx_timed|mtx_recursive) + && type != (mtx_try|mtx_recursive)) + return thrd_error; + pthread_mutexattr_init(&attr); + if ((type & mtx_recursive) != 0) { +#if defined(__linux__) || defined(__linux) + pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE_NP); +#else + pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE); +#endif + } + pthread_mutex_init(mtx, &attr); + pthread_mutexattr_destroy(&attr); + return thrd_success; +} + +// 7.25.4.3 +int mtx_lock(mtx_t *mtx) +{ + if (!mtx) return thrd_error; + pthread_mutex_lock(mtx); + return thrd_success; +} + +// 7.25.4.4 +int mtx_timedlock(mtx_t *mtx, const xtime *xt) +{ + if (!mtx || !xt) return thrd_error; + { +#ifdef EMULATED_THREADS_USE_NATIVE_TIMEDLOCK + struct timespec ts; + int rt; + ts.tv_sec = xt->sec; + ts.tv_nsec = xt->nsec; + rt = pthread_mutex_timedlock(mtx, &ts); + if (rt == 0) + return thrd_success; + return (rt == ETIMEDOUT) ? thrd_busy : thrd_error; +#else + time_t expire = time(NULL); + expire += xt->sec; + while (mtx_trylock(mtx) != thrd_success) { + time_t now = time(NULL); + if (expire < now) + return thrd_busy; + // busy loop! + thrd_yield(); + } + return thrd_success; +#endif + } +} + +// 7.25.4.5 +int mtx_trylock(mtx_t *mtx) +{ + if (!mtx) return thrd_error; + return (pthread_mutex_trylock(mtx) == 0) ? thrd_success : thrd_busy; +} + +// 7.25.4.6 +int mtx_unlock(mtx_t *mtx) +{ + if (!mtx) return thrd_error; + pthread_mutex_unlock(mtx); + return thrd_success; +} + + +/*------------------- 7.25.5 Thread functions -------------------*/ +// 7.25.5.1 +int thrd_create(thrd_t *thr, thrd_start_t func, void *arg) +{ + struct impl_thrd_param *pack; + if (!thr) return thrd_error; + pack = malloc(sizeof(struct impl_thrd_param)); + if (!pack) return thrd_nomem; + pack->func = func; + pack->arg = arg; + if (pthread_create(thr, NULL, impl_thrd_routine, pack) != 0) { + free(pack); + return thrd_error; + } + return thrd_success; +} + +// 7.25.5.2 +thrd_t thrd_current(void) +{ + return pthread_self(); +} + +// 7.25.5.3 +int thrd_detach(thrd_t thr) +{ + return (pthread_detach(thr) == 0) ? thrd_success : thrd_error; +} + +// 7.25.5.4 +int thrd_equal(thrd_t thr0, thrd_t thr1) +{ + return pthread_equal(thr0, thr1); +} + +// 7.25.5.5 +void thrd_exit(int res) +{ + pthread_exit((void*)(intptr_t)res); +} + +// 7.25.5.6 +int thrd_join(thrd_t thr, int *res) +{ + void *code; + if (pthread_join(thr, &code) != 0) + return thrd_error; + if (res) + *res = (int)(intptr_t)code; + return thrd_success; +} + +// 7.25.5.7 +void thrd_sleep(const xtime *xt) +{ + struct timespec req; + assert(xt); + req.tv_sec = xt->sec; + req.tv_nsec = xt->nsec; + nanosleep(&req, NULL); +} + +// 7.25.5.8 +void thrd_yield(void) +{ + sched_yield(); +} + + +/*----------- 7.25.6 Thread-specific storage functions -----------*/ +// 7.25.6.1 +int tss_create(tss_t *key, tss_dtor_t dtor) +{ + if (!key) return thrd_error; + return (pthread_key_create(key, dtor) == 0) ? thrd_success : thrd_error; +} + +// 7.25.6.2 +void tss_delete(tss_t key) +{ + pthread_key_delete(key); +} + +// 7.25.6.3 +void *tss_get(tss_t key) +{ + return pthread_getspecific(key); +} + +// 7.25.6.4 +int tss_set(tss_t key, void *val) +{ + return (pthread_setspecific(key, val) == 0) ? thrd_success : thrd_error; +} + + +/*-------------------- 7.25.7 Time functions --------------------*/ +// 7.25.6.1 +int xtime_get(xtime *xt, int base) +{ + if (!xt) return 0; + if (base == TIME_UTC) { + xt->sec = time(NULL); + xt->nsec = 0; + return base; + } + return 0; +} diff --git a/third_party/threads/threads_win32.c b/third_party/threads/threads_win32.c new file mode 100644 index 0000000..76c2855 --- /dev/null +++ b/third_party/threads/threads_win32.c @@ -0,0 +1,527 @@ +/* + * C11 <threads.h> emulation library + * + * (C) Copyright yohhoy 2012. + * Distributed under the Boost Software License, Version 1.0. + * + * Permission is hereby granted, free of charge, to any person or organization + * obtaining a copy of the software and accompanying documentation covered by + * this license (the "Software") to use, reproduce, display, distribute, + * execute, and transmit the Software, and to prepare [[derivative work]]s of the + * Software, and to permit third-parties to whom the Software is furnished to + * do so, all subject to the following: + * + * The copyright notices in the Software and this entire statement, including + * the above license grant, this restriction and the following disclaimer, + * must be included in all copies of the Software, in whole or in part, and + * all derivative works of the Software, unless such copies or derivative + * works are solely in the form of machine-executable object code generated by + * a source language processor. + * + * THE SOFTWARE IS 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, TITLE AND NON-INFRINGEMENT. IN NO EVENT + * SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE + * FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ +#ifndef assert +#include <assert.h> +#endif +#include <limits.h> +#include <errno.h> +#include <process.h> // MSVCRT + +/* +Configuration macro: + + EMULATED_THREADS_USE_NATIVE_CALL_ONCE + Use native WindowsAPI one-time initialization function. + (requires WinVista or later) + Otherwise emulate by mtx_trylock() + *busy loop* for WinXP. + + EMULATED_THREADS_USE_NATIVE_CV + Use native WindowsAPI condition variable object. + (requires WinVista or later) + Otherwise use emulated implementation for WinXP. + + EMULATED_THREADS_TSS_DTOR_SLOTNUM + Max registerable TSS dtor number. +*/ + +// XXX: Retain XP compatability +#if 0 +#if _WIN32_WINNT >= 0x0600 +// Prefer native WindowsAPI on newer environment. +#define EMULATED_THREADS_USE_NATIVE_CALL_ONCE +#define EMULATED_THREADS_USE_NATIVE_CV +#endif +#endif +#define EMULATED_THREADS_TSS_DTOR_SLOTNUM 64 // see TLS_MINIMUM_AVAILABLE + +#include "threads.h" + + +/* +Implementation limits: + - Conditionally emulation for "Initialization functions" + (see EMULATED_THREADS_USE_NATIVE_CALL_ONCE macro) + - Emulated `mtx_timelock()' with mtx_trylock() + *busy loop* +*/ +static void impl_tss_dtor_invoke(void); // forward decl. + +struct impl_thrd_param { + thrd_start_t func; + void *arg; +}; + +static unsigned __stdcall impl_thrd_routine(void *p) +{ + struct impl_thrd_param pack; + int code; + memcpy(&pack, p, sizeof(struct impl_thrd_param)); + free(p); + code = pack.func(pack.arg); + impl_tss_dtor_invoke(); + return (unsigned)code; +} + +static DWORD impl_xtime2msec(const xtime *xt) +{ + return (DWORD)((xt->sec * 1000U) + (xt->nsec / 1000000L)); +} + +#ifdef EMULATED_THREADS_USE_NATIVE_CALL_ONCE +struct impl_call_once_param { void (*func)(void); }; +static BOOL CALLBACK impl_call_once_callback(PINIT_ONCE InitOnce, PVOID Parameter, PVOID *Context) +{ + struct impl_call_once_param *param = (struct impl_call_once_param*)Parameter; + (param->func)(); + ((void)InitOnce); ((void)Context); // suppress warning + return TRUE; +} +#endif // ifdef EMULATED_THREADS_USE_NATIVE_CALL_ONCE + +#ifndef EMULATED_THREADS_USE_NATIVE_CV +/* +Note: + The implementation of condition variable is ported from Boost.Interprocess + See http://www.boost.org/boost/interprocess/sync/windows/condition.hpp +*/ +static void impl_cond_do_signal(cnd_t *cond, int broadcast) +{ + int nsignal = 0; + + EnterCriticalSection(&cond->monitor); + if (cond->to_unblock != 0) { + if (cond->blocked == 0) { + LeaveCriticalSection(&cond->monitor); + return; + } + if (broadcast) { + cond->to_unblock += nsignal = cond->blocked; + cond->blocked = 0; + } else { + nsignal = 1; + cond->to_unblock++; + cond->blocked--; + } + } else if (cond->blocked > cond->gone) { + WaitForSingleObject(cond->sem_gate, INFINITE); + if (cond->gone != 0) { + cond->blocked -= cond->gone; + cond->gone = 0; + } + if (broadcast) { + nsignal = cond->to_unblock = cond->blocked; + cond->blocked = 0; + } else { + nsignal = cond->to_unblock = 1; + cond->blocked--; + } + } + LeaveCriticalSection(&cond->monitor); + + if (0 < nsignal) + ReleaseSemaphore(cond->sem_queue, nsignal, NULL); +} + +static int impl_cond_do_wait(cnd_t *cond, mtx_t *mtx, const xtime *xt) +{ + int nleft = 0; + int ngone = 0; + int timeout = 0; + DWORD w; + + WaitForSingleObject(cond->sem_gate, INFINITE); + cond->blocked++; + ReleaseSemaphore(cond->sem_gate, 1, NULL); + + mtx_unlock(mtx); + + w = WaitForSingleObject(cond->sem_queue, xt ? impl_xtime2msec(xt) : INFINITE); + timeout = (w == WAIT_TIMEOUT); + + EnterCriticalSection(&cond->monitor); + if ((nleft = cond->to_unblock) != 0) { + if (timeout) { + if (cond->blocked != 0) { + cond->blocked--; + } else { + cond->gone++; + } + } + if (--cond->to_unblock == 0) { + if (cond->blocked != 0) { + ReleaseSemaphore(cond->sem_gate, 1, NULL); + nleft = 0; + } + else if ((ngone = cond->gone) != 0) { + cond->gone = 0; + } + } + } else if (++cond->gone == INT_MAX/2) { + WaitForSingleObject(cond->sem_gate, INFINITE); + cond->blocked -= cond->gone; + ReleaseSemaphore(cond->sem_gate, 1, NULL); + cond->gone = 0; + } + LeaveCriticalSection(&cond->monitor); + + if (nleft == 1) { + while (ngone--) + WaitForSingleObject(cond->sem_queue, INFINITE); + ReleaseSemaphore(cond->sem_gate, 1, NULL); + } + + mtx_lock(mtx); + return timeout ? thrd_busy : thrd_success; +} +#endif // ifndef EMULATED_THREADS_USE_NATIVE_CV + +static struct impl_tss_dtor_entry { + tss_t key; + tss_dtor_t dtor; +} impl_tss_dtor_tbl[EMULATED_THREADS_TSS_DTOR_SLOTNUM]; + +static int impl_tss_dtor_register(tss_t key, tss_dtor_t dtor) +{ + int i; + for (i = 0; i < EMULATED_THREADS_TSS_DTOR_SLOTNUM; i++) { + if (!impl_tss_dtor_tbl[i].dtor) + break; + } + if (i == EMULATED_THREADS_TSS_DTOR_SLOTNUM) + return 1; + impl_tss_dtor_tbl[i].key = key; + impl_tss_dtor_tbl[i].dtor = dtor; + return 0; +} + +static void impl_tss_dtor_invoke() +{ + int i; + for (i = 0; i < EMULATED_THREADS_TSS_DTOR_SLOTNUM; i++) { + if (impl_tss_dtor_tbl[i].dtor) { + void* val = tss_get(impl_tss_dtor_tbl[i].key); + if (val) + (impl_tss_dtor_tbl[i].dtor)(val); + } + } +} + + +/*--------------- 7.25.2 Initialization functions ---------------*/ +// 7.25.2.1 +void call_once(once_flag *flag, void (*func)(void)) +{ + assert(flag && func); +#ifdef EMULATED_THREADS_USE_NATIVE_CALL_ONCE + { + struct impl_call_once_param param; + param.func = func; + InitOnceExecuteOnce(flag, impl_call_once_callback, (PVOID)¶m, NULL); + } +#else + if (InterlockedCompareExchange(&flag->status, 1, 0) == 0) { + (func)(); + InterlockedExchange(&flag->status, 2); + } else { + while (flag->status == 1) { + // busy loop! + thrd_yield(); + } + } +#endif +} + + +/*------------- 7.25.3 Condition variable functions -------------*/ +// 7.25.3.1 +int cnd_broadcast(cnd_t *cond) +{ + if (!cond) return thrd_error; +#ifdef EMULATED_THREADS_USE_NATIVE_CV + WakeAllConditionVariable(&cond->condvar); +#else + impl_cond_do_signal(cond, 1); +#endif + return thrd_success; +} + +// 7.25.3.2 +void cnd_destroy(cnd_t *cond) +{ + assert(cond); +#ifdef EMULATED_THREADS_USE_NATIVE_CV + // do nothing +#else + CloseHandle(cond->sem_queue); + CloseHandle(cond->sem_gate); + DeleteCriticalSection(&cond->monitor); +#endif +} + +// 7.25.3.3 +int cnd_init(cnd_t *cond) +{ + if (!cond) return thrd_error; +#ifdef EMULATED_THREADS_USE_NATIVE_CV + InitializeConditionVariable(&cond->condvar); +#else + cond->blocked = 0; + cond->gone = 0; + cond->to_unblock = 0; + cond->sem_queue = CreateSemaphore(NULL, 0, LONG_MAX, NULL); + cond->sem_gate = CreateSemaphore(NULL, 1, 1, NULL); + InitializeCriticalSection(&cond->monitor); +#endif + return thrd_success; +} + +// 7.25.3.4 +int cnd_signal(cnd_t *cond) +{ + if (!cond) return thrd_error; +#ifdef EMULATED_THREADS_USE_NATIVE_CV + WakeConditionVariable(&cond->condvar); +#else + impl_cond_do_signal(cond, 0); +#endif + return thrd_success; +} + +// 7.25.3.5 +int cnd_timedwait(cnd_t *cond, mtx_t *mtx, const xtime *xt) +{ + if (!cond || !mtx || !xt) return thrd_error; +#ifdef EMULATED_THREADS_USE_NATIVE_CV + if (SleepConditionVariableCS(&cond->condvar, &mtx->cs, impl_xtime2msec(xt))) + return thrd_success; + return (GetLastError() == ERROR_TIMEOUT) ? thrd_busy : thrd_error; +#else + return impl_cond_do_wait(cond, mtx, xt); +#endif +} + +// 7.25.3.6 +int cnd_wait(cnd_t *cond, mtx_t *mtx) +{ + if (!cond || !mtx) return thrd_error; +#ifdef EMULATED_THREADS_USE_NATIVE_CV + SleepConditionVariableCS(&cond->condvar, &mtx->cs, INFINITE); +#else + impl_cond_do_wait(cond, mtx, NULL); +#endif + return thrd_success; +} + + +/*-------------------- 7.25.4 Mutex functions --------------------*/ +// 7.25.4.1 +void mtx_destroy(mtx_t *mtx) +{ + assert(mtx); + DeleteCriticalSection(&mtx->cs); +} + +// 7.25.4.2 +int mtx_init(mtx_t *mtx, int type) +{ + if (!mtx) return thrd_error; + if (type != mtx_plain && type != mtx_timed && type != mtx_try + && type != (mtx_plain|mtx_recursive) + && type != (mtx_timed|mtx_recursive) + && type != (mtx_try|mtx_recursive)) + return thrd_error; + InitializeCriticalSection(&mtx->cs); + return thrd_success; +} + +// 7.25.4.3 +int mtx_lock(mtx_t *mtx) +{ + if (!mtx) return thrd_error; + EnterCriticalSection(&mtx->cs); + return thrd_success; +} + +// 7.25.4.4 +int mtx_timedlock(mtx_t *mtx, const xtime *xt) +{ + time_t expire, now; + if (!mtx || !xt) return thrd_error; + expire = time(NULL); + expire += xt->sec; + while (mtx_trylock(mtx) != thrd_success) { + now = time(NULL); + if (expire < now) + return thrd_busy; + // busy loop! + thrd_yield(); + } + return thrd_success; +} + +// 7.25.4.5 +int mtx_trylock(mtx_t *mtx) +{ + if (!mtx) return thrd_error; + return TryEnterCriticalSection(&mtx->cs) ? thrd_success : thrd_busy; +} + +// 7.25.4.6 +int mtx_unlock(mtx_t *mtx) +{ + if (!mtx) return thrd_error; + LeaveCriticalSection(&mtx->cs); + return thrd_success; +} + + +/*------------------- 7.25.5 Thread functions -------------------*/ +// 7.25.5.1 +int thrd_create(thrd_t *thr, thrd_start_t func, void *arg) +{ + struct impl_thrd_param *pack; + uintptr_t handle; + if (!thr) return thrd_error; + pack = malloc(sizeof(struct impl_thrd_param)); + if (!pack) return thrd_nomem; + pack->func = func; + pack->arg = arg; + handle = _beginthreadex(NULL, 0, impl_thrd_routine, pack, 0, NULL); + if (handle == 0) { + if (errno == EAGAIN || errno == EACCES) + return thrd_nomem; + return thrd_error; + } + *thr = (thrd_t)handle; + return thrd_success; +} + +// 7.25.5.2 +thrd_t thrd_current(void) +{ + return GetCurrentThread(); +} + +// 7.25.5.3 +int thrd_detach(thrd_t thr) +{ + CloseHandle(thr); + return thrd_success; +} + +// 7.25.5.4 +int thrd_equal(thrd_t thr0, thrd_t thr1) +{ + return (thr0 == thr1); +} + +// 7.25.5.5 +void thrd_exit(int res) +{ + impl_tss_dtor_invoke(); + _endthreadex((unsigned)res); +} + +// 7.25.5.6 +int thrd_join(thrd_t thr, int *res) +{ + DWORD w, code; + w = WaitForSingleObject(thr, INFINITE); + if (w != WAIT_OBJECT_0) + return thrd_error; + if (res) { + if (!GetExitCodeThread(thr, &code)) { + CloseHandle(thr); + return thrd_error; + } + *res = (int)code; + } + CloseHandle(thr); + return thrd_success; +} + +// 7.25.5.7 +void thrd_sleep(const xtime *xt) +{ + assert(xt); + Sleep(impl_xtime2msec(xt)); +} + +// 7.25.5.8 +void thrd_yield(void) +{ + SwitchToThread(); +} + + +/*----------- 7.25.6 Thread-specific storage functions -----------*/ +// 7.25.6.1 +int tss_create(tss_t *key, tss_dtor_t dtor) +{ + if (!key) return thrd_error; + *key = TlsAlloc(); + if (dtor) { + if (impl_tss_dtor_register(*key, dtor)) { + TlsFree(*key); + return thrd_error; + } + } + return (*key != 0xFFFFFFFF) ? thrd_success : thrd_error; +} + +// 7.25.6.2 +void tss_delete(tss_t key) +{ + TlsFree(key); +} + +// 7.25.6.3 +void *tss_get(tss_t key) +{ + return TlsGetValue(key); +} + +// 7.25.6.4 +int tss_set(tss_t key, void *val) +{ + return TlsSetValue(key, val) ? thrd_success : thrd_error; +} + + +/*-------------------- 7.25.7 Time functions --------------------*/ +// 7.25.6.1 +int xtime_get(xtime *xt, int base) +{ + if (!xt) return 0; + if (base == TIME_UTC) { + xt->sec = time(NULL); + xt->nsec = 0; + return base; + } + return 0; +} |