commit 322255170257873e4e1f8ddf0e4541a4fb383f08 Author: Simon Fels Date: Thu May 26 22:38:57 2016 +0200 Initial work diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..33960f8 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,92 @@ +project(anbox C CXX) +cmake_minimum_required(VERSION 2.8.2) + +include(CTest) + +if (NOT CMAKE_BUILD_TYPE) + message(STATUS "No build type selected, default to release") + set(CMAKE_BUILD_TYPE "release") +endif() + +set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${CMAKE_CURRENT_SOURCE_DIR}/cmake) + +set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -pedantic -Wno-variadic-macros -Wextra -fPIC") + +# By default, all symbols are visible in the library. We strip out things we don't +# want at link time, by running a version script (see unity-scopes.map and the +# setting of LINK_FLAGS below). +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fvisibility=default") + +set(C_AND_CXX_WARNINGS "-pedantic -Wall -Wextra") + +# Some additional warnings not included by the general flags set above. +set(EXTRA_C_WARNINGS "-Wcast-align -Wcast-qual -Wformat -Wredundant-decls -Wswitch-default") +set(EXTRA_CXX_WARNINGS "-Wnon-virtual-dtor -Wctor-dtor-privacy -Wold-style-cast") + +set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${C_AND_CXX_WARNINGS}") +set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${EXTRA_C_WARNINGS}") + +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 ${C_AND_CXX_WARNINGS}") +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${EXTRA_C_WARNINGS}") +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${EXTRA_CXX_WARNINGS}") + +# -fno-permissive causes warnings with clang, so we only enable it for gcc +if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-permissive") +endif() + +string(TOLOWER "${CMAKE_BUILD_TYPE}" cmake_build_type_lower) + +if ("${cmake_build_type_lower}" STREQUAL "release" OR "${cmake_build_type_lower}" STREQUAL "relwithdebinfo") + option(Werror "Treat warnings as errors" ON) +else() + option(Werror "Treat warnings as errors" OFF) +endif() + +if (${Werror}) + message(STATUS "Treat warnings as errors") + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Werror") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Werror") + if ("${cmake_build_type_lower}" STREQUAL "release" OR "${cmake_build_type_lower}" STREQUAL "relwithdebinfo") + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wno-error=deprecated-declarations") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-error=deprecated-declarations") + endif() +endif() + +set(CMAKE_INCLUDE_CURRENT_DIR ON) + +find_package(Boost COMPONENTS filesystem log serialization system thread program_options) +find_package(PkgConfig) +find_package(Threads) +find_package(EGL REQUIRED) +find_package(GLESv2 REQUIRED) + +pkg_check_modules(MIRCLIENT REQUIRED mirclient) + +##################################################################### +# Enable code coverage calculation with gcov/gcovr/lcov +# Usage: +# * Switch build type to coverage (use ccmake or cmake-gui) +# * Invoke make, make test, make coverage +# * Find html report in subdir coveragereport +# * Find xml report feasible for jenkins in coverage.xml +##################################################################### +IF(CMAKE_BUILD_TYPE MATCHES [cC][oO][vV][eE][rR][aA][gG][eE]) + SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -ftest-coverage -fprofile-arcs" ) + SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -ftest-coverage -fprofile-arcs" ) + SET(CMAKE_MODULE_LINKER_FLAGS "${CMAKE_MODULE_LINKER_FLAGS} -ftest-coverage -fprofile-arcs" ) + SET(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -ftest-coverage -fprofile-arcs" ) +ENDIF(CMAKE_BUILD_TYPE MATCHES [cC][oO][vV][eE][rR][aA][gG][eE]) + +# Build with system gmock and embedded gtest +set (GMOCK_INCLUDE_DIR "/usr/include/gmock/include" CACHE PATH "gmock source include directory") +set (GMOCK_SOURCE_DIR "/usr/src/gmock" CACHE PATH "gmock source directory") +set (GTEST_INCLUDE_DIR "${GMOCK_SOURCE_DIR}/gtest/include" CACHE PATH "gtest source include directory") + +add_subdirectory(${GMOCK_SOURCE_DIR} "${CMAKE_CURRENT_BINARY_DIR}/gmock") + +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++14 -fPIC") + +add_subdirectory(external) +add_subdirectory(src) +add_subdirectory(tests) diff --git a/cmake/FindEGL.cmake b/cmake/FindEGL.cmake new file mode 100644 index 0000000..2ef95ff --- /dev/null +++ b/cmake/FindEGL.cmake @@ -0,0 +1,25 @@ +# - Try to find EGL +# Once done this will define +# EGL_FOUND - System has EGL +# EGL_INCLUDE_DIRS - The EGL include directories +# EGL_LIBRARIES - The libraries needed to use EGL + +find_package(PkgConfig) +pkg_check_modules(PC_EGL QUIET egl) + +find_path(EGL_INCLUDE_DIR EGL/egl.h + HINTS ${PC_EGL_INCLUDEDIR} ${PC_EGL_INCLUDE_DIRS}) + +find_library(EGL_LIBRARY EGL + HINTS ${PC_EGL_LIBDIR} ${PC_EGL_LIBRARY_DIRS}) + +set(EGL_LIBRARIES ${EGL_LIBRARY}) +set(EGL_INCLUDE_DIRS ${EGL_INCLUDE_DIR}) + +include(FindPackageHandleStandardArgs) +# handle the QUIETLY and REQUIRED arguments and set EGL_FOUND to TRUE +# if all listed variables are TRUE +find_package_handle_standard_args(EGL DEFAULT_MSG + EGL_LIBRARY EGL_INCLUDE_DIR) + +mark_as_advanced(EGL_INCLUDE_DIR EGL_LIBRARY) diff --git a/cmake/FindGLESv2.cmake b/cmake/FindGLESv2.cmake new file mode 100644 index 0000000..4cf010b --- /dev/null +++ b/cmake/FindGLESv2.cmake @@ -0,0 +1,25 @@ +# - Try to find GLESv2 +# Once done this will define +# GLESv2_FOUND - System has GLESv2 +# GLESv2_INCLUDE_DIRS - The GLESv2 include directories +# GLESv2_LIBRARIES - The libraries needed to use GLESv2 + +find_package(PkgConfig) +pkg_check_modules(PC_GLESv2 QUIET glesv2) + +find_path(GLESv2_INCLUDE_DIR GLES2/gl2.h + HINTS ${PC_GLESv2_INCLUDEDIR} ${PC_GLESv2_INCLUDE_DIRS}) + +find_library(GLESv2_LIBRARY GLESv2 + HINTS ${PC_GLESv2_LIBDIR} ${PC_GLESv2_LIBRARY_DIRS}) + +set(GLESv2_LIBRARIES ${GLESv2_LIBRARY}) +set(GLESv2_INCLUDE_DIRS ${GLESv2_INCLUDE_DIR}) + +include(FindPackageHandleStandardArgs) +# handle the QUIETLY and REQUIRED arguments and set GLESv2_FOUND to TRUE +# if all listed variables are TRUE +find_package_handle_standard_args(GLESv2 DEFAULT_MSG + GLESv2_LIBRARY GLESv2_INCLUDE_DIR) + +mark_as_advanced(GLESv2_INCLUDE_DIR GLESv2_LIBRARY) diff --git a/cmake/LinuxCrossCompile.cmake b/cmake/LinuxCrossCompile.cmake new file mode 100644 index 0000000..d279424 --- /dev/null +++ b/cmake/LinuxCrossCompile.cmake @@ -0,0 +1,43 @@ +set(CMAKE_SYSTEM_NAME Linux) +set(CMAKE_SYSTEM_VERSION 1) + +set(AC_NDK_PATH $ENV{AC_NDK_PATH} CACHE STRING "path of mir android bundle") + +if (NOT DEFINED AC_TARGET_MACHINE) + set(AC_TARGET_MACHINE $ENV{AC_TARGET_MACHINE} CACHE STRING "target machine") +endif() +if (NOT DEFINED AC_GCC_VARIANT) + set(AC_GCC_VARIANT $ENV{AC_GCC_VARIANT} CACHE STRING "gcc variant required") +endif() + +set(CMAKE_C_COMPILER /usr/bin/${AC_TARGET_MACHINE}-gcc${AC_GCC_VARIANT}) +set(CMAKE_CXX_COMPILER /usr/bin/${AC_TARGET_MACHINE}-g++${AC_GCC_VARIANT}) + +# where to look to find dependencies in the target environment +set(CMAKE_FIND_ROOT_PATH "${AC_NDK_PATH}") + +#treat the chroot's includes as system includes +include_directories(SYSTEM "${AC_NDK_PATH}/usr/include" "${AC_NDK_PATH}/usr/include/${AC_TARGET_MACHINE}") +list(APPEND CMAKE_SYSTEM_INCLUDE_PATH "${AC_NDK_PATH}/usr/include" "${AC_NDK_PATH}/usr/include/${AC_TARGET_MACHINE}" ) + +# Add the chroot libraries as system libraries +list(APPEND CMAKE_SYSTEM_LIBRARY_PATH + "${AC_NDK_PATH}/lib" + "${AC_NDK_PATH}/lib/${AC_TARGET_MACHINE}" + "${AC_NDK_PATH}/usr/lib" + "${AC_NDK_PATH}/usr/lib/${AC_TARGET_MACHINE}" +) + +set(CMAKE_INSTALL_RPATH_USE_LINK_PATH FALSE) +set(CMAKE_BUILD_WITH_INSTALL_RPATH TRUE) +set(CMAKE_EXECUTABLE_RUNTIME_C_FLAG "-Wl,-rpath-link,") +set(CMAKE_EXECUTABLE_RUNTIME_CXX_FLAG "-Wl,-rpath-link,") +set(CMAKE_INSTALL_RPATH "${AC_NDK_PATH}/lib:${AC_NDK_PATH}/lib/${AC_TARGET_MACHINE}:${AC_NDK_PATH}/usr/lib:${AC_NDK_PATH}/usr/lib/${AC_TARGET_MACHINE}") + +set(ENV{PKG_CONFIG_PATH} "${AC_NDK_PATH}/usr/lib/pkgconfig:${AC_NDK_PATH}/usr/lib/${AC_TARGET_MACHINE}/pkgconfig") +set(ENV{PKG_CONFIG_SYSROOT_DIR} "${AC_NDK_PATH}") + +#use only the cross compile system +set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) +set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) +set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) diff --git a/cross-compile-chroot.sh b/cross-compile-chroot.sh new file mode 100755 index 0000000..2dc8790 --- /dev/null +++ b/cross-compile-chroot.sh @@ -0,0 +1,162 @@ +#!/bin/bash +# build script to compile anbox for armhf devices +# +set -e + +usage() { + echo "usage: $(basename $0) [-a ] [-c] [-h] [-d ] [-u]" + echo " -a Specify target architecture (armhf/arm64/powerpc/ppc64el/amd64/i386/host)" + echo " -c Clean before building" + echo " -d Select the distribution to build for (vivid/wily/xenial)" + echo " -h This message" + echo " -u Update partial chroot directory" +} + +clean_build_dir() { + rm -rf ${1} + mkdir ${1} +} + +# Default to a dist-agnostic directory name so as to not break Jenkins right now +BUILD_DIR=build-android-arm +# NUM_JOBS=$(( $(grep -c ^processor /proc/cpuinfo) + 1 )) +NUM_JOBS=5 +_do_update_chroot=0 + +# Default to vivid as we don't seem to have any working wily devices right now +dist=vivid +clean=0 +update_build_dir=0 + +target_arch=armhf + +while getopts "a:cd:hu" OPTNAME +do + case $OPTNAME in + a ) + target_arch=${OPTARG} + update_build_dir=1 + ;; + c ) + clean=1 + ;; + d ) + dist=${OPTARG} + update_build_dir=1 + ;; + u ) + _do_update_chroot=1 + ;; + h ) + usage + exit 0 + ;; + : ) + echo "Parameter -${OPTARG} needs an argument" + usage + exit 1; + ;; + * ) + echo "invalid option specified" + usage + exit 1 + ;; + esac +done + +shift $((${OPTIND}-1)) + +if [ "${target_arch}" = "host" ]; then + target_arch=`dpkg-architecture -qDEB_HOST_ARCH` +fi + +if [ ${clean} -ne 0 ]; then + clean_build_dir ${BUILD_DIR} +fi + +if [ ${update_build_dir} -eq 1 ]; then + BUILD_DIR=build-${target_arch}-${dist} +fi + +if [ "${AC_NDK_PATH}" = "" ]; then + export AC_NDK_PATH=~/.cache/anbox-${target_arch}-chroot-${dist} +fi + +if [ ! -d ${AC_NDK_PATH} ]; then + echo "no partial chroot dir detected. attempting to create one" + _do_update_chroot=1 +fi + +if [ ! -d ${BUILD_DIR} ]; then + mkdir ${BUILD_DIR} +fi + +echo "Building for distro: $dist" +echo "Using AC_NDK_PATH: ${AC_NDK_PATH}" + +additional_repositories="-r http://ppa.launchpad.net/ci-train-ppa-service/stable-phone-overlay/ubuntu" + +gcc_variant= +if [ "${dist}" = "vivid" ]; then + gcc_variant=-4.9 +fi + +case ${target_arch} in + armhf ) + target_machine=arm-linux-gnueabihf + ;; + amd64 ) + target_machine=x86_64-linux-gnu + ;; + i386 ) + target_machine=i386-linux-gnu + ;; + arm64 ) + target_machine=aarch64-linux-gnu + ;; + ppc64el ) + target_machine=powerpc64le-linux-gnu + ;; + powerpc ) + target_machine=powerpc-linux-gnu + ;; + * ) + # A good guess (assuming you have dpkg-architecture) + target_machine=`dpkg-architecture -A${target_arch} -qDEB_HOST_MULTIARCH` || { + echo "Unknown architecture ${target_arch}" + usage + exit 1 + } + ;; +esac + +echo "Target architecture: ${target_arch}" +echo "Target machine: ${target_machine}" + +if [ ${_do_update_chroot} -eq 1 ] ; then + pushd scripts > /dev/null + ./setup-partial-armhf-chroot.sh -d ${dist} -a ${target_arch} ${additional_repositories} ${AC_NDK_PATH} + popd > /dev/null + # force a clean build after an update, since CMake cache maybe out of date + clean_build_dir ${BUILD_DIR} +fi + +pushd ${BUILD_DIR} > /dev/null + + export PKG_CONFIG_PATH="${AC_NDK_PATH}/usr/lib/pkgconfig:${AC_NDK_PATH}/usr/lib/${target_machine}/pkgconfig" + export PKG_CONFIG_ALLOW_SYSTEM_CFLAGS=1 + export PKG_CONFIG_ALLOW_SYSTEM_LIBS=1 + export PKG_CONFIG_SYSROOT_DIR=$AC_NDK_PATH + export PKG_CONFIG_EXECUTABLE=`which pkg-config` + export AC_TARGET_MACHINE=${target_machine} + export AC_GCC_VARIANT=${gcc_variant} + export LDFLAGS=-Wl,-rpath-link,${AC_NDK_PATH}/usr/lib/${target_machine}/pulseaudio + echo "Using PKG_CONFIG_PATH: $PKG_CONFIG_PATH" + echo "Using PKG_CONFIG_EXECUTABLE: $PKG_CONFIG_EXECUTABLE" + cmake -DCMAKE_TOOLCHAIN_FILE=../cmake/LinuxCrossCompile.cmake \ + -DCMAKE_BUILD_TYPE=debug \ + .. + + make -j${NUM_JOBS} $@ + +popd > /dev/null diff --git a/external/CMakeLists.txt b/external/CMakeLists.txt new file mode 100644 index 0000000..f20617c --- /dev/null +++ b/external/CMakeLists.txt @@ -0,0 +1,3 @@ +add_subdirectory(bubblewrap) +add_subdirectory(process-cpp-minimal) +add_subdirectory(android-emugl) diff --git a/external/android-emugl/Android.mk b/external/android-emugl/Android.mk new file mode 100644 index 0000000..d858e69 --- /dev/null +++ b/external/android-emugl/Android.mk @@ -0,0 +1,91 @@ +# This is the top-level build file for the Android HW OpenGL ES emulation +# in Android. +# +# You must define BUILD_EMULATOR_HOST_OPENGL to 'true' in your environment to +# build the following files. +# +# Top-level for all modules +EMUGL_PATH := $(call my-dir) + +EMUGL_OLD_LOCAL_PATH := $(LOCAL_PATH) + +# Directory containing common headers that are part of EmuGL's public API. +# This is always set to a module's LOCAL_C_INCLUDES. See the definition of +# emugl-begin-module in common.mk +EMUGL_COMMON_INCLUDES := $(EMUGL_INCLUDES) + +# common cflags used by several modules +# This is always set to a module's LOCAL_CFLAGS +# See the definition of emugl-begin-module in common.mk + +# Needed to ensure SIZE_MAX is properly defined when including +EMUGL_COMMON_CFLAGS += -D__STDC_LIMIT_MACROS=1 + +ifneq (,$(strip $(BUILD_DEBUG))) +EMUGL_COMMON_CFLAGS += -DEMUGL_DEBUG=1 +endif + +EMUGL_COMMON_CFLAGS += -DEMUGL_BUILD=1 +ifeq (linux,$(BUILD_TARGET_OS)) +EMUGL_COMMON_CFLAGS += -fvisibility=internal +endif +ifeq (darwin,$(BUILD_TARGET_OS)) +EMUGL_COMMON_CFLAGS += -fvisibility=hidden +endif + +# Include common definitions used by all the modules included later +# in this build file. This contains the definition of all useful +# emugl-xxxx functions. +# +include $(EMUGL_PATH)/common.mk + +# IMPORTANT: ORDER IS CRUCIAL HERE +# +# For the import/export feature to work properly, you must include +# modules below in correct order. That is, if module B depends on +# module A, then it must be included after module A below. +# +# This ensures that anything exported by module A will be correctly +# be imported by module B when it is declared. +# +# Note that the build system will complain if you try to import a +# module that hasn't been declared yet anyway. +# + +# Required by our units test. +include $(EMUGL_PATH)/googletest.mk + +# First, build the emugen host source-generation tool +# +# It will be used by other modules to generate wire protocol encode/decoder +# source files (see all emugl-gen-decoder/encoder in common.mk) +# +include $(EMUGL_PATH)/host/tools/emugen/Android.mk + +include $(EMUGL_PATH)/shared/emugl/common/Android.mk +include $(EMUGL_PATH)/shared/OpenglCodecCommon/Android.mk + +ifeq (true,$(EMULATOR_USE_ANGLE)) +# Alternative graphics translation (GT) implementation, stripped off +# and adjust from the equivalent mod in the ARC project. +# This GT acts as a thin wrapper + GLESv1-to-v2 translator +# and forwards GLES calls to underlying GLES API (e.g. ANGLE) +# +include $(EMUGL_PATH)/host/libs/graphics_translation/common/Android.mk +include $(EMUGL_PATH)/host/libs/graphics_translation/gles/Android.mk +endif + +# Host static libraries +include $(EMUGL_PATH)/host/libs/GLESv1_dec/Android.mk +include $(EMUGL_PATH)/host/libs/GLESv2_dec/Android.mk +include $(EMUGL_PATH)/host/libs/renderControl_dec/Android.mk +include $(EMUGL_PATH)/host/libs/Translator/GLcommon/Android.mk +include $(EMUGL_PATH)/host/libs/Translator/GLES_CM/Android.mk +include $(EMUGL_PATH)/host/libs/Translator/GLES_V2/Android.mk +include $(EMUGL_PATH)/host/libs/Translator/EGL/Android.mk +include $(EMUGL_PATH)/host/libs/libOpenGLESDispatch/Android.mk + +# Host shared libraries +include $(EMUGL_PATH)/host/libs/libOpenglRender/Android.mk + +LOCAL_PATH := $(EMUGL_OLD_LOCAL_PATH) diff --git a/external/android-emugl/CMakeLists.txt b/external/android-emugl/CMakeLists.txt new file mode 100644 index 0000000..f515bf4 --- /dev/null +++ b/external/android-emugl/CMakeLists.txt @@ -0,0 +1,20 @@ +# Don't treat any warnings as error as we take the source directly from +# upstream and just compile it. +set(CMAKE_C_FLAGS "-Wall") + +include_directories( + ${CMAKE_SOURCE_DIR}/external/android-emugl/shared + ${CMAKE_SOURCE_DIR}/external/android-emugl/host/include + ${CMAKE_SOURCE_DIR}/external/android-emugl/shared/OpenglCodecCommon + ${CMAKE_SOURCE_DIR}/external/android-emugl/host/libs + ${CMAKE_SOURCE_DIR}/external/android-emugl/host/libs/Translator/include + ${CMAKE_SOURCE_DIR}/external/android-emugl/host/include/libOpenglRender + ${CMAKE_SOURCE_DIR}/external/android-emugl/host/libs/GLESv1_dec + ${CMAKE_BINARY_DIR}/external/android-emugl/host/libs/GLESv1_dec + ${CMAKE_SOURCE_DIR}/external/android-emugl/host/libs/GLESv2_dec + ${CMAKE_BINARY_DIR}/external/android-emugl/host/libs/GLESv2_dec + ${CMAKE_SOURCE_DIR}/external/android-emugl/host/libs/renderControl_dec + ${CMAKE_BINARY_DIR}/external/android-emugl/host/libs/renderControl_dec) + +add_subdirectory(host) +add_subdirectory(shared) diff --git a/external/android-emugl/DESIGN b/external/android-emugl/DESIGN new file mode 100644 index 0000000..943a0e4 --- /dev/null +++ b/external/android-emugl/DESIGN @@ -0,0 +1,615 @@ +Android Hardware OpenGLES emulation design overview +=================================================== + +Introduction: +------------- + +Hardware GLES emulation in the Android platform is implemented with a mix +of components, which are: + + - Several host "translator" libraries. They implement the EGL, GLES 1.1 and + GLES 2.0 ABIs defined by Khronos, and translate the corresponding function + calls into calls to the appropriate desktop APIs, i.e.: + + - GLX (Linux), AGL (OS X) or WGL (Windows) for EGL + - desktop GL 2.0 for GLES 1.1 and GLES 2.0 + + _________ __________ __________ + | | | | | | + |TRANSLATOR |TRANSLATOR| |TRANSLATOR| HOST + | EGL | | GLES 1.1 | | GLES 2.0 | TRANSLATOR + |_________| |__________| |__________| LIBRARIES + | | | + - - - | - - - - - - - - - | - - - - - - - - - | - - - - - + | | | + ____v____ ____v_____ _____v____ HOST + | | | | | | SYSTEM + | GLX | | GL 2.0 | | GL 2.0 | LIBRARIES + |_________| |__________| |__________| + + + + - Several system libraries inside the emulated guest system that implement + the same EGL / GLES 1.1 and GLES 2.0 ABIs. + + They collect the sequence of EGL/GLES function calls and translate then + into a custom wire protocol stream that is sent to the emulator program + through a high-speed communication channel called a "QEMU Pipe". + + For now, all you need to know is that the pipe is implemented with a + custom kernel driver, and provides for _very_ fast bandwidth. All read() + and writes() from/to the pipes are essentially instantaneous from the + guest's point of view. + + + _________ __________ __________ + | | | | | | + |EMULATION| |EMULATION | |EMULATION | GUEST + | EGL | | GLES 1.1 | | GLES 2.0 | SYSTEM + |_________| |__________| |__________| LIBRARIES + | | | + - - - | - - - - - - - - - | - - - - - - - - - | - - - - - + | | | + ____v____________________v____________________v____ GUEST + | | KERNEL + | QEMU PIPE | + |___________________________________________________| + | + - - - - - - - - - - - - - -|- - - - - - - - - - - - - - - - + | + v + EMULATOR + + - Specific code inside the emulator program that is capable of transmitting + the wire protocol stream to a special rendering library or process (called + the "renderer" here), which understands the format. + + | + | PROTOCOL BYTE STREAM + _____v_____ + | | + | EMULATOR | + |___________| + | + | UNMODIFIED PROTOCOL BYTE STREAM + _____v_____ + | | + | RENDERER | + |___________| + + + - The renderer decodes the EGL/GLES commands from the wire + protocol stream, and dispatches them to the translator libraries + appropriately. + + | + | PROTOCOL BYTE STREAM + _____v_____ + | | + | RENDERER | + |___________| + | | | + +-----------------+ | +-----------------+ + | | | + ____v____ ___v______ ____v_____ + | | | | | | HOST + |TRANSLATOR |TRANSLATOR| |TRANSLATOR| HOST + | EGL | | GLES 1.1 | | GLES 2.0 | TRANSLATOR + |_________| |__________| |__________| LIBRARIES + + + + - In reality, the protocol stream flows in both directions, even though most + of the commands result in data going from the guest to the host. A complete + picture of the emulation would thus be: + + + + + + _________ __________ __________ + | | | | | | + |EMULATION| |EMULATION | |EMULATION | GUEST + | EGL | | GLES 1.1 | | GLES 2.0 | SYSTEM + |_________| |__________| |__________| LIBRARIES + ^ ^ ^ + | | | + - - - | - - - - - - - - - | - - - - - - - - - | - - - - - + | | | + ____v____________________v____________________v____ GUEST + | | KERNEL + | QEMU PIPE | + |___________________________________________________| + ^ + | + - - - - - - - - - - - - - -|- - - - - - - - - - - - - - - - + | + | PROTOCOL BYTE STREAM + _____v_____ + | | + | EMULATOR | + |___________| + ^ + | UNMODIFIED PROTOCOL BYTE STREAM + _____v_____ + | | + | RENDERER | + |___________| + ^ ^ ^ + | | | + +-----------------+ | +-----------------+ + | | | + ____v____ ___v______ ____v_____ + | | | | | | + |TRANSLATOR |TRANSLATOR| |TRANSLATOR| HOST + | EGL | | GLES 1.1 | | GLES 2.0 | TRANSLATOR + |_________| |__________| |__________| LIBRARIES + ^ ^ ^ + | | | + - - - | - - - - - - - - - | - - - - - - - - - | - - - - - + | | | + ____v____ ____v_____ _____v____ HOST + | | | | | | SYSTEM + | GLX | | GL 2.0 | | GL 2.0 | LIBRARIES + |_________| |__________| |__________| + + (NOTE: 'GLX' is for Linux only, replace 'AGL' on OS X, and 'WGL' on Windows). + + +Note that, in the above graphics, only the host system libraries at the bottom +are _not_ provided by Android. + + +Design Requirements: +-------------------- + +The above design comes from several important requirements that were decided +early in the project: + +1 - The ability to run the renderer in a separate process from the emulator + itself is important. + + For various practical reasons, we plan to completely separate the core QEMU + emulation from the UI window by using two distinct processes. As such, the + renderer will be implemented as a library inside the UI program, but will + need to receive protocol bytes from the QEMU process. + + The communication channel will be either a fast Unix socket or a Win32 + named pipe between these two. A shared memory segment with appropriate + synchronization primitives might also be used if performance becomes + an issue. + + This explains why the emulator doesn't alter or even try to parse the + protocol byte stream. It only acts as a dumb proxy between the guest + system and the renderer. This also avoids adding lots of GLES-specific + code inside the QEMU code base which is terribly complex. + +2 - The ability to use vendor-specific desktop EGL/GLES libraries is + important. + + GPU vendors like NVidia, AMD or ARM all provide host versions of the + EGL/GLES libraries that emulate their respectivie embedded graphics + chipset. + + The renderer library can be configured to use these instead of the + translator libraries provided with this project. This can be useful to + more accurately emulate the behaviour of specific devices. + + Moreover, these vendor libraries typically expose vendor-specific + extensions that are not provided by the translator libraries. We cannot + expose them without modifying our code, but it's important to be able + to do so without too much pain. + + +Code organization: +------------------ + +All source code for the components above is spread over multiple directories +in the Android source trees: + + - The emulator sources are under $ANDROID/external/qemu, which we'll + call $QEMU in the rest of this document. + + - The guest libraries are under + $ANDROID/device/generic/goldfish/opengl, which we'll call $EMUGL_GUEST + + - The host renderer and translator libraries are under + $QEMU/distrib/android-emugl, which we'll call $EMUGL_HOST + + - The QEMU Pipe kernel driver is under $KERNEL/drivers/misc/qemupipe (3.4) + or $KERNEL/drivers/platform/goldfish/goldfish_pipe.c (3.10) + +Where $ANDROID is the top of the open-source Android source tree, and +$KERNEL is the top of the qemu-specific kernel source tree (using one +of the android-goldfish-xxxx branches here). + +The emulator sources related to this projects are: + + $QEMU/hw/android/goldfish/pipe.c -> implements QEMU pipe virtual hardware. + $QEMU/android/opengles.c -> implements GLES initialization. + $QEMU/android/hw-pipe-net.c -> implements the communication channel + between the QEMU Pipe and the renderer library + +The other sources are: + + $EMUGL_GUEST/system -> system libraries + $EMUGL_GUEST/shared -> guest copy of shared libraries + $EMUGL_GUEST/tests -> various test programs + + $EMUGL_HOST/host -> host libraries (translator + renderer) + $EMUGL_HOST/shared -> host copy of shared libraries + +The reason the shared libraries aren't actually shared is historical: at one +point both guest and host code lived in the same place. That turned out to be +impractical with the way the Android SDK is branched, and didn't support the +requirement that a single emulator binary be able to run several releases +of Android. + + +Translator libraries: +--------------------- + +There are three translator host libraries provided by this project: + + libEGL_translator -> EGL 1.2 translation + libGLES_CM_translator -> GLES 1.1 translation + libGLES_V2_translator -> GLES 2.0 translation + +The full name of the library will depend on the host system. +For simplicity, only the library name suffix will change (i.e. the +'lib' prefix is not dropped on Windows), i.e.: + + libEGL_translator.so -> for Linux + libEGL_translator.dylib -> for OS X + libEGL_translator.dll -> for Windows + +The source code for these libraries is located under the following +path in the Android source tree: + + $EMUGL_HOST/host/libs/Translator/EGL + $EMUGL_HOST/host/libs/Translator/GLES_CM + $EMUGL_HOST/host/libs/Translator/GLES_V2 + +The translator libraries also use a common routines defined under: + + $EMUGL_HOST/host/libs/Translator/GLcommon + + +Wire Protocol Overiew: +---------------------- + +The "wire protocol" is implemented as follows: + + - EGL/GLES function calls are described through several "specification" + files, which describes the types, function signatures and various + attributes for each one of them. + + - These files are read by a tool called "emugen" which generates C + source files and headers based on the specification. These correspond + to both encoding, decoding and "wrappers" (more on this later). + + - System "encoder" static libraries are built using some of these generated + files. They contain code that can serialize EGL/GLES calls into simple + byte messages and send it through a generic "IOStream" object. + + - Host "decoder" static libraries are also built using some of these + generated files. Their code retrieves byte messages from an "IOStream" + object, and translates them into function callbacks. + +IOStream abstraction: +- - - - - - - - - - - + +The "IOStream" is a very simple abstract class used to send byte messages +both in the guest and host. It is defined through a shared header under +$EMUGL/host/include/libOpenglRender/IOStream.h + +Note that despite the path, this header is included by *both* host and guest +source code. The main idea around IOStream's design is that to send a message, +one does the following: + + 1/ call stream->allocBuffer(size), which returns the address of a + memory buffer of at least 'size' bytes. + + 2/ write the content of the serialized command (usually a header + some + payload) directly into the buffer + + 3/ call stream->commitBuffer() to send it. + +Alternatively, one can also pack several commands into a single buffer with +stream->alloc() and stream->flush(), as in: + + 1/ buf1 = stream->alloc(size1) + 2/ write first command bytes into buf1 + 3/ buf2 = stream->alloc(size2) + 4/ write second command bytes into buf2 + 5/ stream->flush() + +Finally, there are also explict read/write methods like stream->readFully() +or stream->writeFully() which can be used when you don't want an intermediate +buffer. This is used in certain cases by the implementation, e.g. to avoid +an intermediate memory copy when sending texture data from the guest to the +host. + +The host IOStream implementations are under $EMUGL/shared/OpenglCodecCommon/, +see in particular: + + $EMUGL_HOST/shared/OpenglCodecCommon/TcpStream.cpp + -> using local TCP sockets + $EMUGL_HOST/shared/OpenglCodecCommon/UnixStream.cpp + -> using Unix sockets + $EMUGL_HOST/shared/OpenglCodecCommon/Win32PipeStream.cpp + -> using Win32 named pipes + +The guest IOStream implementation uses the TcpStream.cpp above, as well as +an alternative QEMU-specific source: + + $EMUGL_GUEST/system/OpenglSystemCommon/QemuPipeStream.cpp + -> uses QEMU pipe from the guest + +The QEMU Pipe implementation is _significantly_ faster (about 20x) due to +several reasons: + + - all succesful read() and write() operations through it are instantaneous + from the guest's point of view. + + - all buffer/memory copies are performed directly by the emulator, and thus + much faster than performing the same thing inside the kernel with emulated + ARM instructions. + + - it doesn't need to go through a kernel TCP/IP stack that will wrap the + data into TCP/IP/MAC packets, send them to an emulated ethernet device, + which is itself connected to an internal firewall implementation that + will unwrap the packets, re-assemble them, then send them through BSD + sockets to the host kernel. + +However, would it be necessary, you could write a guest IOStream implementation +that uses a different transport. If you do, please look at +$EMUGL_GUEST/system/OpenglCodecCommon/HostConnection.cpp which contains the +code used to connect the guest to the host, on a per-thread basis. + + +Source code auto-generation: +- - - - - - - - - - - - - - + +The 'emugen' tool is located under $EMUGL_HOST/host/tools/emugen. There is a +README file that explains how it works. + +You can also look at the following specifications files: + +For GLES 1.1: + $EMUGL_HOST/host/GLESv1_dec/gl.types + $EMUGL_HOST/host/GLESv1_dec/gl.in + $EMUGL_HOST/host/GLESv1_dec/gl.attrib + +For GLES 2.0: + $EMUGL_HOST/host/GLESv2_dec/gl2.types + $EMUGL_HOST/host/GLESv2_dec/gl2.in + $EMUGL_HOST/host/GLESv2_dec/gl2.attrib + +For EGL: + $EMUGL_HOST/host/renderControl_dec/renderControl.types + $EMUGL_HOST/host/renderControl_dec/renderControl.in + $EMUGL_HOST/host/renderControl_dec/renderControl.attrib + +Note that the EGL specification files are under a directory named +"renderControl_dec" and have filenames that begin with "renderControl" + +This is mainly for historic reasons now, but is also related to the fact that +this part of the wire protocol contains support functions/calls/specifications +that are not part of the EGL specification itself, but add a few features +required to make everything works. For example, they have calls related to +the "gralloc" system library module used to manage graphics surfaces at a +lower level than EGL. + +Generally speaking, guest encoder sources are located under directories +named $EMUGL_GUEST/system/_enc/, while the corresponding host decoder +sources will be under $EMUGL_HOST/host/libs/_dec/ + +However, all these sources use the same spec files located under the +decoding directories. + +The encoder files are built from emugen and spec files located in $EMUGL_HOST +and copied to the encoder directories in $EMUGL_GUEST by the gen-encoder.sh +script. They are checked in, so that a given version of Android supports a +specific version of the protocol, even if newer versions of the renderer (and +future Android versions) support a newer protocol version. This step needs to +be done manually when the protocol changes; these changes also need to be +accompanied by changes in the renderer to handle the old version of the +protocol. + + +System libraries: +----------------- + +Meta EGL/GLES system libraries, and egl.cfg: +- - - - - - - - - - - - - - - - - - - - - - + +It is important to understand that the emulation-specific EGL/GLES libraries +are not directly linked by applications at runtime. Instead, the system +provides a set of "meta" EGL/GLES libraries that will load the appropriate +hardware-specific libraries on first use. + +More specifically, the system libEGL.so contains a "loader" which will try +to load: + + - hardware-specific EGL/GLES libraries + - the software-based rendering libraries (called "libagl") + +The system libEGL.so is also capable of merging the EGL configs of both the +hardware and software libraries transparently to the application. The system +libGLESv1_CM.so and libGLESv2.so, work with it to ensure that the thread's +current context will be linked to either the hardware or software libraries +depending on the config selected. + +For the record, the loader's source code in under +frameworks/base/opengl/libs/EGL/Loader.cpp. It depends on a file named +/system/lib/egl/egl.cfg which must contain two lines that look like: + + 0 1 + 0 0 android + +The first number in each line is a display number, and must be 0 since the +system's EGL/GLES libraries don't support anything else. + +The second number must be 1 to indicate hardware libraries, and 0 to indicate +a software one. The line corresponding to the hardware library, if any, must +always appear before the one for the software library. + +The third field is a name corresponding to a shared library suffix. It really +means that the corresponding libraries will be named libEGL_.so, +libGLESv1_CM_.so and libGLESv2_.so. Moreover these libraries must +be placed under /system/lib/egl/ + +The name "android" is reserved for the system software renderer. + +The egl.cfg that comes with this project uses the name "emulation" for the +hardware libraries. This means that it provides an egl.cfg file that contains +the following lines: + + 0 1 emulation + 0 0 android + +See $EMUGL_GUEST/system/egl/egl.cfg and more generally the following build +files: + + $EMUGL_GUEST/system/egl/Android.mk + $EMUGL_GUEST/system/GLESv1/Android.mk + $EMUGL_GUEST/system/GLESv2/Android.mk + +to see how the libraries are named and placed under /system/lib/egl/ by the +build system. + + +Emulation libraries: +- - - - - - - - - - - + +The emulator-specific libraries are under the following: + + $EMUGL_GUEST/system/egl/ + $EMUGL_GUEST/system/GLESv1/ + $EMUGL_GUEST/system/GLESv2/ + +The code for GLESv1 and GLESv2 is pretty small, since it mostly link against +the static encoding libraries. + +The code for EGL is a bit more complex, because it needs to deal with +extensions dynamically. I.e. if an extension is not available on the host +it shouldn't be exposed by the library at runtime. So the EGL code queries +the host for the list of available extensions in order to return them to +clients. Similarly, it must query the list of valid EGLConfigs for the +current host system. + + +"gralloc" module implementation: +- - - - - - - - - - - - - - - - - + +In addition to EGL/GLES libraries, the Android system requires a +hardware-specific library to manage graphics surfaces at a level lower than +EGL. This library must be what is called in Android land as a "HAL module". + +A "HAL module" must provide interfaces defined by Android's HAL +(Hardware Abstraction Library). These interface definitions can be found +under $ANDROID/hardware/libhardware/include/ + +Of all possible HAL modules, the "gralloc" one is used by the system's +SurfaceFlinger to allocate framebuffers and other graphics memory regions, +as well as eventually lock/unlock/swap them when needed. + +The code under $EMUGL/system/gralloc/ implements the module required by the +GLES emulation project. It's not very long, but there are a few things to +notice here: + +- first, it will probe the guest system to determine if the emulator that + is running the virtual device really supports GPU emulation. In certain + circumstances this may not be possible. + + If this is the case, then the module will redirect all calls to the + "default" gralloc module that is normally used by the system when + software-only rendering is enabled. + + The probing happens in the function "fallback_init" which gets called + when the module is first opened. This initializes the 'sFallback' variable + to a pointer to the default gralloc module when required. + +- second, this module is used by SurfaceFlinger to display "software surfaces", + i.e. those that are backed by system memory pixel buffers, and written to + directly through the Skia graphics library (i.e. the non-accelerated ones). + + the default module simply copies the pixel data from the surface to the + virtual framebuffer i/o memory, but this project's gralloc module sends it + to the renderer through the QEMU Pipe instead. + + It turns out that this results in _faster_ rendering/frame-rates overall, + because memory copies inside the guest are slow, while QEMU pipe transfers + are done directly in the emulator. + + +Host Renderer: +-------------- + +The host renderer library is located under +$EMUGL_HOST/host/libs/libOpenglRender, and it provides an interface described +by the headers under $EMUGL_HOST/host/libs/libOpenglRender/render_api.h +(e.g. for use by the emulator). + +In a nutshell, the rendering library is responsible for the following: + + - Providing a virtual off-screen video surface where everything will get + rendered at runtime. Its dimensions are fixed by the call to + initOpenglRender() that must happen just after the library is + initialized. + + - Provide a way to display the virtual video surface on a host application's + UI. This is done by calling createOpenGLSubWindow() which takes as argument + the window ID or handle of a parent window, some display dimensions and + a rotation angle. This allows the surface to be scaled/rotated when it is + displayed, even if the dimensions of the video surface do not change. + + - Provide a way to listen to incoming EGL/GLES commands from the guest. + This is done by providing a so-called "port number" to initOpenglRender(). + + By default, the port number corresponds to a local TCP port number that the + renderer will bind to and listen. Every new connection to this port will + correspond to the creation of a new guest host connection, each such + connection corresponding to a distinct thread in the guest system. + + For performance reasons, it is possible to listen to either Unix sockets + (on Linux and OS X), or to a Win32 named pipe (on Windows). To do so, one + had to call setStreamType() between library initialization + (i.e. initLibrary()) and construction (i.e. initOpenglRender()). + + Note that in these modes, the port number is still used to differentiate + between several emulator instances. These details are normally handled by + the emulator code so you shouldn't care too much. + +Note that an earlier version of the interface allowed a client of the renderer +library to provide its own IOStream implementation. However, this wasn't very +convenient for a number of reasons. This maybe something that could be done +again if it makes sense, but for now the performance numbers are pretty good. + + +Host emulator: +-------------- + +The code under $QEMU/android/opengles.c is in charge of dynamically loading +the rendering library and initializing / constructing it properly. + +QEMU pipe connections to the 'opengles' service are piped through the code +in $QEMU/android/hw-pipe-net.c. Look for the openglesPipe_init() function, +which is in charge of creating a connection to the renderer library +(either through a TCP socket, or a Unix pipe depending on configuration. +support for Win32 named pipes hasn't been implemented yet in the emulator) +whenever a guest process opens the "opengles" service through /dev/qemu_pipe. + +There is also some support code for the display of the GLES framebuffer +(through the renderer library's subwindow) under $QEMU/skin/window. + +Note that at the moment, scaling and rotation are supported. However, +brightness emulation (which used to modify the pixel values from the +hardware framebuffer before displaying them) doesn't work. + +Another issue is that it is not possible to display anything on top of the +GL subwindow at the moment. E.g. this will obscure the emulated trackball +image (that is normally toggled with Ctrl-T during emulation, or enabled +by pressing the Delete key). + diff --git a/external/android-emugl/README b/external/android-emugl/README new file mode 100644 index 0000000..8db2d9d --- /dev/null +++ b/external/android-emugl/README @@ -0,0 +1,96 @@ +This directory contains the host-side modules related to hardware OpenGL ES +emulation. The guest-side modules are in +$ANDROID_BUILD_TOP/device/generic/goldfish/opengl + +I. Overview of components: +========================== + +The 'emugen' tool is used to generate several source files related to the +EGL/GLES command stream used between the guest and the host during emulation. + + host/tools/emugen -> emugen program + +Note that emugen is capable of generating, from a single set of specification +files, three types of auto-generated sources: + + - sources to encode commands into a byte stream. + - sources to decode the byte stream into commands. + - sources to wrap normal procedural EGL/GLES calls into context-aware ones. + +Modules under the system/ directory corresponds to code that runs on the +guest, and implement the marshalling of EGL/GLES commands into a stream of +bytes sent to the host through a fast pipe mechanism. + + system/GLESv1_enc -> encoder for GLES 1.1 commands + system/GLESv2_enc -> encoder for GLES 2.0 commands + system/renderControl_enc -> encoder for rendering control commands + system/egl -> emulator-specific guest EGL library + system/GLESv1 -> emulator-specific guest GLES 1.1 library + system/gralloc -> emulator-specific gralloc module + system/OpenglSystemCommon -> library of common routines + +Modules under the host/ directory corresponds to code that runs on the +host, and implement the decoding of the command stream, translation of +EGL/GLES commands into desktop GL 2.0 ones, and rendering to an off-screen +buffer. + + host/libs/GLESv1_dec -> decoder for GLES 1.1 commands + host/libs/GLESv2_dec -> decoder for GLES 2.0 commands + host/libs/renderControl_dec -> decoder for rendering control commands + + host/libs/Translator/EGL -> translator for EGL commands + host/libs/Translator/GLES_CM -> translator for GLES 1.1 commands + host/libs/Translator/GLES_V2 -> translator for GLES 2.0 commands + host/libs/Translator/GLcommon -> library of common translation routines + + host/libs/libOpenglRender -> rendering library (uses all host libs above) + can be used by the 'renderer' program below, + or directly linked into the emulator UI program. + + host/renderer/ -> stand-alone renderer program executable. + this can run in head-less mode and receive requests from + several emulators at the same time. It is the receiving + end of all command streams. + +Modules under the test/ directory correspond to test programs that are useful +to debug the various modules described above: + + tests/EGL_host_wrapper -> a small library used to dynamically load the + desktop libEGL.so or a replacement named by the + ANDROID_EGL_LIB environment variable. This lib + provides all EGL entry points. + + tests/emulator_test_renderer -> a small program to run the rendering library + in a single SDL window on the host desktop. + + tests/gles_android_wrapper -> guest EGL / GLES libraries that are run on + the device to run some tests. Replace the + system/egl and system/GLESv1 modules for now. + + tests/translator_tests/GLES_CM -> desktop GLESv1 translation unit test + tests/translator_tests/GLES_V2 -> desktop GLESv2 translation unit test + tests/translator_tests/MacCommon -> used by translation tests on Mac only. + + tests/ut_rendercontrol_enc -> guest library used by tests/ut_renderer + tests/ut_rendercontrol_dec -> host library used by tests/ut_renderer + tests/ut_renderer -> unit-test for render control and rendering library. + + +II. Build system considerations: +-------------------------------- + +The dependencies on the more than 20 components described in the previous +section are pretty sophisticated, involving lots of auto-generated code and +non-trivial placement for guest/device libraries. + +To simplify the development and maintenance of these modules, a set of +helper GNU Make function is defined in common.mk, and included from the +Android.mk in this directory. + +These functions all begin with the "emugl-" prefix, and can be used to +declare modules, what information they export to other modules, or import +from them, and also what kind of auto-generated sources they depend on. + +Look at the comments inside common.mk and the Android.mk of the modules +to better understand what's happening. + diff --git a/external/android-emugl/common.mk b/external/android-emugl/common.mk new file mode 100644 index 0000000..76fd4e9 --- /dev/null +++ b/external/android-emugl/common.mk @@ -0,0 +1,241 @@ +# This top-level build file is included by all modules that implement +# the hardware OpenGL ES emulation for Android. +# +# We use it to ensure that all sub-Makefiles are included in the right +# order for various variable definitions and usage to happen in the correct +# order. +# + +# The following macros are used to start a new GLES emulation module. +# +# This will define LOCAL_MODULE as $1, plus a few other variables +# needed by the build system (e.g. LOCAL_MODULE_TAGS, LOCAL_MODULE_CLASS...) +# +# NOTE: You still need to define LOCAL_PATH before this +# +# Usage example: +# +# $(call emugl-begin-static-library,) +# LOCAL_SRC_FILES := .... +# LOCAL_C_INCLUDES += .... +# $(call emugl-end-module) +# +emugl-begin-host-static-library = $(call emugl-begin-module,$1,HOST_STATIC_LIBRARY,HOST) +emugl-begin-host-shared-library = $(call emugl-begin-module,$1,HOST_SHARED_LIBRARY,HOST) +emugl-begin-host-executable = $(call emugl-begin-module,$1,HOST_EXECUTABLE,HOST) + +# Internal list of all declared modules (used for sanity checking) +_emugl_modules := +_emugl_HOST_modules := + +# do not use directly, see functions above instead +emugl-begin-module = \ + $(eval include $(CLEAR_VARS)) \ + $(eval LOCAL_MODULE := $1) \ + $(eval LOCAL_MODULE_TAGS := $(if $3,,debug)) \ + $(eval LOCAL_MODULE_CLASS := $(patsubst HOST_%,%,$(patsubst %EXECUTABLE,%EXECUTABLES,$(patsubst %LIBRARY,%LIBRARIES,$2)))) \ + $(eval LOCAL_IS_HOST_MODULE := $(if $3,true,))\ + $(eval LOCAL_C_INCLUDES += $(EMUGL_COMMON_INCLUDES)) \ + $(eval LOCAL_CFLAGS += $(EMUGL_COMMON_CFLAGS)) \ + $(eval LOCAL_LDLIBS += $(CXX_STD_LIB)) \ + $(eval LOCAL_BUILD_FILE := $(BUILD_$2)) \ + $(call _emugl-init-module,$1,$2,$3) + +# Used to end a module definition, see function definitions above +emugl-end-module = \ + $(eval $(end-emulator-module-ev)) \ + $(eval LOCAL_BUILD_FILE :=) \ + $(eval _emugl_$(_emugl_HOST)modules += $(_emugl_MODULE))\ + $(if $(EMUGL_DEBUG),$(call emugl-dump-module)) + +# Managing module exports and imports. +# +# A module can 'import' another module, by calling emugl-import. This will +# make the current LOCAL_MODULE inherit various definitions exported from +# the imported module. +# +# Module exports are defined by calling emugl-export. Here is an example: +# +# $(call emugl-begin-static-library,foo) +# LOCAL_SRC_FILES := foo.c +# $(call emugl-export,C_INCLUDES,$(LOCAL_PATH)) +# $(call emugl-export,SHARED_LIBRARIES,libcutils) +# $(call emugl-end-module) +# +# $(call emugl-begin-shared-library,bar) +# LOCAL_SRC_FILES := bar.cpp +# $(call emugl-import,foo) +# $(call emugl-end-module) +# +# Here, we define a static library named 'foo' which exports an include +# path and a shared library requirement, and a shared library 'bar' which +# imports it. +# +# What this means is that: +# +# - 'bar' will automatically inherit foo's LOCAL_PATH in its LOCAL_C_INCLUDES +# - 'bar' will automatically inherit libcutils in its own LOCAL_SHARED_LIBRARIES +# +# Note that order of declaration matters. If 'foo' is defined after 'bar' in +# the example above, nothing will work correctly because dependencies are +# computed at import time. +# +# +# IMPORTANT: Imports are transitive, i.e. when module A imports B, +# it automatically imports anything imported by B too. + +# This is the list of recognized export types we support for now. +EMUGL_EXPORT_TYPES := \ + CFLAGS \ + CXXFLAGS \ + LDLIBS \ + LDFLAGS \ + C_INCLUDES \ + SHARED_LIBRARIES \ + STATIC_LIBRARIES \ + ADDITIONAL_DEPENDENCIES + +# Initialize a module in our database +# $1: Module name +# $2: Module type +# $3: "HOST" for a host module, empty for a target one. +_emugl-init-module = \ + $(eval _emugl_HOST := $(if $3,HOST_,))\ + $(eval _emugl_MODULE := $(_emugl_HOST)$1)\ + $(if $(filter $(_emugl_$(_emugl_HOST)modules),$(_emugl_MODULE)),\ + $(error There is already a $(if $3,host,) module named $1!)\ + )\ + $(eval _mod = $(_emugl_MODULE)) \ + $(eval _emugl.$(_mod).type := $(patsubst HOST_%,%,$2))\ + $(eval _emugl.$(_mod).imports :=) \ + $(eval _emugl,$(_mod).moved :=) \ + $(foreach _type,$(EMUGL_EXPORT_TYPES),\ + $(eval _emugl.$(_mod).export.$(_type) :=)\ + ) + +# Called to indicate that a module exports a given local variable for its +# users. This also adds this to LOCAL_$1 +# $1: Local variable type (e.g. CFLAGS, LDLIBS, etc...) +# $2: Value(s) to append to the export +emugl-export = \ + $(eval _emugl.$(_emugl_MODULE).export.$1 += $2)\ + $(eval LOCAL_$1 := $(LOCAL_$1) $2) + +emugl-export-outer = \ + $(eval _emugl.$(_emugl_MODULE).export.$1 += $2) + +# Called to indicate that a module imports the exports of another module +# $1: list of modules to import +# +emugl-import = \ + $(foreach _imod,$1,\ + $(call _emugl-module-import,$(_emugl_HOST)$(_imod))\ + ) + +_emugl-module-import = \ + $(eval _mod := $(_emugl_MODULE))\ + $(if $(filter-out $(_emugl_$(_emugl_HOST)modules),$1),\ + $(info Unknown imported emugles module: $1)\ + $(if $(_emugl_HOST),\ + $(eval _names := $(patsubst HOST_%,%,$(_emugl_HOST_modules))),\ + $(eval _names := $(_emugl_modules))\ + )\ + $(info Please one of the following names: $(_names))\ + $(error Aborting)\ + )\ + $(if $(filter-out $(_emugl.$(_mod).imports),$1),\ + $(eval _emugl.$(_mod).imports += $1)\ + $(foreach _sub,$(_emugl.$1.imports),\ + $(call _emugl-module-import,$(_sub))\ + )\ + $(foreach _type,$(EMUGL_EXPORT_TYPES),\ + $(eval LOCAL_$(_type) := $(LOCAL_$(_type)) $(_emugl.$1.export.$(_type)))\ + )\ + $(if $(filter EXECUTABLE SHARED_LIBRARY,$(_emugl.$(_emugl_MODULE).type)),\ + $(if $(filter STATIC_LIBRARY,$(_emugl.$1.type)),\ + $(eval LOCAL_STATIC_LIBRARIES := $(1:HOST_%=%) $(LOCAL_STATIC_LIBRARIES))\ + )\ + $(if $(filter SHARED_LIBRARY,$(_emugl.$1.type)),\ + $(if $(_emugl.$1.moved),,\ + $(eval LOCAL_SHARED_LIBRARIES := $(1:HOST_%=%) $(LOCAL_SHARED_LIBRARIES))\ + )\ + )\ + )\ + ) + +_emugl-dump-list = \ + $(foreach _list_item,$(strip $1),$(info . $(_list_item))) + +emugl-dump-module = \ + $(info MODULE=$(_emugl_MODULE))\ + $(info . HOST=$(_emugl_HOST))\ + $(info . TYPE=$(_emugl.$(_emugl_MODULE).type))\ + $(info . IMPORTS=$(_emugl.$(_emugl_MODULE).imports))\ + $(foreach _type,$(EMUGL_EXPORT_TYPES),\ + $(if $(filter C_INCLUDES ADDITIONAL_DEPENDENCIES,$(_type)),\ + $(info . EXPORT.$(_type) :=)\ + $(call _emugl-dump-list,$(_emugl.$(_emugl_MODULE).export.$(_type)))\ + $(info . LOCAL_$(_type) :=)\ + $(call _emugl-dump-list,$(LOCAL_$(_type)))\ + ,\ + $(info . EXPORT.$(_type) := $(strip $(_emugl.$(_emugl_MODULE).export.$(_type))))\ + $(info . LOCAL_$(_type) := $(strip $(LOCAL_$(_type))))\ + )\ + )\ + $(info . LOCAL_SRC_FILES := $(LOCAL_SRC_FILES))\ + +# This function can be called to generate the decoder source files. +# LOCAL_MODULE and LOCAL_MODULE_CLASS must be defined or the build will abort. +# Source files will be stored in the local intermediates directory that will +# be automatically added to your LOCAL_C_INCLUDES. +# +# Usage: +# $(call emugl-gen-decoder,,) +# +emugl-gen-decoder = \ + $(eval _emugl_out := $(call intermediates-dir-for,$(BUILD_TARGET_BITS),$2))\ + $(call emugl-gen-decoder-generic,$(_emugl_out),$1,$2)\ + $(call emugl-export,C_INCLUDES,$(_emugl_out)) + +# DO NOT CALL DIRECTLY, USE emugl-gen-decoder instead. +# +# The following function can be called to generate wire protocol decoder +# source files, Usage is: +# +# $(call emugl-gen-decoder-generic,,,) +# +# is the destination directory where the generated sources are stored +# is the source directory where to find .attrib, etc.. +# is the emugen basename (see host/tools/emugen/README) +# +emugl-gen-decoder-generic = $(eval $(emugl-gen-decoder-generic-ev)) + +define emugl-gen-decoder-generic-ev +_emugl_dec := $$1/$$3 +_emugl_src := $$2/$$3 +GEN := $$(_emugl_dec)_dec.cpp \ + $$(_emugl_dec)_dec.h \ + $$(_emugl_dec)_opcodes.h \ + $$(_emugl_dec)_server_context.h \ + $$(_emugl_dec)_server_context.cpp + +$$(GEN): PRIVATE_PATH := $$(LOCAL_PATH) +$$(GEN): PRIVATE_CUSTOM_TOOL := $$(EMUGL_EMUGEN) -D $$1 -i $$2 $$3 +$$(GEN): $$(EMUGL_EMUGEN) $$(_emugl_src).attrib $$(_emugl_src).in $$(_emugl_src).types + $$(transform-generated-source) + +$$(call emugl-export,ADDITIONAL_DEPENDENCIES,$$(GEN)) +LOCAL_GENERATED_SOURCES += $$(GEN) +LOCAL_C_INCLUDES += $$1 +endef + +# Call this function when your shared library must be placed in a non-standard +# library path (i.e. not under /system/lib +# $1: library sub-path,relative to /system/lib +# For example: $(call emugl-set-shared-library-subpath,egl) +emugl-set-shared-library-subpath = \ + $(eval LOCAL_MODULE_PATH := $(TARGET_OUT_SHARED_LIBRARIES)/$1)\ + $(eval LOCAL_UNSTRIPPED_PATH := $(TARGET_OUT_SHARED_LIBRARIES_UNSTRIPPED)/$1)\ + $(eval _emugl.$(LOCAL_MODULE).moved := true)\ + $(call emugl-export-outer,ADDITIONAL_DEPENDENCIES,$(LOCAL_MODULE_PATH)/$(LOCAL_MODULE)$(TARGET_SHLIB_SUFFIX)) + diff --git a/external/android-emugl/googletest.mk b/external/android-emugl/googletest.mk new file mode 100644 index 0000000..9d8e5c0 --- /dev/null +++ b/external/android-emugl/googletest.mk @@ -0,0 +1,37 @@ +# This contains common definitions used to define a host module +# to link GoogleTest with the EmuGL test programs. +# +# This is used instead of including external/gtest/Android.mk to +# be able to build both the 32-bit and 64-bit binaries while +# building a 32-bit only SDK (sdk-eng, sdk_x86-eng, sdk_mips-eng). + + +LOCAL_PATH := $(EMULATOR_GTEST_SOURCES_DIR) + +common_SRC_FILES := \ + src/gtest-all.cc \ + src/gtest_main.cc + +common_CFLAGS := -O0 + +ifneq (windows,$(BUILD_TARGET_OS)) + common_LDLIBS += -lpthread +endif + +$(call emugl-begin-host-static-library,libemugl_gtest) +LOCAL_SRC_FILES := $(common_SRC_FILES) +LOCAL_CFLAGS += $(common_CFLAGS) +LOCAL_CPP_EXTENSION := .cc +$(call emugl-export,C_INCLUDES,$(LOCAL_PATH)/include) +$(call emugl-export,LDLIBS,$(common_LDLIBS)) +$(call emugl-end-module) + +$(call emugl-begin-host-static-library,libemugl_gtest_host) +LOCAL_SRC_FILES := $(common_SRC_FILES) +LOCAL_CFLAGS += $(common_CFLAGS) +LOCAL_C_INCLUDES += $(LOCAL_PATH)/include +LOCAL_CPP_EXTENSION := .cc +$(call emugl-export,C_INCLUDES,$(LOCAL_PATH)/include) +$(call emugl-export,LDLIBS,$(common_LDLIBS) -lpthread) +LOCAL_HOST_BUILD := true +$(call emugl-end-module) diff --git a/external/android-emugl/host/CMakeLists.txt b/external/android-emugl/host/CMakeLists.txt new file mode 100644 index 0000000..4e1de0b --- /dev/null +++ b/external/android-emugl/host/CMakeLists.txt @@ -0,0 +1,2 @@ +add_subdirectory(tools) +add_subdirectory(libs) diff --git a/external/android-emugl/host/include/ETC1/etc1.h b/external/android-emugl/host/include/ETC1/etc1.h new file mode 100644 index 0000000..0d38905 --- /dev/null +++ b/external/android-emugl/host/include/ETC1/etc1.h @@ -0,0 +1,106 @@ +// Copyright 2009 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef __etc1_h__ +#define __etc1_h__ + +#define ETC1_ENCODED_BLOCK_SIZE 8 +#define ETC1_DECODED_BLOCK_SIZE 48 + +#ifndef ETC1_RGB8_OES +#define ETC1_RGB8_OES 0x8D64 +#endif + +typedef unsigned char etc1_byte; +typedef int etc1_bool; +typedef unsigned int etc1_uint32; + +#ifdef __cplusplus +extern "C" { +#endif + +// Encode a block of pixels. +// +// pIn is a pointer to a ETC_DECODED_BLOCK_SIZE array of bytes that represent a +// 4 x 4 square of 3-byte pixels in form R, G, B. Byte (3 * (x + 4 * y) is the R +// value of pixel (x, y). +// +// validPixelMask is a 16-bit mask where bit (1 << (x + y * 4)) indicates whether +// the corresponding (x,y) pixel is valid. Invalid pixel color values are ignored when compressing. +// +// pOut is an ETC1 compressed version of the data. + +void etc1_encode_block(const etc1_byte* pIn, etc1_uint32 validPixelMask, etc1_byte* pOut); + +// Decode a block of pixels. +// +// pIn is an ETC1 compressed version of the data. +// +// pOut is a pointer to a ETC_DECODED_BLOCK_SIZE array of bytes that represent a +// 4 x 4 square of 3-byte pixels in form R, G, B. Byte (3 * (x + 4 * y) is the R +// value of pixel (x, y). + +void etc1_decode_block(const etc1_byte* pIn, etc1_byte* pOut); + +// Return the size of the encoded image data (does not include size of PKM header). + +etc1_uint32 etc1_get_encoded_data_size(etc1_uint32 width, etc1_uint32 height); + +// Encode an entire image. +// pIn - pointer to the image data. Formatted such that +// pixel (x,y) is at pIn + pixelSize * x + stride * y; +// pOut - pointer to encoded data. Must be large enough to store entire encoded image. +// pixelSize can be 2 or 3. 2 is an GL_UNSIGNED_SHORT_5_6_5 image, 3 is a GL_BYTE RGB image. +// returns non-zero if there is an error. + +int etc1_encode_image(const etc1_byte* pIn, etc1_uint32 width, etc1_uint32 height, + etc1_uint32 pixelSize, etc1_uint32 stride, etc1_byte* pOut); + +// Decode an entire image. +// pIn - pointer to encoded data. +// pOut - pointer to the image data. Will be written such that +// pixel (x,y) is at pIn + pixelSize * x + stride * y. Must be +// large enough to store entire image. +// pixelSize can be 2 or 3. 2 is an GL_UNSIGNED_SHORT_5_6_5 image, 3 is a GL_BYTE RGB image. +// returns non-zero if there is an error. + +int etc1_decode_image(const etc1_byte* pIn, etc1_byte* pOut, + etc1_uint32 width, etc1_uint32 height, + etc1_uint32 pixelSize, etc1_uint32 stride); + +// Size of a PKM header, in bytes. + +#define ETC_PKM_HEADER_SIZE 16 + +// Format a PKM header + +void etc1_pkm_format_header(etc1_byte* pHeader, etc1_uint32 width, etc1_uint32 height); + +// Check if a PKM header is correctly formatted. + +etc1_bool etc1_pkm_is_valid(const etc1_byte* pHeader); + +// Read the image width from a PKM header + +etc1_uint32 etc1_pkm_get_width(const etc1_byte* pHeader); + +// Read the image height from a PKM header + +etc1_uint32 etc1_pkm_get_height(const etc1_byte* pHeader); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/external/android-emugl/host/include/GLES/gl.h b/external/android-emugl/host/include/GLES/gl.h new file mode 100644 index 0000000..5b8d85a --- /dev/null +++ b/external/android-emugl/host/include/GLES/gl.h @@ -0,0 +1,770 @@ +#ifndef __gl_h_ +#define __gl_h_ + +/* $Revision: 10601 $ on $Date:: 2010-03-04 22:15:27 -0800 #$ */ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * This document is licensed under the SGI Free Software B License Version + * 2.0. For details, see http://oss.sgi.com/projects/FreeB/ . + */ + +typedef void GLvoid; +typedef char GLchar; +typedef unsigned int GLenum; +typedef unsigned char GLboolean; +typedef unsigned int GLbitfield; +typedef khronos_int8_t GLbyte; +typedef short GLshort; +typedef int GLint; +typedef int GLsizei; +typedef khronos_uint8_t GLubyte; +typedef unsigned short GLushort; +typedef unsigned int GLuint; +typedef khronos_float_t GLfloat; +typedef khronos_float_t GLclampf; +typedef khronos_int32_t GLfixed; +typedef khronos_int32_t GLclampx; + +typedef khronos_intptr_t GLintptr; +typedef khronos_ssize_t GLsizeiptr; + + +/*************************************************************/ + +/* OpenGL ES core versions */ +#define GL_VERSION_ES_CM_1_0 1 +#define GL_VERSION_ES_CL_1_0 1 +#define GL_VERSION_ES_CM_1_1 1 +#define GL_VERSION_ES_CL_1_1 1 + +/* ClearBufferMask */ +#define GL_DEPTH_BUFFER_BIT 0x00000100 +#define GL_STENCIL_BUFFER_BIT 0x00000400 +#define GL_COLOR_BUFFER_BIT 0x00004000 + +/* Boolean */ +#define GL_FALSE 0 +#define GL_TRUE 1 + +/* BeginMode */ +#define GL_POINTS 0x0000 +#define GL_LINES 0x0001 +#define GL_LINE_LOOP 0x0002 +#define GL_LINE_STRIP 0x0003 +#define GL_TRIANGLES 0x0004 +#define GL_TRIANGLE_STRIP 0x0005 +#define GL_TRIANGLE_FAN 0x0006 + +/* AlphaFunction */ +#define GL_NEVER 0x0200 +#define GL_LESS 0x0201 +#define GL_EQUAL 0x0202 +#define GL_LEQUAL 0x0203 +#define GL_GREATER 0x0204 +#define GL_NOTEQUAL 0x0205 +#define GL_GEQUAL 0x0206 +#define GL_ALWAYS 0x0207 + +/* BlendingFactorDest */ +#define GL_ZERO 0 +#define GL_ONE 1 +#define GL_SRC_COLOR 0x0300 +#define GL_ONE_MINUS_SRC_COLOR 0x0301 +#define GL_SRC_ALPHA 0x0302 +#define GL_ONE_MINUS_SRC_ALPHA 0x0303 +#define GL_DST_ALPHA 0x0304 +#define GL_ONE_MINUS_DST_ALPHA 0x0305 + +/* BlendingFactorSrc */ +/* GL_ZERO */ +/* GL_ONE */ +#define GL_DST_COLOR 0x0306 +#define GL_ONE_MINUS_DST_COLOR 0x0307 +#define GL_SRC_ALPHA_SATURATE 0x0308 +/* GL_SRC_ALPHA */ +/* GL_ONE_MINUS_SRC_ALPHA */ +/* GL_DST_ALPHA */ +/* GL_ONE_MINUS_DST_ALPHA */ + +/* ClipPlaneName */ +#define GL_CLIP_PLANE0 0x3000 +#define GL_CLIP_PLANE1 0x3001 +#define GL_CLIP_PLANE2 0x3002 +#define GL_CLIP_PLANE3 0x3003 +#define GL_CLIP_PLANE4 0x3004 +#define GL_CLIP_PLANE5 0x3005 + +/* ColorMaterialFace */ +/* GL_FRONT_AND_BACK */ + +/* ColorMaterialParameter */ +/* GL_AMBIENT_AND_DIFFUSE */ + +/* ColorPointerType */ +/* GL_UNSIGNED_BYTE */ +/* GL_FLOAT */ +/* GL_FIXED */ + +/* CullFaceMode */ +#define GL_FRONT 0x0404 +#define GL_BACK 0x0405 +#define GL_FRONT_AND_BACK 0x0408 + +/* DepthFunction */ +/* GL_NEVER */ +/* GL_LESS */ +/* GL_EQUAL */ +/* GL_LEQUAL */ +/* GL_GREATER */ +/* GL_NOTEQUAL */ +/* GL_GEQUAL */ +/* GL_ALWAYS */ + +/* EnableCap */ +#define GL_FOG 0x0B60 +#define GL_LIGHTING 0x0B50 +#define GL_TEXTURE_2D 0x0DE1 +#define GL_CULL_FACE 0x0B44 +#define GL_ALPHA_TEST 0x0BC0 +#define GL_BLEND 0x0BE2 +#define GL_COLOR_LOGIC_OP 0x0BF2 +#define GL_DITHER 0x0BD0 +#define GL_STENCIL_TEST 0x0B90 +#define GL_DEPTH_TEST 0x0B71 +/* GL_LIGHT0 */ +/* GL_LIGHT1 */ +/* GL_LIGHT2 */ +/* GL_LIGHT3 */ +/* GL_LIGHT4 */ +/* GL_LIGHT5 */ +/* GL_LIGHT6 */ +/* GL_LIGHT7 */ +#define GL_POINT_SMOOTH 0x0B10 +#define GL_LINE_SMOOTH 0x0B20 +#define GL_SCISSOR_TEST 0x0C11 +#define GL_COLOR_MATERIAL 0x0B57 +#define GL_NORMALIZE 0x0BA1 +#define GL_RESCALE_NORMAL 0x803A +#define GL_POLYGON_OFFSET_FILL 0x8037 +#define GL_VERTEX_ARRAY 0x8074 +#define GL_NORMAL_ARRAY 0x8075 +#define GL_COLOR_ARRAY 0x8076 +#define GL_TEXTURE_COORD_ARRAY 0x8078 +#define GL_MULTISAMPLE 0x809D +#define GL_SAMPLE_ALPHA_TO_COVERAGE 0x809E +#define GL_SAMPLE_ALPHA_TO_ONE 0x809F +#define GL_SAMPLE_COVERAGE 0x80A0 + +/* ErrorCode */ +#define GL_NO_ERROR 0 +#define GL_INVALID_ENUM 0x0500 +#define GL_INVALID_VALUE 0x0501 +#define GL_INVALID_OPERATION 0x0502 +#define GL_STACK_OVERFLOW 0x0503 +#define GL_STACK_UNDERFLOW 0x0504 +#define GL_OUT_OF_MEMORY 0x0505 + +/* FogMode */ +/* GL_LINEAR */ +#define GL_EXP 0x0800 +#define GL_EXP2 0x0801 + +/* FogParameter */ +#define GL_FOG_DENSITY 0x0B62 +#define GL_FOG_START 0x0B63 +#define GL_FOG_END 0x0B64 +#define GL_FOG_MODE 0x0B65 +#define GL_FOG_COLOR 0x0B66 + +/* FrontFaceDirection */ +#define GL_CW 0x0900 +#define GL_CCW 0x0901 + +/* GetPName */ +#define GL_CURRENT_COLOR 0x0B00 +#define GL_CURRENT_NORMAL 0x0B02 +#define GL_CURRENT_TEXTURE_COORDS 0x0B03 +#define GL_POINT_SIZE 0x0B11 +#define GL_POINT_SIZE_MIN 0x8126 +#define GL_POINT_SIZE_MAX 0x8127 +#define GL_POINT_FADE_THRESHOLD_SIZE 0x8128 +#define GL_POINT_DISTANCE_ATTENUATION 0x8129 +#define GL_SMOOTH_POINT_SIZE_RANGE 0x0B12 +#define GL_LINE_WIDTH 0x0B21 +#define GL_SMOOTH_LINE_WIDTH_RANGE 0x0B22 +#define GL_ALIASED_POINT_SIZE_RANGE 0x846D +#define GL_ALIASED_LINE_WIDTH_RANGE 0x846E +#define GL_CULL_FACE_MODE 0x0B45 +#define GL_FRONT_FACE 0x0B46 +#define GL_SHADE_MODEL 0x0B54 +#define GL_DEPTH_RANGE 0x0B70 +#define GL_DEPTH_WRITEMASK 0x0B72 +#define GL_DEPTH_CLEAR_VALUE 0x0B73 +#define GL_DEPTH_FUNC 0x0B74 +#define GL_STENCIL_CLEAR_VALUE 0x0B91 +#define GL_STENCIL_FUNC 0x0B92 +#define GL_STENCIL_VALUE_MASK 0x0B93 +#define GL_STENCIL_FAIL 0x0B94 +#define GL_STENCIL_PASS_DEPTH_FAIL 0x0B95 +#define GL_STENCIL_PASS_DEPTH_PASS 0x0B96 +#define GL_STENCIL_REF 0x0B97 +#define GL_STENCIL_WRITEMASK 0x0B98 +#define GL_MATRIX_MODE 0x0BA0 +#define GL_VIEWPORT 0x0BA2 +#define GL_MODELVIEW_STACK_DEPTH 0x0BA3 +#define GL_PROJECTION_STACK_DEPTH 0x0BA4 +#define GL_TEXTURE_STACK_DEPTH 0x0BA5 +#define GL_MODELVIEW_MATRIX 0x0BA6 +#define GL_PROJECTION_MATRIX 0x0BA7 +#define GL_TEXTURE_MATRIX 0x0BA8 +#define GL_ALPHA_TEST_FUNC 0x0BC1 +#define GL_ALPHA_TEST_REF 0x0BC2 +#define GL_BLEND_DST 0x0BE0 +#define GL_BLEND_SRC 0x0BE1 +#define GL_LOGIC_OP_MODE 0x0BF0 +#define GL_SCISSOR_BOX 0x0C10 +#define GL_SCISSOR_TEST 0x0C11 +#define GL_COLOR_CLEAR_VALUE 0x0C22 +#define GL_COLOR_WRITEMASK 0x0C23 +#define GL_UNPACK_ALIGNMENT 0x0CF5 +#define GL_PACK_ALIGNMENT 0x0D05 +#define GL_MAX_LIGHTS 0x0D31 +#define GL_MAX_CLIP_PLANES 0x0D32 +#define GL_MAX_TEXTURE_SIZE 0x0D33 +#define GL_MAX_MODELVIEW_STACK_DEPTH 0x0D36 +#define GL_MAX_PROJECTION_STACK_DEPTH 0x0D38 +#define GL_MAX_TEXTURE_STACK_DEPTH 0x0D39 +#define GL_MAX_VIEWPORT_DIMS 0x0D3A +#define GL_MAX_TEXTURE_UNITS 0x84E2 +#define GL_SUBPIXEL_BITS 0x0D50 +#define GL_RED_BITS 0x0D52 +#define GL_GREEN_BITS 0x0D53 +#define GL_BLUE_BITS 0x0D54 +#define GL_ALPHA_BITS 0x0D55 +#define GL_DEPTH_BITS 0x0D56 +#define GL_STENCIL_BITS 0x0D57 +#define GL_POLYGON_OFFSET_UNITS 0x2A00 +#define GL_POLYGON_OFFSET_FILL 0x8037 +#define GL_POLYGON_OFFSET_FACTOR 0x8038 +#define GL_TEXTURE_BINDING_2D 0x8069 +#define GL_VERTEX_ARRAY_SIZE 0x807A +#define GL_VERTEX_ARRAY_TYPE 0x807B +#define GL_VERTEX_ARRAY_STRIDE 0x807C +#define GL_NORMAL_ARRAY_TYPE 0x807E +#define GL_NORMAL_ARRAY_STRIDE 0x807F +#define GL_COLOR_ARRAY_SIZE 0x8081 +#define GL_COLOR_ARRAY_TYPE 0x8082 +#define GL_COLOR_ARRAY_STRIDE 0x8083 +#define GL_TEXTURE_COORD_ARRAY_SIZE 0x8088 +#define GL_TEXTURE_COORD_ARRAY_TYPE 0x8089 +#define GL_TEXTURE_COORD_ARRAY_STRIDE 0x808A +#define GL_VERTEX_ARRAY_POINTER 0x808E +#define GL_NORMAL_ARRAY_POINTER 0x808F +#define GL_COLOR_ARRAY_POINTER 0x8090 +#define GL_TEXTURE_COORD_ARRAY_POINTER 0x8092 +#define GL_SAMPLE_BUFFERS 0x80A8 +#define GL_SAMPLES 0x80A9 +#define GL_SAMPLE_COVERAGE_VALUE 0x80AA +#define GL_SAMPLE_COVERAGE_INVERT 0x80AB + +/* GetTextureParameter */ +/* GL_TEXTURE_MAG_FILTER */ +/* GL_TEXTURE_MIN_FILTER */ +/* GL_TEXTURE_WRAP_S */ +/* GL_TEXTURE_WRAP_T */ + +#define GL_NUM_COMPRESSED_TEXTURE_FORMATS 0x86A2 +#define GL_COMPRESSED_TEXTURE_FORMATS 0x86A3 + +/* HintMode */ +#define GL_DONT_CARE 0x1100 +#define GL_FASTEST 0x1101 +#define GL_NICEST 0x1102 + +/* HintTarget */ +#define GL_PERSPECTIVE_CORRECTION_HINT 0x0C50 +#define GL_POINT_SMOOTH_HINT 0x0C51 +#define GL_LINE_SMOOTH_HINT 0x0C52 +#define GL_FOG_HINT 0x0C54 +#define GL_GENERATE_MIPMAP_HINT 0x8192 + +/* LightModelParameter */ +#define GL_LIGHT_MODEL_AMBIENT 0x0B53 +#define GL_LIGHT_MODEL_TWO_SIDE 0x0B52 + +/* LightParameter */ +#define GL_AMBIENT 0x1200 +#define GL_DIFFUSE 0x1201 +#define GL_SPECULAR 0x1202 +#define GL_POSITION 0x1203 +#define GL_SPOT_DIRECTION 0x1204 +#define GL_SPOT_EXPONENT 0x1205 +#define GL_SPOT_CUTOFF 0x1206 +#define GL_CONSTANT_ATTENUATION 0x1207 +#define GL_LINEAR_ATTENUATION 0x1208 +#define GL_QUADRATIC_ATTENUATION 0x1209 + +/* DataType */ +#define GL_BYTE 0x1400 +#define GL_UNSIGNED_BYTE 0x1401 +#define GL_SHORT 0x1402 +#define GL_UNSIGNED_SHORT 0x1403 +#define GL_FLOAT 0x1406 +#define GL_FIXED 0x140C + +/* LogicOp */ +#define GL_CLEAR 0x1500 +#define GL_AND 0x1501 +#define GL_AND_REVERSE 0x1502 +#define GL_COPY 0x1503 +#define GL_AND_INVERTED 0x1504 +#define GL_NOOP 0x1505 +#define GL_XOR 0x1506 +#define GL_OR 0x1507 +#define GL_NOR 0x1508 +#define GL_EQUIV 0x1509 +#define GL_INVERT 0x150A +#define GL_OR_REVERSE 0x150B +#define GL_COPY_INVERTED 0x150C +#define GL_OR_INVERTED 0x150D +#define GL_NAND 0x150E +#define GL_SET 0x150F + +/* MaterialFace */ +/* GL_FRONT_AND_BACK */ + +/* MaterialParameter */ +#define GL_EMISSION 0x1600 +#define GL_SHININESS 0x1601 +#define GL_AMBIENT_AND_DIFFUSE 0x1602 +/* GL_AMBIENT */ +/* GL_DIFFUSE */ +/* GL_SPECULAR */ + +/* MatrixMode */ +#define GL_MODELVIEW 0x1700 +#define GL_PROJECTION 0x1701 +#define GL_TEXTURE 0x1702 + +/* NormalPointerType */ +/* GL_BYTE */ +/* GL_SHORT */ +/* GL_FLOAT */ +/* GL_FIXED */ + +/* PixelFormat */ +#define GL_ALPHA 0x1906 +#define GL_RGB 0x1907 +#define GL_RGBA 0x1908 +#define GL_LUMINANCE 0x1909 +#define GL_LUMINANCE_ALPHA 0x190A + +/* PixelStoreParameter */ +#define GL_UNPACK_ALIGNMENT 0x0CF5 +#define GL_PACK_ALIGNMENT 0x0D05 + +/* PixelType */ +/* GL_UNSIGNED_BYTE */ +#define GL_UNSIGNED_SHORT_4_4_4_4 0x8033 +#define GL_UNSIGNED_SHORT_5_5_5_1 0x8034 +#define GL_UNSIGNED_SHORT_5_6_5 0x8363 + +/* ShadingModel */ +#define GL_FLAT 0x1D00 +#define GL_SMOOTH 0x1D01 + +/* StencilFunction */ +/* GL_NEVER */ +/* GL_LESS */ +/* GL_EQUAL */ +/* GL_LEQUAL */ +/* GL_GREATER */ +/* GL_NOTEQUAL */ +/* GL_GEQUAL */ +/* GL_ALWAYS */ + +/* StencilOp */ +/* GL_ZERO */ +#define GL_KEEP 0x1E00 +#define GL_REPLACE 0x1E01 +#define GL_INCR 0x1E02 +#define GL_DECR 0x1E03 +/* GL_INVERT */ + +/* StringName */ +#define GL_VENDOR 0x1F00 +#define GL_RENDERER 0x1F01 +#define GL_VERSION 0x1F02 +#define GL_EXTENSIONS 0x1F03 + +/* TexCoordPointerType */ +/* GL_SHORT */ +/* GL_FLOAT */ +/* GL_FIXED */ +/* GL_BYTE */ + +/* TextureEnvMode */ +#define GL_MODULATE 0x2100 +#define GL_DECAL 0x2101 +/* GL_BLEND */ +#define GL_ADD 0x0104 +/* GL_REPLACE */ + +/* TextureEnvParameter */ +#define GL_TEXTURE_ENV_MODE 0x2200 +#define GL_TEXTURE_ENV_COLOR 0x2201 + +/* TextureEnvTarget */ +#define GL_TEXTURE_ENV 0x2300 + +/* TextureMagFilter */ +#define GL_NEAREST 0x2600 +#define GL_LINEAR 0x2601 + +/* TextureMinFilter */ +/* GL_NEAREST */ +/* GL_LINEAR */ +#define GL_NEAREST_MIPMAP_NEAREST 0x2700 +#define GL_LINEAR_MIPMAP_NEAREST 0x2701 +#define GL_NEAREST_MIPMAP_LINEAR 0x2702 +#define GL_LINEAR_MIPMAP_LINEAR 0x2703 + +/* TextureParameterName */ +#define GL_TEXTURE_MAG_FILTER 0x2800 +#define GL_TEXTURE_MIN_FILTER 0x2801 +#define GL_TEXTURE_WRAP_S 0x2802 +#define GL_TEXTURE_WRAP_T 0x2803 +#define GL_GENERATE_MIPMAP 0x8191 + +/* TextureTarget */ +/* GL_TEXTURE_2D */ + +/* TextureUnit */ +#define GL_TEXTURE0 0x84C0 +#define GL_TEXTURE1 0x84C1 +#define GL_TEXTURE2 0x84C2 +#define GL_TEXTURE3 0x84C3 +#define GL_TEXTURE4 0x84C4 +#define GL_TEXTURE5 0x84C5 +#define GL_TEXTURE6 0x84C6 +#define GL_TEXTURE7 0x84C7 +#define GL_TEXTURE8 0x84C8 +#define GL_TEXTURE9 0x84C9 +#define GL_TEXTURE10 0x84CA +#define GL_TEXTURE11 0x84CB +#define GL_TEXTURE12 0x84CC +#define GL_TEXTURE13 0x84CD +#define GL_TEXTURE14 0x84CE +#define GL_TEXTURE15 0x84CF +#define GL_TEXTURE16 0x84D0 +#define GL_TEXTURE17 0x84D1 +#define GL_TEXTURE18 0x84D2 +#define GL_TEXTURE19 0x84D3 +#define GL_TEXTURE20 0x84D4 +#define GL_TEXTURE21 0x84D5 +#define GL_TEXTURE22 0x84D6 +#define GL_TEXTURE23 0x84D7 +#define GL_TEXTURE24 0x84D8 +#define GL_TEXTURE25 0x84D9 +#define GL_TEXTURE26 0x84DA +#define GL_TEXTURE27 0x84DB +#define GL_TEXTURE28 0x84DC +#define GL_TEXTURE29 0x84DD +#define GL_TEXTURE30 0x84DE +#define GL_TEXTURE31 0x84DF +#define GL_ACTIVE_TEXTURE 0x84E0 +#define GL_CLIENT_ACTIVE_TEXTURE 0x84E1 + +/* TextureWrapMode */ +#define GL_REPEAT 0x2901 +#define GL_CLAMP_TO_EDGE 0x812F + +/* VertexPointerType */ +/* GL_SHORT */ +/* GL_FLOAT */ +/* GL_FIXED */ +/* GL_BYTE */ + +/* LightName */ +#define GL_LIGHT0 0x4000 +#define GL_LIGHT1 0x4001 +#define GL_LIGHT2 0x4002 +#define GL_LIGHT3 0x4003 +#define GL_LIGHT4 0x4004 +#define GL_LIGHT5 0x4005 +#define GL_LIGHT6 0x4006 +#define GL_LIGHT7 0x4007 + +/* Buffer Objects */ +#define GL_ARRAY_BUFFER 0x8892 +#define GL_ELEMENT_ARRAY_BUFFER 0x8893 + +#define GL_ARRAY_BUFFER_BINDING 0x8894 +#define GL_ELEMENT_ARRAY_BUFFER_BINDING 0x8895 +#define GL_VERTEX_ARRAY_BUFFER_BINDING 0x8896 +#define GL_NORMAL_ARRAY_BUFFER_BINDING 0x8897 +#define GL_COLOR_ARRAY_BUFFER_BINDING 0x8898 +#define GL_TEXTURE_COORD_ARRAY_BUFFER_BINDING 0x889A + +#define GL_STATIC_DRAW 0x88E4 +#define GL_DYNAMIC_DRAW 0x88E8 + +#define GL_BUFFER_SIZE 0x8764 +#define GL_BUFFER_USAGE 0x8765 + +/* Texture combine + dot3 */ +#define GL_SUBTRACT 0x84E7 +#define GL_COMBINE 0x8570 +#define GL_COMBINE_RGB 0x8571 +#define GL_COMBINE_ALPHA 0x8572 +#define GL_RGB_SCALE 0x8573 +#define GL_ADD_SIGNED 0x8574 +#define GL_INTERPOLATE 0x8575 +#define GL_CONSTANT 0x8576 +#define GL_PRIMARY_COLOR 0x8577 +#define GL_PREVIOUS 0x8578 +#define GL_OPERAND0_RGB 0x8590 +#define GL_OPERAND1_RGB 0x8591 +#define GL_OPERAND2_RGB 0x8592 +#define GL_OPERAND0_ALPHA 0x8598 +#define GL_OPERAND1_ALPHA 0x8599 +#define GL_OPERAND2_ALPHA 0x859A + +#define GL_ALPHA_SCALE 0x0D1C + +#define GL_SRC0_RGB 0x8580 +#define GL_SRC1_RGB 0x8581 +#define GL_SRC2_RGB 0x8582 +#define GL_SRC0_ALPHA 0x8588 +#define GL_SRC1_ALPHA 0x8589 +#define GL_SRC2_ALPHA 0x858A + +#define GL_DOT3_RGB 0x86AE +#define GL_DOT3_RGBA 0x86AF + +/*------------------------------------------------------------------------* + * required OES extension tokens + *------------------------------------------------------------------------*/ + +/* OES_read_format */ +#ifndef GL_OES_read_format +#define GL_IMPLEMENTATION_COLOR_READ_TYPE_OES 0x8B9A +#define GL_IMPLEMENTATION_COLOR_READ_FORMAT_OES 0x8B9B +#endif + +/* GL_OES_compressed_paletted_texture */ +#ifndef GL_OES_compressed_paletted_texture +#define GL_PALETTE4_RGB8_OES 0x8B90 +#define GL_PALETTE4_RGBA8_OES 0x8B91 +#define GL_PALETTE4_R5_G6_B5_OES 0x8B92 +#define GL_PALETTE4_RGBA4_OES 0x8B93 +#define GL_PALETTE4_RGB5_A1_OES 0x8B94 +#define GL_PALETTE8_RGB8_OES 0x8B95 +#define GL_PALETTE8_RGBA8_OES 0x8B96 +#define GL_PALETTE8_R5_G6_B5_OES 0x8B97 +#define GL_PALETTE8_RGBA4_OES 0x8B98 +#define GL_PALETTE8_RGB5_A1_OES 0x8B99 +#endif + +/* OES_point_size_array */ +#ifndef GL_OES_point_size_array +#define GL_POINT_SIZE_ARRAY_OES 0x8B9C +#define GL_POINT_SIZE_ARRAY_TYPE_OES 0x898A +#define GL_POINT_SIZE_ARRAY_STRIDE_OES 0x898B +#define GL_POINT_SIZE_ARRAY_POINTER_OES 0x898C +#define GL_POINT_SIZE_ARRAY_BUFFER_BINDING_OES 0x8B9F +#endif + +/* GL_OES_point_sprite */ +#ifndef GL_OES_point_sprite +#define GL_POINT_SPRITE_OES 0x8861 +#define GL_COORD_REPLACE_OES 0x8862 +#endif + +/*************************************************************/ + +/* Available only in Common profile */ +GL_API void GL_APIENTRY glAlphaFunc (GLenum func, GLclampf ref); +GL_API void GL_APIENTRY glClearColor (GLclampf red, GLclampf green, GLclampf blue, GLclampf alpha); +GL_API void GL_APIENTRY glClearDepthf (GLclampf depth); +GL_API void GL_APIENTRY glClipPlanef (GLenum plane, const GLfloat *equation); +GL_API void GL_APIENTRY glColor4f (GLfloat red, GLfloat green, GLfloat blue, GLfloat alpha); +GL_API void GL_APIENTRY glDepthRangef (GLclampf zNear, GLclampf zFar); +GL_API void GL_APIENTRY glFogf (GLenum pname, GLfloat param); +GL_API void GL_APIENTRY glFogfv (GLenum pname, const GLfloat *params); +GL_API void GL_APIENTRY glFrustumf (GLfloat left, GLfloat right, GLfloat bottom, GLfloat top, GLfloat zNear, GLfloat zFar); +GL_API void GL_APIENTRY glGetClipPlanef (GLenum pname, GLfloat eqn[4]); +GL_API void GL_APIENTRY glGetFloatv (GLenum pname, GLfloat *params); +GL_API void GL_APIENTRY glGetLightfv (GLenum light, GLenum pname, GLfloat *params); +GL_API void GL_APIENTRY glGetMaterialfv (GLenum face, GLenum pname, GLfloat *params); +GL_API void GL_APIENTRY glGetTexEnvfv (GLenum env, GLenum pname, GLfloat *params); +GL_API void GL_APIENTRY glGetTexParameterfv (GLenum target, GLenum pname, GLfloat *params); +GL_API void GL_APIENTRY glLightModelf (GLenum pname, GLfloat param); +GL_API void GL_APIENTRY glLightModelfv (GLenum pname, const GLfloat *params); +GL_API void GL_APIENTRY glLightf (GLenum light, GLenum pname, GLfloat param); +GL_API void GL_APIENTRY glLightfv (GLenum light, GLenum pname, const GLfloat *params); +GL_API void GL_APIENTRY glLineWidth (GLfloat width); +GL_API void GL_APIENTRY glLoadMatrixf (const GLfloat *m); +GL_API void GL_APIENTRY glMaterialf (GLenum face, GLenum pname, GLfloat param); +GL_API void GL_APIENTRY glMaterialfv (GLenum face, GLenum pname, const GLfloat *params); +GL_API void GL_APIENTRY glMultMatrixf (const GLfloat *m); +GL_API void GL_APIENTRY glMultiTexCoord4f (GLenum target, GLfloat s, GLfloat t, GLfloat r, GLfloat q); +GL_API void GL_APIENTRY glNormal3f (GLfloat nx, GLfloat ny, GLfloat nz); +GL_API void GL_APIENTRY glOrthof (GLfloat left, GLfloat right, GLfloat bottom, GLfloat top, GLfloat zNear, GLfloat zFar); +GL_API void GL_APIENTRY glPointParameterf (GLenum pname, GLfloat param); +GL_API void GL_APIENTRY glPointParameterfv (GLenum pname, const GLfloat *params); +GL_API void GL_APIENTRY glPointSize (GLfloat size); +GL_API void GL_APIENTRY glPolygonOffset (GLfloat factor, GLfloat units); +GL_API void GL_APIENTRY glRotatef (GLfloat angle, GLfloat x, GLfloat y, GLfloat z); +GL_API void GL_APIENTRY glScalef (GLfloat x, GLfloat y, GLfloat z); +GL_API void GL_APIENTRY glTexEnvf (GLenum target, GLenum pname, GLfloat param); +GL_API void GL_APIENTRY glTexEnvfv (GLenum target, GLenum pname, const GLfloat *params); +GL_API void GL_APIENTRY glTexParameterf (GLenum target, GLenum pname, GLfloat param); +GL_API void GL_APIENTRY glTexParameterfv (GLenum target, GLenum pname, const GLfloat *params); +GL_API void GL_APIENTRY glTranslatef (GLfloat x, GLfloat y, GLfloat z); + +/* Available in both Common and Common-Lite profiles */ +GL_API void GL_APIENTRY glActiveTexture (GLenum texture); +GL_API void GL_APIENTRY glAlphaFuncx (GLenum func, GLclampx ref); +GL_API void GL_APIENTRY glBindBuffer (GLenum target, GLuint buffer); +GL_API void GL_APIENTRY glBindTexture (GLenum target, GLuint texture); +GL_API void GL_APIENTRY glBlendFunc (GLenum sfactor, GLenum dfactor); +GL_API void GL_APIENTRY glBufferData (GLenum target, GLsizeiptr size, const GLvoid *data, GLenum usage); +GL_API void GL_APIENTRY glBufferSubData (GLenum target, GLintptr offset, GLsizeiptr size, const GLvoid *data); +GL_API void GL_APIENTRY glClear (GLbitfield mask); +GL_API void GL_APIENTRY glClearColorx (GLclampx red, GLclampx green, GLclampx blue, GLclampx alpha); +GL_API void GL_APIENTRY glClearDepthx (GLclampx depth); +GL_API void GL_APIENTRY glClearStencil (GLint s); +GL_API void GL_APIENTRY glClientActiveTexture (GLenum texture); +GL_API void GL_APIENTRY glClipPlanex (GLenum plane, const GLfixed *equation); +GL_API void GL_APIENTRY glColor4ub (GLubyte red, GLubyte green, GLubyte blue, GLubyte alpha); +GL_API void GL_APIENTRY glColor4x (GLfixed red, GLfixed green, GLfixed blue, GLfixed alpha); +GL_API void GL_APIENTRY glColorMask (GLboolean red, GLboolean green, GLboolean blue, GLboolean alpha); +GL_API void GL_APIENTRY glColorPointer (GLint size, GLenum type, GLsizei stride, const GLvoid *pointer); +GL_API void GL_APIENTRY glCompressedTexImage2D (GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLint border, GLsizei imageSize, const GLvoid *data); +GL_API void GL_APIENTRY glCompressedTexSubImage2D (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLsizei imageSize, const GLvoid *data); +GL_API void GL_APIENTRY glCopyTexImage2D (GLenum target, GLint level, GLenum internalformat, GLint x, GLint y, GLsizei width, GLsizei height, GLint border); +GL_API void GL_APIENTRY glCopyTexSubImage2D (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint x, GLint y, GLsizei width, GLsizei height); +GL_API void GL_APIENTRY glCullFace (GLenum mode); +GL_API void GL_APIENTRY glDeleteBuffers (GLsizei n, const GLuint *buffers); +GL_API void GL_APIENTRY glDeleteTextures (GLsizei n, const GLuint *textures); +GL_API void GL_APIENTRY glDepthFunc (GLenum func); +GL_API void GL_APIENTRY glDepthMask (GLboolean flag); +GL_API void GL_APIENTRY glDepthRangex (GLclampx zNear, GLclampx zFar); +GL_API void GL_APIENTRY glDisable (GLenum cap); +GL_API void GL_APIENTRY glDisableClientState (GLenum array); +GL_API void GL_APIENTRY glDrawArrays (GLenum mode, GLint first, GLsizei count); +GL_API void GL_APIENTRY glDrawElements (GLenum mode, GLsizei count, GLenum type, const GLvoid *indices); +GL_API void GL_APIENTRY glEnable (GLenum cap); +GL_API void GL_APIENTRY glEnableClientState (GLenum array); +GL_API void GL_APIENTRY glFinish (void); +GL_API void GL_APIENTRY glFlush (void); +GL_API void GL_APIENTRY glFogx (GLenum pname, GLfixed param); +GL_API void GL_APIENTRY glFogxv (GLenum pname, const GLfixed *params); +GL_API void GL_APIENTRY glFrontFace (GLenum mode); +GL_API void GL_APIENTRY glFrustumx (GLfixed left, GLfixed right, GLfixed bottom, GLfixed top, GLfixed zNear, GLfixed zFar); +GL_API void GL_APIENTRY glGetBooleanv (GLenum pname, GLboolean *params); +GL_API void GL_APIENTRY glGetBufferParameteriv (GLenum target, GLenum pname, GLint *params); +GL_API void GL_APIENTRY glGetClipPlanex (GLenum pname, GLfixed eqn[4]); +GL_API void GL_APIENTRY glGenBuffers (GLsizei n, GLuint *buffers); +GL_API void GL_APIENTRY glGenTextures (GLsizei n, GLuint *textures); +GL_API GLenum GL_APIENTRY glGetError (void); +GL_API void GL_APIENTRY glGetFixedv (GLenum pname, GLfixed *params); +GL_API void GL_APIENTRY glGetIntegerv (GLenum pname, GLint *params); +GL_API void GL_APIENTRY glGetLightxv (GLenum light, GLenum pname, GLfixed *params); +GL_API void GL_APIENTRY glGetMaterialxv (GLenum face, GLenum pname, GLfixed *params); +GL_API void GL_APIENTRY glGetPointerv (GLenum pname, GLvoid **params); +GL_API const GLubyte * GL_APIENTRY glGetString (GLenum name); +GL_API void GL_APIENTRY glGetTexEnviv (GLenum env, GLenum pname, GLint *params); +GL_API void GL_APIENTRY glGetTexEnvxv (GLenum env, GLenum pname, GLfixed *params); +GL_API void GL_APIENTRY glGetTexParameteriv (GLenum target, GLenum pname, GLint *params); +GL_API void GL_APIENTRY glGetTexParameterxv (GLenum target, GLenum pname, GLfixed *params); +GL_API void GL_APIENTRY glHint (GLenum target, GLenum mode); +GL_API GLboolean GL_APIENTRY glIsBuffer (GLuint buffer); +GL_API GLboolean GL_APIENTRY glIsEnabled (GLenum cap); +GL_API GLboolean GL_APIENTRY glIsTexture (GLuint texture); +GL_API void GL_APIENTRY glLightModelx (GLenum pname, GLfixed param); +GL_API void GL_APIENTRY glLightModelxv (GLenum pname, const GLfixed *params); +GL_API void GL_APIENTRY glLightx (GLenum light, GLenum pname, GLfixed param); +GL_API void GL_APIENTRY glLightxv (GLenum light, GLenum pname, const GLfixed *params); +GL_API void GL_APIENTRY glLineWidthx (GLfixed width); +GL_API void GL_APIENTRY glLoadIdentity (void); +GL_API void GL_APIENTRY glLoadMatrixx (const GLfixed *m); +GL_API void GL_APIENTRY glLogicOp (GLenum opcode); +GL_API void GL_APIENTRY glMaterialx (GLenum face, GLenum pname, GLfixed param); +GL_API void GL_APIENTRY glMaterialxv (GLenum face, GLenum pname, const GLfixed *params); +GL_API void GL_APIENTRY glMatrixMode (GLenum mode); +GL_API void GL_APIENTRY glMultMatrixx (const GLfixed *m); +GL_API void GL_APIENTRY glMultiTexCoord4x (GLenum target, GLfixed s, GLfixed t, GLfixed r, GLfixed q); +GL_API void GL_APIENTRY glNormal3x (GLfixed nx, GLfixed ny, GLfixed nz); +GL_API void GL_APIENTRY glNormalPointer (GLenum type, GLsizei stride, const GLvoid *pointer); +GL_API void GL_APIENTRY glOrthox (GLfixed left, GLfixed right, GLfixed bottom, GLfixed top, GLfixed zNear, GLfixed zFar); +GL_API void GL_APIENTRY glPixelStorei (GLenum pname, GLint param); +GL_API void GL_APIENTRY glPointParameterx (GLenum pname, GLfixed param); +GL_API void GL_APIENTRY glPointParameterxv (GLenum pname, const GLfixed *params); +GL_API void GL_APIENTRY glPointSizex (GLfixed size); +GL_API void GL_APIENTRY glPolygonOffsetx (GLfixed factor, GLfixed units); +GL_API void GL_APIENTRY glPopMatrix (void); +GL_API void GL_APIENTRY glPushMatrix (void); +GL_API void GL_APIENTRY glReadPixels (GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, GLvoid *pixels); +GL_API void GL_APIENTRY glRotatex (GLfixed angle, GLfixed x, GLfixed y, GLfixed z); +GL_API void GL_APIENTRY glSampleCoverage (GLclampf value, GLboolean invert); +GL_API void GL_APIENTRY glSampleCoveragex (GLclampx value, GLboolean invert); +GL_API void GL_APIENTRY glScalex (GLfixed x, GLfixed y, GLfixed z); +GL_API void GL_APIENTRY glScissor (GLint x, GLint y, GLsizei width, GLsizei height); +GL_API void GL_APIENTRY glShadeModel (GLenum mode); +GL_API void GL_APIENTRY glStencilFunc (GLenum func, GLint ref, GLuint mask); +GL_API void GL_APIENTRY glStencilMask (GLuint mask); +GL_API void GL_APIENTRY glStencilOp (GLenum fail, GLenum zfail, GLenum zpass); +GL_API void GL_APIENTRY glTexCoordPointer (GLint size, GLenum type, GLsizei stride, const GLvoid *pointer); +GL_API void GL_APIENTRY glTexEnvi (GLenum target, GLenum pname, GLint param); +GL_API void GL_APIENTRY glTexEnvx (GLenum target, GLenum pname, GLfixed param); +GL_API void GL_APIENTRY glTexEnviv (GLenum target, GLenum pname, const GLint *params); +GL_API void GL_APIENTRY glTexEnvxv (GLenum target, GLenum pname, const GLfixed *params); +GL_API void GL_APIENTRY glTexImage2D (GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, const GLvoid *pixels); +GL_API void GL_APIENTRY glTexParameteri (GLenum target, GLenum pname, GLint param); +GL_API void GL_APIENTRY glTexParameterx (GLenum target, GLenum pname, GLfixed param); +GL_API void GL_APIENTRY glTexParameteriv (GLenum target, GLenum pname, const GLint *params); +GL_API void GL_APIENTRY glTexParameterxv (GLenum target, GLenum pname, const GLfixed *params); +GL_API void GL_APIENTRY glTexSubImage2D (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, const GLvoid *pixels); +GL_API void GL_APIENTRY glTranslatex (GLfixed x, GLfixed y, GLfixed z); +GL_API void GL_APIENTRY glVertexPointer (GLint size, GLenum type, GLsizei stride, const GLvoid *pointer); +GL_API void GL_APIENTRY glViewport (GLint x, GLint y, GLsizei width, GLsizei height); + +/*------------------------------------------------------------------------* + * Required OES extension functions + *------------------------------------------------------------------------*/ + +/* GL_OES_read_format */ +#ifndef GL_OES_read_format +#define GL_OES_read_format 1 +#endif + +/* GL_OES_compressed_paletted_texture */ +#ifndef GL_OES_compressed_paletted_texture +#define GL_OES_compressed_paletted_texture 1 +#endif + +/* GL_OES_point_size_array */ +#ifndef GL_OES_point_size_array +#define GL_OES_point_size_array 1 +GL_API void GL_APIENTRY glPointSizePointerOES (GLenum type, GLsizei stride, const GLvoid *pointer); +#endif + +/* GL_OES_point_sprite */ +#ifndef GL_OES_point_sprite +#define GL_OES_point_sprite 1 +#endif + +#ifdef __cplusplus +} +#endif + +#endif /* __gl_h_ */ + diff --git a/external/android-emugl/host/include/GLES/glext.h b/external/android-emugl/host/include/GLES/glext.h new file mode 100644 index 0000000..130e4b0 --- /dev/null +++ b/external/android-emugl/host/include/GLES/glext.h @@ -0,0 +1,1073 @@ +#ifndef __glext_h_ +#define __glext_h_ + +/* $Revision: 13240 $ on $Date:: 2010-12-17 15:16:00 -0800 #$ */ + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * This document is licensed under the SGI Free Software B License Version + * 2.0. For details, see http://oss.sgi.com/projects/FreeB/ . + */ + +#ifndef GL_APIENTRYP +# define GL_APIENTRYP GL_APIENTRY* +#endif + +/*------------------------------------------------------------------------* + * OES extension tokens + *------------------------------------------------------------------------*/ + +/* GL_OES_blend_equation_separate */ +#ifndef GL_OES_blend_equation_separate +/* BLEND_EQUATION_RGB_OES same as BLEND_EQUATION_OES */ +#define GL_BLEND_EQUATION_RGB_OES 0x8009 +#define GL_BLEND_EQUATION_ALPHA_OES 0x883D +#endif + +/* GL_OES_blend_func_separate */ +#ifndef GL_OES_blend_func_separate +#define GL_BLEND_DST_RGB_OES 0x80C8 +#define GL_BLEND_SRC_RGB_OES 0x80C9 +#define GL_BLEND_DST_ALPHA_OES 0x80CA +#define GL_BLEND_SRC_ALPHA_OES 0x80CB +#endif + +/* GL_OES_blend_subtract */ +#ifndef GL_OES_blend_subtract +#define GL_BLEND_EQUATION_OES 0x8009 +#define GL_FUNC_ADD_OES 0x8006 +#define GL_FUNC_SUBTRACT_OES 0x800A +#define GL_FUNC_REVERSE_SUBTRACT_OES 0x800B +#endif + +/* GL_OES_compressed_ETC1_RGB8_texture */ +#ifndef GL_OES_compressed_ETC1_RGB8_texture +#define GL_ETC1_RGB8_OES 0x8D64 +#endif + +/* GL_OES_depth24 */ +#ifndef GL_OES_depth24 +#define GL_DEPTH_COMPONENT24_OES 0x81A6 +#endif + +/* GL_OES_depth32 */ +#ifndef GL_OES_depth32 +#define GL_DEPTH_COMPONENT32_OES 0x81A7 +#endif + +/* GL_OES_draw_texture */ +#ifndef GL_OES_draw_texture +#define GL_TEXTURE_CROP_RECT_OES 0x8B9D +#endif + +/* GL_OES_EGL_image */ +#ifndef GL_OES_EGL_image +typedef void* GLeglImageOES; +#endif + +/* GL_OES_EGL_image_external */ +#ifndef GL_OES_EGL_image_external +/* GLeglImageOES defined in GL_OES_EGL_image already. */ +#define GL_TEXTURE_EXTERNAL_OES 0x8D65 +#define GL_TEXTURE_BINDING_EXTERNAL_OES 0x8D67 +#define GL_REQUIRED_TEXTURE_IMAGE_UNITS_OES 0x8D68 +#endif + +/* GL_OES_element_index_uint */ +#ifndef GL_OES_element_index_uint +#define GL_UNSIGNED_INT 0x1405 +#endif + +/* GL_OES_fixed_point */ +#ifndef GL_OES_fixed_point +#define GL_FIXED_OES 0x140C +#endif + +/* GL_OES_framebuffer_object */ +#ifndef GL_OES_framebuffer_object +#define GL_NONE_OES 0 +#define GL_FRAMEBUFFER_OES 0x8D40 +#define GL_RENDERBUFFER_OES 0x8D41 +#define GL_RGBA4_OES 0x8056 +#define GL_RGB5_A1_OES 0x8057 +#define GL_RGB565_OES 0x8D62 +#define GL_DEPTH_COMPONENT16_OES 0x81A5 +#define GL_RENDERBUFFER_WIDTH_OES 0x8D42 +#define GL_RENDERBUFFER_HEIGHT_OES 0x8D43 +#define GL_RENDERBUFFER_INTERNAL_FORMAT_OES 0x8D44 +#define GL_RENDERBUFFER_RED_SIZE_OES 0x8D50 +#define GL_RENDERBUFFER_GREEN_SIZE_OES 0x8D51 +#define GL_RENDERBUFFER_BLUE_SIZE_OES 0x8D52 +#define GL_RENDERBUFFER_ALPHA_SIZE_OES 0x8D53 +#define GL_RENDERBUFFER_DEPTH_SIZE_OES 0x8D54 +#define GL_RENDERBUFFER_STENCIL_SIZE_OES 0x8D55 +#define GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE_OES 0x8CD0 +#define GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME_OES 0x8CD1 +#define GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_LEVEL_OES 0x8CD2 +#define GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_CUBE_MAP_FACE_OES 0x8CD3 +#define GL_COLOR_ATTACHMENT0_OES 0x8CE0 +#define GL_DEPTH_ATTACHMENT_OES 0x8D00 +#define GL_STENCIL_ATTACHMENT_OES 0x8D20 +#define GL_FRAMEBUFFER_COMPLETE_OES 0x8CD5 +#define GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT_OES 0x8CD6 +#define GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT_OES 0x8CD7 +#define GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS_OES 0x8CD9 +#define GL_FRAMEBUFFER_INCOMPLETE_FORMATS_OES 0x8CDA +#define GL_FRAMEBUFFER_UNSUPPORTED_OES 0x8CDD +#define GL_FRAMEBUFFER_BINDING_OES 0x8CA6 +#define GL_RENDERBUFFER_BINDING_OES 0x8CA7 +#define GL_MAX_RENDERBUFFER_SIZE_OES 0x84E8 +#define GL_INVALID_FRAMEBUFFER_OPERATION_OES 0x0506 +#endif + +/* GL_OES_mapbuffer */ +#ifndef GL_OES_mapbuffer +#define GL_WRITE_ONLY_OES 0x88B9 +#define GL_BUFFER_ACCESS_OES 0x88BB +#define GL_BUFFER_MAPPED_OES 0x88BC +#define GL_BUFFER_MAP_POINTER_OES 0x88BD +#endif + +/* GL_OES_matrix_get */ +#ifndef GL_OES_matrix_get +#define GL_MODELVIEW_MATRIX_FLOAT_AS_INT_BITS_OES 0x898D +#define GL_PROJECTION_MATRIX_FLOAT_AS_INT_BITS_OES 0x898E +#define GL_TEXTURE_MATRIX_FLOAT_AS_INT_BITS_OES 0x898F +#endif + +/* GL_OES_matrix_palette */ +#ifndef GL_OES_matrix_palette +#define GL_MAX_VERTEX_UNITS_OES 0x86A4 +#define GL_MAX_PALETTE_MATRICES_OES 0x8842 +#define GL_MATRIX_PALETTE_OES 0x8840 +#define GL_MATRIX_INDEX_ARRAY_OES 0x8844 +#define GL_WEIGHT_ARRAY_OES 0x86AD +#define GL_CURRENT_PALETTE_MATRIX_OES 0x8843 +#define GL_MATRIX_INDEX_ARRAY_SIZE_OES 0x8846 +#define GL_MATRIX_INDEX_ARRAY_TYPE_OES 0x8847 +#define GL_MATRIX_INDEX_ARRAY_STRIDE_OES 0x8848 +#define GL_MATRIX_INDEX_ARRAY_POINTER_OES 0x8849 +#define GL_MATRIX_INDEX_ARRAY_BUFFER_BINDING_OES 0x8B9E +#define GL_WEIGHT_ARRAY_SIZE_OES 0x86AB +#define GL_WEIGHT_ARRAY_TYPE_OES 0x86A9 +#define GL_WEIGHT_ARRAY_STRIDE_OES 0x86AA +#define GL_WEIGHT_ARRAY_POINTER_OES 0x86AC +#define GL_WEIGHT_ARRAY_BUFFER_BINDING_OES 0x889E +#endif + +/* GL_OES_packed_depth_stencil */ +#ifndef GL_OES_packed_depth_stencil +#define GL_DEPTH_STENCIL_OES 0x84F9 +#define GL_UNSIGNED_INT_24_8_OES 0x84FA +#define GL_DEPTH24_STENCIL8_OES 0x88F0 +#endif + +/* GL_OES_rgb8_rgba8 */ +#ifndef GL_OES_rgb8_rgba8 +#define GL_RGB8_OES 0x8051 +#define GL_RGBA8_OES 0x8058 +#endif + +/* GL_OES_stencil1 */ +#ifndef GL_OES_stencil1 +#define GL_STENCIL_INDEX1_OES 0x8D46 +#endif + +/* GL_OES_stencil4 */ +#ifndef GL_OES_stencil4 +#define GL_STENCIL_INDEX4_OES 0x8D47 +#endif + +/* GL_OES_stencil8 */ +#ifndef GL_OES_stencil8 +#define GL_STENCIL_INDEX8_OES 0x8D48 +#endif + +/* GL_OES_stencil_wrap */ +#ifndef GL_OES_stencil_wrap +#define GL_INCR_WRAP_OES 0x8507 +#define GL_DECR_WRAP_OES 0x8508 +#endif + +/* GL_OES_texture_cube_map */ +#ifndef GL_OES_texture_cube_map +#define GL_NORMAL_MAP_OES 0x8511 +#define GL_REFLECTION_MAP_OES 0x8512 +#define GL_TEXTURE_CUBE_MAP_OES 0x8513 +#define GL_TEXTURE_BINDING_CUBE_MAP_OES 0x8514 +#define GL_TEXTURE_CUBE_MAP_POSITIVE_X_OES 0x8515 +#define GL_TEXTURE_CUBE_MAP_NEGATIVE_X_OES 0x8516 +#define GL_TEXTURE_CUBE_MAP_POSITIVE_Y_OES 0x8517 +#define GL_TEXTURE_CUBE_MAP_NEGATIVE_Y_OES 0x8518 +#define GL_TEXTURE_CUBE_MAP_POSITIVE_Z_OES 0x8519 +#define GL_TEXTURE_CUBE_MAP_NEGATIVE_Z_OES 0x851A +#define GL_MAX_CUBE_MAP_TEXTURE_SIZE_OES 0x851C +#define GL_TEXTURE_GEN_MODE_OES 0x2500 +#define GL_TEXTURE_GEN_STR_OES 0x8D60 +#endif + +/* GL_OES_texture_mirrored_repeat */ +#ifndef GL_OES_texture_mirrored_repeat +#define GL_MIRRORED_REPEAT_OES 0x8370 +#endif + +/* GL_OES_vertex_array_object */ +#ifndef GL_OES_vertex_array_object +#define GL_VERTEX_ARRAY_BINDING_OES 0x85B5 +#endif + +/*------------------------------------------------------------------------* + * AMD extension tokens + *------------------------------------------------------------------------*/ + +/* GL_AMD_compressed_3DC_texture */ +#ifndef GL_AMD_compressed_3DC_texture +#define GL_3DC_X_AMD 0x87F9 +#define GL_3DC_XY_AMD 0x87FA +#endif + +/* GL_AMD_compressed_ATC_texture */ +#ifndef GL_AMD_compressed_ATC_texture +#define GL_ATC_RGB_AMD 0x8C92 +#define GL_ATC_RGBA_EXPLICIT_ALPHA_AMD 0x8C93 +#define GL_ATC_RGBA_INTERPOLATED_ALPHA_AMD 0x87EE +#endif + +/*------------------------------------------------------------------------* + * APPLE extension tokens + *------------------------------------------------------------------------*/ + +/* GL_APPLE_texture_2D_limited_npot */ +/* No new tokens introduced by this extension. */ + +/* GL_APPLE_framebuffer_multisample */ +#ifndef GL_APPLE_framebuffer_multisample +#define GL_RENDERBUFFER_SAMPLES_APPLE 0x8CAB +#define GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE_APPLE 0x8D56 +#define GL_MAX_SAMPLES_APPLE 0x8D57 +#define GL_READ_FRAMEBUFFER_APPLE 0x8CA8 +#define GL_DRAW_FRAMEBUFFER_APPLE 0x8CA9 +#define GL_DRAW_FRAMEBUFFER_BINDING_APPLE 0x8CA6 +#define GL_READ_FRAMEBUFFER_BINDING_APPLE 0x8CAA +#endif + +/* GL_APPLE_texture_format_BGRA8888 */ +#ifndef GL_APPLE_texture_format_BGRA8888 +#define GL_BGRA_EXT 0x80E1 +#endif + +/* GL_APPLE_texture_max_level */ +#ifndef GL_APPLE_texture_max_level +#define GL_TEXTURE_MAX_LEVEL_APPLE 0x813D +#endif + +/*------------------------------------------------------------------------* + * ARM extension tokens + *------------------------------------------------------------------------*/ + +/* GL_ARM_rgba8 */ +/* No new tokens introduced by this extension. */ + +/*------------------------------------------------------------------------* + * EXT extension tokens + *------------------------------------------------------------------------*/ + +/* GL_EXT_blend_minmax */ +#ifndef GL_EXT_blend_minmax +#define GL_MIN_EXT 0x8007 +#define GL_MAX_EXT 0x8008 +#endif + +/* GL_EXT_discard_framebuffer */ +#ifndef GL_EXT_discard_framebuffer +#define GL_COLOR_EXT 0x1800 +#define GL_DEPTH_EXT 0x1801 +#define GL_STENCIL_EXT 0x1802 +#endif + +/* GL_EXT_multi_draw_arrays */ +/* No new tokens introduced by this extension. */ + +/* GL_EXT_read_format_bgra */ +#ifndef GL_EXT_read_format_bgra +#define GL_BGRA_EXT 0x80E1 +#define GL_UNSIGNED_SHORT_4_4_4_4_REV_EXT 0x8365 +#define GL_UNSIGNED_SHORT_1_5_5_5_REV_EXT 0x8366 +#endif + +/* GL_EXT_texture_filter_anisotropic */ +#ifndef GL_EXT_texture_filter_anisotropic +#define GL_TEXTURE_MAX_ANISOTROPY_EXT 0x84FE +#define GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT 0x84FF +#endif + +/* GL_EXT_texture_format_BGRA8888 */ +#ifndef GL_EXT_texture_format_BGRA8888 +#define GL_BGRA_EXT 0x80E1 +#endif + +/* GL_EXT_texture_lod_bias */ +#ifndef GL_EXT_texture_lod_bias +#define GL_MAX_TEXTURE_LOD_BIAS_EXT 0x84FD +#define GL_TEXTURE_FILTER_CONTROL_EXT 0x8500 +#define GL_TEXTURE_LOD_BIAS_EXT 0x8501 +#endif + +/*------------------------------------------------------------------------* + * IMG extension tokens + *------------------------------------------------------------------------*/ + +/* GL_IMG_read_format */ +#ifndef GL_IMG_read_format +#define GL_BGRA_IMG 0x80E1 +#define GL_UNSIGNED_SHORT_4_4_4_4_REV_IMG 0x8365 +#endif + +/* GL_IMG_texture_compression_pvrtc */ +#ifndef GL_IMG_texture_compression_pvrtc +#define GL_COMPRESSED_RGB_PVRTC_4BPPV1_IMG 0x8C00 +#define GL_COMPRESSED_RGB_PVRTC_2BPPV1_IMG 0x8C01 +#define GL_COMPRESSED_RGBA_PVRTC_4BPPV1_IMG 0x8C02 +#define GL_COMPRESSED_RGBA_PVRTC_2BPPV1_IMG 0x8C03 +#endif + +/* GL_IMG_texture_env_enhanced_fixed_function */ +#ifndef GL_IMG_texture_env_enhanced_fixed_function +#define GL_MODULATE_COLOR_IMG 0x8C04 +#define GL_RECIP_ADD_SIGNED_ALPHA_IMG 0x8C05 +#define GL_TEXTURE_ALPHA_MODULATE_IMG 0x8C06 +#define GL_FACTOR_ALPHA_MODULATE_IMG 0x8C07 +#define GL_FRAGMENT_ALPHA_MODULATE_IMG 0x8C08 +#define GL_ADD_BLEND_IMG 0x8C09 +#define GL_DOT3_RGBA_IMG 0x86AF +#endif + +/* GL_IMG_user_clip_plane */ +#ifndef GL_IMG_user_clip_plane +#define GL_CLIP_PLANE0_IMG 0x3000 +#define GL_CLIP_PLANE1_IMG 0x3001 +#define GL_CLIP_PLANE2_IMG 0x3002 +#define GL_CLIP_PLANE3_IMG 0x3003 +#define GL_CLIP_PLANE4_IMG 0x3004 +#define GL_CLIP_PLANE5_IMG 0x3005 +#define GL_MAX_CLIP_PLANES_IMG 0x0D32 +#endif + +/* GL_IMG_multisampled_render_to_texture */ +#ifndef GL_IMG_multisampled_render_to_texture +#define GL_RENDERBUFFER_SAMPLES_IMG 0x9133 +#define GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE_IMG 0x9134 +#define GL_MAX_SAMPLES_IMG 0x9135 +#define GL_TEXTURE_SAMPLES_IMG 0x9136 +#endif + +/*------------------------------------------------------------------------* + * NV extension tokens + *------------------------------------------------------------------------*/ + +/* GL_NV_fence */ +#ifndef GL_NV_fence +#define GL_ALL_COMPLETED_NV 0x84F2 +#define GL_FENCE_STATUS_NV 0x84F3 +#define GL_FENCE_CONDITION_NV 0x84F4 +#endif + +/*------------------------------------------------------------------------* + * QCOM extension tokens + *------------------------------------------------------------------------*/ + +/* GL_QCOM_driver_control */ +/* No new tokens introduced by this extension. */ + +/* GL_QCOM_extended_get */ +#ifndef GL_QCOM_extended_get +#define GL_TEXTURE_WIDTH_QCOM 0x8BD2 +#define GL_TEXTURE_HEIGHT_QCOM 0x8BD3 +#define GL_TEXTURE_DEPTH_QCOM 0x8BD4 +#define GL_TEXTURE_INTERNAL_FORMAT_QCOM 0x8BD5 +#define GL_TEXTURE_FORMAT_QCOM 0x8BD6 +#define GL_TEXTURE_TYPE_QCOM 0x8BD7 +#define GL_TEXTURE_IMAGE_VALID_QCOM 0x8BD8 +#define GL_TEXTURE_NUM_LEVELS_QCOM 0x8BD9 +#define GL_TEXTURE_TARGET_QCOM 0x8BDA +#define GL_TEXTURE_OBJECT_VALID_QCOM 0x8BDB +#define GL_STATE_RESTORE 0x8BDC +#endif + +/* GL_QCOM_extended_get2 */ +/* No new tokens introduced by this extension. */ + +/* GL_QCOM_perfmon_global_mode */ +#ifndef GL_QCOM_perfmon_global_mode +#define GL_PERFMON_GLOBAL_MODE_QCOM 0x8FA0 +#endif + +/* GL_QCOM_writeonly_rendering */ +#ifndef GL_QCOM_writeonly_rendering +#define GL_WRITEONLY_RENDERING_QCOM 0x8823 +#endif + +/* GL_QCOM_tiled_rendering */ +#ifndef GL_QCOM_tiled_rendering +#define GL_COLOR_BUFFER_BIT0_QCOM 0x00000001 +#define GL_COLOR_BUFFER_BIT1_QCOM 0x00000002 +#define GL_COLOR_BUFFER_BIT2_QCOM 0x00000004 +#define GL_COLOR_BUFFER_BIT3_QCOM 0x00000008 +#define GL_COLOR_BUFFER_BIT4_QCOM 0x00000010 +#define GL_COLOR_BUFFER_BIT5_QCOM 0x00000020 +#define GL_COLOR_BUFFER_BIT6_QCOM 0x00000040 +#define GL_COLOR_BUFFER_BIT7_QCOM 0x00000080 +#define GL_DEPTH_BUFFER_BIT0_QCOM 0x00000100 +#define GL_DEPTH_BUFFER_BIT1_QCOM 0x00000200 +#define GL_DEPTH_BUFFER_BIT2_QCOM 0x00000400 +#define GL_DEPTH_BUFFER_BIT3_QCOM 0x00000800 +#define GL_DEPTH_BUFFER_BIT4_QCOM 0x00001000 +#define GL_DEPTH_BUFFER_BIT5_QCOM 0x00002000 +#define GL_DEPTH_BUFFER_BIT6_QCOM 0x00004000 +#define GL_DEPTH_BUFFER_BIT7_QCOM 0x00008000 +#define GL_STENCIL_BUFFER_BIT0_QCOM 0x00010000 +#define GL_STENCIL_BUFFER_BIT1_QCOM 0x00020000 +#define GL_STENCIL_BUFFER_BIT2_QCOM 0x00040000 +#define GL_STENCIL_BUFFER_BIT3_QCOM 0x00080000 +#define GL_STENCIL_BUFFER_BIT4_QCOM 0x00100000 +#define GL_STENCIL_BUFFER_BIT5_QCOM 0x00200000 +#define GL_STENCIL_BUFFER_BIT6_QCOM 0x00400000 +#define GL_STENCIL_BUFFER_BIT7_QCOM 0x00800000 +#define GL_MULTISAMPLE_BUFFER_BIT0_QCOM 0x01000000 +#define GL_MULTISAMPLE_BUFFER_BIT1_QCOM 0x02000000 +#define GL_MULTISAMPLE_BUFFER_BIT2_QCOM 0x04000000 +#define GL_MULTISAMPLE_BUFFER_BIT3_QCOM 0x08000000 +#define GL_MULTISAMPLE_BUFFER_BIT4_QCOM 0x10000000 +#define GL_MULTISAMPLE_BUFFER_BIT5_QCOM 0x20000000 +#define GL_MULTISAMPLE_BUFFER_BIT6_QCOM 0x40000000 +#define GL_MULTISAMPLE_BUFFER_BIT7_QCOM 0x80000000 +#endif + +/*------------------------------------------------------------------------* + * End of extension tokens, start of corresponding extension functions + *------------------------------------------------------------------------*/ + +/*------------------------------------------------------------------------* + * OES extension functions + *------------------------------------------------------------------------*/ + +/* GL_OES_blend_equation_separate */ +#ifndef GL_OES_blend_equation_separate +#define GL_OES_blend_equation_separate 1 +#ifdef GL_GLEXT_PROTOTYPES +GL_API void GL_APIENTRY glBlendEquationSeparateOES (GLenum modeRGB, GLenum modeAlpha); +#endif +typedef void (GL_APIENTRYP PFNGLBLENDEQUATIONSEPARATEOESPROC) (GLenum modeRGB, GLenum modeAlpha); +#endif + +/* GL_OES_blend_func_separate */ +#ifndef GL_OES_blend_func_separate +#define GL_OES_blend_func_separate 1 +#ifdef GL_GLEXT_PROTOTYPES +GL_API void GL_APIENTRY glBlendFuncSeparateOES (GLenum srcRGB, GLenum dstRGB, GLenum srcAlpha, GLenum dstAlpha); +#endif +typedef void (GL_APIENTRYP PFNGLBLENDFUNCSEPARATEOESPROC) (GLenum srcRGB, GLenum dstRGB, GLenum srcAlpha, GLenum dstAlpha); +#endif + +/* GL_OES_blend_subtract */ +#ifndef GL_OES_blend_subtract +#define GL_OES_blend_subtract 1 +#ifdef GL_GLEXT_PROTOTYPES +GL_API void GL_APIENTRY glBlendEquationOES (GLenum mode); +#endif +typedef void (GL_APIENTRYP PFNGLBLENDEQUATIONOESPROC) (GLenum mode); +#endif + +/* GL_OES_byte_coordinates */ +#ifndef GL_OES_byte_coordinates +#define GL_OES_byte_coordinates 1 +#endif + +/* GL_OES_compressed_ETC1_RGB8_texture */ +#ifndef GL_OES_compressed_ETC1_RGB8_texture +#define GL_OES_compressed_ETC1_RGB8_texture 1 +#endif + +/* GL_OES_depth24 */ +#ifndef GL_OES_depth24 +#define GL_OES_depth24 1 +#endif + +/* GL_OES_depth32 */ +#ifndef GL_OES_depth32 +#define GL_OES_depth32 1 +#endif + +/* GL_OES_draw_texture */ +#ifndef GL_OES_draw_texture +#define GL_OES_draw_texture 1 +#ifdef GL_GLEXT_PROTOTYPES +GL_API void GL_APIENTRY glDrawTexsOES (GLshort x, GLshort y, GLshort z, GLshort width, GLshort height); +GL_API void GL_APIENTRY glDrawTexiOES (GLint x, GLint y, GLint z, GLint width, GLint height); +GL_API void GL_APIENTRY glDrawTexxOES (GLfixed x, GLfixed y, GLfixed z, GLfixed width, GLfixed height); +GL_API void GL_APIENTRY glDrawTexsvOES (const GLshort *coords); +GL_API void GL_APIENTRY glDrawTexivOES (const GLint *coords); +GL_API void GL_APIENTRY glDrawTexxvOES (const GLfixed *coords); +GL_API void GL_APIENTRY glDrawTexfOES (GLfloat x, GLfloat y, GLfloat z, GLfloat width, GLfloat height); +GL_API void GL_APIENTRY glDrawTexfvOES (const GLfloat *coords); +#endif +typedef void (GL_APIENTRYP PFNGLDRAWTEXSOESPROC) (GLshort x, GLshort y, GLshort z, GLshort width, GLshort height); +typedef void (GL_APIENTRYP PFNGLDRAWTEXIOESPROC) (GLint x, GLint y, GLint z, GLint width, GLint height); +typedef void (GL_APIENTRYP PFNGLDRAWTEXXOESPROC) (GLfixed x, GLfixed y, GLfixed z, GLfixed width, GLfixed height); +typedef void (GL_APIENTRYP PFNGLDRAWTEXSVOESPROC) (const GLshort *coords); +typedef void (GL_APIENTRYP PFNGLDRAWTEXIVOESPROC) (const GLint *coords); +typedef void (GL_APIENTRYP PFNGLDRAWTEXXVOESPROC) (const GLfixed *coords); +typedef void (GL_APIENTRYP PFNGLDRAWTEXFOESPROC) (GLfloat x, GLfloat y, GLfloat z, GLfloat width, GLfloat height); +typedef void (GL_APIENTRYP PFNGLDRAWTEXFVOESPROC) (const GLfloat *coords); +#endif + +/* GL_OES_EGL_image */ +#ifndef GL_OES_EGL_image +#define GL_OES_EGL_image 1 +#ifdef GL_GLEXT_PROTOTYPES +GL_API void GL_APIENTRY glEGLImageTargetTexture2DOES (GLenum target, GLeglImageOES image); +GL_API void GL_APIENTRY glEGLImageTargetRenderbufferStorageOES (GLenum target, GLeglImageOES image); +#endif +typedef void (GL_APIENTRYP PFNGLEGLIMAGETARGETTEXTURE2DOESPROC) (GLenum target, GLeglImageOES image); +typedef void (GL_APIENTRYP PFNGLEGLIMAGETARGETRENDERBUFFERSTORAGEOESPROC) (GLenum target, GLeglImageOES image); +#endif + +/* GL_OES_EGL_image_external */ +#ifndef GL_OES_EGL_image_external +#define GL_OES_EGL_image_external 1 +/* glEGLImageTargetTexture2DOES defined in GL_OES_EGL_image already. */ +#endif + +/* GL_OES_element_index_uint */ +#ifndef GL_OES_element_index_uint +#define GL_OES_element_index_uint 1 +#endif + +/* GL_OES_extended_matrix_palette */ +#ifndef GL_OES_extended_matrix_palette +#define GL_OES_extended_matrix_palette 1 +#endif + +/* GL_OES_fbo_render_mipmap */ +#ifndef GL_OES_fbo_render_mipmap +#define GL_OES_fbo_render_mipmap 1 +#endif + +/* GL_OES_fixed_point */ +#ifndef GL_OES_fixed_point +#define GL_OES_fixed_point 1 +#ifdef GL_GLEXT_PROTOTYPES +GL_API void GL_APIENTRY glAlphaFuncxOES (GLenum func, GLclampx ref); +GL_API void GL_APIENTRY glClearColorxOES (GLclampx red, GLclampx green, GLclampx blue, GLclampx alpha); +GL_API void GL_APIENTRY glClearDepthxOES (GLclampx depth); +GL_API void GL_APIENTRY glClipPlanexOES (GLenum plane, const GLfixed *equation); +GL_API void GL_APIENTRY glColor4xOES (GLfixed red, GLfixed green, GLfixed blue, GLfixed alpha); +GL_API void GL_APIENTRY glDepthRangexOES (GLclampx zNear, GLclampx zFar); +GL_API void GL_APIENTRY glFogxOES (GLenum pname, GLfixed param); +GL_API void GL_APIENTRY glFogxvOES (GLenum pname, const GLfixed *params); +GL_API void GL_APIENTRY glFrustumxOES (GLfixed left, GLfixed right, GLfixed bottom, GLfixed top, GLfixed zNear, GLfixed zFar); +GL_API void GL_APIENTRY glGetClipPlanexOES (GLenum pname, GLfixed eqn[4]); +GL_API void GL_APIENTRY glGetFixedvOES (GLenum pname, GLfixed *params); +GL_API void GL_APIENTRY glGetLightxvOES (GLenum light, GLenum pname, GLfixed *params); +GL_API void GL_APIENTRY glGetMaterialxvOES (GLenum face, GLenum pname, GLfixed *params); +GL_API void GL_APIENTRY glGetTexEnvxvOES (GLenum env, GLenum pname, GLfixed *params); +GL_API void GL_APIENTRY glGetTexParameterxvOES (GLenum target, GLenum pname, GLfixed *params); +GL_API void GL_APIENTRY glLightModelxOES (GLenum pname, GLfixed param); +GL_API void GL_APIENTRY glLightModelxvOES (GLenum pname, const GLfixed *params); +GL_API void GL_APIENTRY glLightxOES (GLenum light, GLenum pname, GLfixed param); +GL_API void GL_APIENTRY glLightxvOES (GLenum light, GLenum pname, const GLfixed *params); +GL_API void GL_APIENTRY glLineWidthxOES (GLfixed width); +GL_API void GL_APIENTRY glLoadMatrixxOES (const GLfixed *m); +GL_API void GL_APIENTRY glMaterialxOES (GLenum face, GLenum pname, GLfixed param); +GL_API void GL_APIENTRY glMaterialxvOES (GLenum face, GLenum pname, const GLfixed *params); +GL_API void GL_APIENTRY glMultMatrixxOES (const GLfixed *m); +GL_API void GL_APIENTRY glMultiTexCoord4xOES (GLenum target, GLfixed s, GLfixed t, GLfixed r, GLfixed q); +GL_API void GL_APIENTRY glNormal3xOES (GLfixed nx, GLfixed ny, GLfixed nz); +GL_API void GL_APIENTRY glOrthoxOES (GLfixed left, GLfixed right, GLfixed bottom, GLfixed top, GLfixed zNear, GLfixed zFar); +GL_API void GL_APIENTRY glPointParameterxOES (GLenum pname, GLfixed param); +GL_API void GL_APIENTRY glPointParameterxvOES (GLenum pname, const GLfixed *params); +GL_API void GL_APIENTRY glPointSizexOES (GLfixed size); +GL_API void GL_APIENTRY glPolygonOffsetxOES (GLfixed factor, GLfixed units); +GL_API void GL_APIENTRY glRotatexOES (GLfixed angle, GLfixed x, GLfixed y, GLfixed z); +GL_API void GL_APIENTRY glSampleCoveragexOES (GLclampx value, GLboolean invert); +GL_API void GL_APIENTRY glScalexOES (GLfixed x, GLfixed y, GLfixed z); +GL_API void GL_APIENTRY glTexEnvxOES (GLenum target, GLenum pname, GLfixed param); +GL_API void GL_APIENTRY glTexEnvxvOES (GLenum target, GLenum pname, const GLfixed *params); +GL_API void GL_APIENTRY glTexParameterxOES (GLenum target, GLenum pname, GLfixed param); +GL_API void GL_APIENTRY glTexParameterxvOES (GLenum target, GLenum pname, const GLfixed *params); +GL_API void GL_APIENTRY glTranslatexOES (GLfixed x, GLfixed y, GLfixed z); +#endif +typedef void (GL_APIENTRYP PFNGLALPHAFUNCXOESPROC) (GLenum func, GLclampx ref); +typedef void (GL_APIENTRYP PFNGLCLEARCOLORXOESPROC) (GLclampx red, GLclampx green, GLclampx blue, GLclampx alpha); +typedef void (GL_APIENTRYP PFNGLCLEARDEPTHXOESPROC) (GLclampx depth); +typedef void (GL_APIENTRYP PFNGLCLIPPLANEXOESPROC) (GLenum plane, const GLfixed *equation); +typedef void (GL_APIENTRYP PFNGLCOLOR4XOESPROC) (GLfixed red, GLfixed green, GLfixed blue, GLfixed alpha); +typedef void (GL_APIENTRYP PFNGLDEPTHRANGEXOESPROC) (GLclampx zNear, GLclampx zFar); +typedef void (GL_APIENTRYP PFNGLFOGXOESPROC) (GLenum pname, GLfixed param); +typedef void (GL_APIENTRYP PFNGLFOGXVOESPROC) (GLenum pname, const GLfixed *params); +typedef void (GL_APIENTRYP PFNGLFRUSTUMXOESPROC) (GLfixed left, GLfixed right, GLfixed bottom, GLfixed top, GLfixed zNear, GLfixed zFar); +typedef void (GL_APIENTRYP PFNGLGETCLIPPLANEXOESPROC) (GLenum pname, GLfixed eqn[4]); +typedef void (GL_APIENTRYP PFNGLGETFIXEDVOESPROC) (GLenum pname, GLfixed *params); +typedef void (GL_APIENTRYP PFNGLGETLIGHTXVOESPROC) (GLenum light, GLenum pname, GLfixed *params); +typedef void (GL_APIENTRYP PFNGLGETMATERIALXVOESPROC) (GLenum face, GLenum pname, GLfixed *params); +typedef void (GL_APIENTRYP PFNGLGETTEXENVXVOESPROC) (GLenum env, GLenum pname, GLfixed *params); +typedef void (GL_APIENTRYP PFNGLGETTEXPARAMETERXVOESPROC) (GLenum target, GLenum pname, GLfixed *params); +typedef void (GL_APIENTRYP PFNGLLIGHTMODELXOESPROC) (GLenum pname, GLfixed param); +typedef void (GL_APIENTRYP PFNGLLIGHTMODELXVOESPROC) (GLenum pname, const GLfixed *params); +typedef void (GL_APIENTRYP PFNGLLIGHTXOESPROC) (GLenum light, GLenum pname, GLfixed param); +typedef void (GL_APIENTRYP PFNGLLIGHTXVOESPROC) (GLenum light, GLenum pname, const GLfixed *params); +typedef void (GL_APIENTRYP PFNGLLINEWIDTHXOESPROC) (GLfixed width); +typedef void (GL_APIENTRYP PFNGLLOADMATRIXXOESPROC) (const GLfixed *m); +typedef void (GL_APIENTRYP PFNGLMATERIALXOESPROC) (GLenum face, GLenum pname, GLfixed param); +typedef void (GL_APIENTRYP PFNGLMATERIALXVOESPROC) (GLenum face, GLenum pname, const GLfixed *params); +typedef void (GL_APIENTRYP PFNGLMULTMATRIXXOESPROC) (const GLfixed *m); +typedef void (GL_APIENTRYP PFNGLMULTITEXCOORD4XOESPROC) (GLenum target, GLfixed s, GLfixed t, GLfixed r, GLfixed q); +typedef void (GL_APIENTRYP PFNGLNORMAL3XOESPROC) (GLfixed nx, GLfixed ny, GLfixed nz); +typedef void (GL_APIENTRYP PFNGLORTHOXOESPROC) (GLfixed left, GLfixed right, GLfixed bottom, GLfixed top, GLfixed zNear, GLfixed zFar); +typedef void (GL_APIENTRYP PFNGLPOINTPARAMETERXOESPROC) (GLenum pname, GLfixed param); +typedef void (GL_APIENTRYP PFNGLPOINTPARAMETERXVOESPROC) (GLenum pname, const GLfixed *params); +typedef void (GL_APIENTRYP PFNGLPOINTSIZEXOESPROC) (GLfixed size); +typedef void (GL_APIENTRYP PFNGLPOLYGONOFFSETXOESPROC) (GLfixed factor, GLfixed units); +typedef void (GL_APIENTRYP PFNGLROTATEXOESPROC) (GLfixed angle, GLfixed x, GLfixed y, GLfixed z); +typedef void (GL_APIENTRYP PFNGLSAMPLECOVERAGEXOESPROC) (GLclampx value, GLboolean invert); +typedef void (GL_APIENTRYP PFNGLSCALEXOESPROC) (GLfixed x, GLfixed y, GLfixed z); +typedef void (GL_APIENTRYP PFNGLTEXENVXOESPROC) (GLenum target, GLenum pname, GLfixed param); +typedef void (GL_APIENTRYP PFNGLTEXENVXVOESPROC) (GLenum target, GLenum pname, const GLfixed *params); +typedef void (GL_APIENTRYP PFNGLTEXPARAMETERXOESPROC) (GLenum target, GLenum pname, GLfixed param); +typedef void (GL_APIENTRYP PFNGLTEXPARAMETERXVOESPROC) (GLenum target, GLenum pname, const GLfixed *params); +typedef void (GL_APIENTRYP PFNGLTRANSLATEXOESPROC) (GLfixed x, GLfixed y, GLfixed z); +#endif + +/* GL_OES_framebuffer_object */ +#ifndef GL_OES_framebuffer_object +#define GL_OES_framebuffer_object 1 +#ifdef GL_GLEXT_PROTOTYPES +GL_API GLboolean GL_APIENTRY glIsRenderbufferOES (GLuint renderbuffer); +GL_API void GL_APIENTRY glBindRenderbufferOES (GLenum target, GLuint renderbuffer); +GL_API void GL_APIENTRY glDeleteRenderbuffersOES (GLsizei n, const GLuint* renderbuffers); +GL_API void GL_APIENTRY glGenRenderbuffersOES (GLsizei n, GLuint* renderbuffers); +GL_API void GL_APIENTRY glRenderbufferStorageOES (GLenum target, GLenum internalformat, GLsizei width, GLsizei height); +GL_API void GL_APIENTRY glGetRenderbufferParameterivOES (GLenum target, GLenum pname, GLint* params); +GL_API GLboolean GL_APIENTRY glIsFramebufferOES (GLuint framebuffer); +GL_API void GL_APIENTRY glBindFramebufferOES (GLenum target, GLuint framebuffer); +GL_API void GL_APIENTRY glDeleteFramebuffersOES (GLsizei n, const GLuint* framebuffers); +GL_API void GL_APIENTRY glGenFramebuffersOES (GLsizei n, GLuint* framebuffers); +GL_API GLenum GL_APIENTRY glCheckFramebufferStatusOES (GLenum target); +GL_API void GL_APIENTRY glFramebufferRenderbufferOES (GLenum target, GLenum attachment, GLenum renderbuffertarget, GLuint renderbuffer); +GL_API void GL_APIENTRY glFramebufferTexture2DOES (GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level); +GL_API void GL_APIENTRY glGetFramebufferAttachmentParameterivOES (GLenum target, GLenum attachment, GLenum pname, GLint* params); +GL_API void GL_APIENTRY glGenerateMipmapOES (GLenum target); +#endif +typedef GLboolean (GL_APIENTRYP PFNGLISRENDERBUFFEROESPROC) (GLuint renderbuffer); +typedef void (GL_APIENTRYP PFNGLBINDRENDERBUFFEROESPROC) (GLenum target, GLuint renderbuffer); +typedef void (GL_APIENTRYP PFNGLDELETERENDERBUFFERSOESPROC) (GLsizei n, const GLuint* renderbuffers); +typedef void (GL_APIENTRYP PFNGLGENRENDERBUFFERSOESPROC) (GLsizei n, GLuint* renderbuffers); +typedef void (GL_APIENTRYP PFNGLRENDERBUFFERSTORAGEOESPROC) (GLenum target, GLenum internalformat, GLsizei width, GLsizei height); +typedef void (GL_APIENTRYP PFNGLGETRENDERBUFFERPARAMETERIVOESPROC) (GLenum target, GLenum pname, GLint* params); +typedef GLboolean (GL_APIENTRYP PFNGLISFRAMEBUFFEROESPROC) (GLuint framebuffer); +typedef void (GL_APIENTRYP PFNGLBINDFRAMEBUFFEROESPROC) (GLenum target, GLuint framebuffer); +typedef void (GL_APIENTRYP PFNGLDELETEFRAMEBUFFERSOESPROC) (GLsizei n, const GLuint* framebuffers); +typedef void (GL_APIENTRYP PFNGLGENFRAMEBUFFERSOESPROC) (GLsizei n, GLuint* framebuffers); +typedef GLenum (GL_APIENTRYP PFNGLCHECKFRAMEBUFFERSTATUSOESPROC) (GLenum target); +typedef void (GL_APIENTRYP PFNGLFRAMEBUFFERRENDERBUFFEROESPROC) (GLenum target, GLenum attachment, GLenum renderbuffertarget, GLuint renderbuffer); +typedef void (GL_APIENTRYP PFNGLFRAMEBUFFERTEXTURE2DOESPROC) (GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level); +typedef void (GL_APIENTRYP PFNGLGETFRAMEBUFFERATTACHMENTPARAMETERIVOESPROC) (GLenum target, GLenum attachment, GLenum pname, GLint* params); +typedef void (GL_APIENTRYP PFNGLGENERATEMIPMAPOESPROC) (GLenum target); +#endif + +/* GL_OES_mapbuffer */ +#ifndef GL_OES_mapbuffer +#define GL_OES_mapbuffer 1 +#ifdef GL_GLEXT_PROTOTYPES +GL_API void* GL_APIENTRY glMapBufferOES (GLenum target, GLenum access); +GL_API GLboolean GL_APIENTRY glUnmapBufferOES (GLenum target); +GL_API void GL_APIENTRY glGetBufferPointervOES (GLenum target, GLenum pname, GLvoid ** params); +#endif +typedef void* (GL_APIENTRYP PFNGLMAPBUFFEROESPROC) (GLenum target, GLenum access); +typedef GLboolean (GL_APIENTRYP PFNGLUNMAPBUFFEROESPROC) (GLenum target); +typedef void (GL_APIENTRYP PFNGLGETBUFFERPOINTERVOESPROC) (GLenum target, GLenum pname, GLvoid ** params); +#endif + +/* GL_OES_matrix_get */ +#ifndef GL_OES_matrix_get +#define GL_OES_matrix_get 1 +#endif + +/* GL_OES_matrix_palette */ +#ifndef GL_OES_matrix_palette +#define GL_OES_matrix_palette 1 +#ifdef GL_GLEXT_PROTOTYPES +GL_API void GL_APIENTRY glCurrentPaletteMatrixOES (GLuint matrixpaletteindex); +GL_API void GL_APIENTRY glLoadPaletteFromModelViewMatrixOES (void); +GL_API void GL_APIENTRY glMatrixIndexPointerOES (GLint size, GLenum type, GLsizei stride, const GLvoid *pointer); +GL_API void GL_APIENTRY glWeightPointerOES (GLint size, GLenum type, GLsizei stride, const GLvoid *pointer); +#endif +typedef void (GL_APIENTRYP PFNGLCURRENTPALETTEMATRIXOESPROC) (GLuint matrixpaletteindex); +typedef void (GL_APIENTRYP PFNGLLOADPALETTEFROMMODELVIEWMATRIXOESPROC) (void); +typedef void (GL_APIENTRYP PFNGLMATRIXINDEXPOINTEROESPROC) (GLint size, GLenum type, GLsizei stride, const GLvoid *pointer); +typedef void (GL_APIENTRYP PFNGLWEIGHTPOINTEROESPROC) (GLint size, GLenum type, GLsizei stride, const GLvoid *pointer); +#endif + +/* GL_OES_packed_depth_stencil */ +#ifndef GL_OES_packed_depth_stencil +#define GL_OES_packed_depth_stencil 1 +#endif + +/* GL_OES_query_matrix */ +#ifndef GL_OES_query_matrix +#define GL_OES_query_matrix 1 +#ifdef GL_GLEXT_PROTOTYPES +GL_API GLbitfield GL_APIENTRY glQueryMatrixxOES (GLfixed mantissa[16], GLint exponent[16]); +#endif +typedef GLbitfield (GL_APIENTRYP PFNGLQUERYMATRIXXOESPROC) (GLfixed mantissa[16], GLint exponent[16]); +#endif + +/* GL_OES_rgb8_rgba8 */ +#ifndef GL_OES_rgb8_rgba8 +#define GL_OES_rgb8_rgba8 1 +#endif + +/* GL_OES_single_precision */ +#ifndef GL_OES_single_precision +#define GL_OES_single_precision 1 +#ifdef GL_GLEXT_PROTOTYPES +GL_API void GL_APIENTRY glDepthRangefOES (GLclampf zNear, GLclampf zFar); +GL_API void GL_APIENTRY glFrustumfOES (GLfloat left, GLfloat right, GLfloat bottom, GLfloat top, GLfloat zNear, GLfloat zFar); +GL_API void GL_APIENTRY glOrthofOES (GLfloat left, GLfloat right, GLfloat bottom, GLfloat top, GLfloat zNear, GLfloat zFar); +GL_API void GL_APIENTRY glClipPlanefOES (GLenum plane, const GLfloat *equation); +GL_API void GL_APIENTRY glGetClipPlanefOES (GLenum pname, GLfloat eqn[4]); +GL_API void GL_APIENTRY glClearDepthfOES (GLclampf depth); +#endif +typedef void (GL_APIENTRYP PFNGLDEPTHRANGEFOESPROC) (GLclampf zNear, GLclampf zFar); +typedef void (GL_APIENTRYP PFNGLFRUSTUMFOESPROC) (GLfloat left, GLfloat right, GLfloat bottom, GLfloat top, GLfloat zNear, GLfloat zFar); +typedef void (GL_APIENTRYP PFNGLORTHOFOESPROC) (GLfloat left, GLfloat right, GLfloat bottom, GLfloat top, GLfloat zNear, GLfloat zFar); +typedef void (GL_APIENTRYP PFNGLCLIPPLANEFOESPROC) (GLenum plane, const GLfloat *equation); +typedef void (GL_APIENTRYP PFNGLGETCLIPPLANEFOESPROC) (GLenum pname, GLfloat eqn[4]); +typedef void (GL_APIENTRYP PFNGLCLEARDEPTHFOESPROC) (GLclampf depth); +#endif + +/* GL_OES_stencil1 */ +#ifndef GL_OES_stencil1 +#define GL_OES_stencil1 1 +#endif + +/* GL_OES_stencil4 */ +#ifndef GL_OES_stencil4 +#define GL_OES_stencil4 1 +#endif + +/* GL_OES_stencil8 */ +#ifndef GL_OES_stencil8 +#define GL_OES_stencil8 1 +#endif + +/* GL_OES_stencil_wrap */ +#ifndef GL_OES_stencil_wrap +#define GL_OES_stencil_wrap 1 +#endif + +/* GL_OES_texture_cube_map */ +#ifndef GL_OES_texture_cube_map +#define GL_OES_texture_cube_map 1 +#ifdef GL_GLEXT_PROTOTYPES +GL_API void GL_APIENTRY glTexGenfOES (GLenum coord, GLenum pname, GLfloat param); +GL_API void GL_APIENTRY glTexGenfvOES (GLenum coord, GLenum pname, const GLfloat *params); +GL_API void GL_APIENTRY glTexGeniOES (GLenum coord, GLenum pname, GLint param); +GL_API void GL_APIENTRY glTexGenivOES (GLenum coord, GLenum pname, const GLint *params); +GL_API void GL_APIENTRY glTexGenxOES (GLenum coord, GLenum pname, GLfixed param); +GL_API void GL_APIENTRY glTexGenxvOES (GLenum coord, GLenum pname, const GLfixed *params); +GL_API void GL_APIENTRY glGetTexGenfvOES (GLenum coord, GLenum pname, GLfloat *params); +GL_API void GL_APIENTRY glGetTexGenivOES (GLenum coord, GLenum pname, GLint *params); +GL_API void GL_APIENTRY glGetTexGenxvOES (GLenum coord, GLenum pname, GLfixed *params); +#endif +typedef void (GL_APIENTRYP PFNGLTEXGENFOESPROC) (GLenum coord, GLenum pname, GLfloat param); +typedef void (GL_APIENTRYP PFNGLTEXGENFVOESPROC) (GLenum coord, GLenum pname, const GLfloat *params); +typedef void (GL_APIENTRYP PFNGLTEXGENIOESPROC) (GLenum coord, GLenum pname, GLint param); +typedef void (GL_APIENTRYP PFNGLTEXGENIVOESPROC) (GLenum coord, GLenum pname, const GLint *params); +typedef void (GL_APIENTRYP PFNGLTEXGENXOESPROC) (GLenum coord, GLenum pname, GLfixed param); +typedef void (GL_APIENTRYP PFNGLTEXGENXVOESPROC) (GLenum coord, GLenum pname, const GLfixed *params); +typedef void (GL_APIENTRYP PFNGLGETTEXGENFVOESPROC) (GLenum coord, GLenum pname, GLfloat *params); +typedef void (GL_APIENTRYP PFNGLGETTEXGENIVOESPROC) (GLenum coord, GLenum pname, GLint *params); +typedef void (GL_APIENTRYP PFNGLGETTEXGENXVOESPROC) (GLenum coord, GLenum pname, GLfixed *params); +#endif + +/* GL_OES_texture_env_crossbar */ +#ifndef GL_OES_texture_env_crossbar +#define GL_OES_texture_env_crossbar 1 +#endif + +/* GL_OES_texture_mirrored_repeat */ +#ifndef GL_OES_texture_mirrored_repeat +#define GL_OES_texture_mirrored_repeat 1 +#endif + +/* GL_OES_vertex_array_object */ +#ifndef GL_OES_vertex_array_object +#define GL_OES_vertex_array_object 1 +#ifdef GL_GLEXT_PROTOTYPES +GL_API void GL_APIENTRY glBindVertexArrayOES (GLuint array); +GL_API void GL_APIENTRY glDeleteVertexArraysOES (GLsizei n, const GLuint *arrays); +GL_API void GL_APIENTRY glGenVertexArraysOES (GLsizei n, GLuint *arrays); +GL_API GLboolean GL_APIENTRY glIsVertexArrayOES (GLuint array); +#endif +typedef void (GL_APIENTRYP PFNGLBINDVERTEXARRAYOESPROC) (GLuint array); +typedef void (GL_APIENTRYP PFNGLDELETEVERTEXARRAYSOESPROC) (GLsizei n, const GLuint *arrays); +typedef void (GL_APIENTRYP PFNGLGENVERTEXARRAYSOESPROC) (GLsizei n, GLuint *arrays); +typedef GLboolean (GL_APIENTRYP PFNGLISVERTEXARRAYOESPROC) (GLuint array); +#endif + +/*------------------------------------------------------------------------* + * AMD extension functions + *------------------------------------------------------------------------*/ + +/* GL_AMD_compressed_3DC_texture */ +#ifndef GL_AMD_compressed_3DC_texture +#define GL_AMD_compressed_3DC_texture 1 +#endif + +/* GL_AMD_compressed_ATC_texture */ +#ifndef GL_AMD_compressed_ATC_texture +#define GL_AMD_compressed_ATC_texture 1 +#endif + +/*------------------------------------------------------------------------* + * APPLE extension functions + *------------------------------------------------------------------------*/ + +/* GL_APPLE_texture_2D_limited_npot */ +#ifndef GL_APPLE_texture_2D_limited_npot +#define GL_APPLE_texture_2D_limited_npot 1 +#endif + +/* GL_APPLE_framebuffer_multisample */ +#ifndef GL_APPLE_framebuffer_multisample +#define GL_APPLE_framebuffer_multisample 1 +#ifdef GL_GLEXT_PROTOTYPES +GL_API void GL_APIENTRY glRenderbufferStorageMultisampleAPPLE (GLenum, GLsizei, GLenum, GLsizei, GLsizei); +GL_API void GL_APIENTRY glResolveMultisampleFramebufferAPPLE (void); +#endif /* GL_GLEXT_PROTOTYPES */ +typedef void (GL_APIENTRYP PFNGLRENDERBUFFERSTORAGEMULTISAMPLEAPPLEPROC) (GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height); +typedef void (GL_APIENTRYP PFNGLRESOLVEMULTISAMPLEFRAMEBUFFERAPPLEPROC) (void); +#endif + +/* GL_APPLE_texture_format_BGRA8888 */ +#ifndef GL_APPLE_texture_format_BGRA8888 +#define GL_APPLE_texture_format_BGRA8888 1 +#endif + +/* GL_APPLE_texture_max_level */ +#ifndef GL_APPLE_texture_max_level +#define GL_APPLE_texture_max_level 1 +#endif + +/*------------------------------------------------------------------------* + * ARM extension functions + *------------------------------------------------------------------------*/ + +/* GL_ARM_rgba8 */ +#ifndef GL_ARM_rgba8 +#define GL_ARM_rgba8 1 +#endif + +/*------------------------------------------------------------------------* + * EXT extension functions + *------------------------------------------------------------------------*/ + +/* GL_EXT_blend_minmax */ +#ifndef GL_EXT_blend_minmax +#define GL_EXT_blend_minmax 1 +#endif + +/* GL_EXT_discard_framebuffer */ +#ifndef GL_EXT_discard_framebuffer +#define GL_EXT_discard_framebuffer 1 +#ifdef GL_GLEXT_PROTOTYPES +GL_API void GL_APIENTRY glDiscardFramebufferEXT (GLenum target, GLsizei numAttachments, const GLenum *attachments); +#endif +typedef void (GL_APIENTRYP PFNGLDISCARDFRAMEBUFFEREXTPROC) (GLenum target, GLsizei numAttachments, const GLenum *attachments); +#endif + +/* GL_EXT_multi_draw_arrays */ +#ifndef GL_EXT_multi_draw_arrays +#define GL_EXT_multi_draw_arrays 1 +#ifdef GL_GLEXT_PROTOTYPES +GL_API void GL_APIENTRY glMultiDrawArraysEXT (GLenum, GLint *, GLsizei *, GLsizei); +GL_API void GL_APIENTRY glMultiDrawElementsEXT (GLenum, const GLsizei *, GLenum, const GLvoid* *, GLsizei); +#endif /* GL_GLEXT_PROTOTYPES */ +typedef void (GL_APIENTRYP PFNGLMULTIDRAWARRAYSEXTPROC) (GLenum mode, GLint *first, GLsizei *count, GLsizei primcount); +typedef void (GL_APIENTRYP PFNGLMULTIDRAWELEMENTSEXTPROC) (GLenum mode, const GLsizei *count, GLenum type, const GLvoid* *indices, GLsizei primcount); +#endif + +/* GL_EXT_read_format_bgra */ +#ifndef GL_EXT_read_format_bgra +#define GL_EXT_read_format_bgra 1 +#endif + +/* GL_EXT_texture_filter_anisotropic */ +#ifndef GL_EXT_texture_filter_anisotropic +#define GL_EXT_texture_filter_anisotropic 1 +#endif + +/* GL_EXT_texture_format_BGRA8888 */ +#ifndef GL_EXT_texture_format_BGRA8888 +#define GL_EXT_texture_format_BGRA8888 1 +#endif + +/* GL_EXT_texture_lod_bias */ +#ifndef GL_EXT_texture_lod_bias +#define GL_EXT_texture_lod_bias 1 +#endif + +/*------------------------------------------------------------------------* + * IMG extension functions + *------------------------------------------------------------------------*/ + +/* GL_IMG_read_format */ +#ifndef GL_IMG_read_format +#define GL_IMG_read_format 1 +#endif + +/* GL_IMG_texture_compression_pvrtc */ +#ifndef GL_IMG_texture_compression_pvrtc +#define GL_IMG_texture_compression_pvrtc 1 +#endif + +/* GL_IMG_texture_env_enhanced_fixed_function */ +#ifndef GL_IMG_texture_env_enhanced_fixed_function +#define GL_IMG_texture_env_enhanced_fixed_function 1 +#endif + +/* GL_IMG_user_clip_plane */ +#ifndef GL_IMG_user_clip_plane +#define GL_IMG_user_clip_plane 1 +#ifdef GL_GLEXT_PROTOTYPES +GL_API void GL_APIENTRY glClipPlanefIMG (GLenum, const GLfloat *); +GL_API void GL_APIENTRY glClipPlanexIMG (GLenum, const GLfixed *); +#endif +typedef void (GL_APIENTRYP PFNGLCLIPPLANEFIMGPROC) (GLenum p, const GLfloat *eqn); +typedef void (GL_APIENTRYP PFNGLCLIPPLANEXIMGPROC) (GLenum p, const GLfixed *eqn); +#endif + +/* GL_IMG_multisampled_render_to_texture */ +#ifndef GL_IMG_multisampled_render_to_texture +#define GL_IMG_multisampled_render_to_texture 1 +#ifdef GL_GLEXT_PROTOTYPES +GL_API void GL_APIENTRY glRenderbufferStorageMultisampleIMG (GLenum, GLsizei, GLenum, GLsizei, GLsizei); +GL_API void GL_APIENTRY glFramebufferTexture2DMultisampleIMG (GLenum, GLenum, GLenum, GLuint, GLint, GLsizei); +#endif +typedef void (GL_APIENTRYP PFNGLRENDERBUFFERSTORAGEMULTISAMPLEIMG) (GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height); +typedef void (GL_APIENTRYP PFNGLFRAMEBUFFERTEXTURE2DMULTISAMPLEIMG) (GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level, GLsizei samples); +#endif + +/*------------------------------------------------------------------------* + * NV extension functions + *------------------------------------------------------------------------*/ + +/* NV_fence */ +#ifndef GL_NV_fence +#define GL_NV_fence 1 +#ifdef GL_GLEXT_PROTOTYPES +GL_API void GL_APIENTRY glDeleteFencesNV (GLsizei, const GLuint *); +GL_API void GL_APIENTRY glGenFencesNV (GLsizei, GLuint *); +GL_API GLboolean GL_APIENTRY glIsFenceNV (GLuint); +GL_API GLboolean GL_APIENTRY glTestFenceNV (GLuint); +GL_API void GL_APIENTRY glGetFenceivNV (GLuint, GLenum, GLint *); +GL_API void GL_APIENTRY glFinishFenceNV (GLuint); +GL_API void GL_APIENTRY glSetFenceNV (GLuint, GLenum); +#endif +typedef void (GL_APIENTRYP PFNGLDELETEFENCESNVPROC) (GLsizei n, const GLuint *fences); +typedef void (GL_APIENTRYP PFNGLGENFENCESNVPROC) (GLsizei n, GLuint *fences); +typedef GLboolean (GL_APIENTRYP PFNGLISFENCENVPROC) (GLuint fence); +typedef GLboolean (GL_APIENTRYP PFNGLTESTFENCENVPROC) (GLuint fence); +typedef void (GL_APIENTRYP PFNGLGETFENCEIVNVPROC) (GLuint fence, GLenum pname, GLint *params); +typedef void (GL_APIENTRYP PFNGLFINISHFENCENVPROC) (GLuint fence); +typedef void (GL_APIENTRYP PFNGLSETFENCENVPROC) (GLuint fence, GLenum condition); +#endif + +/*------------------------------------------------------------------------* + * QCOM extension functions + *------------------------------------------------------------------------*/ + +/* GL_QCOM_driver_control */ +#ifndef GL_QCOM_driver_control +#define GL_QCOM_driver_control 1 +#ifdef GL_GLEXT_PROTOTYPES +GL_API void GL_APIENTRY glGetDriverControlsQCOM (GLint *num, GLsizei size, GLuint *driverControls); +GL_API void GL_APIENTRY glGetDriverControlStringQCOM (GLuint driverControl, GLsizei bufSize, GLsizei *length, GLchar *driverControlString); +GL_API void GL_APIENTRY glEnableDriverControlQCOM (GLuint driverControl); +GL_API void GL_APIENTRY glDisableDriverControlQCOM (GLuint driverControl); +#endif +typedef void (GL_APIENTRYP PFNGLGETDRIVERCONTROLSQCOMPROC) (GLint *num, GLsizei size, GLuint *driverControls); +typedef void (GL_APIENTRYP PFNGLGETDRIVERCONTROLSTRINGQCOMPROC) (GLuint driverControl, GLsizei bufSize, GLsizei *length, GLchar *driverControlString); +typedef void (GL_APIENTRYP PFNGLENABLEDRIVERCONTROLQCOMPROC) (GLuint driverControl); +typedef void (GL_APIENTRYP PFNGLDISABLEDRIVERCONTROLQCOMPROC) (GLuint driverControl); +#endif + +/* GL_QCOM_extended_get */ +#ifndef GL_QCOM_extended_get +#define GL_QCOM_extended_get 1 +#ifdef GL_GLEXT_PROTOTYPES +GL_API void GL_APIENTRY glExtGetTexturesQCOM (GLuint *textures, GLint maxTextures, GLint *numTextures); +GL_API void GL_APIENTRY glExtGetBuffersQCOM (GLuint *buffers, GLint maxBuffers, GLint *numBuffers); +GL_API void GL_APIENTRY glExtGetRenderbuffersQCOM (GLuint *renderbuffers, GLint maxRenderbuffers, GLint *numRenderbuffers); +GL_API void GL_APIENTRY glExtGetFramebuffersQCOM (GLuint *framebuffers, GLint maxFramebuffers, GLint *numFramebuffers); +GL_API void GL_APIENTRY glExtGetTexLevelParameterivQCOM (GLuint texture, GLenum face, GLint level, GLenum pname, GLint *params); +GL_API void GL_APIENTRY glExtTexObjectStateOverrideiQCOM (GLenum target, GLenum pname, GLint param); +GL_API void GL_APIENTRY glExtGetTexSubImageQCOM (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, GLvoid *texels); +GL_API void GL_APIENTRY glExtGetBufferPointervQCOM (GLenum target, GLvoid **params); +#endif +typedef void (GL_APIENTRYP PFNGLEXTGETTEXTURESQCOMPROC) (GLuint *textures, GLint maxTextures, GLint *numTextures); +typedef void (GL_APIENTRYP PFNGLEXTGETBUFFERSQCOMPROC) (GLuint *buffers, GLint maxBuffers, GLint *numBuffers); +typedef void (GL_APIENTRYP PFNGLEXTGETRENDERBUFFERSQCOMPROC) (GLuint *renderbuffers, GLint maxRenderbuffers, GLint *numRenderbuffers); +typedef void (GL_APIENTRYP PFNGLEXTGETFRAMEBUFFERSQCOMPROC) (GLuint *framebuffers, GLint maxFramebuffers, GLint *numFramebuffers); +typedef void (GL_APIENTRYP PFNGLEXTGETTEXLEVELPARAMETERIVQCOMPROC) (GLuint texture, GLenum face, GLint level, GLenum pname, GLint *params); +typedef void (GL_APIENTRYP PFNGLEXTTEXOBJECTSTATEOVERRIDEIQCOMPROC) (GLenum target, GLenum pname, GLint param); +typedef void (GL_APIENTRYP PFNGLEXTGETTEXSUBIMAGEQCOMPROC) (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, GLvoid *texels); +typedef void (GL_APIENTRYP PFNGLEXTGETBUFFERPOINTERVQCOMPROC) (GLenum target, GLvoid **params); +#endif + +/* GL_QCOM_extended_get2 */ +#ifndef GL_QCOM_extended_get2 +#define GL_QCOM_extended_get2 1 +#ifdef GL_GLEXT_PROTOTYPES +GL_API void GL_APIENTRY glExtGetShadersQCOM (GLuint *shaders, GLint maxShaders, GLint *numShaders); +GL_API void GL_APIENTRY glExtGetProgramsQCOM (GLuint *programs, GLint maxPrograms, GLint *numPrograms); +GL_API GLboolean GL_APIENTRY glExtIsProgramBinaryQCOM (GLuint program); +GL_API void GL_APIENTRY glExtGetProgramBinarySourceQCOM (GLuint program, GLenum shadertype, GLchar *source, GLint *length); +#endif +typedef void (GL_APIENTRYP PFNGLEXTGETSHADERSQCOMPROC) (GLuint *shaders, GLint maxShaders, GLint *numShaders); +typedef void (GL_APIENTRYP PFNGLEXTGETPROGRAMSQCOMPROC) (GLuint *programs, GLint maxPrograms, GLint *numPrograms); +typedef GLboolean (GL_APIENTRYP PFNGLEXTISPROGRAMBINARYQCOMPROC) (GLuint program); +typedef void (GL_APIENTRYP PFNGLEXTGETPROGRAMBINARYSOURCEQCOMPROC) (GLuint program, GLenum shadertype, GLchar *source, GLint *length); +#endif + +/* GL_QCOM_perfmon_global_mode */ +#ifndef GL_QCOM_perfmon_global_mode +#define GL_QCOM_perfmon_global_mode 1 +#endif + +/* GL_QCOM_writeonly_rendering */ +#ifndef GL_QCOM_writeonly_rendering +#define GL_QCOM_writeonly_rendering 1 +#endif + +/* GL_QCOM_tiled_rendering */ +#ifndef GL_QCOM_tiled_rendering +#define GL_QCOM_tiled_rendering 1 +#ifdef GL_GLEXT_PROTOTYPES +GL_API void GL_APIENTRY glStartTilingQCOM (GLuint x, GLuint y, GLuint width, GLuint height, GLbitfield preserveMask); +GL_API void GL_APIENTRY glEndTilingQCOM (GLbitfield preserveMask); +#endif +typedef void (GL_APIENTRYP PFNGLSTARTTILINGQCOMPROC) (GLuint x, GLuint y, GLuint width, GLuint height, GLbitfield preserveMask); +typedef void (GL_APIENTRYP PFNGLENDTILINGQCOMPROC) (GLbitfield preserveMask); +#endif + +#ifdef __cplusplus +} +#endif + +#endif /* __glext_h_ */ + diff --git a/external/android-emugl/host/include/GLES/glplatform.h b/external/android-emugl/host/include/GLES/glplatform.h new file mode 100644 index 0000000..2db6ee2 --- /dev/null +++ b/external/android-emugl/host/include/GLES/glplatform.h @@ -0,0 +1,30 @@ +#ifndef __glplatform_h_ +#define __glplatform_h_ + +/* $Revision: 10601 $ on $Date:: 2010-03-04 22:15:27 -0800 #$ */ + +/* + * This document is licensed under the SGI Free Software B License Version + * 2.0. For details, see http://oss.sgi.com/projects/FreeB/ . + */ + +/* Platform-specific types and definitions for OpenGL ES 1.X gl.h + * + * Adopters may modify khrplatform.h and this file to suit their platform. + * You are encouraged to submit all modifications to the Khronos group so that + * they can be included in future versions of this file. Please submit changes + * by sending them to the public Khronos Bugzilla (http://khronos.org/bugzilla) + * by filing a bug against product "OpenGL-ES" component "Registry". + */ + +#include + +#ifndef GL_API +#define GL_API KHRONOS_APICALL +#endif + +#ifndef GL_APIENTRY +#define GL_APIENTRY KHRONOS_APIENTRY +#endif + +#endif /* __glplatform_h_ */ diff --git a/external/android-emugl/host/include/OpenGLESDispatch/EGLDispatch.h b/external/android-emugl/host/include/OpenGLESDispatch/EGLDispatch.h new file mode 100644 index 0000000..a361954 --- /dev/null +++ b/external/android-emugl/host/include/OpenGLESDispatch/EGLDispatch.h @@ -0,0 +1,53 @@ +/* +* Copyright (C) 2011 The Android Open Source Project +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ +#pragma once + +#include "OpenGLESDispatch/RenderEGL_functions.h" +#include "OpenGLESDispatch/RenderEGL_extensions_functions.h" + +// This header is used to define the EGLDispatch structure that contains +// pointers to the EGL shared library used by libOpenglRender. Normally, +// this will be our own libEGL_translator, but one could imagine a +// vendor-specific being used instead. + +// There is a single global instance of this structure, named |s_egl|, +// which must be initialized by calling init_egl_dispatch() before use. + +// Note that our code requires the implementation of misc EGL extensions +// including eglSetSwapRectangleANDROID(), see RenderEGL_extensions_functions.h +// for a full list. + +#define RENDER_EGL_DEFINE_TYPE(return_type, function_name, signature) \ + typedef return_type (EGLAPIENTRY *function_name ## _t) signature; + +#define RENDER_EGL_DECLARE_MEMBER(return_type, function_name, signature) \ + function_name ## _t function_name; + +// Define function typedefs. +LIST_RENDER_EGL_FUNCTIONS(RENDER_EGL_DEFINE_TYPE) +LIST_RENDER_EGL_EXTENSIONS_FUNCTIONS(RENDER_EGL_DEFINE_TYPE) + +// Define EGLDispatch structure. +struct EGLDispatch { + LIST_RENDER_EGL_FUNCTIONS(RENDER_EGL_DECLARE_MEMBER) + LIST_RENDER_EGL_EXTENSIONS_FUNCTIONS(RENDER_EGL_DECLARE_MEMBER) +}; + +// Initialize EGLDispatch function. Return true on success, false on failure. +bool init_egl_dispatch(); + +// Global EGLDispatch instance. Call init_egl_dispatch() before using it. +extern EGLDispatch s_egl; diff --git a/external/android-emugl/host/include/OpenGLESDispatch/GLESv1Dispatch.h b/external/android-emugl/host/include/OpenGLESDispatch/GLESv1Dispatch.h new file mode 100644 index 0000000..0260e52 --- /dev/null +++ b/external/android-emugl/host/include/OpenGLESDispatch/GLESv1Dispatch.h @@ -0,0 +1,41 @@ +/* +* Copyright (C) 2011 The Android Open Source Project +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ +#pragma once + +#include "OpenGLESDispatch/gldefs.h" +#include "OpenGLESDispatch/gles_functions.h" +#include "KHR/khrplatform.h" + +// Define function pointer types. +#define GLES1_DISPATCH_DEFINE_TYPE(return_type,func_name,signature,callargs) \ + typedef return_type (KHRONOS_APIENTRY * func_name ## _t) signature; + +LIST_GLES1_FUNCTIONS(GLES1_DISPATCH_DEFINE_TYPE,GLES1_DISPATCH_DEFINE_TYPE) + +struct GLESv1Dispatch { +#define GLES1_DISPATCH_DECLARE_POINTER(return_type,func_name,signature,callargs) \ + func_name ## _t func_name; + LIST_GLES1_FUNCTIONS(GLES1_DISPATCH_DECLARE_POINTER, + GLES1_DISPATCH_DECLARE_POINTER) +}; + +#undef GLES1_DISPATCH_DECLARE_POINTER +#undef GLES1_DISPATCH_DEFINE_TYPE + +bool gles1_dispatch_init(GLESv1Dispatch* dispatch_table); + +// Used to initialize the decoder. +void* gles1_dispatch_get_proc_func(const char* name, void* userData); diff --git a/external/android-emugl/host/include/OpenGLESDispatch/GLESv2Dispatch.h b/external/android-emugl/host/include/OpenGLESDispatch/GLESv2Dispatch.h new file mode 100644 index 0000000..511e7b8 --- /dev/null +++ b/external/android-emugl/host/include/OpenGLESDispatch/GLESv2Dispatch.h @@ -0,0 +1,41 @@ +/* +* Copyright (C) 2011 The Android Open Source Project +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ +#pragma once + +#include "OpenGLESDispatch/gldefs.h" +#include "OpenGLESDispatch/gles_functions.h" +#include "KHR/khrplatform.h" + +// Define function pointer types. +#define GLES2_DISPATCH_DEFINE_TYPE(return_type,func_name,signature,callargs) \ + typedef return_type (KHRONOS_APIENTRY * func_name ## _t) signature; + +LIST_GLES2_FUNCTIONS(GLES2_DISPATCH_DEFINE_TYPE,GLES2_DISPATCH_DEFINE_TYPE) + +struct GLESv2Dispatch { +#define GLES2_DISPATCH_DECLARE_POINTER(return_type,func_name,signature,callargs) \ + func_name ## _t func_name; + LIST_GLES2_FUNCTIONS(GLES2_DISPATCH_DECLARE_POINTER, + GLES2_DISPATCH_DECLARE_POINTER) +}; + +#undef GLES2_DISPATCH_DECLARE_POINTER +#undef GLES2_DISPATCH_DEFINE_TYPE + +bool gles2_dispatch_init(GLESv2Dispatch* dispatch_table); + +// Used to initialize the decoder. +void* gles2_dispatch_get_proc_func(const char* name, void* userData); diff --git a/external/android-emugl/host/include/OpenGLESDispatch/RenderEGL_extensions_functions.h b/external/android-emugl/host/include/OpenGLESDispatch/RenderEGL_extensions_functions.h new file mode 100644 index 0000000..163789b --- /dev/null +++ b/external/android-emugl/host/include/OpenGLESDispatch/RenderEGL_extensions_functions.h @@ -0,0 +1,15 @@ +// Auto-generated with: android/scripts/gen-entries.py --mode=functions distrib/android-emugl/host/libs/libOpenGLESDispatch/render_egl_extensions.entries --output=distrib/android-emugl/host/include/OpenGLESDispatch/RenderEGL_extensions_functions.h +// DO NOT EDIT THIS FILE + +#ifndef RENDER_EGL_EXTENSIONS_FUNCTIONS_H +#define RENDER_EGL_EXTENSIONS_FUNCTIONS_H + +#include +#define EGL_EGLEXT_PROTOTYPES +#include +#define LIST_RENDER_EGL_EXTENSIONS_FUNCTIONS(X) \ + X(EGLImageKHR, eglCreateImageKHR, (EGLDisplay display, EGLContext context, EGLenum target, EGLClientBuffer buffer, const EGLint* attrib_list)) \ + X(EGLBoolean, eglDestroyImageKHR, (EGLDisplay display, EGLImageKHR image)) \ + + +#endif // RENDER_EGL_EXTENSIONS_FUNCTIONS_H diff --git a/external/android-emugl/host/include/OpenGLESDispatch/RenderEGL_functions.h b/external/android-emugl/host/include/OpenGLESDispatch/RenderEGL_functions.h new file mode 100644 index 0000000..bbd0a48 --- /dev/null +++ b/external/android-emugl/host/include/OpenGLESDispatch/RenderEGL_functions.h @@ -0,0 +1,31 @@ +// Auto-generated with: android/scripts/gen-entries.py --mode=functions distrib/android-emugl/host/libs/libOpenGLESDispatch/render_egl.entries --output=distrib/android-emugl/host/include/OpenGLESDispatch/RenderEGL_functions.h +// DO NOT EDIT THIS FILE + +#ifndef RENDER_EGL_FUNCTIONS_H +#define RENDER_EGL_FUNCTIONS_H + +#include +#define LIST_RENDER_EGL_FUNCTIONS(X) \ + X(EGLint, eglGetError, ()) \ + X(EGLDisplay, eglGetDisplay, (EGLNativeDisplayType dpy)) \ + X(EGLBoolean, eglInitialize, (EGLDisplay dpy, EGLint* major, EGLint* minor)) \ + X(char*, eglQueryString, (EGLDisplay dpy, EGLint id)) \ + X(EGLBoolean, eglGetConfigs, (EGLDisplay display, EGLConfig* configs, EGLint config_size, EGLint* num_config)) \ + X(EGLBoolean, eglChooseConfig, (EGLDisplay display, const EGLint* attribs, EGLConfig* configs, EGLint config_size, EGLint* num_config)) \ + X(EGLBoolean, eglGetConfigAttrib, (EGLDisplay display, EGLConfig config, EGLint attribute, EGLint* value)) \ + X(EGLSurface, eglCreateWindowSurface, (EGLDisplay display, EGLConfig config, EGLNativeWindowType native_window, const EGLint* attrib_list)) \ + X(EGLSurface, eglCreatePbufferSurface, (EGLDisplay display, EGLConfig config, const EGLint* attrib_list)) \ + X(EGLBoolean, eglDestroySurface, (EGLDisplay display, EGLSurface surface)) \ + X(EGLBoolean, eglBindAPI, (EGLenum api)) \ + X(EGLenum, eglQueryAPI, ()) \ + X(EGLBoolean, eglReleaseThread, ()) \ + X(EGLContext, eglCreateContext, (EGLDisplay display, EGLConfig config, EGLContext share_context, const EGLint* attrib_list)) \ + X(EGLBoolean, eglDestroyContext, (EGLDisplay display, EGLContext context)) \ + X(EGLBoolean, eglMakeCurrent, (EGLDisplay display, EGLSurface draw, EGLSurface read, EGLContext context)) \ + X(EGLContext, eglGetCurrentContext, ()) \ + X(EGLSurface, eglGetCurrentSurface, (EGLint readdraw)) \ + X(EGLBoolean, eglSwapBuffers, (EGLDisplay display, EGLSurface surface)) \ + X(void*, eglGetProcAddress, (const char* function_name)) \ + + +#endif // RENDER_EGL_FUNCTIONS_H diff --git a/external/android-emugl/host/include/OpenGLESDispatch/gldefs.h b/external/android-emugl/host/include/OpenGLESDispatch/gldefs.h new file mode 100644 index 0000000..77df117 --- /dev/null +++ b/external/android-emugl/host/include/OpenGLESDispatch/gldefs.h @@ -0,0 +1,45 @@ +/* +* Copyright (C) 2011 The Android Open Source Project +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +#pragma once + +typedef double GLclampd; /* double precision float in [0,1] */ +typedef double GLdouble; /* double precision float */ +typedef void* GLeglImageOES; + +#define GL_S 0x2000 +#define GL_T 0x2001 +#define GL_R 0x2002 +#define GL_Q 0x2003 +#define GL_TEXTURE_GEN_S 0x0C60 +#define GL_TEXTURE_GEN_T 0x0C61 +#define GL_TEXTURE_GEN_R 0x0C62 +#define GL_CLIENT_VERTEX_ARRAY_BIT 0x00000002 +#define GL_TRANSFORM_BIT 0x00001000 +#define GL_INT 0x1404 +#define GL_HALF_FLOAT_NV 0x140B +#define GL_HALF_FLOAT 0x140B +#define GL_VERTEX_PROGRAM_POINT_SIZE 0x8642 +#define GL_POINT_SPRITE 0x8861 +#define GL_FRAMEBUFFER_EXT 0x8D40 +#define GL_TEXTURE_WIDTH 0x1000 +#define GL_TEXTURE_HEIGHT 0x1001 +#define GL_TEXTURE_RED_SIZE 0x805C +#define GL_TEXTURE_GREEN_SIZE 0x805D +#define GL_TEXTURE_BLUE_SIZE 0x805E +#define GL_TEXTURE_ALPHA_SIZE 0x805F +#define GL_TEXTURE_DEPTH_SIZE 0x884A +#define GL_TEXTURE_INTERNAL_FORMAT 0x1003 diff --git a/external/android-emugl/host/include/OpenGLESDispatch/gles1_extensions_functions.h b/external/android-emugl/host/include/OpenGLESDispatch/gles1_extensions_functions.h new file mode 100644 index 0000000..71fdfea --- /dev/null +++ b/external/android-emugl/host/include/OpenGLESDispatch/gles1_extensions_functions.h @@ -0,0 +1,27 @@ +// Auto-generated with: android/scripts/gen-entries.py --mode=funcargs distrib/android-emugl/host/libs/libOpenGLESDispatch/gles1_extensions.entries --output=distrib/android-emugl/host/include/OpenGLESDispatch/gles1_extensions_functions.h +// DO NOT EDIT THIS FILE + +#ifndef GLES1_EXTENSIONS_FUNCTIONS_H +#define GLES1_EXTENSIONS_FUNCTIONS_H + +#define LIST_GLES1_EXTENSIONS_FUNCTIONS(X) \ + X(void, glCurrentPaletteMatrixARB, (GLint index), (index)) \ + X(void, glMatrixIndexuivARB, (GLint size, GLuint * indices), (size, indices)) \ + X(void, glMatrixIndexPointerARB, (GLint size, GLenum type, GLsizei stride, const GLvoid* pointer), (size, type, stride, pointer)) \ + X(void, glWeightPointerARB, (GLint size, GLenum type, GLsizei stride, const GLvoid* pointer), (size, type, stride, pointer)) \ + X(void, glTexGenf, (GLenum coord, GLenum pname, GLfloat param), (coord, pname, param)) \ + X(void, glTexGeni, (GLenum coord, GLenum pname, GLint param), (coord, pname, param)) \ + X(void, glTexGenfv, (GLenum coord, GLenum pname, const GLfloat * params), (coord, pname, params)) \ + X(void, glTexGeniv, (GLenum coord, GLenum pname, const GLint * params), (coord, pname, params)) \ + X(void, glGetTexGenfv, (GLenum coord, GLenum pname, GLfloat * params), (coord, pname, params)) \ + X(void, glGetTexGeniv, (GLenum coord, GLenum pname, GLint * params), (coord, pname, params)) \ + X(void, glDrawTexsOES, (GLshort x, GLshort y, GLshort z, GLshort width, GLshort height), (x, y, z, width, height)) \ + X(void, glDrawTexiOES, (GLshort x, GLint y, GLint z, GLint width, GLshort height), (x, y, z, width, height)) \ + X(void, glDrawTexfOES, (GLfloat x, GLfloat y, GLfloat z, GLfloat width, GLfloat height), (x, y, z, width, height)) \ + X(void, glDrawTexxOES, (GLfixed x, GLfixed y, GLfixed z, GLfixed width, GLfixed height), (x, y, z, width, height)) \ + X(void, glDrawTexsvOES, (const GLshort *coords), (coords)) \ + X(void, glDrawTexivOES, (const GLint *coords), (coords)) \ + X(void, glDrawTexxvOES, (const GLfixed *coords), (coords)) \ + X(void, glDrawTexfvOES, (const GLfloat *coords), (coords)) \ + +#endif // GLES1_EXTENSIONS_FUNCTIONS_H diff --git a/external/android-emugl/host/include/OpenGLESDispatch/gles1_only_functions.h b/external/android-emugl/host/include/OpenGLESDispatch/gles1_only_functions.h new file mode 100644 index 0000000..6a9824c --- /dev/null +++ b/external/android-emugl/host/include/OpenGLESDispatch/gles1_only_functions.h @@ -0,0 +1,71 @@ +// Auto-generated with: android/scripts/gen-entries.py --mode=funcargs distrib/android-emugl/host/libs/libOpenGLESDispatch/gles1_only.entries --output=distrib/android-emugl/host/include/OpenGLESDispatch/gles1_only_functions.h +// DO NOT EDIT THIS FILE + +#ifndef GLES1_ONLY_FUNCTIONS_H +#define GLES1_ONLY_FUNCTIONS_H + +#define LIST_GLES1_ONLY_FUNCTIONS(X) \ + X(void, glAlphaFunc, (GLenum func, GLclampf ref), (func, ref)) \ + X(void, glBegin, (GLenum mode), (mode)) \ + X(void, glClientActiveTexture, (GLenum texture), (texture)) \ + X(void, glClipPlane, (GLenum plane, const GLdouble * equation), (plane, equation)) \ + X(void, glColor4d, (GLdouble red, GLdouble green, GLdouble blue, GLdouble alpha), (red, green, blue, alpha)) \ + X(void, glColor4f, (GLfloat red, GLfloat green, GLfloat blue, GLfloat alpha), (red, green, blue, alpha)) \ + X(void, glColor4fv, (const GLfloat * v), (v)) \ + X(void, glColor4ub, (GLubyte red, GLubyte green, GLubyte blue, GLubyte alpha), (red, green, blue, alpha)) \ + X(void, glColor4ubv, (const GLubyte * v), (v)) \ + X(void, glColorPointer, (GLint size, GLenum type, GLsizei stride, const GLvoid * pointer), (size, type, stride, pointer)) \ + X(void, glDisableClientState, (GLenum array), (array)) \ + X(void, glEnableClientState, (GLenum array), (array)) \ + X(void, glEnd, (), ()) \ + X(void, glFogf, (GLenum pname, GLfloat param), (pname, param)) \ + X(void, glFogfv, (GLenum pname, const GLfloat * params), (pname, params)) \ + X(void, glFrustum, (GLdouble left, GLdouble right, GLdouble bottom, GLdouble top, GLdouble zNear, GLdouble zFar), (left, right, bottom, top, zNear, zFar)) \ + X(void, glGetClipPlane, (GLenum plane, GLdouble * equation), (plane, equation)) \ + X(void, glGetDoublev, (GLenum pname, GLdouble * params), (pname, params)) \ + X(void, glGetLightfv, (GLenum light, GLenum pname, GLfloat * params), (light, pname, params)) \ + X(void, glGetMaterialfv, (GLenum face, GLenum pname, GLfloat * params), (face, pname, params)) \ + X(void, glGetPointerv, (GLenum pname, GLvoid* * params), (pname, params)) \ + X(void, glGetTexEnvfv, (GLenum target, GLenum pname, GLfloat * params), (target, pname, params)) \ + X(void, glGetTexEnviv, (GLenum target, GLenum pname, GLint * params), (target, pname, params)) \ + X(void, glLightf, (GLenum light, GLenum pname, GLfloat param), (light, pname, param)) \ + X(void, glLightfv, (GLenum light, GLenum pname, const GLfloat * params), (light, pname, params)) \ + X(void, glLightModelf, (GLenum pname, GLfloat param), (pname, param)) \ + X(void, glLightModelfv, (GLenum pname, const GLfloat * params), (pname, params)) \ + X(void, glLoadIdentity, (), ()) \ + X(void, glLoadMatrixf, (const GLfloat * m), (m)) \ + X(void, glLogicOp, (GLenum opcode), (opcode)) \ + X(void, glMaterialf, (GLenum face, GLenum pname, GLfloat param), (face, pname, param)) \ + X(void, glMaterialfv, (GLenum face, GLenum pname, const GLfloat * params), (face, pname, params)) \ + X(void, glMultiTexCoord2fv, (GLenum target, const GLfloat * v), (target, v)) \ + X(void, glMultiTexCoord2sv, (GLenum target, const GLshort * v), (target, v)) \ + X(void, glMultiTexCoord3fv, (GLenum target, const GLfloat * v), (target, v)) \ + X(void, glMultiTexCoord3sv, (GLenum target, const GLshort * v), (target, v)) \ + X(void, glMultiTexCoord4f, (GLenum target, GLfloat s, GLfloat t, GLfloat r, GLfloat q), (target, s, t, r, q)) \ + X(void, glMultiTexCoord4fv, (GLenum target, const GLfloat * v), (target, v)) \ + X(void, glMultiTexCoord4sv, (GLenum target, const GLshort * v), (target, v)) \ + X(void, glMultMatrixf, (const GLfloat * m), (m)) \ + X(void, glNormal3f, (GLfloat nx, GLfloat ny, GLfloat nz), (nx, ny, nz)) \ + X(void, glNormal3fv, (const GLfloat * v), (v)) \ + X(void, glNormal3sv, (const GLshort * v), (v)) \ + X(void, glOrtho, (GLdouble left, GLdouble right, GLdouble bottom, GLdouble top, GLdouble zNear, GLdouble zFar), (left, right, bottom, top, zNear, zFar)) \ + X(void, glPointParameterf, (GLenum param, GLfloat value), (param, value)) \ + X(void, glPointParameterfv, (GLenum param, const GLfloat * values), (param, values)) \ + X(void, glPointSize, (GLfloat size), (size)) \ + X(void, glRotatef, (GLfloat angle, GLfloat x, GLfloat y, GLfloat z), (angle, x, y, z)) \ + X(void, glScalef, (GLfloat x, GLfloat y, GLfloat z), (x, y, z)) \ + X(void, glTexEnvf, (GLenum target, GLenum pname, GLfloat param), (target, pname, param)) \ + X(void, glTexEnvfv, (GLenum target, GLenum pname, const GLfloat * params), (target, pname, params)) \ + X(void, glMatrixMode, (GLenum mode), (mode)) \ + X(void, glNormalPointer, (GLenum type, GLsizei stride, const GLvoid * pointer), (type, stride, pointer)) \ + X(void, glPopMatrix, (), ()) \ + X(void, glPushMatrix, (), ()) \ + X(void, glShadeModel, (GLenum mode), (mode)) \ + X(void, glTexCoordPointer, (GLint size, GLenum type, GLsizei stride, const GLvoid * pointer), (size, type, stride, pointer)) \ + X(void, glTexEnvi, (GLenum target, GLenum pname, GLint param), (target, pname, param)) \ + X(void, glTexEnviv, (GLenum target, GLenum pname, const GLint * params), (target, pname, params)) \ + X(void, glTranslatef, (GLfloat x, GLfloat y, GLfloat z), (x, y, z)) \ + X(void, glVertexPointer, (GLint size, GLenum type, GLsizei stride, const GLvoid * pointer), (size, type, stride, pointer)) \ + + +#endif // GLES1_ONLY_FUNCTIONS_H diff --git a/external/android-emugl/host/include/OpenGLESDispatch/gles2_extensions_functions.h b/external/android-emugl/host/include/OpenGLESDispatch/gles2_extensions_functions.h new file mode 100644 index 0000000..980963f --- /dev/null +++ b/external/android-emugl/host/include/OpenGLESDispatch/gles2_extensions_functions.h @@ -0,0 +1,13 @@ +// Auto-generated with: android/scripts/gen-entries.py --mode=funcargs distrib/android-emugl/host/libs/libOpenGLESDispatch/gles2_extensions.entries --output=distrib/android-emugl/host/include/OpenGLESDispatch/gles2_extensions_functions.h +// DO NOT EDIT THIS FILE + +#ifndef GLES2_EXTENSIONS_FUNCTIONS_H +#define GLES2_EXTENSIONS_FUNCTIONS_H + +#define LIST_GLES2_EXTENSIONS_FUNCTIONS(X) \ + X(void, glGetShaderPrecisionFormat, (GLenum shadertype, GLenum precisiontype, GLint* range, GLint* precision), (shadertype, precisiontype, range, precision)) \ + X(void, glReleaseShaderCompiler, (), ()) \ + X(void, glShaderBinary, (GLsizei n, const GLuint* shaders, GLenum binaryformat, const GLvoid* binary, GLsizei length), (n, shaders, binaryformat, binary, length)) \ + + +#endif // GLES2_EXTENSIONS_FUNCTIONS_H diff --git a/external/android-emugl/host/include/OpenGLESDispatch/gles2_only_functions.h b/external/android-emugl/host/include/OpenGLESDispatch/gles2_only_functions.h new file mode 100644 index 0000000..b82c8c9 --- /dev/null +++ b/external/android-emugl/host/include/OpenGLESDispatch/gles2_only_functions.h @@ -0,0 +1,85 @@ +// Auto-generated with: android/scripts/gen-entries.py --mode=funcargs distrib/android-emugl/host/libs/libOpenGLESDispatch/gles2_only.entries --output=distrib/android-emugl/host/include/OpenGLESDispatch/gles2_only_functions.h +// DO NOT EDIT THIS FILE + +#ifndef GLES2_ONLY_FUNCTIONS_H +#define GLES2_ONLY_FUNCTIONS_H + +#define LIST_GLES2_ONLY_FUNCTIONS(X) \ + X(void, glBlendColor, (GLclampf red, GLclampf green, GLclampf blue, GLclampf alpha), (red, green, blue, alpha)) \ + X(void, glStencilFuncSeparate, (GLenum face, GLenum func, GLint ref, GLuint mask), (face, func, ref, mask)) \ + X(void, glStencilMaskSeparate, (GLenum face, GLuint mask), (face, mask)) \ + X(void, glStencilOpSeparate, (GLenum face, GLenum fail, GLenum zfail, GLenum zpass), (face, fail, zfail, zpass)) \ + X(GLboolean, glIsProgram, (GLuint program), (program)) \ + X(GLboolean, glIsShader, (GLuint shader), (shader)) \ + X(void, glVertexAttrib1f, (GLuint indx, GLfloat x), (indx, x)) \ + X(void, glVertexAttrib1fv, (GLuint indx, const GLfloat* values), (indx, values)) \ + X(void, glVertexAttrib2f, (GLuint indx, GLfloat x, GLfloat y), (indx, x, y)) \ + X(void, glVertexAttrib2fv, (GLuint indx, const GLfloat* values), (indx, values)) \ + X(void, glVertexAttrib3f, (GLuint indx, GLfloat x, GLfloat y, GLfloat z), (indx, x, y, z)) \ + X(void, glVertexAttrib3fv, (GLuint indx, const GLfloat* values), (indx, values)) \ + X(void, glVertexAttrib4f, (GLuint indx, GLfloat x, GLfloat y, GLfloat z, GLfloat w), (indx, x, y, z, w)) \ + X(void, glVertexAttrib4fv, (GLuint indx, const GLfloat* values), (indx, values)) \ + X(void, glVertexAttribPointer, (GLuint indx, GLint size, GLenum type, GLboolean normalized, GLsizei stride, const GLvoid* ptr), (indx, size, type, normalized, stride, ptr)) \ + X(void, glDisableVertexAttribArray, (GLuint index), (index)) \ + X(void, glEnableVertexAttribArray, (GLuint index), (index)) \ + X(void, glGetVertexAttribfv, (GLuint index, GLenum pname, GLfloat* params), (index, pname, params)) \ + X(void, glGetVertexAttribiv, (GLuint index, GLenum pname, GLint* params), (index, pname, params)) \ + X(void, glGetVertexAttribPointerv, (GLuint index, GLenum pname, GLvoid** pointer), (index, pname, pointer)) \ + X(void, glUniform1f, (GLint location, GLfloat x), (location, x)) \ + X(void, glUniform1fv, (GLint location, GLsizei count, const GLfloat* v), (location, count, v)) \ + X(void, glUniform1i, (GLint location, GLint x), (location, x)) \ + X(void, glUniform1iv, (GLint location, GLsizei count, const GLint* v), (location, count, v)) \ + X(void, glUniform2f, (GLint location, GLfloat x, GLfloat y), (location, x, y)) \ + X(void, glUniform2fv, (GLint location, GLsizei count, const GLfloat* v), (location, count, v)) \ + X(void, glUniform2i, (GLint location, GLint x, GLint y), (location, x, y)) \ + X(void, glUniform2iv, (GLint location, GLsizei count, const GLint* v), (location, count, v)) \ + X(void, glUniform3f, (GLint location, GLfloat x, GLfloat y, GLfloat z), (location, x, y, z)) \ + X(void, glUniform3fv, (GLint location, GLsizei count, const GLfloat* v), (location, count, v)) \ + X(void, glUniform3i, (GLint location, GLint x, GLint y, GLint z), (location, x, y, z)) \ + X(void, glUniform3iv, (GLint location, GLsizei count, const GLint* v), (location, count, v)) \ + X(void, glUniform4f, (GLint location, GLfloat x, GLfloat y, GLfloat z, GLfloat w), (location, x, y, z, w)) \ + X(void, glUniform4fv, (GLint location, GLsizei count, const GLfloat* v), (location, count, v)) \ + X(void, glUniform4i, (GLint location, GLint x, GLint y, GLint z, GLint w), (location, x, y, z, w)) \ + X(void, glUniform4iv, (GLint location, GLsizei count, const GLint* v), (location, count, v)) \ + X(void, glUniformMatrix2fv, (GLint location, GLsizei count, GLboolean transpose, const GLfloat* value), (location, count, transpose, value)) \ + X(void, glUniformMatrix3fv, (GLint location, GLsizei count, GLboolean transpose, const GLfloat* value), (location, count, transpose, value)) \ + X(void, glUniformMatrix4fv, (GLint location, GLsizei count, GLboolean transpose, const GLfloat* value), (location, count, transpose, value)) \ + X(void, glAttachShader, (GLuint program, GLuint shader), (program, shader)) \ + X(void, glBindAttribLocation, (GLuint program, GLuint index, const GLchar* name), (program, index, name)) \ + X(void, glCompileShader, (GLuint shader), (shader)) \ + X(GLuint, glCreateProgram, (), ()) \ + X(GLuint, glCreateShader, (GLenum type), (type)) \ + X(void, glDeleteProgram, (GLuint program), (program)) \ + X(void, glDeleteShader, (GLuint shader), (shader)) \ + X(void, glDetachShader, (GLuint program, GLuint shader), (program, shader)) \ + X(void, glLinkProgram, (GLuint program), (program)) \ + X(void, glUseProgram, (GLuint program), (program)) \ + X(void, glValidateProgram, (GLuint program), (program)) \ + X(void, glGetActiveAttrib, (GLuint program, GLuint index, GLsizei bufsize, GLsizei* length, GLint* size, GLenum* type, GLchar* name), (program, index, bufsize, length, size, type, name)) \ + X(void, glGetActiveUniform, (GLuint program, GLuint index, GLsizei bufsize, GLsizei* length, GLint* size, GLenum* type, GLchar* name), (program, index, bufsize, length, size, type, name)) \ + X(void, glGetAttachedShaders, (GLuint program, GLsizei maxcount, GLsizei* count, GLuint* shaders), (program, maxcount, count, shaders)) \ + X(int, glGetAttribLocation, (GLuint program, const GLchar* name), (program, name)) \ + X(void, glGetProgramiv, (GLuint program, GLenum pname, GLint* params), (program, pname, params)) \ + X(void, glGetProgramInfoLog, (GLuint program, GLsizei bufsize, GLsizei* length, GLchar* infolog), (program, bufsize, length, infolog)) \ + X(void, glGetShaderiv, (GLuint shader, GLenum pname, GLint* params), (shader, pname, params)) \ + X(void, glGetShaderInfoLog, (GLuint shader, GLsizei bufsize, GLsizei* length, GLchar* infolog), (shader, bufsize, length, infolog)) \ + X(void, glGetShaderSource, (GLuint shader, GLsizei bufsize, GLsizei* length, GLchar* source), (shader, bufsize, length, source)) \ + X(void, glGetUniformfv, (GLuint program, GLint location, GLfloat* params), (program, location, params)) \ + X(void, glGetUniformiv, (GLuint program, GLint location, GLint* params), (program, location, params)) \ + X(int, glGetUniformLocation, (GLuint program, const GLchar* name), (program, name)) \ + X(void, glShaderSource, (GLuint shader, GLsizei count, const GLchar* const* string, const GLint* length), (shader, count, string, length)) \ + X(void, glBindFramebuffer, (GLenum target, GLuint framebuffer), (target, framebuffer)) \ + X(void, glGenFramebuffers, (GLsizei n, GLuint* framebuffers), (n, framebuffers)) \ + X(void, glFramebufferTexture2D, (GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level), (target, attachment, textarget, texture, level)) \ + X(GLenum, glCheckFramebufferStatus, (GLenum target), (target)) \ + X(void, glDeleteFramebuffers, (GLsizei n, const GLuint* framebuffers), (n, framebuffers)) \ + X(GLboolean, glIsRenderbuffer, (GLuint renderbuffer), (renderbuffer)) \ + X(void, glBindRenderbuffer, (GLenum target, GLuint renderbuffer), (target, renderbuffer)) \ + X(void, glDeleteRenderbuffers, (GLsizei n, const GLuint * renderbuffers), (n, renderbuffers)) \ + X(void, glGenRenderbuffers, (GLsizei n, GLuint * renderbuffers), (n, renderbuffers)) \ + X(void, glRenderbufferStorage, (GLenum target, GLenum internalformat, GLsizei width, GLsizei height), (target, internalformat, width, height)) \ + X(void, glGetRenderbufferParameteriv, (GLenum target, GLenum pname, GLint * params), (target, pname, params)) \ + X(void, glFramebufferRenderbuffer, (GLenum target, GLenum attachment, GLenum renderbuffertarget, GLuint renderbuffer), (target, attachment, renderbuffertarget, renderbuffer)) \ + + +#endif // GLES2_ONLY_FUNCTIONS_H diff --git a/external/android-emugl/host/include/OpenGLESDispatch/gles3_only_functions.h b/external/android-emugl/host/include/OpenGLESDispatch/gles3_only_functions.h new file mode 100644 index 0000000..340a2c5 --- /dev/null +++ b/external/android-emugl/host/include/OpenGLESDispatch/gles3_only_functions.h @@ -0,0 +1,18 @@ +// Auto-generated with: android/scripts/gen-entries.py --mode=funcargs distrib/android-emugl/host/libs/libOpenGLESDispatch/gles3_only.entries --output=distrib/android-emugl/host/include/OpenGLESDispatch/gles3_only_functions.h +// DO NOT EDIT THIS FILE + +#ifndef GLES3_ONLY_FUNCTIONS_H +#define GLES3_ONLY_FUNCTIONS_H + +#include + +// Used to avoid adding GLES3/gl3.h to our headers. +#ifndef GL_NUM_EXTENSIONS +#define GL_NUM_EXTENSIONS 0x821D +#endif +typedef const GLubyte* GLconstubyteptr; +#define LIST_GLES3_ONLY_FUNCTIONS(X) \ + X(GLconstubyteptr, glGetStringi, (GLenum name, GLint index), (name, index)) \ + + +#endif // GLES3_ONLY_FUNCTIONS_H diff --git a/external/android-emugl/host/include/OpenGLESDispatch/gles_common_functions.h b/external/android-emugl/host/include/OpenGLESDispatch/gles_common_functions.h new file mode 100644 index 0000000..bef2236 --- /dev/null +++ b/external/android-emugl/host/include/OpenGLESDispatch/gles_common_functions.h @@ -0,0 +1,79 @@ +// Auto-generated with: android/scripts/gen-entries.py --mode=funcargs distrib/android-emugl/host/libs/libOpenGLESDispatch/gles_common.entries --output=distrib/android-emugl/host/include/OpenGLESDispatch/gles_common_functions.h +// DO NOT EDIT THIS FILE + +#ifndef GLES_COMMON_FUNCTIONS_H +#define GLES_COMMON_FUNCTIONS_H + +#include +// Return types must be single words, see GLDispatch.cpp +typedef const GLubyte* GLconstubyteptr; +#define LIST_GLES_COMMON_FUNCTIONS(X) \ + X(void, glActiveTexture, (GLenum texture), (texture)) \ + X(void, glBindBuffer, (GLenum target, GLuint buffer), (target, buffer)) \ + X(void, glBindTexture, (GLenum target, GLuint texture), (target, texture)) \ + X(void, glBlendFunc, (GLenum sfactor, GLenum dfactor), (sfactor, dfactor)) \ + X(void, glBlendEquation, (GLenum mode), (mode)) \ + X(void, glBlendEquationSeparate, (GLenum modeRGB, GLenum modeAlpha), (modeRGB, modeAlpha)) \ + X(void, glBlendFuncSeparate, (GLenum srcRGB, GLenum dstRGB, GLenum srcAlpha, GLenum dstAlpha), (srcRGB, dstRGB, srcAlpha, dstAlpha)) \ + X(void, glBufferData, (GLenum target, GLsizeiptr size, const GLvoid * data, GLenum usage), (target, size, data, usage)) \ + X(void, glBufferSubData, (GLenum target, GLintptr offset, GLsizeiptr size, const GLvoid * data), (target, offset, size, data)) \ + X(void, glClear, (GLbitfield mask), (mask)) \ + X(void, glClearColor, (GLclampf red, GLclampf green, GLclampf blue, GLclampf alpha), (red, green, blue, alpha)) \ + X(void, glClearDepth, (GLclampd depth), (depth)) \ + X(void, glClearStencil, (GLint s), (s)) \ + X(void, glColorMask, (GLboolean red, GLboolean green, GLboolean blue, GLboolean alpha), (red, green, blue, alpha)) \ + X(void, glCompressedTexImage2D, (GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLint border, GLsizei imageSize, const GLvoid * data), (target, level, internalformat, width, height, border, imageSize, data)) \ + X(void, glCompressedTexSubImage2D, (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLsizei imageSize, const GLvoid * data), (target, level, xoffset, yoffset, width, height, format, imageSize, data)) \ + X(void, glCopyTexImage2D, (GLenum target, GLint level, GLenum internalFormat, GLint x, GLint y, GLsizei width, GLsizei height, GLint border), (target, level, internalFormat, x, y, width, height, border)) \ + X(void, glCopyTexSubImage2D, (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint x, GLint y, GLsizei width, GLsizei height), (target, level, xoffset, yoffset, x, y, width, height)) \ + X(void, glCullFace, (GLenum mode), (mode)) \ + X(void, glDeleteBuffers, (GLsizei n, const GLuint * buffers), (n, buffers)) \ + X(void, glDeleteTextures, (GLsizei n, const GLuint * textures), (n, textures)) \ + X(void, glDepthFunc, (GLenum func), (func)) \ + X(void, glDepthMask, (GLboolean flag), (flag)) \ + X(void, glDepthRange, (GLclampd zNear, GLclampd zFar), (zNear, zFar)) \ + X(void, glDisable, (GLenum cap), (cap)) \ + X(void, glDrawArrays, (GLenum mode, GLint first, GLsizei count), (mode, first, count)) \ + X(void, glDrawElements, (GLenum mode, GLsizei count, GLenum type, const GLvoid * indices), (mode, count, type, indices)) \ + X(void, glEnable, (GLenum cap), (cap)) \ + X(void, glFinish, (), ()) \ + X(void, glFlush, (), ()) \ + X(void, glFrontFace, (GLenum mode), (mode)) \ + X(void, glGenBuffers, (GLsizei n, GLuint * buffers), (n, buffers)) \ + X(void, glGenTextures, (GLsizei n, GLuint * textures), (n, textures)) \ + X(void, glGetBooleanv, (GLenum pname, GLboolean * params), (pname, params)) \ + X(void, glGetBufferParameteriv, (GLenum buffer, GLenum parameter, GLint * value), (buffer, parameter, value)) \ + X(GLenum, glGetError, (), ()) \ + X(void, glGetFloatv, (GLenum pname, GLfloat * params), (pname, params)) \ + X(void, glGetIntegerv, (GLenum pname, GLint * params), (pname, params)) \ + X(GLconstubyteptr, glGetString, (GLenum name), (name)) \ + X(void, glTexParameterf, (GLenum target, GLenum pname, GLfloat param), (target, pname, param)) \ + X(void, glTexParameterfv, (GLenum target, GLenum pname, const GLfloat * params), (target, pname, params)) \ + X(void, glGetTexParameterfv, (GLenum target, GLenum pname, GLfloat * params), (target, pname, params)) \ + X(void, glGetTexParameteriv, (GLenum target, GLenum pname, GLint * params), (target, pname, params)) \ + X(void, glGetTexLevelParameteriv, (GLenum target, GLint level, GLenum pname, GLint * params), (target, level, pname, params)) \ + X(void, glHint, (GLenum target, GLenum mode), (target, mode)) \ + X(GLboolean, glIsBuffer, (GLuint buffer), (buffer)) \ + X(GLboolean, glIsEnabled, (GLenum cap), (cap)) \ + X(GLboolean, glIsTexture, (GLuint texture), (texture)) \ + X(void, glLineWidth, (GLfloat width), (width)) \ + X(void, glPolygonOffset, (GLfloat factor, GLfloat units), (factor, units)) \ + X(void, glPixelStorei, (GLenum pname, GLint param), (pname, param)) \ + X(void, glReadPixels, (GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, GLvoid * pixels), (x, y, width, height, format, type, pixels)) \ + X(void, glSampleCoverage, (GLclampf value, GLboolean invert), (value, invert)) \ + X(void, glScissor, (GLint x, GLint y, GLsizei width, GLsizei height), (x, y, width, height)) \ + X(void, glStencilFunc, (GLenum func, GLint ref, GLuint mask), (func, ref, mask)) \ + X(void, glStencilMask, (GLuint mask), (mask)) \ + X(void, glStencilOp, (GLenum fail, GLenum zfail, GLenum zpass), (fail, zfail, zpass)) \ + X(void, glTexImage2D, (GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, const GLvoid * pixels), (target, level, internalformat, width, height, border, format, type, pixels)) \ + X(void, glTexParameteri, (GLenum target, GLenum pname, GLint param), (target, pname, param)) \ + X(void, glTexParameteriv, (GLenum target, GLenum pname, const GLint * params), (target, pname, params)) \ + X(void, glTexSubImage2D, (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, const GLvoid * pixels), (target, level, xoffset, yoffset, width, height, format, type, pixels)) \ + X(void, glViewport, (GLint x, GLint y, GLsizei width, GLsizei height), (x, y, width, height)) \ + X(void, glPushAttrib, (GLbitfield mask), (mask)) \ + X(void, glPushClientAttrib, (GLbitfield mask), (mask)) \ + X(void, glPopAttrib, (), ()) \ + X(void, glPopClientAttrib, (), ()) \ + + +#endif // GLES_COMMON_FUNCTIONS_H diff --git a/external/android-emugl/host/include/OpenGLESDispatch/gles_extensions_functions.h b/external/android-emugl/host/include/OpenGLESDispatch/gles_extensions_functions.h new file mode 100644 index 0000000..f673240 --- /dev/null +++ b/external/android-emugl/host/include/OpenGLESDispatch/gles_extensions_functions.h @@ -0,0 +1,29 @@ +// Auto-generated with: android/scripts/gen-entries.py --mode=funcargs distrib/android-emugl/host/libs/libOpenGLESDispatch/gles_extensions.entries --output=distrib/android-emugl/host/include/OpenGLESDispatch/gles_extensions_functions.h +// DO NOT EDIT THIS FILE + +#ifndef GLES_EXTENSIONS_FUNCTIONS_H +#define GLES_EXTENSIONS_FUNCTIONS_H + +#define LIST_GLES_EXTENSIONS_FUNCTIONS(X) \ + X(GLboolean, glIsRenderbufferEXT, (GLuint renderbuffer), (renderbuffer)) \ + X(void, glBindRenderbufferEXT, (GLenum target, GLuint renderbuffer), (target, renderbuffer)) \ + X(void, glDeleteRenderbuffersEXT, (GLsizei n, const GLuint * renderbuffers), (n, renderbuffers)) \ + X(void, glGenRenderbuffersEXT, (GLsizei n, GLuint * renderbuffers), (n, renderbuffers)) \ + X(void, glRenderbufferStorageEXT, (GLenum target, GLenum internalformat, GLsizei width, GLsizei height), (target, internalformat, width, height)) \ + X(void, glGetRenderbufferParameterivEXT, (GLenum target, GLenum pname, GLint * params), (target, pname, params)) \ + X(GLboolean, glIsFramebufferEXT, (GLuint framebuffer), (framebuffer)) \ + X(void, glBindFramebufferEXT, (GLenum target, GLuint framebuffer), (target, framebuffer)) \ + X(void, glDeleteFramebuffersEXT, (GLsizei n, const GLuint * framebuffers), (n, framebuffers)) \ + X(void, glGenFramebuffersEXT, (GLsizei n, GLuint * framebuffers), (n, framebuffers)) \ + X(GLenum, glCheckFramebufferStatusEXT, (GLenum target), (target)) \ + X(void, glFramebufferTexture1DEXT, (GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level), (target, attachment, textarget, texture, level)) \ + X(void, glFramebufferTexture2DEXT, (GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level), (target, attachment, textarget, texture, level)) \ + X(void, glFramebufferTexture3DEXT, (GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level, GLint zoffset), (target, attachment, textarget, texture, level, zoffset)) \ + X(void, glFramebufferRenderbufferEXT, (GLenum target, GLenum attachment, GLenum renderbuffertarget, GLuint renderbuffer), (target, attachment, renderbuffertarget, renderbuffer)) \ + X(void, glGetFramebufferAttachmentParameterivEXT, (GLenum target, GLenum attachment, GLenum pname, GLint * params), (target, attachment, pname, params)) \ + X(void, glGenerateMipmapEXT, (GLenum target), (target)) \ + X(void, glEGLImageTargetTexture2DOES, (GLenum target, GLeglImageOES image), (target, image)) \ + X(void, glEGLImageTargetRenderbufferStorageOES, (GLenum target, GLeglImageOES image), (target, image)) \ + + +#endif // GLES_EXTENSIONS_FUNCTIONS_H diff --git a/external/android-emugl/host/include/OpenGLESDispatch/gles_functions.h b/external/android-emugl/host/include/OpenGLESDispatch/gles_functions.h new file mode 100644 index 0000000..6e4dfa1 --- /dev/null +++ b/external/android-emugl/host/include/OpenGLESDispatch/gles_functions.h @@ -0,0 +1,50 @@ +// Copyright 2016 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#pragma once + +#include "OpenGLESDispatch/gles_common_functions.h" +#include "OpenGLESDispatch/gles_extensions_functions.h" +#include "OpenGLESDispatch/gles1_only_functions.h" +#include "OpenGLESDispatch/gles1_extensions_functions.h" +#include "OpenGLESDispatch/gles2_only_functions.h" +#include "OpenGLESDispatch/gles2_extensions_functions.h" +#include "OpenGLESDispatch/gles3_only_functions.h" + +// As a special case, LIST_GLES3_ONLY_FUNCTIONS below uses the Y parameter +// instead of the X one, meaning that the corresponding functions are +// optional extensions. This is only because currently, the only GLESv3 +// API we support is glGetStringi(), which is not always provided by +// host desktop GL drivers (though most do). +#define LIST_GLES_FUNCTIONS(X,Y) \ + LIST_GLES_COMMON_FUNCTIONS(X) \ + LIST_GLES_EXTENSIONS_FUNCTIONS(Y) \ + LIST_GLES1_ONLY_FUNCTIONS(X) \ + LIST_GLES1_EXTENSIONS_FUNCTIONS(Y) \ + LIST_GLES2_ONLY_FUNCTIONS(X) \ + LIST_GLES2_EXTENSIONS_FUNCTIONS(Y) \ + LIST_GLES3_ONLY_FUNCTIONS(Y) \ + +#define LIST_GLES1_FUNCTIONS(X, Y) \ + LIST_GLES_COMMON_FUNCTIONS(X) \ + LIST_GLES_EXTENSIONS_FUNCTIONS(Y) \ + LIST_GLES1_ONLY_FUNCTIONS(X) \ + LIST_GLES1_EXTENSIONS_FUNCTIONS(Y) \ + +#define LIST_GLES2_FUNCTIONS(X, Y) \ + LIST_GLES_COMMON_FUNCTIONS(X) \ + LIST_GLES_EXTENSIONS_FUNCTIONS(Y) \ + LIST_GLES2_ONLY_FUNCTIONS(X) \ + LIST_GLES2_EXTENSIONS_FUNCTIONS(Y) \ + diff --git a/external/android-emugl/host/include/OpenglRender/render_api.h b/external/android-emugl/host/include/OpenglRender/render_api.h new file mode 100644 index 0000000..0bcf485 --- /dev/null +++ b/external/android-emugl/host/include/OpenglRender/render_api.h @@ -0,0 +1,56 @@ +/* +* Copyright (C) 2011 The Android Open Source Project +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ +#pragma once + +#include "OpenglRender/render_api_functions.h" + +#include + +/* This header and its declarations must be usable from C code. + * + * If RENDER_API_NO_PROTOTYPES is #defined before including this header, only + * the interface function pointer types will be declared, not the prototypes. + * This allows the client to use those names for its function pointer variables. + * + * All interfaces which can fail return an int, with zero indicating failure + * and anything else indicating success. + */ + +#ifdef __cplusplus +extern "C" { +#endif + +// Use KHRONOS_APICALL to control visibility, but do not use KHRONOS_APIENTRY +// because we don't need the functions to be __stdcall on Win32. +#define RENDER_APICALL KHRONOS_APICALL +#define RENDER_APIENTRY + +#define RENDER_API_DECLARE(return_type, func_name, signature, callargs) \ + typedef return_type (RENDER_APIENTRY *func_name ## Fn) signature; \ + RENDER_APICALL return_type RENDER_APIENTRY func_name signature; + +typedef void (*emugl_logger_func_t)(const char* fmt, ...); + +typedef struct { + emugl_logger_func_t coarse; + emugl_logger_func_t fine; +} emugl_logger_struct; + +LIST_RENDER_API_FUNCTIONS(RENDER_API_DECLARE) + +#ifdef __cplusplus +} +#endif diff --git a/external/android-emugl/host/include/OpenglRender/render_api_functions.h b/external/android-emugl/host/include/OpenglRender/render_api_functions.h new file mode 100644 index 0000000..d55bc2b --- /dev/null +++ b/external/android-emugl/host/include/OpenglRender/render_api_functions.h @@ -0,0 +1,37 @@ +// Auto-generated with: android/scripts/gen-entries.py --mode=funcargs distrib/android-emugl/host/libs/libOpenglRender/render_api.entries --output=distrib/android-emugl/host/include/OpenglRender/render_api_functions.h +// DO NOT EDIT THIS FILE + +#ifndef RENDER_API_FUNCTIONS_H +#define RENDER_API_FUNCTIONS_H + +#include "OpenglRender/render_api_platform_types.h" + +#include +#include +#include + +/* list of constants to be passed to setStreamMode */ +#define RENDER_API_STREAM_MODE_DEFAULT 0 +#define RENDER_API_STREAM_MODE_TCP 1 +#define RENDER_API_STREAM_MODE_UNIX 2 +#define RENDER_API_STREAM_MODE_PIPE 3 + +typedef void (*OnPostFn)(void* context, int width, int height, int ydir, + int format, int type, unsigned char* pixels); +typedef void (*emugl_crash_func_t)(const char* format, ...); + +#define LIST_RENDER_API_FUNCTIONS(X) \ + X(int, initLibrary, (), ()) \ + X(int, setStreamMode, (int mode), (mode)) \ + X(int, initOpenGLRenderer, (int width, int height, bool useSubWindow, char* addr, size_t addrLen, emugl_logger_struct logfuncs, emugl_crash_func_t crashfunc), (width, height, useSubWindow, addr, addrLen, logfuncs, crashfunc)) \ + X(void, getHardwareStrings, (const char** vendor, const char** renderer, const char** version), (vendor, renderer, version)) \ + X(void, setPostCallback, (OnPostFn onPost, void* onPostContext), (onPost, onPostContext)) \ + X(bool, showOpenGLSubwindow, (FBNativeWindowType window, int wx, int wy, int ww, int wh, int fbw, int fbh, float dpr, float zRot), (window, wx, wy, ww, wh, fbw, fbh, dpr, zRot)) \ + X(bool, destroyOpenGLSubwindow, (), ()) \ + X(void, setOpenGLDisplayRotation, (float zRot), (zRot)) \ + X(void, setOpenGLDisplayTranslation, (float px, float py), (px, py)) \ + X(void, repaintOpenGLDisplay, (), ()) \ + X(int, stopOpenGLRenderer, (), ()) \ + + +#endif // RENDER_API_FUNCTIONS_H diff --git a/external/android-emugl/host/include/OpenglRender/render_api_platform_types.h b/external/android-emugl/host/include/OpenglRender/render_api_platform_types.h new file mode 100644 index 0000000..12b993f --- /dev/null +++ b/external/android-emugl/host/include/OpenglRender/render_api_platform_types.h @@ -0,0 +1,39 @@ +/* +* Copyright 2011 The Android Open Source Project +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +#pragma once + +// Define FBNativeWindowType which corresponds to the type of native +// host UI window handles. + +#if defined(_WIN32) || defined(__VC32__) && !defined(__CYGWIN__) && !defined(__SCITECH_SNAP__) /* Win32 and WinCE */ +#include + +typedef HWND FBNativeWindowType; + +#elif defined(__linux__) + +// Really a Window handle, but we don't want to include the X11 headers here. +#include +typedef uint32_t FBNativeWindowType; + +#elif defined(__APPLE__) + +typedef void* FBNativeWindowType; + +#else +#warning "Unsupported platform" +#endif diff --git a/external/android-emugl/host/include/libOpenglRender/IOStream.h b/external/android-emugl/host/include/libOpenglRender/IOStream.h new file mode 100644 index 0000000..672f5a7 --- /dev/null +++ b/external/android-emugl/host/include/libOpenglRender/IOStream.h @@ -0,0 +1,103 @@ +/* +* Copyright (C) 2011 The Android Open Source Project +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ +#ifndef __IO_STREAM_H__ +#define __IO_STREAM_H__ + +#include +#include + +#include "ErrorLog.h" + +class IOStream { +public: + + IOStream(size_t bufSize) { + m_buf = NULL; + m_bufsize = bufSize; + m_free = 0; + } + + virtual void *allocBuffer(size_t minSize) = 0; + virtual int commitBuffer(size_t size) = 0; + virtual const unsigned char *readFully( void *buf, size_t len) = 0; + virtual const unsigned char *read( void *buf, size_t *inout_len) = 0; + virtual int writeFully(const void* buf, size_t len) = 0; + virtual void forceStop() = 0; + + virtual ~IOStream() { + + // NOTE: m_buf is 'owned' by the child class thus we expect it to be released by it + } + + unsigned char *alloc(size_t len) { + + if (m_buf && len > m_free) { + if (flush() < 0) { + ERR("Failed to flush in alloc\n"); + return NULL; // we failed to flush so something is wrong + } + } + + if (!m_buf || len > m_bufsize) { + int allocLen = m_bufsize < len ? len : m_bufsize; + m_buf = (unsigned char *)allocBuffer(allocLen); + if (!m_buf) { + ERR("Alloc (%u bytes) failed\n", allocLen); + return NULL; + } + m_bufsize = m_free = allocLen; + } + + unsigned char *ptr; + + ptr = m_buf + (m_bufsize - m_free); + m_free -= len; + + return ptr; + } + + int flush() { + + if (!m_buf || m_free == m_bufsize) return 0; + + int stat = commitBuffer(m_bufsize - m_free); + m_buf = NULL; + m_free = 0; + return stat; + } + + const unsigned char *readback(void *buf, size_t len) { + flush(); + return readFully(buf, len); + } + + +private: + unsigned char *m_buf; + size_t m_bufsize; + size_t m_free; +}; + +// +// When a client opens a connection to the renderer, it should +// send unsigned int value indicating the "clientFlags". +// The following are the bitmask of the clientFlags. +// currently only one bit is used which flags the server +// it should exit. +// +#define IOSTREAM_CLIENT_EXIT_SERVER 1 + +#endif diff --git a/external/android-emugl/host/libs/CMakeLists.txt b/external/android-emugl/host/libs/CMakeLists.txt new file mode 100644 index 0000000..3a509f4 --- /dev/null +++ b/external/android-emugl/host/libs/CMakeLists.txt @@ -0,0 +1,6 @@ +add_subdirectory(mir_support) +add_subdirectory(GLESv1_dec) +add_subdirectory(GLESv2_dec) +add_subdirectory(libOpenGLESDispatch) +add_subdirectory(libOpenglRender) +add_subdirectory(renderControl_dec) diff --git a/external/android-emugl/host/libs/GLESv1_dec/Android.mk b/external/android-emugl/host/libs/GLESv1_dec/Android.mk new file mode 100644 index 0000000..55aa8af --- /dev/null +++ b/external/android-emugl/host/libs/GLESv1_dec/Android.mk @@ -0,0 +1,16 @@ +LOCAL_PATH := $(call my-dir) + +### host library ######################################### +$(call emugl-begin-host-static-library,libGLESv1_dec) + +$(call emugl-import, libOpenglCodecCommon) +$(call emugl-export,C_INCLUDES,$(LOCAL_PATH)) + +$(call emugl-gen-decoder,$(LOCAL_PATH),gles1) + +LOCAL_SRC_FILES := GLESv1Decoder.cpp + +$(call emugl-export,CFLAGS,$(EMUGL_USER_CFLAGS)) +$(call emugl-export,LDLIBS,$(CXX_STD_LIB)) + +$(call emugl-end-module) diff --git a/external/android-emugl/host/libs/GLESv1_dec/CMakeLists.txt b/external/android-emugl/host/libs/GLESv1_dec/CMakeLists.txt new file mode 100644 index 0000000..52edee2 --- /dev/null +++ b/external/android-emugl/host/libs/GLESv1_dec/CMakeLists.txt @@ -0,0 +1,18 @@ +set(GENERATED_SOURCES + gles1_dec.cpp + gles1_opcodes.h + gles1_server_context.cpp) + +add_custom_command( + OUTPUT ${GENERATED_SOURCES} + POST_BUILD + COMMAND ${CMAKE_BINARY_DIR}/external/android-emugl/host/tools/emugen/emugen + -D ${CMAKE_CURRENT_BINARY_DIR} gles1 + WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} + DEPENDS emugen) + +set(SOURCES + GLESv1Decoder.cpp) + +add_library(GLESv1_dec ${SOURCES} ${GENERATED_SOURCES}) +target_link_libraries(GLESv1_dec OpenglCodecCommon) diff --git a/external/android-emugl/host/libs/GLESv1_dec/GLESv1Decoder.cpp b/external/android-emugl/host/libs/GLESv1_dec/GLESv1Decoder.cpp new file mode 100644 index 0000000..4a227dc --- /dev/null +++ b/external/android-emugl/host/libs/GLESv1_dec/GLESv1Decoder.cpp @@ -0,0 +1,229 @@ +/* +* Copyright (C) 2011 The Android Open Source Project +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ +#include "GLESv1Decoder.h" + +#include +#include +#include + +#include +#include +#include + +static inline void* SafePointerFromUInt(GLuint value) { + return (void*)(uintptr_t)value; +} + +GLESv1Decoder::GLESv1Decoder() +{ + m_contextData = NULL; + m_glesDso = NULL; +} + +GLESv1Decoder::~GLESv1Decoder() +{ + if (m_glesDso != NULL) { + delete m_glesDso; + } +} + + +int GLESv1Decoder::initGL(get_proc_func_t getProcFunc, void *getProcFuncData) +{ + this->initDispatchByName(getProcFunc, getProcFuncData); + + glGetCompressedTextureFormats = s_glGetCompressedTextureFormats; + glVertexPointerOffset = s_glVertexPointerOffset; + glColorPointerOffset = s_glColorPointerOffset; + glNormalPointerOffset = s_glNormalPointerOffset; + glTexCoordPointerOffset = s_glTexCoordPointerOffset; + glPointSizePointerOffset = s_glPointSizePointerOffset; + glWeightPointerOffset = s_glWeightPointerOffset; + glMatrixIndexPointerOffset = s_glMatrixIndexPointerOffset; + + glVertexPointerData = s_glVertexPointerData; + glColorPointerData = s_glColorPointerData; + glNormalPointerData = s_glNormalPointerData; + glTexCoordPointerData = s_glTexCoordPointerData; + glPointSizePointerData = s_glPointSizePointerData; + glWeightPointerData = s_glWeightPointerData; + glMatrixIndexPointerData = s_glMatrixIndexPointerData; + + glDrawElementsOffset = s_glDrawElementsOffset; + glDrawElementsData = s_glDrawElementsData; + glFinishRoundTrip = s_glFinishRoundTrip; + + return 0; +} + +int GLESv1Decoder::s_glFinishRoundTrip(void *self) +{ + GLESv1Decoder *ctx = (GLESv1Decoder *)self; + ctx->glFinish(); + return 0; +} + +void GLESv1Decoder::s_glVertexPointerOffset(void *self, GLint size, GLenum type, GLsizei stride, GLuint offset) +{ + GLESv1Decoder *ctx = (GLESv1Decoder *)self; + ctx->glVertexPointer(size, type, stride, SafePointerFromUInt(offset)); +} + +void GLESv1Decoder::s_glColorPointerOffset(void *self, GLint size, GLenum type, GLsizei stride, GLuint offset) +{ + GLESv1Decoder *ctx = (GLESv1Decoder *)self; + ctx->glColorPointer(size, type, stride, SafePointerFromUInt(offset)); +} + +void GLESv1Decoder::s_glTexCoordPointerOffset(void *self, GLint size, GLenum type, GLsizei stride, GLuint offset) +{ + GLESv1Decoder *ctx = (GLESv1Decoder *)self; + ctx->glTexCoordPointer(size, type, stride, SafePointerFromUInt(offset)); +} + +void GLESv1Decoder::s_glNormalPointerOffset(void *self, GLenum type, GLsizei stride, GLuint offset) +{ + GLESv1Decoder *ctx = (GLESv1Decoder *)self; + ctx->glNormalPointer(type, stride, SafePointerFromUInt(offset)); +} + +void GLESv1Decoder::s_glPointSizePointerOffset(void *self, GLenum type, GLsizei stride, GLuint offset) +{ + GLESv1Decoder *ctx = (GLESv1Decoder *)self; + ctx->glPointSizePointerOES(type, stride, SafePointerFromUInt(offset)); +} + +void GLESv1Decoder::s_glWeightPointerOffset(void * self, GLint size, GLenum type, GLsizei stride, GLuint offset) +{ + GLESv1Decoder *ctx = (GLESv1Decoder *)self; + ctx->glWeightPointerOES(size, type, stride, SafePointerFromUInt(offset)); +} + +void GLESv1Decoder::s_glMatrixIndexPointerOffset(void * self, GLint size, GLenum type, GLsizei stride, GLuint offset) +{ + GLESv1Decoder *ctx = (GLESv1Decoder *)self; + ctx->glMatrixIndexPointerOES(size, type, stride, SafePointerFromUInt(offset)); +} + + + +#define STORE_POINTER_DATA_OR_ABORT(location) \ + if (ctx->m_contextData != NULL) { \ + ctx->m_contextData->storePointerData((location), data, datalen); \ + } else { \ + return; \ + } + +void GLESv1Decoder::s_glVertexPointerData(void *self, GLint size, GLenum type, GLsizei stride, void *data, GLuint datalen) +{ + GLESv1Decoder *ctx = (GLESv1Decoder *)self; + + STORE_POINTER_DATA_OR_ABORT(GLDecoderContextData::VERTEX_LOCATION); + + ctx->glVertexPointer(size, type, 0, ctx->m_contextData->pointerData(GLDecoderContextData::VERTEX_LOCATION)); +} + +void GLESv1Decoder::s_glColorPointerData(void *self, GLint size, GLenum type, GLsizei stride, void *data, GLuint datalen) +{ + GLESv1Decoder *ctx = (GLESv1Decoder *)self; + + STORE_POINTER_DATA_OR_ABORT(GLDecoderContextData::COLOR_LOCATION); + + ctx->glColorPointer(size, type, 0, ctx->m_contextData->pointerData(GLDecoderContextData::COLOR_LOCATION)); +} + +void GLESv1Decoder::s_glTexCoordPointerData(void *self, GLint unit, GLint size, GLenum type, GLsizei stride, void *data, GLuint datalen) +{ + GLESv1Decoder *ctx = (GLESv1Decoder *)self; + STORE_POINTER_DATA_OR_ABORT((GLDecoderContextData::PointerDataLocation) + (GLDecoderContextData::TEXCOORD0_LOCATION + unit)); + + ctx->glTexCoordPointer(size, type, 0, + ctx->m_contextData->pointerData((GLDecoderContextData::PointerDataLocation) + (GLDecoderContextData::TEXCOORD0_LOCATION + unit))); +} + +void GLESv1Decoder::s_glNormalPointerData(void *self, GLenum type, GLsizei stride, void *data, GLuint datalen) +{ + GLESv1Decoder *ctx = (GLESv1Decoder *)self; + + STORE_POINTER_DATA_OR_ABORT(GLDecoderContextData::NORMAL_LOCATION); + + ctx->glNormalPointer(type, 0, ctx->m_contextData->pointerData(GLDecoderContextData::NORMAL_LOCATION)); +} + +void GLESv1Decoder::s_glPointSizePointerData(void *self, GLenum type, GLsizei stride, void *data, GLuint datalen) +{ + GLESv1Decoder *ctx = (GLESv1Decoder *)self; + + STORE_POINTER_DATA_OR_ABORT(GLDecoderContextData::POINTSIZE_LOCATION); + + ctx->glPointSizePointerOES(type, 0, ctx->m_contextData->pointerData(GLDecoderContextData::POINTSIZE_LOCATION)); +} + +void GLESv1Decoder::s_glWeightPointerData(void * self, GLint size, GLenum type, GLsizei stride, void * data, GLuint datalen) +{ + GLESv1Decoder *ctx = (GLESv1Decoder *)self; + + STORE_POINTER_DATA_OR_ABORT(GLDecoderContextData::WEIGHT_LOCATION); + + ctx->glWeightPointerOES(size, type, 0, ctx->m_contextData->pointerData(GLDecoderContextData::WEIGHT_LOCATION)); +} + +void GLESv1Decoder::s_glMatrixIndexPointerData(void * self, GLint size, GLenum type, GLsizei stride, void * data, GLuint datalen) +{ + GLESv1Decoder *ctx = (GLESv1Decoder *)self; + + STORE_POINTER_DATA_OR_ABORT(GLDecoderContextData::MATRIXINDEX_LOCATION); + + ctx->glMatrixIndexPointerOES(size, type, 0, ctx->m_contextData->pointerData(GLDecoderContextData::MATRIXINDEX_LOCATION)); +} + +void GLESv1Decoder::s_glDrawElementsOffset(void *self, GLenum mode, GLsizei count, GLenum type, GLuint offset) +{ + GLESv1Decoder *ctx = (GLESv1Decoder *)self; + ctx->glDrawElements(mode, count, type, SafePointerFromUInt(offset)); +} + +void GLESv1Decoder::s_glDrawElementsData(void *self, GLenum mode, GLsizei count, GLenum type, void * data, GLuint datalen) +{ + GLESv1Decoder *ctx = (GLESv1Decoder *)self; + ctx->glDrawElements(mode, count, type, data); +} + +void GLESv1Decoder::s_glGetCompressedTextureFormats(void *self, GLint count, GLint *data) +{ + GLESv1Decoder *ctx = (GLESv1Decoder *) self; + ctx->glGetIntegerv(GL_COMPRESSED_TEXTURE_FORMATS, data); +} + +void *GLESv1Decoder::s_getProc(const char *name, void *userData) +{ + GLESv1Decoder *ctx = (GLESv1Decoder *)userData; + + if (ctx == NULL || ctx->m_glesDso == NULL) { + return NULL; + } + + void *func = NULL; +#ifdef USE_EGL_GETPROCADDRESS + func = (void *) eglGetProcAddress(name); +#endif + if (func == NULL) { + func = (void *)(ctx->m_glesDso->findSymbol(name)); + } + return func; +} diff --git a/external/android-emugl/host/libs/GLESv1_dec/GLESv1Decoder.h b/external/android-emugl/host/libs/GLESv1_dec/GLESv1Decoder.h new file mode 100644 index 0000000..477a1ae --- /dev/null +++ b/external/android-emugl/host/libs/GLESv1_dec/GLESv1Decoder.h @@ -0,0 +1,68 @@ +/* +* Copyright (C) 2011 The Android Open Source Project +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ +#ifndef _GL_DECODER_H_ +#define _GL_DECODER_H_ + +#include "gles1_dec.h" + +#include "GLDecoderContextData.h" +#include "emugl/common/shared_library.h" + +class GLESv1Decoder : public gles1_decoder_context_t +{ +public: + typedef void *(*get_proc_func_t)(const char *name, void *userData); + + GLESv1Decoder(); + ~GLESv1Decoder(); + int initGL(get_proc_func_t getProcFunc, void *getProcFuncData); + void setContextData(GLDecoderContextData *contextData) { m_contextData = contextData; } + +private: + static void gles1_APIENTRY s_glGetCompressedTextureFormats(void * self, GLint cont, GLint *data); + static void gles1_APIENTRY s_glVertexPointerData(void *self, GLint size, GLenum type, GLsizei stride, void *data, GLuint datalen); + static void gles1_APIENTRY s_glVertexPointerOffset(void *self, GLint size, GLenum type, GLsizei stride, GLuint offset); + + static void gles1_APIENTRY s_glColorPointerData(void *self, GLint size, GLenum type, GLsizei stride, void *data, GLuint datalen); + static void gles1_APIENTRY s_glColorPointerOffset(void *self, GLint size, GLenum type, GLsizei stride, GLuint offset); + + static void gles1_APIENTRY s_glTexCoordPointerData(void *self, GLint unit, GLint size, GLenum type, GLsizei stride, void *data, GLuint datalen); + static void gles1_APIENTRY s_glTexCoordPointerOffset(void *self, GLint size, GLenum type, GLsizei stride, GLuint offset); + + static void gles1_APIENTRY s_glNormalPointerData(void *self, GLenum type, GLsizei stride, void *data, GLuint datalen); + static void gles1_APIENTRY s_glNormalPointerOffset(void *self, GLenum type, GLsizei stride, GLuint offset); + + static void gles1_APIENTRY s_glPointSizePointerData(void *self, GLenum type, GLsizei stride, void *data, GLuint datalen); + static void gles1_APIENTRY s_glPointSizePointerOffset(void *self, GLenum type, GLsizei stride, GLuint offset); + + static void gles1_APIENTRY s_glDrawElementsOffset(void *self, GLenum mode, GLsizei count, GLenum type, GLuint offset); + static void gles1_APIENTRY s_glDrawElementsData(void *self, GLenum mode, GLsizei count, GLenum type, void * data, GLuint datalen); + + static void gles1_APIENTRY s_glWeightPointerData(void * self, GLint size, GLenum type, GLsizei stride, void * data, GLuint datalen); + static void gles1_APIENTRY s_glWeightPointerOffset(void * self, GLint size, GLenum type, GLsizei stride, GLuint offset); + + static void gles1_APIENTRY s_glMatrixIndexPointerData(void * self, GLint size, GLenum type, GLsizei stride, void * data, GLuint datalen); + static void gles1_APIENTRY s_glMatrixIndexPointerOffset(void * self, GLint size, GLenum type, GLsizei stride, GLuint offset); + + static int gles1_APIENTRY s_glFinishRoundTrip(void *self); + + static void * s_getProc(const char *name, void *userData); + + GLDecoderContextData *m_contextData; + emugl::SharedLibrary* m_glesDso; +}; + +#endif diff --git a/external/android-emugl/host/libs/GLESv1_dec/gles1.addon b/external/android-emugl/host/libs/GLESv1_dec/gles1.addon new file mode 100644 index 0000000..2331f87 --- /dev/null +++ b/external/android-emugl/host/libs/GLESv1_dec/gles1.addon @@ -0,0 +1,15 @@ +GL_ENTRY(void, glVertexPointerOffset, GLint size, GLenum type, GLsizei stride, GLuint offset) +GL_ENTRY(void, glColorPointerOffset, GLint size, GLenum type, GLsizei stride, GLuint offset) +GL_ENTRY(void, glNormalPointerOffset, GLenum type, GLsizei stride, GLuint offset) +GL_ENTRY(void, glPointSizePointerOffset, GLenum type, GLsizei stride, GLuint offset) +GL_ENTRY(void, glTexCoordPointerOffset, GLint size, GLenum type, GLsizei stride, GLuint offset) + +GL_ENTRY(void, glVertexPointerData, GLint size, GLenum type, GLsizei stride, void * data, GLuint datalen) +GL_ENTRY(void, glColorPointerData, GLint size, GLenum type, GLsizei stride, void * data, GLuint datalen) +GL_ENTRY(void, glNormalPointerData, GLenum type, GLsizei stride, void * data, GLuint datalen) +GL_ENTRY(void, glTexCoordPointerData, GLint size, GLenum type, GLsizei stride, void * data, GLuint datalen) +GL_ENTRY(void, glPointSizePointerData, GLenum type, GLsizei stride, void * data, GLuint datalen) + +GL_ENTRY(void, glDrawElementsOffset, GLenum mode, GLsizei count, GLenum type, GLuint offset); +GL_ENTRY(void, glDrawElementsData, GLenum mode, GLsizei count, GLenum type, void *data, GLuint datalen); + diff --git a/external/android-emugl/host/libs/GLESv1_dec/gles1.attrib b/external/android-emugl/host/libs/GLESv1_dec/gles1.attrib new file mode 100644 index 0000000..8889bd7 --- /dev/null +++ b/external/android-emugl/host/libs/GLESv1_dec/gles1.attrib @@ -0,0 +1,694 @@ +GLOBAL + base_opcode 1024 + encoder_headers "glUtils.h" "GLEncoderUtils.h" + +#void glClipPlanef(GLenum plane, GLfloat *equation) +glClipPlanef + dir equation in + len equation (4 * sizeof(float)) + +#void glFogfv(GLenum pname, GLfloat *params) +glFogfv + len params (glUtilsParamSize(pname) * sizeof(GLfloat)) + +#void glGetFloatv(GLenum pname, GLfloat *params) +glGetFloatv + dir params out + len params (glUtilsParamSize(pname) * sizeof(GLfloat)) + +#void glGetLightfv(GLenum light, GLenum pname, GLfloat *params) +glGetLightfv + dir params out + len params (glUtilsParamSize(pname) * sizeof(GLfloat)) + +#void glGetMaterialfv(GLenum face, GLenum pname, GLfloat *params) +glGetMaterialfv + dir params out + len params (glUtilsParamSize(pname) * sizeof(GLfloat)) + +#void glGetTexEnvfv(GLenum env, GLenum pname, GLfloat *params) +glGetTexEnvfv + dir params out + len params (glUtilsParamSize(pname) * sizeof(GLfloat)) + +#void glGetTexParameterfv(GLenum target, GLenum pname, GLfloat *params) +glGetTexParameterfv + dir params out + len params (glUtilsParamSize(pname) * sizeof(GLfloat)) + +#void glLightModelfv(GLenum pname, GLfloat *params) +glLightModelfv + len params (glUtilsParamSize(pname) * sizeof(GLfloat)) + +#void glLightfv(GLenum light, GLenum pname, GLfloat *params) +glLightfv + len params (glUtilsParamSize(pname) * sizeof(GLfloat)) + +#void glLoadMatrixf(GLfloat *m) +glLoadMatrixf + len m (16 * sizeof(GLfloat)) + +#void glMaterialfv(GLenum face, GLenum pname, GLfloat *params) +glMaterialfv + len params (glUtilsParamSize(pname) * sizeof(GLfloat)) + +#void glMultMatrixf(GLfloat *m) +glMultMatrixf + len m (16 * sizeof(GLfloat)) + +#void glPointParameterfv(GLenum pname, GLfloat *params) +glPointParameterfv + len params (glUtilsParamSize(pname) * sizeof(GLfloat)) + +#void glTexEnvfv(GLenum target, GLenum pname, GLfloat *params) +glTexEnvfv + len params (glUtilsParamSize(pname) * sizeof(GLfloat)) + +#void glTexParameterfv(GLenum target, GLenum pname, GLfloat *params) +glTexParameterfv + len params (glUtilsParamSize(pname) * sizeof(GLfloat)) + +#void glBufferData(GLenum target, GLsizeiptr size, GLvoid *data, GLenum usage) +glBufferData + len data size + var_flag data nullAllowed + +#void glBufferSubData(GLenum target, GLintptr offset, GLsizeiptr size, GLvoid *data) +glBufferSubData + dir data in + len data size + var_flag data nullAllowed + +#void glClipPlanex(GLenum plane, GLfixed *eqn) +glClipPlanex + dir eqn in + len eqn (4 * sizeof(GLfixed)) + +#void glColorPointer(GLint size, GLenum type, GLsizei stride, GLvoid *pointer) +#we treat the pointer as offset to a VBO +glColorPointer + len pointer (sizeof(unsigned int)) + flag unsupported + +#void glCompressedTexImage2D(GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLint border, GLsizei imageSize, GLvoid *data) +glCompressedTexImage2D + len data imageSize + var_flag data nullAllowed + +#void glCompressedTexSubImage2D(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLsizei imageSize, GLvoid *data) +glCompressedTexSubImage2D + len data imageSize + var_flag data nullAllowed + +#void glDeleteBuffers(GLsizei n, GLuint *buffers) +glDeleteBuffers + len buffers (n * sizeof(GLuint)) + param_check n if(n<0){ ctx->setError(GL_INVALID_VALUE); return; } + +#void glDeleteTextures(GLsizei n, GLuint *textures) +glDeleteTextures + len textures (n * sizeof(GLuint)) + param_check n if(n<0){ ctx->setError(GL_INVALID_VALUE); return; } + +#this function is marked as unsupported - it shouldn't be called directly +#instead it translated into - glDrawDirectElements and glDrawIndirectElements +#void glDrawElements(GLenum mode, GLsizei count, GLenum type, GLvoid *indices) +glDrawElements + flag unsupported + + +#void glFogxv(GLenum pname, GLfixed *params) +glFogxv + len params (glUtilsParamSize(pname) * sizeof(GLfixed)) + +#void glGetBooleanv(GLenum pname, GLboolean *params) +glGetBooleanv + dir params out + len params (glUtilsParamSize(pname) * sizeof(GLboolean)) + +#void glGetBufferParameteriv(GLenum target, GLenum pname, GLint *params) +glGetBufferParameteriv + len params (sizeof(GLint)) + dir params out + +#void glGenBuffers(GLsizei n, GLuint *buffers) +glGenBuffers + len buffers (n * sizeof(GLuint)) + dir buffers out + param_check n if(n<0){ ctx->setError(GL_INVALID_VALUE); return; } + +#void glGenTextures(GLsizei n, GLuint *textures) +glGenTextures + len textures (n * sizeof(GLuint)) + dir textures out + param_check n if(n<0){ ctx->setError(GL_INVALID_VALUE); return; } + +#void glGetFixedv(GLenum pname, GLfixed *params) +glGetFixedv + dir params out + len params (glUtilsParamSize(pname) * sizeof(GLfixed)) + +#void glGetIntegerv(GLenum pname, GLint *params) +glGetIntegerv + dir params out + len params (glUtilsParamSize(pname) * sizeof(GLint)) + +#void glGetLightxv(GLenum light, GLenum pname, GLfixed *params) +glGetLightxv + dir params out + len params (glUtilsParamSize(pname) * sizeof(GLfixed)) + +#void glGetMaterialxv(GLenum face, GLenum pname, GLfixed *params) +glGetMaterialxv + dir params out + len params (glUtilsParamSize(pname) * sizeof(GLfixed)) + +#void glGetPointerv(GLenum pname, void **params) +glGetPointerv + flag unsupported + +#GLubyte* glGetString(GLenum name) +glGetString + flag unsupported + +#void glGetTexEnviv(GLenum env, GLenum pname, GLint *params) +glGetTexEnviv + dir params out + len params (glUtilsParamSize(pname) * sizeof(GLint)) + +#void glGetTexEnvxv(GLenum env, GLenum pname, GLfixed *params) +glGetTexEnvxv + dir params out + len params (glUtilsParamSize(pname) * sizeof(GLfixed)) + +#void glGetTexParameteriv(GLenum target, GLenum pname, GLint *params) +glGetTexParameteriv + dir params out + len params (sizeof(GLint)) + +#void glGetTexParameterxv(GLenum target, GLenum pname, GLfixed *params) +glGetTexParameterxv + dir params out + len params (sizeof(GLfixed)) + +#void glLightModelxv(GLenum pname, GLfixed *params) +glLightModelxv + len params (glUtilsParamSize(pname) * sizeof(GLfixed)) + +#void glLightxv(GLenum light, GLenum pname, GLfixed *params) +glLightxv + len params (glUtilsParamSize(pname) * sizeof(GLfixed)) + +#void glLoadMatrixx(GLfixed *m) +glLoadMatrixx + len m (16 * sizeof(GLfixed)) + +#void glMaterialxv(GLenum face, GLenum pname, GLfixed *params) +glMaterialxv + len params (glUtilsParamSize(pname) * sizeof(GLfixed)) + +#void glMultMatrixx(GLfixed *m) +glMultMatrixx + len m (16 * sizeof(GLfixed)) + +#void glNormalPointer(GLenum type, GLsizei stride, GLvoid *pointer) +#we treat the pointer as an offset to a VBO +glNormalPointer + len pointer (sizeof(unsigned int)) + flag unsupported + +#void glPointParameterxv(GLenum pname, GLfixed *params) +glPointParameterxv + len params (glUtilsParamSize(pname) * sizeof(GLfixed)) + +#void glReadPixels(GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, GLvoid *pixels) +glReadPixels + dir pixels out + len pixels glesv1_enc::pixelDataSize(self, width, height, format, type, 1) + +#void glTexCoordPointer(GLint size, GLenum type, GLsizei stride, GLvoid *pointer) +glTexCoordPointer + len pointer (sizeof(unsigned int)) + flag unsupported + +#void glTexEnviv(GLenum target, GLenum pname, GLint *params) +glTexEnviv + len params (glUtilsParamSize(pname) * sizeof(GLint)) + +#void glTexEnvxv(GLenum target, GLenum pname, GLfixed *params) +glTexEnvxv + len params (glUtilsParamSize(pname) * sizeof(GLfixed)) + +#void glTexImage2D(GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, GLvoid *pixels) +glTexImage2D + dir pixels in + len pixels glesv1_enc::pixelDataSize(self, width, height, format, type, 0) + var_flag pixels nullAllowed isLarge + +#void glTexParameteriv(GLenum target, GLenum pname, GLint *params) +glTexParameteriv + len params (glUtilsParamSize(pname) * sizeof(GLint)) + +#void glTexParameterxv(GLenum target, GLenum pname, GLfixed *params) +glTexParameterxv + len params (glUtilsParamSize(pname) * sizeof(GLfixed)) + +#void glTexSubImage2D(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, GLvoid *pixels) +glTexSubImage2D + len pixels glesv1_enc::pixelDataSize(self, width, height, format, type, 0) + var_flag pixels nullAllowed isLarge + +#void glVertexPointer(GLint size, GLenum type, GLsizei stride, GLvoid *pointer) +# we treat the pointer as an offset to a VBO +glVertexPointer + flag unsupported + +#void glPointSizePointerOES(GLenum type, GLsizei stride, GLvoid *pointer) +glPointSizePointerOES + len pointer (sizeof(unsigned int)) + flag unsupported + +#void glGetClipPlanef(GLenum pname, GLfloat * eqn) +glGetClipPlanef + dir eqn out + len eqn (4 * sizeof(GLfloat)) + +#void glVertexPointerData(GLint size, GLenum type, GLsizei stride, void *data, GLuint datalen) +glVertexPointerData + len data datalen + custom_pack data glUtilsPackPointerData((unsigned char *)ptr, (unsigned char *)data, size, type, stride, datalen) + flag custom_decoder + flag not_api + +#void glColorPointerData(GLint size, GLenum type, GLsizei stride, void *data, GLuint datalen) +glColorPointerData + len data datalen + flag custom_decoder + custom_pack data glUtilsPackPointerData((unsigned char *)ptr, (unsigned char *)data, size, type, stride, datalen) + flag not_api + +#void glNormalPointerData(GLenum type, GLsizei stride, void *data, GLuint datalen) +glNormalPointerData + len data datalen + flag custom_decoder + custom_pack data glUtilsPackPointerData((unsigned char *)ptr, (unsigned char *)data, 3, type, stride, datalen) + flag not_api + +#void glPointSizePointerData(GLenum type, GLsizei stride, void *data, GLuint datalen) +glPointSizePointerData + len data datalen + flag custom_decoder + custom_pack data glUtilsPackPointerData((unsigned char *)ptr, (unsigned char *)data, 1, type, stride, datalen) + flag not_api + +#void glTexCoordPointerData(GLint size, GLenum type, GLsizei stride, void *data, GLuint datalen) +glTexCoordPointerData + len data datalen + flag custom_decoder + custom_pack data glUtilsPackPointerData((unsigned char *)ptr, (unsigned char *)data, size, type, stride, datalen) + flag not_api + +#void glWeightPointerData(GLint size, GLenum type, GLsizei stride, void * data, GLuint datalen) +glWeightPointerData + len data datalen + custom_pack data glUtilsPackPointerData((unsigned char *)ptr, (unsigned char*)data, size, type, stride, datalen) + flag custom_decoder + flag not_api + +#void glMatrixIndexPointerData(GLint size, GLenum type, GLsizei stride, void * data, GLuint datalen) +glMatrixIndexPointerData + len data datalen + custom_pack data glUtilsPackPointerData((unsigned char *)ptr, (unsigned char*)data, size, type, stride, datalen) + flag custom_decoder + flag not_api + +glVertexPointerOffset + flag custom_decoder + flag not_api +glNormalPointerOffset + flag custom_decoder + flag not_api +glTexCoordPointerOffset + flag custom_decoder + flag not_api +glPointSizePointerOffset + flag custom_decoder + flag not_api +glColorPointerOffset + flag custom_decoder + flag not_api +glWeightPointerOffset + flag custom_decoder + flag not_api +glMatrixIndexPointerOffset + flag custom_decoder + flag not_api + +glDrawElementsData + len data datalen + flag custom_decoder + flag not_api + +glDrawElementsOffset + flag custom_decoder + flag not_api + +glGetCompressedTextureFormats + dir formats out + len formats (count * sizeof(GLint)) + flag custom_decoder + flag not_api + +glFinishRoundTrip + flag custom_decoder + flag not_api + +#gles1 extensions + +#void glDrawTexsvOES(GLshort *coords) +glDrawTexsvOES + len coords (5 * sizeof(GLshort)) + +#void glDrawTexivOES(GLint *coords) +glDrawTexivOES + len coords (5 * sizeof(GLint)) + +#void glDrawTexxvOES(GLfixed *coords) +glDrawTexxvOES + len coords (5 * sizeof(GLfixed)) + +#void glDrawTexfvOES(GLfloat *coords) +glDrawTexfvOES + len coords (5 * sizeof(GLfloat)) + +#glClipPlanexOES(GLenum plane, const GLfixed * equation) +glClipPlanexOES + dir equation in + len equation (4 * sizeof(GLfixed)) + +#glClipPlanexIMG(GLenum plane, const GLfixed * equation) +glClipPlanexIMG + dir equation in + len equation (4 * sizeof(GLfixed)) + +#void glFogxvOES(GLenum pname, GLfixed *params) +glFogxvOES + dir params in + len params (glUtilsParamSize(pname) * sizeof(GLfixed)) + +#void glGetClipPlanexOES(GLenum pname, GLfixed * eqn) +glGetClipPlanexOES + dir eqn out + len eqn (4 * sizeof(GLfixed)) + +#void glGetClipPlanex(GLenum pname, GLfixed * eqn) +glGetClipPlanex + dir eqn out + len eqn (4 * sizeof(GLfixed)) + +#void glGetFixedvOES(GLenum pname, GLfixed *params) +glGetFixedvOES + dir params out + len params (glUtilsParamSize(pname) * sizeof(GLfixed)) + +#void glGetLightxvOES(GLenum light, GLenum pname, GLfixed *params) +glGetLightxvOES + dir params out + len params (glUtilsParamSize(pname) * sizeof(GLfixed)) + +#void glGetMaterialxvOES(GLenum face, GLenum pname, GLfixed *params) +glGetMaterialxvOES + dir params out + len params (glUtilsParamSize(pname) * sizeof(GLfixed)) + +#void glGetTexEnvxvOES(GLenum env, GLenum pname, GLfixed *params) +glGetTexEnvxvOES + dir params out + len params (glUtilsParamSize(pname) * sizeof(GLfixed)) + +#void glGetTexParameterxvOES(GLenum target, GLenum pname, GLfixed *params) +glGetTexParameterxvOES + dir params out + len params (glUtilsParamSize(pname) * sizeof(GLfixed)) + +#void glLightModelxvOES(GLenum pname, GLfixed *params) +glLightModelxvOES + dir params in + len params (glUtilsParamSize(pname) * sizeof(GLfixed)) + +#void glLightxvOES(GLenum light, GLenum pname, GLfixed *params) +glLightxvOES + dir params in + len params (glUtilsParamSize(pname) * sizeof(GLfixed)) + +#void glLoadMatrixxOES(GLfixed *m) +glLoadMatrixxOES + dir m in + len m (16 * sizeof(GLfixed)) + +#void glMaterialxvOES(GLenum face, GLenum pname, GLfixed *params) +glMaterialxvOES + dir params in + len params (glUtilsParamSize(pname) * sizeof(GLfixed)) + +#void glMultMatrixxOES(GLfixed *m) +glMultMatrixxOES + dir m in + len m (16 * sizeof(GLfixed)) + +#void glPointParameterxvOES(GLenum pname, GLfixed *params) +glPointParameterxvOES + dir params in + len params (glUtilsParamSize(pname) * sizeof(GLfixed)) + +#void glTexEnvxvOES(GLenum target, GLenum pname, GLfixed *params) +glTexEnvxvOES + dir params in + len params (glUtilsParamSize(pname) * sizeof(GLfixed)) + +#void glTexParameterxvOES(GLenum target, GLenum pname, GLfixed *params) +glTexParameterxvOES + dir params in + len params (glUtilsParamSize(pname) * sizeof(GLfixed)) + +#void glDeleteRenderbuffersOES(GLsizei n, GLuint *renderbuffers) +glDeleteRenderbuffersOES + dir renderbuffers in + len renderbuffers (n * sizeof(GLuint)) + param_check n if(n<0){ ctx->setError(GL_INVALID_VALUE); return; } + +#void glGenRenderbuffersOES(GLsizei n, GLuint *renderbuffers) +glGenRenderbuffersOES + dir renderbuffers out + len renderbuffers (n * sizeof(GLuint)) + param_check n if(n<0){ ctx->setError(GL_INVALID_VALUE); return; } + +#void glGetRenderbufferParameterivOES(GLenum target, GLenum pname, GLint *params) +glGetRenderbufferParameterivOES + dir params out + len params (glUtilsParamSize(pname) * sizeof(GLint)) + +#void glDeleteFramebuffersOES(GLsizei n, GLuint *framebuffers) +glDeleteFramebuffersOES + dir framebuffers in + len framebuffers (n * sizeof(GLuint)) + param_check n if(n<0){ ctx->setError(GL_INVALID_VALUE); return; } + +#void glGenFramebuffersOES(GLsizei n, GLuint *framebuffers) +glGenFramebuffersOES + dir framebuffers out + len framebuffers (n * sizeof(GLuint)) + param_check n if(n<0){ ctx->setError(GL_INVALID_VALUE); return; } + +#void glGetFramebufferAttachmentParameterivOES(GLenum target, GLenum attachment, GLenum pname, GLint *params) +glGetFramebufferAttachmentParameterivOES + dir params out + len params (glUtilsParamSize(pname) * sizeof(GLint)) + +#void* glMapBufferOES(GLenum target, GLenum access) +glMapBufferOES + flag unsupported + +#void glGetBufferPointervOES(GLenum target, GLenum pname, GLvoid ** params) +glGetBufferPointervOES + flag unsupported + +#void glMatrixIndexPointerOES(GLint size, GLenum type, GLsizei stride, GLvoid *pointer) +glMatrixIndexPointerOES + len pointer (sizeof(unsigned int)) + flag unsupported + +#void glWeightPointerOES(GLint size, GLenum type, GLsizei stride, GLvoid *pointer) +glWeightPointerOES + len pointer (sizeof(unsigned int)) + flag unsupported + +#glQueryMatrixxOES(GLfixed * mantissa, GLint * exponent) +glQueryMatrixxOES + dir mantissa out + len mantissa (16 * sizeof(GLfixed)) + dir exponent out + len exponent (16 * sizeof(GLfixed)) + +#void glClipPlanefOES(GLenum plane, GLfloat *equation) +glClipPlanefOES + dir equation in + len equation (4 * sizeof(GLfloat)) + +#void glClipPlanefIMG(GLenum plane, GLfloat *equation) +glClipPlanefIMG + dir equation in + len equation (4 * sizeof(GLfloat)) + +#void glGetClipPlanefOES(GLenum pname, GLfloat * eqn) +glGetClipPlanefOES + dir eqn out + len eqn (4 * sizeof(GLfloat)) + +#void glTexGenfvOES(GLenum coord, GLenum pname, GLfloat *params) +glTexGenfvOES + len params (glUtilsParamSize(pname) * sizeof(GLfloat)) + +#void glTexGenivOES(GLenum coord, GLenum pname, GLint *params) +glTexGenivOES + len params (glUtilsParamSize(pname) * sizeof(GLint)) + +#void glTexGenxvOES(GLenum coord, GLenum pname, GLfixed *params) +glTexGenxvOES + len params (glUtilsParamSize(pname) * sizeof(GLfixed)) + +#void glGetTexGenfvOES(GLenum coord, GLenum pname, GLfloat *params) +glGetTexGenfvOES + len params (glUtilsParamSize(pname) * sizeof(GLfloat)) + +#void glGetTexGenivOES(GLenum coord, GLenum pname, GLint *params) +glGetTexGenivOES + len params (glUtilsParamSize(pname) * sizeof(GLint)) + +#void glGetTexGenxvOES(GLenum coord, GLenum pname, GLfixed *params) +glGetTexGenxvOES + len params (glUtilsParamSize(pname) * sizeof(GLfixed)) + +#void glDeleteVertexArraysOES(GLsizei n, const GLuint *arrays) +glDeleteVertexArraysOES + dir arrays in + len arrays (n * sizeof(GLuint)) + param_check n if(n<0){ ctx->setError(GL_INVALID_VALUE); return; } + +#void glGenVertexArraysOES(GLsizei n, GLuint *arrays) +glGenVertexArraysOES + dir arrays out + len arrays (n * sizeof(GLuint)) + param_check n if(n<0){ ctx->setError(GL_INVALID_VALUE); return; } + +#void glDiscardFramebufferEXT(GLenum target, GLsizei numAttachments, const GLenum *attachments) +glDiscardFramebufferEXT + dir attachments in + len attachments (numAttachments * sizeof(const GLenum)) + +#void glMultiDrawArraysEXT(GLenum mode, const GLint *first, const GLsizei *count, GLsizei primcount) +glMultiDrawArraysEXT + flag unsupported + +#void glMultiDrawElementsEXT(GLenum mode, const GLsizei *count, GLenum type, const GLvoid* const *indices, GLsizei primcount) +glMultiDrawElementsEXT + flag unsupported + +#void glMultiDrawArraysSUN(GLenum mode, GLint *first, GLsizei *count, GLsizei primcount) +glMultiDrawArraysSUN + flag unsupported + +#void glMultiDrawElementsSUN(GLenum mode, const GLsizei *count, GLenum type, const GLvoid* *indices, GLsizei primcount) +glMultiDrawElementsSUN + flag unsupported + +#void glDeleteFencesNV(GLsizei n, const GLuint *fences) +glDeleteFencesNV + dir fences in + len fences (n * sizeof(GLuint)) + param_check n if(n<0){ ctx->setError(GL_INVALID_VALUE); return; } + +#void glGenFencesNV(GLsizei n, GLuint *fences) +glGenFencesNV + dir fences in + len fences (n * sizeof(GLuint)) + param_check n if(n<0){ ctx->setError(GL_INVALID_VALUE); return; } + +#void glGetFenceivNV(GLuint fence, GLenum pname, GLint *params) +glGetFenceivNV + dir params out + len params (glUtilsParamSize(pname) * sizeof(GLint)) + +#void glGetDriverControlsQCOM(GLint *num, GLsizei size, GLuint *driverControls) +glGetDriverControlsQCOM + dir num out + len num (1 * sizeof(GLint)) + dir driverControls out + len driverControls (size * sizeof(GLuint)) + +#void glGetDriverControlStringQCOM(GLuint driverControl, GLsizei bufSize, GLsizei *length, GLchar *driverControlString) +glGetDriverControlStringQCOM + dir length out + len length (1 * sizeof(GLsizei)) + dir driverControlString out + len driverControlString (1 * sizeof(GLchar)) + +#void glExtGetTexturesQCOM(GLuint *textures, GLint maxTextures, GLint *numTextures) +glExtGetTexturesQCOM + dir textures out + len textures (maxTextures * sizeof(GLuint)) + dir numTextures out + len numTextures (1 * sizeof(GLint)) + +#void glExtGetBuffersQCOM(GLuint *buffers, GLint maxBuffers, GLint *numBuffers) +glExtGetBuffersQCOM + dir buffers out + len buffers (maxBuffers * sizeof(GLuint)) + dir numBuffers out + len numBuffers (1 * sizeof(GLint)) + +#void glExtGetRenderbuffersQCOM(GLuint *renderbuffers, GLint maxRenderbuffers, GLint *numRenderbuffers) +glExtGetRenderbuffersQCOM + dir renderbuffers out + len renderbuffers (maxRenderbuffers * sizeof(GLuint)) + dir numRenderbuffers out + len numRenderbuffers (1 * sizeof(GLint)) + +#void glExtGetFramebuffersQCOM(GLuint *framebuffers, GLint maxFramebuffers, GLint *numFramebuffers) +glExtGetFramebuffersQCOM + dir framebuffers out + len framebuffers (maxFramebuffers * sizeof(GLuint)) + dir numFramebuffers out + len numFramebuffers (1 * sizeof(GLint)) + +#void glExtGetTexLevelParameterivQCOM(GLuint texture, GLenum face, GLint level, GLenum pname, GLint *params) +glExtGetTexLevelParameterivQCOM + dir params out + len params (glUtilsParamSize(pname) * sizeof(GLint)) + +#void glExtGetTexSubImageQCOM(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, GLvoid *texels) +glExtGetTexSubImageQCOM + dir texels out + len texels (depth * glesv1_enc::pixelDataSize(self, width, height, format, type, 0)) + +#void glExtGetBufferPointervQCOM(GLenum target, GLvoid **params) +glExtGetBufferPointervQCOM + flag unsupported + +#void glExtGetShadersQCOM(GLuint *shaders, GLint maxShaders, GLint *numShaders) +glExtGetShadersQCOM + dir shaders out + len shaders (maxShaders * sizeof(GLuint)) + dir numShaders out + len numShaders (1 * sizeof(GLint)) + +#void glExtGetProgramsQCOM(GLuint *programs, GLint maxPrograms, GLint *numPrograms) +glExtGetProgramsQCOM + dir programs out + len programs (maxPrograms * sizeof(GLuint)) + dir numPrograms out + len numPrograms (1 * sizeof(GLint)) + +#void glExtGetProgramBinarySourceQCOM(GLuint program, GLenum shadertype, GLchar *source, GLint *length) +glExtGetProgramBinarySourceQCOM + flag unsupported diff --git a/external/android-emugl/host/libs/GLESv1_dec/gles1.in b/external/android-emugl/host/libs/GLESv1_dec/gles1.in new file mode 100644 index 0000000..74d5454 --- /dev/null +++ b/external/android-emugl/host/libs/GLESv1_dec/gles1.in @@ -0,0 +1,298 @@ +GL_ENTRY(void, glAlphaFunc, GLenum func, GLclampf ref) +GL_ENTRY(void, glClearColor, GLclampf red, GLclampf green, GLclampf blue, GLclampf alpha) +GL_ENTRY(void, glClearDepthf, GLclampf depth) +GL_ENTRY(void, glClipPlanef, GLenum plane, const GLfloat *equation) +GL_ENTRY(void, glColor4f, GLfloat red, GLfloat green, GLfloat blue, GLfloat alpha) +GL_ENTRY(void, glDepthRangef, GLclampf zNear, GLclampf zFar) +GL_ENTRY(void, glFogf, GLenum pname, GLfloat param) +GL_ENTRY(void, glFogfv, GLenum pname, const GLfloat *params) +GL_ENTRY(void, glFrustumf, GLfloat left, GLfloat right, GLfloat bottom, GLfloat top, GLfloat zNear, GLfloat zFar) +GL_ENTRY(void, glGetClipPlanef, GLenum pname, GLfloat* eqn) +GL_ENTRY(void, glGetFloatv, GLenum pname, GLfloat *params) +GL_ENTRY(void, glGetLightfv, GLenum light, GLenum pname, GLfloat *params) +GL_ENTRY(void, glGetMaterialfv, GLenum face, GLenum pname, GLfloat *params) +GL_ENTRY(void, glGetTexEnvfv, GLenum env, GLenum pname, GLfloat *params) +GL_ENTRY(void, glGetTexParameterfv, GLenum target, GLenum pname, GLfloat *params) +GL_ENTRY(void, glLightModelf, GLenum pname, GLfloat param) +GL_ENTRY(void, glLightModelfv, GLenum pname, const GLfloat *params) +GL_ENTRY(void, glLightf, GLenum light, GLenum pname, GLfloat param) +GL_ENTRY(void, glLightfv, GLenum light, GLenum pname, const GLfloat *params) +GL_ENTRY(void, glLineWidth, GLfloat width) +GL_ENTRY(void, glLoadMatrixf, const GLfloat *m) +GL_ENTRY(void, glMaterialf, GLenum face, GLenum pname, GLfloat param) +GL_ENTRY(void, glMaterialfv, GLenum face, GLenum pname, const GLfloat *params) +GL_ENTRY(void, glMultMatrixf, const GLfloat *m) +GL_ENTRY(void, glMultiTexCoord4f, GLenum target, GLfloat s, GLfloat t, GLfloat r, GLfloat q) +GL_ENTRY(void, glNormal3f, GLfloat nx, GLfloat ny, GLfloat nz) +GL_ENTRY(void, glOrthof, GLfloat left, GLfloat right, GLfloat bottom, GLfloat top, GLfloat zNear, GLfloat zFar) +GL_ENTRY(void, glPointParameterf, GLenum pname, GLfloat param) +GL_ENTRY(void, glPointParameterfv, GLenum pname, const GLfloat *params) +GL_ENTRY(void, glPointSize, GLfloat size) +GL_ENTRY(void, glPolygonOffset, GLfloat factor, GLfloat units) +GL_ENTRY(void, glRotatef, GLfloat angle, GLfloat x, GLfloat y, GLfloat z) +GL_ENTRY(void, glScalef, GLfloat x, GLfloat y, GLfloat z) +GL_ENTRY(void, glTexEnvf, GLenum target, GLenum pname, GLfloat param) +GL_ENTRY(void, glTexEnvfv, GLenum target, GLenum pname, const GLfloat *params) +GL_ENTRY(void, glTexParameterf, GLenum target, GLenum pname, GLfloat param) +GL_ENTRY(void, glTexParameterfv, GLenum target, GLenum pname, const GLfloat *params) +GL_ENTRY(void, glTranslatef, GLfloat x, GLfloat y, GLfloat z) +GL_ENTRY(void, glActiveTexture, GLenum texture) +GL_ENTRY(void, glAlphaFuncx, GLenum func, GLclampx ref) +GL_ENTRY(void, glBindBuffer, GLenum target, GLuint buffer) +GL_ENTRY(void, glBindTexture, GLenum target, GLuint texture) +GL_ENTRY(void, glBlendFunc, GLenum sfactor, GLenum dfactor) +GL_ENTRY(void, glBufferData, GLenum target, GLsizeiptr size, const GLvoid *data, GLenum usage) +GL_ENTRY(void, glBufferSubData, GLenum target, GLintptr offset, GLsizeiptr size, const GLvoid *data) +GL_ENTRY(void, glClear, GLbitfield mask) +GL_ENTRY(void, glClearColorx, GLclampx red, GLclampx green, GLclampx blue, GLclampx alpha) +GL_ENTRY(void, glClearDepthx, GLclampx depth) +GL_ENTRY(void, glClearStencil, GLint s) +GL_ENTRY(void, glClientActiveTexture, GLenum texture) +GL_ENTRY(void, glColor4ub, GLubyte red, GLubyte green, GLubyte blue, GLubyte alpha) +GL_ENTRY(void, glColor4x, GLfixed red, GLfixed green, GLfixed blue, GLfixed alpha) +GL_ENTRY(void, glColorMask, GLboolean red, GLboolean green, GLboolean blue, GLboolean alpha) +GL_ENTRY(void, glColorPointer, GLint size, GLenum type, GLsizei stride, const GLvoid *pointer) +GL_ENTRY(void, glCompressedTexImage2D, GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLint border, GLsizei imageSize, const GLvoid *data) +GL_ENTRY(void, glCompressedTexSubImage2D, GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLsizei imageSize, const GLvoid *data) +GL_ENTRY(void, glCopyTexImage2D, GLenum target, GLint level, GLenum internalformat, GLint x, GLint y, GLsizei width, GLsizei height, GLint border) +GL_ENTRY(void, glCopyTexSubImage2D, GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint x, GLint y, GLsizei width, GLsizei height) +GL_ENTRY(void, glCullFace, GLenum mode) +GL_ENTRY(void, glDeleteBuffers, GLsizei n, const GLuint *buffers) +GL_ENTRY(void, glDeleteTextures, GLsizei n, const GLuint *textures) +GL_ENTRY(void, glDepthFunc, GLenum func) +GL_ENTRY(void, glDepthMask, GLboolean flag) +GL_ENTRY(void, glDepthRangex, GLclampx zNear, GLclampx zFar) +GL_ENTRY(void, glDisable, GLenum cap) +GL_ENTRY(void, glDisableClientState, GLenum array) +GL_ENTRY(void, glDrawArrays, GLenum mode, GLint first, GLsizei count) +GL_ENTRY(void, glDrawElements, GLenum mode, GLsizei count, GLenum type, const GLvoid *indices) +GL_ENTRY(void, glEnable, GLenum cap) +GL_ENTRY(void, glEnableClientState, GLenum array) +GL_ENTRY(void, glFinish, void) +GL_ENTRY(void, glFlush, void) +GL_ENTRY(void, glFogx, GLenum pname, GLfixed param) +GL_ENTRY(void, glFogxv, GLenum pname, const GLfixed *params) +GL_ENTRY(void, glFrontFace, GLenum mode) +GL_ENTRY(void, glFrustumx, GLfixed left, GLfixed right, GLfixed bottom, GLfixed top, GLfixed zNear, GLfixed zFar) +GL_ENTRY(void, glGetBooleanv, GLenum pname, GLboolean *params) +GL_ENTRY(void, glGetBufferParameteriv, GLenum target, GLenum pname, GLint *params) +GL_ENTRY(void, glClipPlanex, GLenum pname, const GLfixed * eqn) +GL_ENTRY(void, glGenBuffers, GLsizei n, GLuint *buffers) +GL_ENTRY(void, glGenTextures, GLsizei n, GLuint *textures) +GL_ENTRY(GLenum, glGetError, void) +GL_ENTRY(void, glGetFixedv, GLenum pname, GLfixed *params) +GL_ENTRY(void, glGetIntegerv, GLenum pname, GLint *params) +GL_ENTRY(void, glGetLightxv, GLenum light, GLenum pname, GLfixed *params) +GL_ENTRY(void, glGetMaterialxv, GLenum face, GLenum pname, GLfixed *params) +GL_ENTRY(void, glGetPointerv, GLenum pname, GLvoid **params) +GL_ENTRY(const GLubyte *, glGetString, GLenum name) +GL_ENTRY(void, glGetTexEnviv, GLenum env, GLenum pname, GLint *params) +GL_ENTRY(void, glGetTexEnvxv, GLenum env, GLenum pname, GLfixed *params) +GL_ENTRY(void, glGetTexParameteriv, GLenum target, GLenum pname, GLint *params) +GL_ENTRY(void, glGetTexParameterxv, GLenum target, GLenum pname, GLfixed *params) +GL_ENTRY(void, glHint, GLenum target, GLenum mode) +GL_ENTRY(GLboolean, glIsBuffer, GLuint buffer) +GL_ENTRY(GLboolean, glIsEnabled, GLenum cap) +GL_ENTRY(GLboolean, glIsTexture, GLuint texture) +GL_ENTRY(void, glLightModelx, GLenum pname, GLfixed param) +GL_ENTRY(void, glLightModelxv, GLenum pname, const GLfixed *params) +GL_ENTRY(void, glLightx, GLenum light, GLenum pname, GLfixed param) +GL_ENTRY(void, glLightxv, GLenum light, GLenum pname, const GLfixed *params) +GL_ENTRY(void, glLineWidthx, GLfixed width) +GL_ENTRY(void, glLoadIdentity, void) +GL_ENTRY(void, glLoadMatrixx, const GLfixed *m) +GL_ENTRY(void, glLogicOp, GLenum opcode) +GL_ENTRY(void, glMaterialx, GLenum face, GLenum pname, GLfixed param) +GL_ENTRY(void, glMaterialxv, GLenum face, GLenum pname, const GLfixed *params) +GL_ENTRY(void, glMatrixMode, GLenum mode) +GL_ENTRY(void, glMultMatrixx, const GLfixed *m) +GL_ENTRY(void, glMultiTexCoord4x, GLenum target, GLfixed s, GLfixed t, GLfixed r, GLfixed q) +GL_ENTRY(void, glNormal3x, GLfixed nx, GLfixed ny, GLfixed nz) +GL_ENTRY(void, glNormalPointer, GLenum type, GLsizei stride, const GLvoid *pointer) +GL_ENTRY(void, glOrthox, GLfixed left, GLfixed right, GLfixed bottom, GLfixed top, GLfixed zNear, GLfixed zFar) +GL_ENTRY(void, glPixelStorei, GLenum pname, GLint param) +GL_ENTRY(void, glPointParameterx, GLenum pname, GLfixed param) +GL_ENTRY(void, glPointParameterxv, GLenum pname, const GLfixed *params) +GL_ENTRY(void, glPointSizex, GLfixed size) +GL_ENTRY(void, glPolygonOffsetx, GLfixed factor, GLfixed units) +GL_ENTRY(void, glPopMatrix, void) +GL_ENTRY(void, glPushMatrix, void) +GL_ENTRY(void, glReadPixels, GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, GLvoid *pixels) +GL_ENTRY(void, glRotatex, GLfixed angle, GLfixed x, GLfixed y, GLfixed z) +GL_ENTRY(void, glSampleCoverage, GLclampf value, GLboolean invert) +GL_ENTRY(void, glSampleCoveragex, GLclampx value, GLboolean invert) +GL_ENTRY(void, glScalex, GLfixed x, GLfixed y, GLfixed z) +GL_ENTRY(void, glScissor, GLint x, GLint y, GLsizei width, GLsizei height) +GL_ENTRY(void, glShadeModel, GLenum mode) +GL_ENTRY(void, glStencilFunc, GLenum func, GLint ref, GLuint mask) +GL_ENTRY(void, glStencilMask, GLuint mask) +GL_ENTRY(void, glStencilOp, GLenum fail, GLenum zfail, GLenum zpass) +GL_ENTRY(void, glTexCoordPointer, GLint size, GLenum type, GLsizei stride, const GLvoid *pointer) +GL_ENTRY(void, glTexEnvi, GLenum target, GLenum pname, GLint param) +GL_ENTRY(void, glTexEnvx, GLenum target, GLenum pname, GLfixed param) +GL_ENTRY(void, glTexEnviv, GLenum target, GLenum pname, const GLint *params) +GL_ENTRY(void, glTexEnvxv, GLenum target, GLenum pname, const GLfixed *params) +GL_ENTRY(void, glTexImage2D, GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, const GLvoid *pixels) +GL_ENTRY(void, glTexParameteri, GLenum target, GLenum pname, GLint param) +GL_ENTRY(void, glTexParameterx, GLenum target, GLenum pname, GLfixed param) +GL_ENTRY(void, glTexParameteriv, GLenum target, GLenum pname, const GLint *params) +GL_ENTRY(void, glTexParameterxv, GLenum target, GLenum pname, const GLfixed *params) +GL_ENTRY(void, glTexSubImage2D, GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, const GLvoid *pixels) +GL_ENTRY(void, glTranslatex, GLfixed x, GLfixed y, GLfixed z) +GL_ENTRY(void, glVertexPointer, GLint size, GLenum type, GLsizei stride, const GLvoid *pointer) +GL_ENTRY(void, glViewport, GLint x, GLint y, GLsizei width, GLsizei height) +GL_ENTRY(void, glPointSizePointerOES, GLenum type, GLsizei stride, const GLvoid *pointer) + +GL_ENTRY(void, glVertexPointerOffset, GLint size, GLenum type, GLsizei stride, GLuint offset) +GL_ENTRY(void, glColorPointerOffset, GLint size, GLenum type, GLsizei stride, GLuint offset) +GL_ENTRY(void, glNormalPointerOffset, GLenum type, GLsizei stride, GLuint offset) +GL_ENTRY(void, glPointSizePointerOffset, GLenum type, GLsizei stride, GLuint offset) +GL_ENTRY(void, glTexCoordPointerOffset, GLint size, GLenum type, GLsizei stride, GLuint offset) +GL_ENTRY(void, glWeightPointerOffset, GLint size, GLenum type, GLsizei stride, GLuint offset) +GL_ENTRY(void, glMatrixIndexPointerOffset, GLint size, GLenum type, GLsizei stride, GLuint offset) + +GL_ENTRY(void, glVertexPointerData, GLint size, GLenum type, GLsizei stride, void * data, GLuint datalen) +GL_ENTRY(void, glColorPointerData, GLint size, GLenum type, GLsizei stride, void * data, GLuint datalen) +GL_ENTRY(void, glNormalPointerData, GLenum type, GLsizei stride, void * data, GLuint datalen) +GL_ENTRY(void, glTexCoordPointerData, GLint unit, GLint size, GLenum type, GLsizei stride, void * data, GLuint datalen) +GL_ENTRY(void, glPointSizePointerData, GLenum type, GLsizei stride, void * data, GLuint datalen) +GL_ENTRY(void, glWeightPointerData, GLint size, GLenum type, GLsizei stride, void * data, GLuint datalen) +GL_ENTRY(void, glMatrixIndexPointerData, GLint size, GLenum type, GLsizei stride, void * data, GLuint datalen) + +GL_ENTRY(void, glDrawElementsOffset, GLenum mode, GLsizei count, GLenum type, GLuint offset) +GL_ENTRY(void, glDrawElementsData, GLenum mode, GLsizei count, GLenum type, void *data, GLuint datalen) +GL_ENTRY(void, glGetCompressedTextureFormats, int count, GLint *formats); + +GL_ENTRY(int, glFinishRoundTrip, void) + +#opengl extensions + +GL_ENTRY(void, glBlendEquationSeparateOES, GLenum modeRGB, GLenum modeAlpha) +GL_ENTRY(void, glBlendFuncSeparateOES, GLenum srcRGB, GLenum dstRGB, GLenum srcAlpha, GLenum dstAlpha) +GL_ENTRY(void, glBlendEquationOES, GLenum mode) +GL_ENTRY(void, glDrawTexsOES, GLshort x, GLshort y, GLshort z, GLshort width, GLshort height) +GL_ENTRY(void, glDrawTexiOES, GLint x, GLint y, GLint z, GLint width, GLint height) +GL_ENTRY(void, glDrawTexxOES, GLfixed x, GLfixed y, GLfixed z, GLfixed width, GLfixed height) +GL_ENTRY(void, glDrawTexsvOES, const GLshort *coords) +GL_ENTRY(void, glDrawTexivOES, const GLint *coords) +GL_ENTRY(void, glDrawTexxvOES, const GLfixed *coords) +GL_ENTRY(void, glDrawTexfOES, GLfloat x, GLfloat y, GLfloat z, GLfloat width, GLfloat height) +GL_ENTRY(void, glDrawTexfvOES, const GLfloat *coords) +GL_ENTRY(void, glEGLImageTargetTexture2DOES, GLenum target, GLeglImageOES image) +GL_ENTRY(void, glEGLImageTargetRenderbufferStorageOES, GLenum target, GLeglImageOES image) +GL_ENTRY(void, glAlphaFuncxOES, GLenum func, GLclampx ref) +GL_ENTRY(void, glClearColorxOES, GLclampx red, GLclampx green, GLclampx blue, GLclampx alpha) +GL_ENTRY(void, glClearDepthxOES, GLclampx depth) +GL_ENTRY(void, glClipPlanexOES, GLenum plane, const GLfixed * equation) +GL_ENTRY(void, glClipPlanexIMG, GLenum plane, const GLfixed * equation) +GL_ENTRY(void, glColor4xOES, GLfixed red, GLfixed green, GLfixed blue, GLfixed alpha) +GL_ENTRY(void, glDepthRangexOES, GLclampx zNear, GLclampx zFar) +GL_ENTRY(void, glFogxOES, GLenum pname, GLfixed param) +GL_ENTRY(void, glFogxvOES, GLenum pname, const GLfixed *params) +GL_ENTRY(void, glFrustumxOES, GLfixed left, GLfixed right, GLfixed bottom, GLfixed top, GLfixed zNear, GLfixed zFar) +GL_ENTRY(void, glGetClipPlanexOES, GLenum pname, GLfixed* eqn) +GL_ENTRY(void, glGetClipPlanex, GLenum pname, GLfixed* eqn) +GL_ENTRY(void, glGetFixedvOES, GLenum pname, GLfixed *params) +GL_ENTRY(void, glGetLightxvOES, GLenum light, GLenum pname, GLfixed *params) +GL_ENTRY(void, glGetMaterialxvOES, GLenum face, GLenum pname, GLfixed *params) +GL_ENTRY(void, glGetTexEnvxvOES, GLenum env, GLenum pname, GLfixed *params) +GL_ENTRY(void, glGetTexParameterxvOES, GLenum target, GLenum pname, GLfixed *params) +GL_ENTRY(void, glLightModelxOES, GLenum pname, GLfixed param) +GL_ENTRY(void, glLightModelxvOES, GLenum pname, const GLfixed *params) +GL_ENTRY(void, glLightxOES, GLenum light, GLenum pname, GLfixed param) +GL_ENTRY(void, glLightxvOES, GLenum light, GLenum pname, const GLfixed *params) +GL_ENTRY(void, glLineWidthxOES, GLfixed width) +GL_ENTRY(void, glLoadMatrixxOES, const GLfixed *m) +GL_ENTRY(void, glMaterialxOES, GLenum face, GLenum pname, GLfixed param) +GL_ENTRY(void, glMaterialxvOES, GLenum face, GLenum pname, const GLfixed *params) +GL_ENTRY(void, glMultMatrixxOES, const GLfixed *m) +GL_ENTRY(void, glMultiTexCoord4xOES, GLenum target, GLfixed s, GLfixed t, GLfixed r, GLfixed q) +GL_ENTRY(void, glNormal3xOES, GLfixed nx, GLfixed ny, GLfixed nz) +GL_ENTRY(void, glOrthoxOES, GLfixed left, GLfixed right, GLfixed bottom, GLfixed top, GLfixed zNear, GLfixed zFar) +GL_ENTRY(void, glPointParameterxOES, GLenum pname, GLfixed param) +GL_ENTRY(void, glPointParameterxvOES, GLenum pname, const GLfixed *params) +GL_ENTRY(void, glPointSizexOES, GLfixed size) +GL_ENTRY(void, glPolygonOffsetxOES, GLfixed factor, GLfixed units) +GL_ENTRY(void, glRotatexOES, GLfixed angle, GLfixed x, GLfixed y, GLfixed z) +GL_ENTRY(void, glSampleCoveragexOES, GLclampx value, GLboolean invert) +GL_ENTRY(void, glScalexOES, GLfixed x, GLfixed y, GLfixed z) +GL_ENTRY(void, glTexEnvxOES, GLenum target, GLenum pname, GLfixed param) +GL_ENTRY(void, glTexEnvxvOES, GLenum target, GLenum pname, const GLfixed *params) +GL_ENTRY(void, glTexParameterxOES, GLenum target, GLenum pname, GLfixed param) +GL_ENTRY(void, glTexParameterxvOES, GLenum target, GLenum pname, const GLfixed *params) +GL_ENTRY(void, glTranslatexOES, GLfixed x, GLfixed y, GLfixed z) +GL_ENTRY(GLboolean, glIsRenderbufferOES, GLuint renderbuffer) +GL_ENTRY(void, glBindRenderbufferOES, GLenum target, GLuint renderbuffer) +GL_ENTRY(void, glDeleteRenderbuffersOES, GLsizei n, const GLuint* renderbuffers) +GL_ENTRY(void, glGenRenderbuffersOES, GLsizei n, GLuint* renderbuffers) +GL_ENTRY(void, glRenderbufferStorageOES, GLenum target, GLenum internalformat, GLsizei width, GLsizei height) +GL_ENTRY(void, glGetRenderbufferParameterivOES, GLenum target, GLenum pname, GLint* params) +GL_ENTRY(GLboolean, glIsFramebufferOES, GLuint framebuffer) +GL_ENTRY(void, glBindFramebufferOES, GLenum target, GLuint framebuffer) +GL_ENTRY(void, glDeleteFramebuffersOES, GLsizei n, const GLuint* framebuffers) +GL_ENTRY(void, glGenFramebuffersOES, GLsizei n, GLuint* framebuffers) +GL_ENTRY(GLenum, glCheckFramebufferStatusOES, GLenum target) +GL_ENTRY(void, glFramebufferRenderbufferOES, GLenum target, GLenum attachment, GLenum renderbuffertarget, GLuint renderbuffer) +GL_ENTRY(void, glFramebufferTexture2DOES, GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level) +GL_ENTRY(void, glGetFramebufferAttachmentParameterivOES, GLenum target, GLenum attachment, GLenum pname, GLint* params) +GL_ENTRY(void, glGenerateMipmapOES, GLenum target) +GL_ENTRY(void*, glMapBufferOES, GLenum target, GLenum access) +GL_ENTRY(GLboolean, glUnmapBufferOES, GLenum target) +GL_ENTRY(void, glGetBufferPointervOES, GLenum target, GLenum pname, GLvoid* *params) +GL_ENTRY(void, glCurrentPaletteMatrixOES, GLuint matrixpaletteindex) +GL_ENTRY(void, glLoadPaletteFromModelViewMatrixOES, void) +GL_ENTRY(void, glMatrixIndexPointerOES, GLint size, GLenum type, GLsizei stride, const GLvoid * pointer) +GL_ENTRY(void, glWeightPointerOES, GLint size, GLenum type, GLsizei stride, const GLvoid * pointer) +GL_ENTRY(GLbitfield, glQueryMatrixxOES, GLfixed * mantissa, GLint * exponent) +GL_ENTRY(void, glDepthRangefOES, GLclampf zNear, GLclampf zFar) +GL_ENTRY(void, glFrustumfOES, GLfloat left, GLfloat right, GLfloat bottom, GLfloat top, GLfloat zNear, GLfloat zFar) +GL_ENTRY(void, glOrthofOES, GLfloat left, GLfloat right, GLfloat bottom, GLfloat top, GLfloat zNear, GLfloat zFar) +GL_ENTRY(void, glClipPlanefOES, GLenum plane, const GLfloat *equation) +GL_ENTRY(void, glClipPlanefIMG, GLenum plane, const GLfloat *equation) +GL_ENTRY(void, glGetClipPlanefOES, GLenum pname, GLfloat * eqn) +GL_ENTRY(void, glClearDepthfOES, GLclampf depth) +GL_ENTRY(void, glTexGenfOES, GLenum coord, GLenum pname, GLfloat param) +GL_ENTRY(void, glTexGenfvOES, GLenum coord, GLenum pname, const GLfloat *params) +GL_ENTRY(void, glTexGeniOES, GLenum coord, GLenum pname, GLint param) +GL_ENTRY(void, glTexGenivOES, GLenum coord, GLenum pname, const GLint *params) +GL_ENTRY(void, glTexGenxOES, GLenum coord, GLenum pname, GLfixed param) +GL_ENTRY(void, glTexGenxvOES, GLenum coord, GLenum pname, const GLfixed *params) +GL_ENTRY(void, glGetTexGenfvOES, GLenum coord, GLenum pname, GLfloat *params) +GL_ENTRY(void, glGetTexGenivOES, GLenum coord, GLenum pname, GLint *params) +GL_ENTRY(void, glGetTexGenxvOES, GLenum coord, GLenum pname, GLfixed *params) +GL_ENTRY(void, glBindVertexArrayOES, GLuint array) +GL_ENTRY(void, glDeleteVertexArraysOES, GLsizei n, const GLuint *arrays) +GL_ENTRY(void, glGenVertexArraysOES, GLsizei n, GLuint *arrays) +GL_ENTRY(GLboolean, glIsVertexArrayOES, GLuint array) +GL_ENTRY(void, glDiscardFramebufferEXT, GLenum target, GLsizei numAttachments, const GLenum *attachments) +GL_ENTRY(void, glMultiDrawArraysEXT, GLenum mode, const GLint *first, const GLsizei *count, GLsizei primcount) +GL_ENTRY(void, glMultiDrawElementsEXT, GLenum mode, const GLsizei *count, GLenum type, const GLvoid*const *indices, GLsizei primcount) +GL_ENTRY(void, glMultiDrawArraysSUN, GLenum mode, GLint *first, GLsizei *count, GLsizei primcount) +GL_ENTRY(void, glMultiDrawElementsSUN, GLenum mode, const GLsizei *count, GLenum type, const GLvoid **indices, GLsizei primcount) +GL_ENTRY(void, glRenderbufferStorageMultisampleIMG, GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height) +GL_ENTRY(void, glFramebufferTexture2DMultisampleIMG, GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level, GLsizei samples) +GL_ENTRY(void, glDeleteFencesNV, GLsizei n, const GLuint *fences) +GL_ENTRY(void, glGenFencesNV, GLsizei n, GLuint *fences) +GL_ENTRY(GLboolean, glIsFenceNV, GLuint fence) +GL_ENTRY(GLboolean, glTestFenceNV, GLuint fence) +GL_ENTRY(void, glGetFenceivNV, GLuint fence, GLenum pname, GLint *params) +GL_ENTRY(void, glFinishFenceNV, GLuint fence) +GL_ENTRY(void, glSetFenceNV, GLuint fence, GLenum condition) +GL_ENTRY(void, glGetDriverControlsQCOM, GLint *num, GLsizei size, GLuint *driverControls) +GL_ENTRY(void, glGetDriverControlStringQCOM, GLuint driverControl, GLsizei bufSize, GLsizei *length, GLchar *driverControlString) +GL_ENTRY(void, glEnableDriverControlQCOM, GLuint driverControl) +GL_ENTRY(void, glDisableDriverControlQCOM, GLuint driverControl) +GL_ENTRY(void, glExtGetTexturesQCOM, GLuint *textures, GLint maxTextures, GLint *numTextures) +GL_ENTRY(void, glExtGetBuffersQCOM, GLuint *buffers, GLint maxBuffers, GLint *numBuffers) +GL_ENTRY(void, glExtGetRenderbuffersQCOM, GLuint * renderbuffers, GLint maxRenderbuffers, GLint * numRenderbuffers) +GL_ENTRY(void, glExtGetFramebuffersQCOM, GLuint *framebuffers, GLint maxFramebuffers, GLint *numFramebuffers) +GL_ENTRY(void, glExtGetTexLevelParameterivQCOM, GLuint texture, GLenum face, GLint level, GLenum pname, GLint *params) +GL_ENTRY(void, glExtTexObjectStateOverrideiQCOM, GLenum target, GLenum pname, GLint param) +GL_ENTRY(void, glExtGetTexSubImageQCOM, GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, GLvoid *texels) +GL_ENTRY(void, glExtGetBufferPointervQCOM, GLenum target, GLvoid* *params) +GL_ENTRY(void, glExtGetShadersQCOM, GLuint *shaders, GLint maxShaders, GLint *numShaders) +GL_ENTRY(void, glExtGetProgramsQCOM, GLuint *programs, GLint maxPrograms, GLint *numPrograms) +GL_ENTRY(GLboolean, glExtIsProgramBinaryQCOM, GLuint program) +GL_ENTRY(void, glExtGetProgramBinarySourceQCOM, GLuint program, GLenum shadertype, GLchar *source, GLint *length) +GL_ENTRY(void, glStartTilingQCOM, GLuint x, GLuint y, GLuint width, GLuint height, GLbitfield preserveMask) +GL_ENTRY(void, glEndTilingQCOM, GLbitfield preserveMask) diff --git a/external/android-emugl/host/libs/GLESv1_dec/gles1.types b/external/android-emugl/host/libs/GLESv1_dec/gles1.types new file mode 100644 index 0000000..2d9a3b0 --- /dev/null +++ b/external/android-emugl/host/libs/GLESv1_dec/gles1.types @@ -0,0 +1,34 @@ +GLbitfield 32 0x%08x +GLboolean 8 %d +GLclampf 32 %f +GLclampx 32 0x%08x +GLeglImageOES 32 %p +GLenum 32 0x%08x +GLfixed 32 0x%08x +GLfloat 32 %f +GLint 32 %d +GLintptr 32 %p +GLshort 16 %d +GLsizei 32 %d +GLsizeiptr 32 %p +GLubyte 8 0x%02x +GLuint 32 %u +GLvoid 0 %x +GLchar 8 %d +GLenum* 32 0x%08x +GLboolean* 32 0x%08x +GLclampf* 32 0x%08x +GLclampx* 32 0x%08x +GLeglImageOES* 32 0x%08x +GLfixed* 32 0x%08x +GLfloat* 32 0x%08x +GLint* 32 0x%08x +GLshort* 32 0x%08x +GLsizei* 32 0x%08x +GLubyte* 32 0x%08x +GLuint* 32 0x%08x +GLvoid* 32 0x%08x +GLchar* 32 0x%08x +GLvoid** 32 0x%08x +void* 32 0x%08x +GLvoid*const* 32 0x%08x diff --git a/external/android-emugl/host/libs/GLESv1_dec/gles1_types.h b/external/android-emugl/host/libs/GLESv1_dec/gles1_types.h new file mode 100644 index 0000000..c8355ad --- /dev/null +++ b/external/android-emugl/host/libs/GLESv1_dec/gles1_types.h @@ -0,0 +1,21 @@ +/* +* Copyright (C) 2011 The Android Open Source Project +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ +#ifndef __GLES1_TYPES__H +#define __GLES1_TYPES__H + +#include "gl_base_types.h" + +#endif // __GLES1_TYPES__H diff --git a/external/android-emugl/host/libs/GLESv2_dec/Android.mk b/external/android-emugl/host/libs/GLESv2_dec/Android.mk new file mode 100644 index 0000000..711b17c --- /dev/null +++ b/external/android-emugl/host/libs/GLESv2_dec/Android.mk @@ -0,0 +1,15 @@ +LOCAL_PATH := $(call my-dir) + +### host library ########################################## +$(call emugl-begin-host-static-library,libGLESv2_dec) +$(call emugl-import, libOpenglCodecCommon) +$(call emugl-gen-decoder,$(LOCAL_PATH),gles2) + +# For gl2_types.h ! +$(call emugl-export,C_INCLUDES,$(LOCAL_PATH)) + +$(call emugl-export,CFLAGS,$(EMUGL_USER_CFLAGS)) + +LOCAL_SRC_FILES := GLESv2Decoder.cpp + +$(call emugl-end-module) diff --git a/external/android-emugl/host/libs/GLESv2_dec/CMakeLists.txt b/external/android-emugl/host/libs/GLESv2_dec/CMakeLists.txt new file mode 100644 index 0000000..08e3071 --- /dev/null +++ b/external/android-emugl/host/libs/GLESv2_dec/CMakeLists.txt @@ -0,0 +1,18 @@ +set(GENERATED_SOURCES + gles2_dec.cpp + gles2_opcodes.h + gles2_server_context.cpp) + +add_custom_command( + OUTPUT ${GENERATED_SOURCES} + POST_BUILD + COMMAND ${CMAKE_BINARY_DIR}/external/android-emugl/host/tools/emugen/emugen + -D ${CMAKE_CURRENT_BINARY_DIR} gles2 + WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} + DEPENDS emugen) + +set(SOURCES + GLESv2Decoder.cpp) + +add_library(GLESv2_dec ${SOURCES} ${GENERATED_SOURCES}) +target_link_libraries(GLESv2_dec OpenglCodecCommon) diff --git a/external/android-emugl/host/libs/GLESv2_dec/GLESv2Decoder.cpp b/external/android-emugl/host/libs/GLESv2_dec/GLESv2Decoder.cpp new file mode 100644 index 0000000..f8fc907 --- /dev/null +++ b/external/android-emugl/host/libs/GLESv2_dec/GLESv2Decoder.cpp @@ -0,0 +1,129 @@ +/* +* Copyright 2011 The Android Open Source Project +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +#include "GLESv2Decoder.h" + +#include +#include +#include + +static inline void* SafePointerFromUInt(GLuint value) { + return (void*)(uintptr_t)value; +} + +GLESv2Decoder::GLESv2Decoder() +{ + m_contextData = NULL; + m_GL2library = NULL; +} + +GLESv2Decoder::~GLESv2Decoder() +{ + delete m_GL2library; +} + +void *GLESv2Decoder::s_getProc(const char *name, void *userData) +{ + GLESv2Decoder *ctx = (GLESv2Decoder *) userData; + + if (ctx == NULL || ctx->m_GL2library == NULL) { + return NULL; + } + + void *func = NULL; +#ifdef USE_EGL_GETPROCADDRESS + func = (void *) eglGetProcAddress(name); +#endif + if (func == NULL) { + func = (void *) ctx->m_GL2library->findSymbol(name); + } + return func; +} + +int GLESv2Decoder::initGL(get_proc_func_t getProcFunc, void *getProcFuncData) +{ + this->initDispatchByName(getProcFunc, getProcFuncData); + + glGetCompressedTextureFormats = s_glGetCompressedTextureFormats; + glVertexAttribPointerData = s_glVertexAttribPointerData; + glVertexAttribPointerOffset = s_glVertexAttribPointerOffset; + + glDrawElementsOffset = s_glDrawElementsOffset; + glDrawElementsData = s_glDrawElementsData; + glShaderString = s_glShaderString; + glFinishRoundTrip = s_glFinishRoundTrip; + return 0; + +} + +int GLESv2Decoder::s_glFinishRoundTrip(void *self) +{ + GLESv2Decoder *ctx = (GLESv2Decoder *)self; + ctx->glFinish(); + return 0; +} + +void GLESv2Decoder::s_glGetCompressedTextureFormats(void *self, int count, GLint *formats) +{ + GLESv2Decoder *ctx = (GLESv2Decoder *) self; + + int nFormats; + ctx->glGetIntegerv(GL_NUM_COMPRESSED_TEXTURE_FORMATS, &nFormats); + if (nFormats > count) { + fprintf(stderr, "%s: GetCompressedTextureFormats: The requested number of formats does not match the number that is reported by OpenGL\n", __FUNCTION__); + } else { + ctx->glGetIntegerv(GL_COMPRESSED_TEXTURE_FORMATS, formats); + } +} + +void GLESv2Decoder::s_glVertexAttribPointerData(void *self, GLuint indx, GLint size, GLenum type, + GLboolean normalized, GLsizei stride, void * data, GLuint datalen) +{ + GLESv2Decoder *ctx = (GLESv2Decoder *) self; + if (ctx->m_contextData != NULL) { + ctx->m_contextData->storePointerData(indx, data, datalen); + // note - the stride of the data is always zero when it comes out of the codec. + // See gl2.attrib for the packing function call. + ctx->glVertexAttribPointer(indx, size, type, normalized, 0, ctx->m_contextData->pointerData(indx)); + } +} + +void GLESv2Decoder::s_glVertexAttribPointerOffset(void *self, GLuint indx, GLint size, GLenum type, + GLboolean normalized, GLsizei stride, GLuint data) +{ + GLESv2Decoder *ctx = (GLESv2Decoder *) self; + ctx->glVertexAttribPointer(indx, size, type, normalized, stride, SafePointerFromUInt(data)); +} + + +void GLESv2Decoder::s_glDrawElementsData(void *self, GLenum mode, GLsizei count, GLenum type, void * data, GLuint datalen) +{ + GLESv2Decoder *ctx = (GLESv2Decoder *)self; + ctx->glDrawElements(mode, count, type, data); +} + + +void GLESv2Decoder::s_glDrawElementsOffset(void *self, GLenum mode, GLsizei count, GLenum type, GLuint offset) +{ + GLESv2Decoder *ctx = (GLESv2Decoder *)self; + ctx->glDrawElements(mode, count, type, SafePointerFromUInt(offset)); +} + +void GLESv2Decoder::s_glShaderString(void *self, GLuint shader, const GLchar* string, GLsizei len) +{ + GLESv2Decoder *ctx = (GLESv2Decoder *)self; + ctx->glShaderSource(shader, 1, &string, NULL); +} diff --git a/external/android-emugl/host/libs/GLESv2_dec/GLESv2Decoder.h b/external/android-emugl/host/libs/GLESv2_dec/GLESv2Decoder.h new file mode 100644 index 0000000..8d6f4af --- /dev/null +++ b/external/android-emugl/host/libs/GLESv2_dec/GLESv2Decoder.h @@ -0,0 +1,48 @@ +/* +* Copyright 2011 The Android Open Source Project +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +#ifndef _GL2_DECODER_H_ +#define _GL2_DECODER_H_ + +#include "gles2_dec.h" +#include "GLDecoderContextData.h" +#include "emugl/common/shared_library.h" + +class GLESv2Decoder : public gles2_decoder_context_t +{ +public: + typedef void *(*get_proc_func_t)(const char *name, void *userData); + GLESv2Decoder(); + ~GLESv2Decoder(); + int initGL(get_proc_func_t getProcFunc, void *getProcFuncData); + void setContextData(GLDecoderContextData *contextData) { m_contextData = contextData; } +private: + GLDecoderContextData *m_contextData; + emugl::SharedLibrary* m_GL2library; + + static void *s_getProc(const char *name, void *userData); + static void gles2_APIENTRY s_glGetCompressedTextureFormats(void *self, int count, GLint *formats); + static void gles2_APIENTRY s_glVertexAttribPointerData(void *self, GLuint indx, GLint size, GLenum type, + GLboolean normalized, GLsizei stride, void * data, GLuint datalen); + static void gles2_APIENTRY s_glVertexAttribPointerOffset(void *self, GLuint indx, GLint size, GLenum type, + GLboolean normalized, GLsizei stride, GLuint offset); + + static void gles2_APIENTRY s_glDrawElementsOffset(void *self, GLenum mode, GLsizei count, GLenum type, GLuint offset); + static void gles2_APIENTRY s_glDrawElementsData(void *self, GLenum mode, GLsizei count, GLenum type, void * data, GLuint datalen); + static void gles2_APIENTRY s_glShaderString(void *self, GLuint shader, const GLchar* string, GLsizei len); + static int gles2_APIENTRY s_glFinishRoundTrip(void *self); +}; +#endif diff --git a/external/android-emugl/host/libs/GLESv2_dec/gles2.attrib b/external/android-emugl/host/libs/GLESv2_dec/gles2.attrib new file mode 100644 index 0000000..5cba008 --- /dev/null +++ b/external/android-emugl/host/libs/GLESv2_dec/gles2.attrib @@ -0,0 +1,595 @@ +GLOBAL + base_opcode 2048 + encoder_headers "glUtils.h" "GLESv2EncoderUtils.h" + +#void glBindAttribLocation(GLuint program, GLuint index, GLchar *name) +glBindAttribLocation + len name (strlen(name) + 1) + +#void glBufferData(GLenum target, GLsizeiptr size, GLvoid *data, GLenum usage) +glBufferData + len data size + var_flag data nullAllowed isLarge + +#void glBufferSubData(GLenum target, GLintptr offset, GLsizeiptr size, GLvoid *data) +glBufferSubData + len data size + var_flag data nullAllowed isLarge + +#void glCompressedTexImage2D(GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLint border, GLsizei imageSize, GLvoid *data) +glCompressedTexImage2D + len data imageSize + var_flag data nullAllowed isLarge + +#void glCompressedTexSubImage2D(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLsizei imageSize, GLvoid *data) +glCompressedTexSubImage2D + len data imageSize + var_flag data nullAllowed isLarge + +#void glDeleteBuffers(GLsizei n, GLuint *buffers) +glDeleteBuffers + len buffers (n * sizeof(GLuint)) + param_check n if(n<0){ ctx->setError(GL_INVALID_VALUE); return; } + +#void glDeleteFramebuffers(GLsizei n, GLuint *framebuffers) +glDeleteFramebuffers + len framebuffers (n * sizeof(GLuint)) + param_check n if(n<0){ ctx->setError(GL_INVALID_VALUE); return; } + +#void glDeleteRenderbuffers(GLsizei n, GLuint *renderbuffers) +glDeleteRenderbuffers + len renderbuffers (n * sizeof(GLuint)) + param_check n if(n<0){ ctx->setError(GL_INVALID_VALUE); return; } + +#void glDeleteTextures(GLsizei n, GLuint *textures) +glDeleteTextures + len textures (n * sizeof(GLuint)) + param_check n if(n<0){ ctx->setError(GL_INVALID_VALUE); return; } + +#void glDrawElements(GLenum mode, GLsizei count, GLenum type, GLvoid *indices) +glDrawElements + flag unsupported + +#void glGenBuffers(GLsizei n, GLuint *buffers) +glGenBuffers + len buffers (n * sizeof(GLuint)) + dir buffers out + param_check n if(n<0){ ctx->setError(GL_INVALID_VALUE); return; } + +#void glGenFramebuffers(GLsizei n, GLuint *framebuffers) +glGenFramebuffers + len framebuffers (n * sizeof(GLuint)) + dir framebuffers out + param_check n if(n<0){ ctx->setError(GL_INVALID_VALUE); return; } + +#void glGenRenderbuffers(GLsizei n, GLuint *renderbuffers) +glGenRenderbuffers + len renderbuffers (n * sizeof(GLuint)) + dir renderbuffers out + param_check n if(n<0){ ctx->setError(GL_INVALID_VALUE); return; } + +#void glGenTextures(GLsizei n, GLuint *textures) +glGenTextures + len textures (n * sizeof(GLuint)) + dir textures out + param_check n if(n<0){ ctx->setError(GL_INVALID_VALUE); return; } + +#void glGetActiveAttrib(GLuint program, GLuint index, GLsizei bufsize, GLsizei *length, GLint *size, GLenum *type, GLchar *name) +glGetActiveAttrib + len name bufsize + dir name out + var_flag name nullAllowed + dir length out + len length (sizeof(GLsizei)) + var_flag length nullAllowed + dir size out + len size (sizeof(GLint)) + var_flag size nullAllowed + dir type out + len type (sizeof(GLenum)) + var_flag type nullAllowed + +#void glGetActiveUniform(GLuint program, GLuint index, GLsizei bufsize, GLsizei *length, GLint *size, GLenum *type, GLchar *name) +glGetActiveUniform + len name bufsize + dir name out + var_flag name nullAllowed + dir length out + len length (sizeof(GLsizei)) + var_flag length nullAllowed + dir size out + len size (sizeof(GLint)) + var_flag size nullAllowed + dir type out + len type (sizeof(GLenum)) + var_flag type nullAllowed + + +#void glGetAttachedShaders(GLuint program, GLsizei maxcount, GLsizei *count, GLuint *shaders) +glGetAttachedShaders + len shaders (maxcount*sizeof(GLuint)) + dir shaders out + dir count out + var_flag count nullAllowed + len count (sizeof(GLsizei)) + +#int glGetAttribLocation(GLuint program, GLchar *name) +glGetAttribLocation + len name (strlen(name) + 1) + +#void glGetBooleanv(GLenum pname, GLboolean *params) +glGetBooleanv + dir params out + len params (glUtilsParamSize(pname) * sizeof(GLboolean)) + +#void glGetBufferParameteriv(GLenum target, GLenum pname, GLint *params) +glGetBufferParameteriv + len params (sizeof(GLint)) + dir params out + +#void glGetFloatv(GLenum pname, GLfloat *params) +glGetFloatv + dir params out + len params (glUtilsParamSize(pname) * sizeof(GLfloat)) + +#void glGetFramebufferAttachmentParameteriv(GLenum target, GLenum attachment, GLenum pname, GLint *params) +glGetFramebufferAttachmentParameteriv + dir params out + len params (sizeof(GLint)) + +#void glGetIntegerv(GLenum pname, GLint *params) +glGetIntegerv + dir params out + len params (glUtilsParamSize(pname) * sizeof(GLint)) + +#void glGetProgramiv(GLuint program, GLenum pname, GLint *params) +glGetProgramiv + dir params out + len params sizeof(GLint) +#XXX - might change if extension constants that return more then one value + + +#void glGetProgramInfoLog(GLuint program, GLsizei bufsize, GLsizei *length, GLchar *infolog) +glGetProgramInfoLog + dir infolog out + len infolog bufsize + dir length out + len length sizeof(GLsizei) + var_flag length nullAllowed + +#void glGetRenderbufferParameteriv(GLenum target, GLenum pname, GLint *params) +glGetRenderbufferParameteriv + dir params out + len params sizeof(GLint) +# XXX - might change if pname with value larger then one is added + +#void glGetShaderiv(GLuint shader, GLenum pname, GLint *params) +glGetShaderiv + dir params out + len params sizeof(GLint) +# XXX - might change if pname with value larger then one is added + +#void glGetShaderInfoLog(GLuint shader, GLsizei bufsize, GLsizei *length, GLchar *infolog) +glGetShaderInfoLog + dir length out + len length (sizeof(GLsizei)) + var_flag length nullAllowed + dir infolog out + len infolog bufsize + + +#void glGetShaderPrecisionFormat(GLenum shadertype, GLenum precisiontype, GLint *range, GLint *precision) +glGetShaderPrecisionFormat + dir range out + len range (2 * sizeof(GLint)) + dir precision out + len precision (sizeof(GLint)) + +#void glGetShaderSource(GLuint shader, GLsizei bufsize, GLsizei *length, GLchar *source) +glGetShaderSource + dir length out + len length (sizeof(GLsizei)) + var_flag length nullAllowed + dir source out + len source bufsize + +#GLubyte* glGetString(GLenum name) +glGetString + flag unsupported + +#void glGetTexParameterfv(GLenum target, GLenum pname, GLfloat *params) +glGetTexParameterfv + dir params out + len params (glUtilsParamSize(pname) * sizeof(GLfloat)) + +#void glGetTexParameteriv(GLenum target, GLenum pname, GLint *params) +glGetTexParameteriv + dir params out + len params (glUtilsParamSize(pname) * sizeof(GLint)) + +#void glGetUniformfv(GLuint program, GLint location, GLfloat *params) +glGetUniformfv + dir params out + len params glSizeof(glesv2_enc::uniformType(self, program, location)) + +#void glGetUniformiv(GLuint program, GLint location, GLint *params) +glGetUniformiv + dir params out + len params glSizeof(glesv2_enc::uniformType(self, program, location)) + +#int glGetUniformLocation(GLuint program, GLchar *name) +glGetUniformLocation + len name (strlen(name) + 1) + +# client-state shall be handled locally by the encoder in most cases. +# however, GL_CURRENT_VERTEX_ATTRIB and potential others are handled by the server side, +# thus we still need to implement it. +#void glGetVertexAttribfv(GLuint index, GLenum pname, GLfloat *params) +glGetVertexAttribfv + dir params out + len params (glUtilsParamSize(pname) * sizeof(GLfloat)) + +#see glGetVertexAttribfv for comments +#void glGetVertexAttribiv(GLuint index, GLenum pname, GLint *params) +glGetVertexAttribiv + dir params out + len params (glUtilsParamSize(pname) * sizeof(GLint)) + + + +#void glReadPixels(GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, GLvoid *pixels) +glReadPixels + dir pixels out + len pixels glesv2_enc::pixelDataSize(self, width, height, format, type, 1) + +#void glShaderBinary(GLsizei n, GLuint *shaders, GLenum binaryformat, GLvoid *binary, GLsizei length) +glShaderBinary + flag unsupported + +#void glTexImage2D(GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, GLvoid *pixels) +glTexImage2D + dir pixels in + len pixels glesv2_enc::pixelDataSize(self, width, height, format, type, 0) + var_flag pixels nullAllowed isLarge + +#void glTexParameterfv(GLenum target, GLenum pname, GLfloat *params) +glTexParameterfv + len params (glUtilsParamSize(pname) * sizeof(GLfloat)) +#void glTexParameteriv(GLenum target, GLenum pname, GLint *params) +glTexParameteriv + len params (glUtilsParamSize(pname) * sizeof(GLint)) + +#void glTexSubImage2D(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, GLvoid *pixels) +glTexSubImage2D + len pixels glesv2_enc::pixelDataSize(self, width, height, format, type, 0) + var_flag pixels nullAllowed isLarge + +#void glUniform1fv(GLint location, GLsizei count, GLfloat *v) +glUniform1fv + len v (count * sizeof(GLfloat)) + +#void glUniform1iv(GLint location, GLsizei count, GLint *v) +glUniform1iv + len v (count * sizeof(GLint)) + +#void glUniform2fv(GLint location, GLsizei count, GLfloat *v) +glUniform2fv + len v (count * 2 * sizeof(GLfloat)) + +#void glUniform2iv(GLint location, GLsizei count, GLint *v) +glUniform2iv + len v (count * 2 * sizeof(GLint)) + +#void glUniform3fv(GLint location, GLsizei count, GLfloat *v) +glUniform3fv + len v (count * 3 * sizeof(GLfloat)) + +#void glUniform3iv(GLint location, GLsizei count, GLint *v) +glUniform3iv + len v (3 * count * sizeof(GLint)) + +#void glUniform4fv(GLint location, GLsizei count, GLfloat *v) +glUniform4fv + len v (4 * count * sizeof(GLfloat)) + +#void glUniform4iv(GLint location, GLsizei count, GLint *v) +glUniform4iv + len v (4 * count * sizeof(GLint)) + +#void glUniformMatrix2fv(GLint location, GLsizei count, GLboolean transpose, GLfloat *value) +glUniformMatrix2fv + len value (count * 4 * sizeof(GLfloat)) + +#void glUniformMatrix3fv(GLint location, GLsizei count, GLboolean transpose, GLfloat *value) +glUniformMatrix3fv + len value (count * 9 * sizeof(GLfloat)) + +#void glUniformMatrix4fv(GLint location, GLsizei count, GLboolean transpose, GLfloat *value) +glUniformMatrix4fv + len value (count * 16 * sizeof(GLfloat)) + +#void glVertexAttrib1fv(GLuint indx, GLfloat *values) +glVertexAttrib1fv + len values (sizeof(GLfloat)) +#void glVertexAttrib2fv(GLuint indx, GLfloat *values) +glVertexAttrib2fv + len values (2 * sizeof(GLfloat)) + +#void glVertexAttrib3fv(GLuint indx, GLfloat *values) +glVertexAttrib3fv + len values (3 * sizeof(GLfloat)) + +#void glVertexAttrib4fv(GLuint indx, GLfloat *values) +glVertexAttrib4fv + len values (4 * sizeof(GLfloat)) + +#void glVertexAttribPointer(GLuint indx, GLint size, GLenum type, GLboolean normalized, GLsizei stride, GLvoid *ptr) +glVertexAttribPointer + flag unsupported + +#void glGetProgramBinaryOES(GLuint program, GLsizei bufSize, GLsizei *length, GLenum *binaryFormat, GLvoid *binary) +glGetProgramBinaryOES + flag unsupported + +#void glProgramBinaryOES(GLuint program, GLenum binaryFormat, GLvoid *binary, GLint length) +glProgramBinaryOES + flag unsupported + +#void* glMapBufferOES(GLenum target, GLenum access) +glMapBufferOES + flag unsupported + +#void glTexImage3DOES(GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLenum format, GLenum type, GLvoid *pixels) +glTexImage3DOES + len pixels glesv2_enc::pixelDataSize3D(self, width, height, depth, format, type, 0) + var_flag pixels nullAllowed isLarge + +#void glTexSubImage3DOES(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, GLvoid *pixels) +glTexSubImage3DOES + len pixels glesv2_enc::pixelDataSize3D(self, width, height, depth, format, type, 0) + var_flag pixels nullAllowed isLarge + +#void glCompressedTexImage3DOES(GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLsizei imageSize, GLvoid *data) +glCompressedTexImage3DOES + len data imageSize + var_flag data nullAllowed isLarge + +#void glCompressedTexSubImage3DOES(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLsizei imageSize, GLvoid *data) +glCompressedTexSubImage3DOES + len data imageSize + var_flag data nullAllowed isLarge + +#void glDeleteVertexArraysOES(GLsizei n, GLuint *arrays) +glDeleteVertexArraysOES + len arrays (n * sizeof(GLuint)) + param_check n if(n<0){ ctx->setError(GL_INVALID_VALUE); return; } + +#void glGenVertexArraysOES(GLsizei n, GLuint *arrays) +glGenVertexArraysOES + len arrays (n * sizeof(GLuint)) + dir arrays out + param_check n if(n<0){ ctx->setError(GL_INVALID_VALUE); return; } + + +#void glDiscardFramebufferEXT(GLenum target, GLsizei numAttachments, GLenum *attachments) +glDiscardFramebufferEXT + len attachments (numAttachments * sizeof(GLenum)) + +#void glMultiDrawArraysEXT(GLenum mode, const GLint *first, const GLsizei *count, GLsizei primcount) +glMultiDrawArraysEXT + flag unsupported +#void glMultiDrawElementsEXT(GLenum mode, GLsizei *count, GLenum type, const GLvoid* const *indices, GLsizei primcount) +glMultiDrawElementsEXT + flag unsupported + + +# handled by encoder +#void glShaderSource(GLuint shader, GLsizei count, GLstr *string, const GLint *length) +glShaderSource + flag unsupported + + + +#void glGetPerfMonitorGroupsAMD(GLint *numGroups, GLsizei groupsSize, GLuint *groups) +glGetPerfMonitorGroupsAMD + flag unsupported + +#void glGetPerfMonitorCountersAMD(GLuint group, GLint *numCounters, GLint *maxActiveCounters, GLsizei counterSize, GLuint *counters) +glGetPerfMonitorCountersAMD + flag unsupported + +#void glGetPerfMonitorGroupStringAMD(GLuint group, GLsizei bufSize, GLsizei *length, GLchar *groupString) +glGetPerfMonitorGroupStringAMD + flag unsupported + +#void glGetPerfMonitorCounterStringAMD(GLuint group, GLuint counter, GLsizei bufSize, GLsizei *length, GLchar *counterString) +glGetPerfMonitorCounterStringAMD + flag unsupported + +#void glGetPerfMonitorCounterInfoAMD(GLuint group, GLuint counter, GLenum pname, GLvoid *data) +glGetPerfMonitorCounterInfoAMD + flag unsupported + +#void glGenPerfMonitorsAMD(GLsizei n, GLuint *monitors) +glGenPerfMonitorsAMD + flag unsupported + +#void glDeletePerfMonitorsAMD(GLsizei n, GLuint *monitors) +glDeletePerfMonitorsAMD + flag unsupported + +#void glSelectPerfMonitorCountersAMD(GLuint monitor, GLboolean enable, GLuint group, GLint numCounters, GLuint *countersList) +glSelectPerfMonitorCountersAMD + flag unsupported + +#void glBeginPerfMonitorAMD(GLuint monitor) +glBeginPerfMonitorAMD + flag unsupported + +#void glEndPerfMonitorAMD(GLuint monitor) +glEndPerfMonitorAMD + flag unsupported + +#void glGetPerfMonitorCounterDataAMD(GLuint monitor, GLenum pname, GLsizei dataSize, GLuint *data, GLint *bytesWritten) +glGetPerfMonitorCounterDataAMD + flag unsupported + +#void glRenderbufferStorageMultisampleIMG(GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height) +glRenderbufferStorageMultisampleIMG + flag unsupported + +#void glFramebufferTexture2DMultisampleIMG(GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level, GLsizei samples) +glFramebufferTexture2DMultisampleIMG + flag unsupported + +#void glDeleteFencesNV(GLsizei n, GLuint *fences) +glDeleteFencesNV + flag unsupported + +#void glGenFencesNV(GLsizei n, GLuint *fences) +glGenFencesNV + flag unsupported + +#GLboolean glIsFenceNV(GLuint fence) +glIsFenceNV + flag unsupported + +#GLboolean glTestFenceNV(GLuint fence) +glTestFenceNV + flag unsupported + +#void glGetFenceivNV(GLuint fence, GLenum pname, GLint *params) +glGetFenceivNV + flag unsupported + +#void glFinishFenceNV(GLuint fence) +glFinishFenceNV + flag unsupported + +#void glSetFenceNV(GLuint fence, GLenum condition) +glSetFenceNV + flag unsupported + +#void glCoverageMaskNV(GLboolean mask) +glCoverageMaskNV + flag unsupported + +#void glCoverageOperationNV(GLenum operation) +glCoverageOperationNV + flag unsupported + +#void glGetDriverControlsQCOM(GLint *num, GLsizei size, GLuint *driverControls) +glGetDriverControlsQCOM + flag unsupported + +#void glGetDriverControlStringQCOM(GLuint driverControl, GLsizei bufSize, GLsizei *length, GLchar *driverControlString) +glGetDriverControlStringQCOM + flag unsupported + +#void glEnableDriverControlQCOM(GLuint driverControl) +glEnableDriverControlQCOM + flag unsupported + +#void glDisableDriverControlQCOM(GLuint driverControl) +glDisableDriverControlQCOM + flag unsupported + +#void glExtGetTexturesQCOM(GLuint *textures, GLint maxTextures, GLint *numTextures) +glExtGetTexturesQCOM + flag unsupported + +#void glExtGetBuffersQCOM(GLuint *buffers, GLint maxBuffers, GLint *numBuffers) +glExtGetBuffersQCOM + flag unsupported + +#void glExtGetRenderbuffersQCOM(GLuint *renderbuffers, GLint maxRenderbuffers, GLint *numRenderbuffers) +glExtGetRenderbuffersQCOM + flag unsupported + +#void glExtGetFramebuffersQCOM(GLuint *framebuffers, GLint maxFramebuffers, GLint *numFramebuffers) +glExtGetFramebuffersQCOM + flag unsupported + +#void glExtGetTexLevelParameterivQCOM(GLuint texture, GLenum face, GLint level, GLenum pname, GLint *params) +glExtGetTexLevelParameterivQCOM + flag unsupported + +#void glExtTexObjectStateOverrideiQCOM(GLenum target, GLenum pname, GLint param) +glExtTexObjectStateOverrideiQCOM + flag unsupported + +#void glExtGetTexSubImageQCOM(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, GLvoid *texels) +glExtGetTexSubImageQCOM + flag unsupported + +#void glExtGetBufferPointervQCOM(GLenum target, GLvoidptr *params) +glExtGetBufferPointervQCOM + flag unsupported + +#void glExtGetShadersQCOM(GLuint *shaders, GLint maxShaders, GLint *numShaders) +glExtGetShadersQCOM + flag unsupported + +#void glExtGetProgramsQCOM(GLuint *programs, GLint maxPrograms, GLint *numPrograms) +glExtGetProgramsQCOM + flag unsupported + +#GLboolean glExtIsProgramBinaryQCOM(GLuint program) +glExtIsProgramBinaryQCOM + flag unsupported + +#void glExtGetProgramBinarySourceQCOM(GLuint program, GLenum shadertype, GLchar *source, GLint *length) +glExtGetProgramBinarySourceQCOM + flag unsupported + +#void glStartTilingQCOM(GLuint x, GLuint y, GLuint width, GLuint height, GLbitfield preserveMask) +glStartTilingQCOM + flag unsupported + +#void glEndTilingQCOM(GLbitfield preserveMask) +glEndTilingQCOM + flag unsupported + + +#void glVertexAttribPointerData(GLuint indx, GLint size, GLenum type, GLboolean normalized, GLsizei stride, void * data, GLuint datalen) +glVertexAttribPointerData + len data datalen + custom_pack data glUtilsPackPointerData((unsigned char *)ptr, (unsigned char *)data, size, type, stride, datalen) + flag custom_decoder + flag not_api + +glVertexAttribPointerOffset + flag custom_decoder + flag not_api + +#client-state, handled by the encoder +#GL_ENTRY(void, glGetVertexAttribPointerv, GLuint index, GLenum pname, GLvoid** pointer) +glGetVertexAttribPointerv + flag unsupported + +glDrawElementsData + len data datalen + flag custom_decoder + flag not_api + +glDrawElementsOffset + flag custom_decoder + flag not_api + +#GL_ENTRY(void, glGetCompressedTextureFormats, int count, GLint *formats) +glGetCompressedTextureFormats + dir formats out + len formats (count * sizeof(GLint)) + flag custom_decoder + flag not_api + +#GL_ENTRY(void, glShaderString, GLuint shader, GLchar *string, GLsizei len) +glShaderString + len string len + flag custom_decoder + flag not_api + +glFinishRoundTrip + flag custom_decoder + flag not_api + diff --git a/external/android-emugl/host/libs/GLESv2_dec/gles2.in b/external/android-emugl/host/libs/GLESv2_dec/gles2.in new file mode 100644 index 0000000..f60db16 --- /dev/null +++ b/external/android-emugl/host/libs/GLESv2_dec/gles2.in @@ -0,0 +1,214 @@ +GL_ENTRY(void, glActiveTexture, GLenum texture) +GL_ENTRY(void, glAttachShader, GLuint program, GLuint shader) +GL_ENTRY(void, glBindAttribLocation, GLuint program, GLuint index, const GLchar* name) +GL_ENTRY(void, glBindBuffer, GLenum target, GLuint buffer) +GL_ENTRY(void, glBindFramebuffer, GLenum target, GLuint framebuffer) +GL_ENTRY(void, glBindRenderbuffer, GLenum target, GLuint renderbuffer) +GL_ENTRY(void, glBindTexture, GLenum target, GLuint texture) +GL_ENTRY(void, glBlendColor, GLclampf red, GLclampf green, GLclampf blue, GLclampf alpha) +GL_ENTRY(void, glBlendEquation, GLenum mode ) +GL_ENTRY(void, glBlendEquationSeparate, GLenum modeRGB, GLenum modeAlpha) +GL_ENTRY(void, glBlendFunc, GLenum sfactor, GLenum dfactor) +GL_ENTRY(void, glBlendFuncSeparate, GLenum srcRGB, GLenum dstRGB, GLenum srcAlpha, GLenum dstAlpha) +GL_ENTRY(void, glBufferData, GLenum target, GLsizeiptr size, const GLvoid* data, GLenum usage) +GL_ENTRY(void, glBufferSubData, GLenum target, GLintptr offset, GLsizeiptr size, const GLvoid* data) +GL_ENTRY(GLenum, glCheckFramebufferStatus, GLenum target) +GL_ENTRY(void, glClear, GLbitfield mask) +GL_ENTRY(void, glClearColor, GLclampf red, GLclampf green, GLclampf blue, GLclampf alpha) +GL_ENTRY(void, glClearDepthf, GLclampf depth) +GL_ENTRY(void, glClearStencil, GLint s) +GL_ENTRY(void, glColorMask, GLboolean red, GLboolean green, GLboolean blue, GLboolean alpha) +GL_ENTRY(void, glCompileShader, GLuint shader) +GL_ENTRY(void, glCompressedTexImage2D, GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLint border, GLsizei imageSize, const GLvoid* data) +GL_ENTRY(void, glCompressedTexSubImage2D, GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLsizei imageSize, const GLvoid* data) +GL_ENTRY(void, glCopyTexImage2D, GLenum target, GLint level, GLenum internalformat, GLint x, GLint y, GLsizei width, GLsizei height, GLint border) +GL_ENTRY(void, glCopyTexSubImage2D, GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint x, GLint y, GLsizei width, GLsizei height) +GL_ENTRY(GLuint, glCreateProgram, void) +GL_ENTRY(GLuint, glCreateShader, GLenum type) +GL_ENTRY(void, glCullFace, GLenum mode) +GL_ENTRY(void, glDeleteBuffers, GLsizei n, const GLuint* buffers) +GL_ENTRY(void, glDeleteFramebuffers, GLsizei n, const GLuint* framebuffers) +GL_ENTRY(void, glDeleteProgram, GLuint program) +GL_ENTRY(void, glDeleteRenderbuffers, GLsizei n, const GLuint* renderbuffers) +GL_ENTRY(void, glDeleteShader, GLuint shader) +GL_ENTRY(void, glDeleteTextures, GLsizei n, const GLuint* textures) +GL_ENTRY(void, glDepthFunc, GLenum func) +GL_ENTRY(void, glDepthMask, GLboolean flag) +GL_ENTRY(void, glDepthRangef, GLclampf zNear, GLclampf zFar) +GL_ENTRY(void, glDetachShader, GLuint program, GLuint shader) +GL_ENTRY(void, glDisable, GLenum cap) +GL_ENTRY(void, glDisableVertexAttribArray, GLuint index) +GL_ENTRY(void, glDrawArrays, GLenum mode, GLint first, GLsizei count) +GL_ENTRY(void, glDrawElements, GLenum mode, GLsizei count, GLenum type, const GLvoid* indices) +GL_ENTRY(void, glEnable, GLenum cap) +GL_ENTRY(void, glEnableVertexAttribArray, GLuint index) +GL_ENTRY(void, glFinish, void) +GL_ENTRY(void, glFlush, void) +GL_ENTRY(void, glFramebufferRenderbuffer, GLenum target, GLenum attachment, GLenum renderbuffertarget, GLuint renderbuffer) +GL_ENTRY(void, glFramebufferTexture2D, GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level) +GL_ENTRY(void, glFrontFace, GLenum mode) +GL_ENTRY(void, glGenBuffers, GLsizei n, GLuint* buffers) +GL_ENTRY(void, glGenerateMipmap, GLenum target) +GL_ENTRY(void, glGenFramebuffers, GLsizei n, GLuint* framebuffers) +GL_ENTRY(void, glGenRenderbuffers, GLsizei n, GLuint* renderbuffers) +GL_ENTRY(void, glGenTextures, GLsizei n, GLuint* textures) +GL_ENTRY(void, glGetActiveAttrib, GLuint program, GLuint index, GLsizei bufsize, GLsizei* length, GLint* size, GLenum* type, GLchar* name) +GL_ENTRY(void, glGetActiveUniform, GLuint program, GLuint index, GLsizei bufsize, GLsizei* length, GLint* size, GLenum* type, GLchar* name) +GL_ENTRY(void, glGetAttachedShaders, GLuint program, GLsizei maxcount, GLsizei* count, GLuint* shaders) +GL_ENTRY(int, glGetAttribLocation, GLuint program, const GLchar* name) +GL_ENTRY(void, glGetBooleanv, GLenum pname, GLboolean* params) +GL_ENTRY(void, glGetBufferParameteriv, GLenum target, GLenum pname, GLint* params) +GL_ENTRY(GLenum, glGetError, void) +GL_ENTRY(void, glGetFloatv, GLenum pname, GLfloat* params) +GL_ENTRY(void, glGetFramebufferAttachmentParameteriv, GLenum target, GLenum attachment, GLenum pname, GLint* params) +GL_ENTRY(void, glGetIntegerv, GLenum pname, GLint* params) +GL_ENTRY(void, glGetProgramiv, GLuint program, GLenum pname, GLint* params) +GL_ENTRY(void, glGetProgramInfoLog, GLuint program, GLsizei bufsize, GLsizei* length, GLchar* infolog) +GL_ENTRY(void, glGetRenderbufferParameteriv, GLenum target, GLenum pname, GLint* params) +GL_ENTRY(void, glGetShaderiv, GLuint shader, GLenum pname, GLint* params) +GL_ENTRY(void, glGetShaderInfoLog, GLuint shader, GLsizei bufsize, GLsizei* length, GLchar* infolog) +GL_ENTRY(void, glGetShaderPrecisionFormat, GLenum shadertype, GLenum precisiontype, GLint* range, GLint* precision) +GL_ENTRY(void, glGetShaderSource, GLuint shader, GLsizei bufsize, GLsizei* length, GLchar* source) +GL_ENTRY(const GLubyte*, glGetString, GLenum name) +GL_ENTRY(void, glGetTexParameterfv, GLenum target, GLenum pname, GLfloat* params) +GL_ENTRY(void, glGetTexParameteriv, GLenum target, GLenum pname, GLint* params) +GL_ENTRY(void, glGetUniformfv, GLuint program, GLint location, GLfloat* params) +GL_ENTRY(void, glGetUniformiv, GLuint program, GLint location, GLint* params) +GL_ENTRY(int, glGetUniformLocation, GLuint program, const GLchar* name) +GL_ENTRY(void, glGetVertexAttribfv, GLuint index, GLenum pname, GLfloat* params) +GL_ENTRY(void, glGetVertexAttribiv, GLuint index, GLenum pname, GLint* params) +GL_ENTRY(void, glGetVertexAttribPointerv, GLuint index, GLenum pname, GLvoid** pointer) +GL_ENTRY(void, glHint, GLenum target, GLenum mode) +GL_ENTRY(GLboolean, glIsBuffer, GLuint buffer) +GL_ENTRY(GLboolean, glIsEnabled, GLenum cap) +GL_ENTRY(GLboolean, glIsFramebuffer, GLuint framebuffer) +GL_ENTRY(GLboolean, glIsProgram, GLuint program) +GL_ENTRY(GLboolean, glIsRenderbuffer, GLuint renderbuffer) +GL_ENTRY(GLboolean, glIsShader, GLuint shader) +GL_ENTRY(GLboolean, glIsTexture, GLuint texture) +GL_ENTRY(void, glLineWidth, GLfloat width) +GL_ENTRY(void, glLinkProgram, GLuint program) +GL_ENTRY(void, glPixelStorei, GLenum pname, GLint param) +GL_ENTRY(void, glPolygonOffset, GLfloat factor, GLfloat units) +GL_ENTRY(void, glReadPixels, GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, GLvoid* pixels) +GL_ENTRY(void, glReleaseShaderCompiler, void) +GL_ENTRY(void, glRenderbufferStorage, GLenum target, GLenum internalformat, GLsizei width, GLsizei height) +GL_ENTRY(void, glSampleCoverage, GLclampf value, GLboolean invert) +GL_ENTRY(void, glScissor, GLint x, GLint y, GLsizei width, GLsizei height) +GL_ENTRY(void, glShaderBinary, GLsizei n, const GLuint* shaders, GLenum binaryformat, const GLvoid* binary, GLsizei length) +GL_ENTRY(void, glShaderSource, GLuint shader, GLsizei count, const GLchar*const* string, const GLint* length) +GL_ENTRY(void, glStencilFunc, GLenum func, GLint ref, GLuint mask) +GL_ENTRY(void, glStencilFuncSeparate, GLenum face, GLenum func, GLint ref, GLuint mask) +GL_ENTRY(void, glStencilMask, GLuint mask) +GL_ENTRY(void, glStencilMaskSeparate, GLenum face, GLuint mask) +GL_ENTRY(void, glStencilOp, GLenum fail, GLenum zfail, GLenum zpass) +GL_ENTRY(void, glStencilOpSeparate, GLenum face, GLenum fail, GLenum zfail, GLenum zpass) +GL_ENTRY(void, glTexImage2D, GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, const GLvoid* pixels) +GL_ENTRY(void, glTexParameterf, GLenum target, GLenum pname, GLfloat param) +GL_ENTRY(void, glTexParameterfv, GLenum target, GLenum pname, const GLfloat* params) +GL_ENTRY(void, glTexParameteri, GLenum target, GLenum pname, GLint param) +GL_ENTRY(void, glTexParameteriv, GLenum target, GLenum pname, const GLint* params) +GL_ENTRY(void, glTexSubImage2D, GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, const GLvoid* pixels) +GL_ENTRY(void, glUniform1f, GLint location, GLfloat x) +GL_ENTRY(void, glUniform1fv, GLint location, GLsizei count, const GLfloat* v) +GL_ENTRY(void, glUniform1i, GLint location, GLint x) +GL_ENTRY(void, glUniform1iv, GLint location, GLsizei count, const GLint* v) +GL_ENTRY(void, glUniform2f, GLint location, GLfloat x, GLfloat y) +GL_ENTRY(void, glUniform2fv, GLint location, GLsizei count, const GLfloat* v) +GL_ENTRY(void, glUniform2i, GLint location, GLint x, GLint y) +GL_ENTRY(void, glUniform2iv, GLint location, GLsizei count, const GLint* v) +GL_ENTRY(void, glUniform3f, GLint location, GLfloat x, GLfloat y, GLfloat z) +GL_ENTRY(void, glUniform3fv, GLint location, GLsizei count, const GLfloat* v) +GL_ENTRY(void, glUniform3i, GLint location, GLint x, GLint y, GLint z) +GL_ENTRY(void, glUniform3iv, GLint location, GLsizei count, const GLint* v) +GL_ENTRY(void, glUniform4f, GLint location, GLfloat x, GLfloat y, GLfloat z, GLfloat w) +GL_ENTRY(void, glUniform4fv, GLint location, GLsizei count, const GLfloat* v) +GL_ENTRY(void, glUniform4i, GLint location, GLint x, GLint y, GLint z, GLint w) +GL_ENTRY(void, glUniform4iv, GLint location, GLsizei count, const GLint* v) +GL_ENTRY(void, glUniformMatrix2fv, GLint location, GLsizei count, GLboolean transpose, const GLfloat* value) +GL_ENTRY(void, glUniformMatrix3fv, GLint location, GLsizei count, GLboolean transpose, const GLfloat* value) +GL_ENTRY(void, glUniformMatrix4fv, GLint location, GLsizei count, GLboolean transpose, const GLfloat* value) +GL_ENTRY(void, glUseProgram, GLuint program) +GL_ENTRY(void, glValidateProgram, GLuint program) +GL_ENTRY(void, glVertexAttrib1f, GLuint indx, GLfloat x) +GL_ENTRY(void, glVertexAttrib1fv, GLuint indx, const GLfloat* values) +GL_ENTRY(void, glVertexAttrib2f, GLuint indx, GLfloat x, GLfloat y) +GL_ENTRY(void, glVertexAttrib2fv, GLuint indx, const GLfloat* values) +GL_ENTRY(void, glVertexAttrib3f, GLuint indx, GLfloat x, GLfloat y, GLfloat z) +GL_ENTRY(void, glVertexAttrib3fv, GLuint indx, const GLfloat* values) +GL_ENTRY(void, glVertexAttrib4f, GLuint indx, GLfloat x, GLfloat y, GLfloat z, GLfloat w) +GL_ENTRY(void, glVertexAttrib4fv, GLuint indx, const GLfloat* values) +GL_ENTRY(void, glVertexAttribPointer, GLuint indx, GLint size, GLenum type, GLboolean normalized, GLsizei stride, const GLvoid* ptr) +GL_ENTRY(void, glViewport, GLint x, GLint y, GLsizei width, GLsizei height) +GL_ENTRY(void, glEGLImageTargetTexture2DOES, GLenum target, GLeglImageOES image) +GL_ENTRY(void, glEGLImageTargetRenderbufferStorageOES, GLenum target, GLeglImageOES image) +GL_ENTRY(void, glGetProgramBinaryOES, GLuint program, GLsizei bufSize, GLsizei *length, GLenum *binaryFormat, GLvoid *binary) +GL_ENTRY(void, glProgramBinaryOES, GLuint program, GLenum binaryFormat, const GLvoid *binary, GLint length) +GL_ENTRY(void*, glMapBufferOES, GLenum target, GLenum access) +GL_ENTRY(GLboolean, glUnmapBufferOES, GLenum target) +#GL_ENTRY(void, glGetBufferPointervOES, GLenum target, GLenum pname, GLvoid** params) +GL_ENTRY(void, glTexImage3DOES, GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLenum format, GLenum type, const GLvoid* pixels) +GL_ENTRY(void, glTexSubImage3DOES, GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, const GLvoid* pixels) +GL_ENTRY(void, glCopyTexSubImage3DOES, GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLint x, GLint y, GLsizei width, GLsizei height) +GL_ENTRY(void, glCompressedTexImage3DOES, GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLsizei imageSize, const GLvoid* data) +GL_ENTRY(void, glCompressedTexSubImage3DOES, GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLsizei imageSize, const GLvoid* data) +GL_ENTRY(void, glFramebufferTexture3DOES, GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level, GLint zoffset) +GL_ENTRY(void, glBindVertexArrayOES, GLuint array) +GL_ENTRY(void, glDeleteVertexArraysOES, GLsizei n, const GLuint *arrays) +GL_ENTRY(void, glGenVertexArraysOES, GLsizei n, GLuint *arrays) +GL_ENTRY(GLboolean, glIsVertexArrayOES, GLuint array) +GL_ENTRY(void, glDiscardFramebufferEXT, GLenum target, GLsizei numAttachments, const GLenum *attachments) +GL_ENTRY(void, glMultiDrawArraysEXT, GLenum mode, const GLint *first, const GLsizei *count, GLsizei primcount) +GL_ENTRY(void, glMultiDrawElementsEXT, GLenum mode, const GLsizei *count, GLenum type, const GLvoid*const* indices, GLsizei primcount) + +#not supported +GL_ENTRY(void, glGetPerfMonitorGroupsAMD, GLint *numGroups, GLsizei groupsSize, GLuint *groups) +GL_ENTRY(void, glGetPerfMonitorCountersAMD, GLuint group, GLint *numCounters, GLint *maxActiveCounters, GLsizei counterSize, GLuint *counters) +GL_ENTRY(void, glGetPerfMonitorGroupStringAMD, GLuint group, GLsizei bufSize, GLsizei *length, GLchar *groupString) +GL_ENTRY(void, glGetPerfMonitorCounterStringAMD, GLuint group, GLuint counter, GLsizei bufSize, GLsizei *length, GLchar *counterString) +GL_ENTRY(void, glGetPerfMonitorCounterInfoAMD, GLuint group, GLuint counter, GLenum pname, GLvoid *data) +GL_ENTRY(void, glGenPerfMonitorsAMD, GLsizei n, GLuint *monitors) +GL_ENTRY(void, glDeletePerfMonitorsAMD, GLsizei n, GLuint *monitors) +GL_ENTRY(void, glSelectPerfMonitorCountersAMD, GLuint monitor, GLboolean enable, GLuint group, GLint numCounters, GLuint *countersList) +GL_ENTRY(void, glBeginPerfMonitorAMD, GLuint monitor) +GL_ENTRY(void, glEndPerfMonitorAMD, GLuint monitor) +GL_ENTRY(void, glGetPerfMonitorCounterDataAMD, GLuint monitor, GLenum pname, GLsizei dataSize, GLuint *data, GLint *bytesWritten) + +GL_ENTRY(void, glRenderbufferStorageMultisampleIMG, GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height) +GL_ENTRY(void, glFramebufferTexture2DMultisampleIMG, GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level, GLsizei samples) +GL_ENTRY(void, glDeleteFencesNV, GLsizei n, const GLuint *fences) +GL_ENTRY(void, glGenFencesNV, GLsizei n, GLuint *fences) +GL_ENTRY(GLboolean, glIsFenceNV, GLuint fence) +GL_ENTRY(GLboolean, glTestFenceNV, GLuint fence) +GL_ENTRY(void, glGetFenceivNV, GLuint fence, GLenum pname, GLint *params) +GL_ENTRY(void, glFinishFenceNV, GLuint fence) +GL_ENTRY(void, glSetFenceNV, GLuint fence, GLenum condition) +GL_ENTRY(void, glCoverageMaskNV, GLboolean mask) +GL_ENTRY(void, glCoverageOperationNV, GLenum operation) +GL_ENTRY(void, glGetDriverControlsQCOM, GLint *num, GLsizei size, GLuint *driverControls) +GL_ENTRY(void, glGetDriverControlStringQCOM, GLuint driverControl, GLsizei bufSize, GLsizei *length, GLchar *driverControlString) +GL_ENTRY(void, glEnableDriverControlQCOM, GLuint driverControl) +GL_ENTRY(void, glDisableDriverControlQCOM, GLuint driverControl) +GL_ENTRY(void, glExtGetTexturesQCOM, GLuint *textures, GLint maxTextures, GLint *numTextures) +GL_ENTRY(void, glExtGetBuffersQCOM, GLuint *buffers, GLint maxBuffers, GLint *numBuffers) +GL_ENTRY(void, glExtGetRenderbuffersQCOM, GLuint *renderbuffers, GLint maxRenderbuffers, GLint *numRenderbuffers) +GL_ENTRY(void, glExtGetFramebuffersQCOM, GLuint *framebuffers, GLint maxFramebuffers, GLint *numFramebuffers) +GL_ENTRY(void, glExtGetTexLevelParameterivQCOM, GLuint texture, GLenum face, GLint level, GLenum pname, GLint *params) +GL_ENTRY(void, glExtTexObjectStateOverrideiQCOM, GLenum target, GLenum pname, GLint param) +GL_ENTRY(void, glExtGetTexSubImageQCOM, GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, GLvoid *texels) +GL_ENTRY(void, glExtGetBufferPointervQCOM, GLenum target, GLvoidptr *params) +GL_ENTRY(void, glExtGetShadersQCOM, GLuint *shaders, GLint maxShaders, GLint *numShaders) +GL_ENTRY(void, glExtGetProgramsQCOM, GLuint *programs, GLint maxPrograms, GLint *numPrograms) +GL_ENTRY(GLboolean, glExtIsProgramBinaryQCOM, GLuint program) +GL_ENTRY(void, glExtGetProgramBinarySourceQCOM, GLuint program, GLenum shadertype, GLchar *source, GLint *length) +GL_ENTRY(void, glStartTilingQCOM, GLuint x, GLuint y, GLuint width, GLuint height, GLbitfield preserveMask) +GL_ENTRY(void, glEndTilingQCOM, GLbitfield preserveMask) + +# add-ons +GL_ENTRY(void, glVertexAttribPointerData, GLuint indx, GLint size, GLenum type, GLboolean normalized, GLsizei stride, void * data, GLuint datalen) +GL_ENTRY(void, glVertexAttribPointerOffset, GLuint indx, GLint size, GLenum type, GLboolean normalized, GLsizei stride, GLuint offset) +GL_ENTRY(void, glDrawElementsOffset, GLenum mode, GLsizei count, GLenum type, GLuint offset) +GL_ENTRY(void, glDrawElementsData, GLenum mode, GLsizei count, GLenum type, void *data, GLuint datalen) +GL_ENTRY(void, glGetCompressedTextureFormats, int count, GLint *formats) +GL_ENTRY(void, glShaderString, GLuint shader, const GLchar* string, GLsizei len) +GL_ENTRY(int, glFinishRoundTrip, void) diff --git a/external/android-emugl/host/libs/GLESv2_dec/gles2.types b/external/android-emugl/host/libs/GLESv2_dec/gles2.types new file mode 100644 index 0000000..86e10f9 --- /dev/null +++ b/external/android-emugl/host/libs/GLESv2_dec/gles2.types @@ -0,0 +1,38 @@ +GLbitfield 32 0x%08x +GLboolean 8 %d +GLclampf 32 %f +GLclampx 32 0x%08x +GLeglImageOES 32 %p +GLenum 32 0x%08x +GLfixed 32 0x%08x +GLfloat 32 %f +GLint 32 %d +GLintptr 32 %p +GLshort 16 %d +GLsizei 32 %d +GLsizeiptr 32 %p +GLubyte 8 0x%02x +GLuint 32 %u +GLvoid 0 %x +GLchar 8 %d +GLenum* 32 0x%08x +GLboolean* 32 0x%08x +GLclampf* 32 0x%08x +GLclampx* 32 0x%08x +GLeglImageOES* 32 0x%08x +GLfixed* 32 0x%08x +GLfloat* 32 0x%08x +GLint* 32 0x%08x +GLshort* 32 0x%08x +GLsizei* 32 0x%08x +GLubyte* 32 0x%08x +GLuint* 32 0x%08x +GLvoid* 32 0x%08x +GLchar* 32 0x%08x +GLchar** 32 0x%08x +GLvoid** 32 0x%08x +void* 32 0x%08x +GLstr* 32 0x%08x +GLvoidptr* 32 0x%08x +GLchar*const* 32 0x%08x +GLvoid*const* 32 0x%08x diff --git a/external/android-emugl/host/libs/GLESv2_dec/gles2_types.h b/external/android-emugl/host/libs/GLESv2_dec/gles2_types.h new file mode 100644 index 0000000..bfff61d --- /dev/null +++ b/external/android-emugl/host/libs/GLESv2_dec/gles2_types.h @@ -0,0 +1,21 @@ +/* +* Copyright (C) 2011 The Android Open Source Project +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ +#ifndef _GL_2_TYPES_H_ +#define _GL_2_TYPES_H_ +#include "gl_base_types.h" + +typedef void *GLvoidptr; +#endif diff --git a/external/android-emugl/host/libs/libOpenGLESDispatch/Android.mk b/external/android-emugl/host/libs/libOpenGLESDispatch/Android.mk new file mode 100644 index 0000000..0922628 --- /dev/null +++ b/external/android-emugl/host/libs/libOpenGLESDispatch/Android.mk @@ -0,0 +1,14 @@ +LOCAL_PATH := $(call my-dir) + +### host library ########################################## +$(call emugl-begin-host-static-library,libOpenGLESDispatch) +$(call emugl-import,libGLESv2_dec libGLESv1_dec) + +# use Translator's egl headers +LOCAL_C_INCLUDES += $(EMUGL_PATH)/host/libs/Translator/include +LOCAL_C_INCLUDES += $(EMUGL_PATH)/shared + +LOCAL_SRC_FILES := EGLDispatch.cpp \ + GLESv2Dispatch.cpp \ + GLESv1Dispatch.cpp +$(call emugl-end-module) diff --git a/external/android-emugl/host/libs/libOpenGLESDispatch/CMakeLists.txt b/external/android-emugl/host/libs/libOpenGLESDispatch/CMakeLists.txt new file mode 100644 index 0000000..715fa8b --- /dev/null +++ b/external/android-emugl/host/libs/libOpenGLESDispatch/CMakeLists.txt @@ -0,0 +1,10 @@ +set(SOURCES + EGLDispatch.cpp + GLESv2Dispatch.cpp + GLESv1Dispatch.cpp) + +add_library(OpenGLESDispatch ${SOURCES} ${GENERATED_SOURCES}) +target_link_libraries(OpenGLESDispatch + emugl_common + GLESv2_dec + GLESv1_dec) diff --git a/external/android-emugl/host/libs/libOpenGLESDispatch/EGLDispatch.cpp b/external/android-emugl/host/libs/libOpenGLESDispatch/EGLDispatch.cpp new file mode 100644 index 0000000..2ad9028 --- /dev/null +++ b/external/android-emugl/host/libs/libOpenGLESDispatch/EGLDispatch.cpp @@ -0,0 +1,52 @@ +/* +* Copyright (C) 2011 The Android Open Source Project +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ +#include "OpenGLESDispatch/EGLDispatch.h" + +#include "emugl/common/shared_library.h" + +#include +#include + +EGLDispatch s_egl; + +#define DEFAULT_EGL_LIB EMUGL_LIBNAME("EGL_translator") + +#define RENDER_EGL_LOAD_FIELD(return_type, function_name, signature) \ + s_egl. function_name = (function_name ## _t) lib->findSymbol(#function_name); + +#define RENDER_EGL_LOAD_OPTIONAL_FIELD(return_type, function_name, signature) \ + if (s_egl.eglGetProcAddress) s_egl. function_name = \ + (function_name ## _t) s_egl.eglGetProcAddress(#function_name); \ + if (!s_egl.function_name || !s_egl.eglGetProcAddress) \ + RENDER_EGL_LOAD_FIELD(return_type, function_name, signature) + +bool init_egl_dispatch() +{ + + const char *libName = getenv("ANDROID_EGL_LIB"); + if (!libName) libName = DEFAULT_EGL_LIB; + + char error[256]; + emugl::SharedLibrary *lib = emugl::SharedLibrary::open(libName, error, sizeof(error)); + if (!lib) { + printf("Failed to open %s: [%s]\n", libName, error); + return NULL; + } + LIST_RENDER_EGL_FUNCTIONS(RENDER_EGL_LOAD_FIELD) + LIST_RENDER_EGL_EXTENSIONS_FUNCTIONS(RENDER_EGL_LOAD_OPTIONAL_FIELD) + + return true; +} diff --git a/external/android-emugl/host/libs/libOpenGLESDispatch/GLESv1Dispatch.cpp b/external/android-emugl/host/libs/libOpenGLESDispatch/GLESv1Dispatch.cpp new file mode 100644 index 0000000..0780af4 --- /dev/null +++ b/external/android-emugl/host/libs/libOpenGLESDispatch/GLESv1Dispatch.cpp @@ -0,0 +1,95 @@ +/* +* Copyright (C) 2011 The Android Open Source Project +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ +#include "OpenGLESDispatch/GLESv1Dispatch.h" +#include "OpenGLESDispatch/EGLDispatch.h" + +#include +#include + +#include "emugl/common/shared_library.h" + +extern EGLDispatch s_egl; + +static emugl::SharedLibrary *s_gles1_lib = NULL; + +// An unimplemented function which prints out an error message. +// To make it consistent with the guest, all GLES1 functions not supported by +// the driver should be redirected to this function. + +static void gles1_unimplemented() { + fprintf(stderr, "Called unimplemented GLESv1 API\n"); +} + +// +// This function is called only once during initialiation before +// any thread has been created - hence it should NOT be thread safe. +// + +#define DEFAULT_GLES_CM_LIB EMUGL_LIBNAME("GLES_CM_translator") + +bool gles1_dispatch_init(GLESv1Dispatch* dispatch_table) { + const char* libName = getenv("ANDROID_GLESv1_LIB"); + if (!libName) { + libName = DEFAULT_GLES_CM_LIB; + } + + char error[256]; + s_gles1_lib = emugl::SharedLibrary::open(libName, error, sizeof(error)); + if (!s_gles1_lib) { + fprintf(stderr, "%s: Could not load %s [%s]\n", __FUNCTION__, + libName, error); + return false; + } + + // + // init the GLES dispatch table + // +#define LOOKUP_SYMBOL(return_type,function_name,signature,callargs) \ + dispatch_table-> function_name = reinterpret_cast< function_name ## _t >( \ + s_gles1_lib->findSymbol(#function_name)); + +#define LOOKUP_EXT_SYMBOL(return_type,function_name,signature,callargs) \ + dispatch_table-> function_name = reinterpret_cast< function_name ## _t >( \ + s_egl.eglGetProcAddress(#function_name)); + + LIST_GLES1_FUNCTIONS(LOOKUP_SYMBOL,LOOKUP_EXT_SYMBOL) + + return true; +} + +// +// This function is called only during initialization of the decoder before +// any thread has been created - hence it should NOT be thread safe. +// +void *gles1_dispatch_get_proc_func(const char *name, void *userData) +{ + void* func = NULL; + if (s_gles1_lib && !func) { + func = (void *)s_gles1_lib->findSymbol(name); + } + + if (!func) { + func = (void *)s_egl.eglGetProcAddress(name); + } + + // To make it consistent with the guest, redirect any unsupported functions + // to gles1_unimplemented. + if (!func) { + fprintf(stderr, "ERROR: Failed to find symbol for %s\n", name); + func = (void *)gles1_unimplemented; + } + return func; +} diff --git a/external/android-emugl/host/libs/libOpenGLESDispatch/GLESv2Dispatch.cpp b/external/android-emugl/host/libs/libOpenGLESDispatch/GLESv2Dispatch.cpp new file mode 100644 index 0000000..6bc0fa6 --- /dev/null +++ b/external/android-emugl/host/libs/libOpenGLESDispatch/GLESv2Dispatch.cpp @@ -0,0 +1,95 @@ +/* +* Copyright (C) 2011 The Android Open Source Project +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ +#include "OpenGLESDispatch/GLESv2Dispatch.h" +#include "OpenGLESDispatch/EGLDispatch.h" + +#include +#include + +#include "emugl/common/shared_library.h" + +extern EGLDispatch s_egl; + +static emugl::SharedLibrary *s_gles2_lib = NULL; + +#define DEFAULT_GLES_V2_LIB EMUGL_LIBNAME("GLES_V2_translator") + +// An unimplemented function which prints out an error message. +// To make it consistent with the guest, all GLES2 functions not supported by +// the driver should be redirected to this function. + +static void gles2_unimplemented() { + fprintf(stderr, "Called unimplemented GLESv2 API\n"); +} + +// +// This function is called only once during initialiation before +// any thread has been created - hence it should NOT be thread safe. +// +bool gles2_dispatch_init(GLESv2Dispatch* dispatch_table) +{ + const char *libName = getenv("ANDROID_GLESv2_LIB"); + if (!libName) { + libName = DEFAULT_GLES_V2_LIB; + } + + char error[256]; + s_gles2_lib = emugl::SharedLibrary::open(libName, error, sizeof(error)); + if (!s_gles2_lib) { + fprintf(stderr, "%s: Could not load %s [%s]\n", __FUNCTION__, + libName, error); + return false; + } + + // + // init the GLES dispatch table + // +#define LOOKUP_SYMBOL(return_type,function_name,signature,callargs) \ + dispatch_table-> function_name = reinterpret_cast< function_name ## _t >( \ + s_gles2_lib->findSymbol(#function_name)); + +#define LOOKUP_EXT_SYMBOL(return_type,function_name,signature,callargs) \ + dispatch_table-> function_name = reinterpret_cast< function_name ## _t >( \ + s_egl.eglGetProcAddress(#function_name)); + + LIST_GLES2_FUNCTIONS(LOOKUP_SYMBOL,LOOKUP_EXT_SYMBOL) + + return true; +} + +// +// This function is called only during initialization before +// any thread has been created - hence it should NOT be thread safe. +// +void *gles2_dispatch_get_proc_func(const char *name, void *userData) +{ + void* func = NULL; + + if (s_gles2_lib && !func) { + func = (void *)s_gles2_lib->findSymbol(name); + } + + if (!func) { + func = (void *)s_egl.eglGetProcAddress(name); + } + + // To make it consistent with the guest, redirect any unsupported functions + // to gles2_unimplemented. + if (!func) { + func = (void *)gles2_unimplemented; + } + return func; +} diff --git a/external/android-emugl/host/libs/libOpenGLESDispatch/gles1_extensions.entries b/external/android-emugl/host/libs/libOpenGLESDispatch/gles1_extensions.entries new file mode 100644 index 0000000..1e7f2f1 --- /dev/null +++ b/external/android-emugl/host/libs/libOpenGLESDispatch/gles1_extensions.entries @@ -0,0 +1,13 @@ +!gles1_extensions + +# OpenGL functions which are needed ONLY for implementing GLES 1.1 EXTENSIONS +void glCurrentPaletteMatrixARB(GLint index); +void glMatrixIndexuivARB(GLint size, GLuint * indices); +void glMatrixIndexPointerARB(GLint size, GLenum type, GLsizei stride, const GLvoid* pointer); +void glWeightPointerARB(GLint size, GLenum type, GLsizei stride, const GLvoid* pointer); +void glTexGenf(GLenum coord, GLenum pname, GLfloat param ); +void glTexGeni(GLenum coord, GLenum pname, GLint param ); +void glTexGenfv(GLenum coord, GLenum pname, const GLfloat *params ); +void glTexGeniv(GLenum coord, GLenum pname, const GLint *params ); +void glGetTexGenfv(GLenum coord, GLenum pname, GLfloat *params ); +void glGetTexGeniv(GLenum coord, GLenum pname, GLint *params ); diff --git a/external/android-emugl/host/libs/libOpenGLESDispatch/gles1_only.entries b/external/android-emugl/host/libs/libOpenGLESDispatch/gles1_only.entries new file mode 100644 index 0000000..650b6a8 --- /dev/null +++ b/external/android-emugl/host/libs/libOpenGLESDispatch/gles1_only.entries @@ -0,0 +1,65 @@ +!gles1_only + +# OpenGL functions which are needed ONLY for implementing GLES 1.1 + +void glAlphaFunc(GLenum func, GLclampf ref); +void glBegin( GLenum mode ); +void glClientActiveTexture( GLenum texture ); +void glClipPlane(GLenum plane, const GLdouble *equation); +void glColor4d(GLdouble red, GLdouble green, GLdouble blue, GLdouble alpha); +void glColor4f(GLfloat red, GLfloat green, GLfloat blue, GLfloat alpha); +void glColor4fv( const GLfloat *v ); +void glColor4ub(GLubyte red, GLubyte green, GLubyte blue, GLubyte alpha); +void glColor4ubv( const GLubyte *v ); +void glColorPointer(GLint size, GLenum type, GLsizei stride, const GLvoid *pointer); +void glDisableClientState(GLenum array); +void glEnableClientState(GLenum array); +void glEnd(void); +void glFogf(GLenum pname, GLfloat param); +void glFogfv(GLenum pname, const GLfloat *params); +void glFrustum(GLdouble left, GLdouble right, GLdouble bottom, GLdouble top, GLdouble zNear, GLdouble zFar); +void glGetClipPlane(GLenum plane, GLdouble *equation); +void glGetDoublev( GLenum pname, GLdouble *params ); +void glGetLightfv(GLenum light, GLenum pname, GLfloat *params); +void glGetMaterialfv(GLenum face, GLenum pname, GLfloat *params); +void glGetPointerv(GLenum pname, GLvoid* *params); +void glGetTexEnvfv(GLenum target, GLenum pname, GLfloat *params); +void glGetTexEnviv(GLenum target, GLenum pname, GLint *params); +void glLightf(GLenum light, GLenum pname, GLfloat param); +void glLightfv(GLenum light, GLenum pname, const GLfloat *params); +void glLightModelf(GLenum pname, GLfloat param); +void glLightModelfv(GLenum pname, const GLfloat *params); +void glLoadIdentity(void); +void glLoadMatrixf(const GLfloat *m); +void glLogicOp(GLenum opcode); +void glMaterialf(GLenum face, GLenum pname, GLfloat param); +void glMaterialfv(GLenum face, GLenum pname, const GLfloat *params); +void glMultiTexCoord2fv( GLenum target, const GLfloat *v ); +void glMultiTexCoord2sv( GLenum target, const GLshort *v ); +void glMultiTexCoord3fv( GLenum target, const GLfloat *v ); +void glMultiTexCoord3sv( GLenum target, const GLshort *v ); +void glMultiTexCoord4f( GLenum target, GLfloat s, GLfloat t, GLfloat r, GLfloat q ); +void glMultiTexCoord4fv( GLenum target, const GLfloat *v ); +void glMultiTexCoord4sv( GLenum target, const GLshort *v ); +void glMultMatrixf(const GLfloat *m); +void glNormal3f(GLfloat nx, GLfloat ny, GLfloat nz); +void glNormal3fv( const GLfloat *v ); +void glNormal3sv( const GLshort *v ); +void glOrtho(GLdouble left, GLdouble right, GLdouble bottom, GLdouble top, GLdouble zNear, GLdouble zFar); +void glPointParameterf(GLenum param, GLfloat value); +void glPointParameterfv(GLenum param, const GLfloat *values); +void glPointSize(GLfloat size); +void glRotatef(GLfloat angle, GLfloat x, GLfloat y, GLfloat z); +void glScalef(GLfloat x, GLfloat y, GLfloat z); +void glTexEnvf(GLenum target, GLenum pname, GLfloat param); +void glTexEnvfv(GLenum target, GLenum pname, const GLfloat *params); +void glMatrixMode(GLenum mode); +void glNormalPointer(GLenum type, GLsizei stride, const GLvoid *pointer); +void glPopMatrix(void); +void glPushMatrix(void); +void glShadeModel(GLenum mode); +void glTexCoordPointer(GLint size, GLenum type, GLsizei stride, const GLvoid *pointer); +void glTexEnvi(GLenum target, GLenum pname, GLint param); +void glTexEnviv(GLenum target, GLenum pname, const GLint *params); +void glTranslatef(GLfloat x, GLfloat y, GLfloat z); +void glVertexPointer(GLint size, GLenum type, GLsizei stride, const GLvoid *pointer); diff --git a/external/android-emugl/host/libs/libOpenGLESDispatch/gles2_extensions.entries b/external/android-emugl/host/libs/libOpenGLESDispatch/gles2_extensions.entries new file mode 100644 index 0000000..06ee78f --- /dev/null +++ b/external/android-emugl/host/libs/libOpenGLESDispatch/gles2_extensions.entries @@ -0,0 +1,6 @@ +!gles2_extensions + +# GLES 2.0 extensions +void glGetShaderPrecisionFormat(GLenum shadertype, GLenum precisiontype, GLint* range, GLint* precision); +void glReleaseShaderCompiler(void); +void glShaderBinary(GLsizei n, const GLuint* shaders, GLenum binaryformat, const GLvoid* binary, GLsizei length); diff --git a/external/android-emugl/host/libs/libOpenGLESDispatch/gles2_only.entries b/external/android-emugl/host/libs/libOpenGLESDispatch/gles2_only.entries new file mode 100644 index 0000000..55bdff4 --- /dev/null +++ b/external/android-emugl/host/libs/libOpenGLESDispatch/gles2_only.entries @@ -0,0 +1,80 @@ +!gles2_only + +# OpenGL functions which are needed ONLY for implementing GLES 2.0 +void glBlendColor(GLclampf red, GLclampf green, GLclampf blue, GLclampf alpha); +void glStencilFuncSeparate(GLenum face, GLenum func, GLint ref, GLuint mask); +void glStencilMaskSeparate(GLenum face, GLuint mask); +GLboolean glIsProgram(GLuint program); +GLboolean glIsShader(GLuint shader); +void glVertexAttrib1f(GLuint indx, GLfloat x); +void glVertexAttrib1fv(GLuint indx, const GLfloat* values); +void glVertexAttrib2f(GLuint indx, GLfloat x, GLfloat y); +void glVertexAttrib2fv(GLuint indx, const GLfloat* values); +void glVertexAttrib3f(GLuint indx, GLfloat x, GLfloat y, GLfloat z); +void glVertexAttrib3fv(GLuint indx, const GLfloat* values); +void glVertexAttrib4f(GLuint indx, GLfloat x, GLfloat y, GLfloat z, GLfloat w); +void glVertexAttrib4fv(GLuint indx, const GLfloat* values); +void glVertexAttribPointer(GLuint indx, GLint size, GLenum type, GLboolean normalized, GLsizei stride, const GLvoid* ptr); +void glDisableVertexAttribArray(GLuint index); +void glEnableVertexAttribArray(GLuint index); +void glGetVertexAttribfv(GLuint index, GLenum pname, GLfloat* params); +void glGetVertexAttribiv(GLuint index, GLenum pname, GLint* params); +void glGetVertexAttribPointerv(GLuint index, GLenum pname, GLvoid** pointer); +void glUniform1f(GLint location, GLfloat x); +void glUniform1fv(GLint location, GLsizei count, const GLfloat* v); +void glUniform1i(GLint location, GLint x); +void glUniform1iv(GLint location, GLsizei count, const GLint* v); +void glUniform2f(GLint location, GLfloat x, GLfloat y); +void glUniform2fv(GLint location, GLsizei count, const GLfloat* v); +void glUniform2i(GLint location, GLint x, GLint y); +void glUniform2iv(GLint location, GLsizei count, const GLint* v); +void glUniform3f(GLint location, GLfloat x, GLfloat y, GLfloat z); +void glUniform3fv(GLint location, GLsizei count, const GLfloat* v); +void glUniform3i(GLint location, GLint x, GLint y, GLint z); +void glUniform3iv(GLint location, GLsizei count, const GLint* v); +void glUniform4f(GLint location, GLfloat x, GLfloat y, GLfloat z, GLfloat w); +void glUniform4fv(GLint location, GLsizei count, const GLfloat* v); +void glUniform4i(GLint location, GLint x, GLint y, GLint z, GLint w); +void glUniform4iv(GLint location, GLsizei count, const GLint* v); +void glUniformMatrix2fv(GLint location, GLsizei count, GLboolean transpose, const GLfloat* value); +void glUniformMatrix3fv(GLint location, GLsizei count, GLboolean transpose, const GLfloat* value); +void glUniformMatrix4fv(GLint location, GLsizei count, GLboolean transpose, const GLfloat* value); +void glAttachShader(GLuint program, GLuint shader); +void glBindAttribLocation(GLuint program, GLuint index, const GLchar* name); +void glCompileShader(GLuint shader); +GLuint glCreateProgram(void); +GLuint glCreateShader(GLenum type); +void glDeleteProgram(GLuint program); +void glDeleteShader(GLuint shader); +void glDetachShader(GLuint program, GLuint shader); +void glLinkProgram(GLuint program); +void glUseProgram(GLuint program); +void glValidateProgram(GLuint program); +void glGetActiveAttrib(GLuint program, GLuint index, GLsizei bufsize, GLsizei* length, GLint* size, GLenum* type, GLchar* name); +void glGetActiveUniform(GLuint program, GLuint index, GLsizei bufsize, GLsizei* length, GLint* size, GLenum* type, GLchar* name); +void glGetAttachedShaders(GLuint program, GLsizei maxcount, GLsizei* count, GLuint* shaders); +int glGetAttribLocation(GLuint program, const GLchar* name); +void glGetProgramiv(GLuint program, GLenum pname, GLint* params); +void glGetProgramInfoLog(GLuint program, GLsizei bufsize, GLsizei* length, GLchar* infolog); +void glGetShaderiv(GLuint shader, GLenum pname, GLint* params); +void glGetShaderInfoLog(GLuint shader, GLsizei bufsize, GLsizei* length, GLchar* infolog); +void glGetShaderSource(GLuint shader, GLsizei bufsize, GLsizei* length, GLchar* source); +void glGetUniformfv(GLuint program, GLint location, GLfloat* params); +void glGetUniformiv(GLuint program, GLint location, GLint* params); +int glGetUniformLocation(GLuint program, const GLchar* name); +void glShaderSource(GLuint shader, GLsizei count, const GLchar* const* string, const GLint* length); + +# The following are not used by GLDispatch but by GLESv2Dispatch +void glBindFramebuffer(GLenum target, GLuint framebuffer); +void glGenFramebuffers(GLsizei n, GLuint* framebuffers); +void glFramebufferTexture2D(GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level); +GLenum glCheckFramebufferStatus(GLenum target); +void glDeleteFramebuffers(GLsizei n, const GLuint* framebuffers); + +GLboolean glIsRenderbuffer(GLuint renderbuffer); +void glBindRenderbuffer(GLenum target, GLuint renderbuffer); +void glDeleteRenderbuffers(GLsizei n, const GLuint *renderbuffers); +void glGenRenderbuffers(GLsizei n, GLuint *renderbuffers); +void glRenderbufferStorage(GLenum target, GLenum internalformat, GLsizei width, GLsizei height); +void glGetRenderbufferParameteriv(GLenum target, GLenum pname, GLint *params); +void glFramebufferRenderbuffer(GLenum target, GLenum attachment, GLenum renderbuffertarget, GLuint renderbuffer); diff --git a/external/android-emugl/host/libs/libOpenGLESDispatch/gles3_only.entries b/external/android-emugl/host/libs/libOpenGLESDispatch/gles3_only.entries new file mode 100644 index 0000000..2feb1b2 --- /dev/null +++ b/external/android-emugl/host/libs/libOpenGLESDispatch/gles3_only.entries @@ -0,0 +1,18 @@ +!gles3_only + +# GLES 3.x functions required by the translator library. +# Right now, this is only use to get glGetStringi() from the host GL library +# in order to deal with the fact that glGetString(GL_EXTENSIONS) is obsolete +# in OpenGL 3.0, and some drivers don't implement it anymore (i.e. the +# function just returns NULL). + +%#include +% +%// Used to avoid adding GLES3/gl3.h to our headers. +%#ifndef GL_NUM_EXTENSIONS +%#define GL_NUM_EXTENSIONS 0x821D +%#endif + +%typedef const GLubyte* GLconstubyteptr; + +GLconstubyteptr glGetStringi(GLenum name, GLint index); diff --git a/external/android-emugl/host/libs/libOpenGLESDispatch/gles_common.entries b/external/android-emugl/host/libs/libOpenGLESDispatch/gles_common.entries new file mode 100644 index 0000000..bf3b9ce --- /dev/null +++ b/external/android-emugl/host/libs/libOpenGLESDispatch/gles_common.entries @@ -0,0 +1,74 @@ +!gles_common + +# Functions common to both GLES 1.x and 2.0 + +%#include +%// Return types must be single words, see GLDispatch.cpp +%typedef const GLubyte* GLconstubyteptr; + +void glActiveTexture( GLenum texture ); +void glBindBuffer(GLenum target, GLuint buffer); +void glBindTexture(GLenum target, GLuint texture); +void glBlendFunc(GLenum sfactor, GLenum dfactor); +void glBlendEquation( GLenum mode ); +void glBlendEquationSeparate(GLenum modeRGB, GLenum modeAlpha); +void glBlendFuncSeparate(GLenum srcRGB, GLenum dstRGB, GLenum srcAlpha, GLenum dstAlpha); +void glBufferData(GLenum target, GLsizeiptr size, const GLvoid *data, GLenum usage); +void glBufferSubData(GLenum target, GLintptr offset, GLsizeiptr size, const GLvoid *data); +void glClear(GLbitfield mask); +void glClearColor(GLclampf red, GLclampf green, GLclampf blue, GLclampf alpha); +void glClearDepth(GLclampd depth); +void glClearStencil(GLint s); +void glColorMask(GLboolean red, GLboolean green, GLboolean blue, GLboolean alpha); +void glCompressedTexImage2D( GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLint border, GLsizei imageSize, const GLvoid *data ); +void glCompressedTexSubImage2D( GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLsizei imageSize, const GLvoid *data ); +void glCopyTexImage2D(GLenum target, GLint level, GLenum internalFormat, GLint x, GLint y, GLsizei width, GLsizei height, GLint border); +void glCopyTexSubImage2D(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint x, GLint y, GLsizei width, GLsizei height); +void glCullFace(GLenum mode); +void glDeleteBuffers(GLsizei n, const GLuint *buffers); +void glDeleteTextures(GLsizei n, const GLuint *textures); +void glDepthFunc(GLenum func); +void glDepthMask(GLboolean flag); +void glDepthRange(GLclampd zNear, GLclampd zFar); +void glDisable(GLenum cap); +void glDrawArrays(GLenum mode, GLint first, GLsizei count); +void glDrawElements(GLenum mode, GLsizei count, GLenum type, const GLvoid *indices); +void glEnable(GLenum cap); +void glFinish(void); +void glFlush(void); +void glFrontFace(GLenum mode); +void glGenBuffers(GLsizei n, GLuint *buffers); +void glGenTextures(GLsizei n, GLuint *textures); +void glGetBooleanv(GLenum pname, GLboolean *params); +void glGetBufferParameteriv(GLenum buffer, GLenum parameter, GLint *value); +GLenum glGetError(void); +void glGetFloatv(GLenum pname, GLfloat *params); +void glGetIntegerv(GLenum pname, GLint *params); +GLconstubyteptr glGetString(GLenum name); +void glTexParameterf(GLenum target, GLenum pname, GLfloat param); +void glTexParameterfv(GLenum target, GLenum pname, const GLfloat *params); +void glGetTexParameterfv(GLenum target, GLenum pname, GLfloat *params); +void glGetTexParameteriv(GLenum target, GLenum pname, GLint *params); +void glGetTexLevelParameteriv(GLenum target, GLint level, GLenum pname, GLint *params); +void glHint(GLenum target, GLenum mode); +GLboolean glIsBuffer(GLuint buffer); +GLboolean glIsEnabled(GLenum cap); +GLboolean glIsTexture(GLuint texture); +void glLineWidth(GLfloat width); +void glPolygonOffset(GLfloat factor, GLfloat units); +void glPixelStorei(GLenum pname, GLint param); +void glReadPixels(GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, GLvoid *pixels); +void glSampleCoverage( GLclampf value, GLboolean invert ); +void glScissor(GLint x, GLint y, GLsizei width, GLsizei height); +void glStencilFunc(GLenum func, GLint ref, GLuint mask); +void glStencilMask(GLuint mask); +void glStencilOp(GLenum fail, GLenum zfail, GLenum zpass); +void glTexImage2D(GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, const GLvoid *pixels); +void glTexParameteri(GLenum target, GLenum pname, GLint param); +void glTexParameteriv(GLenum target, GLenum pname, const GLint *params); +void glTexSubImage2D(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, const GLvoid *pixels); +void glViewport(GLint x, GLint y, GLsizei width, GLsizei height); +void glPushAttrib( GLbitfield mask ); +void glPushClientAttrib( GLbitfield mask ); +void glPopAttrib( void ); +void glPopClientAttrib( void ); diff --git a/external/android-emugl/host/libs/libOpenGLESDispatch/gles_extensions.entries b/external/android-emugl/host/libs/libOpenGLESDispatch/gles_extensions.entries new file mode 100644 index 0000000..817e8ad --- /dev/null +++ b/external/android-emugl/host/libs/libOpenGLESDispatch/gles_extensions.entries @@ -0,0 +1,24 @@ +!gles_extensions + +# Common GLES 1.x / 2.0 extension functions +GLboolean glIsRenderbufferEXT(GLuint renderbuffer); +void glBindRenderbufferEXT(GLenum target, GLuint renderbuffer); +void glDeleteRenderbuffersEXT(GLsizei n, const GLuint *renderbuffers); +void glGenRenderbuffersEXT(GLsizei n, GLuint *renderbuffers); +void glRenderbufferStorageEXT(GLenum target, GLenum internalformat, GLsizei width, GLsizei height); +void glGetRenderbufferParameterivEXT(GLenum target, GLenum pname, GLint *params); +GLboolean glIsFramebufferEXT(GLuint framebuffer); +void glBindFramebufferEXT(GLenum target, GLuint framebuffer); +void glDeleteFramebuffersEXT(GLsizei n, const GLuint *framebuffers); +void glGenFramebuffersEXT(GLsizei n, GLuint *framebuffers); +GLenum glCheckFramebufferStatusEXT(GLenum target); +void glFramebufferTexture1DEXT(GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level); +void glFramebufferTexture2DEXT(GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level); +void glFramebufferTexture3DEXT(GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level, GLint zoffset); +void glFramebufferRenderbufferEXT(GLenum target, GLenum attachment, GLenum renderbuffertarget, GLuint renderbuffer); +void glGetFramebufferAttachmentParameterivEXT(GLenum target, GLenum attachment, GLenum pname, GLint *params); +void glGenerateMipmapEXT(GLenum target); + +# The following extensions are used by GLESv1Dispatch and GLESv2Dispatch, but not by GLDispatch +void glEGLImageTargetTexture2DOES(GLenum target, GLeglImageOES image); +void glEGLImageTargetRenderbufferStorageOES(GLenum target, GLeglImageOES image); diff --git a/external/android-emugl/host/libs/libOpenGLESDispatch/render_egl.entries b/external/android-emugl/host/libs/libOpenGLESDispatch/render_egl.entries new file mode 100644 index 0000000..314c9be --- /dev/null +++ b/external/android-emugl/host/libs/libOpenGLESDispatch/render_egl.entries @@ -0,0 +1,26 @@ +# The list of EGL functions used by libOpenglRender, without extensions. +# This is only a subset of the full EGL API. + +!Render_EGL +%#include + +EGLint eglGetError(void); +EGLDisplay eglGetDisplay(EGLNativeDisplayType dpy); +EGLBoolean eglInitialize(EGLDisplay dpy, EGLint* major, EGLint* minor); +char* eglQueryString(EGLDisplay dpy, EGLint id); +EGLBoolean eglGetConfigs(EGLDisplay display, EGLConfig* configs, EGLint config_size, EGLint* num_config); +EGLBoolean eglChooseConfig(EGLDisplay display, const EGLint* attribs, EGLConfig* configs, EGLint config_size, EGLint* num_config); +EGLBoolean eglGetConfigAttrib(EGLDisplay display, EGLConfig config, EGLint attribute, EGLint* value); +EGLSurface eglCreateWindowSurface(EGLDisplay display, EGLConfig config, EGLNativeWindowType native_window, const EGLint* attrib_list); +EGLSurface eglCreatePbufferSurface(EGLDisplay display, EGLConfig config, const EGLint* attrib_list); +EGLBoolean eglDestroySurface(EGLDisplay display, EGLSurface surface); +EGLBoolean eglBindAPI(EGLenum api); +EGLenum eglQueryAPI(void); +EGLBoolean eglReleaseThread(void); +EGLContext eglCreateContext(EGLDisplay display, EGLConfig config, EGLContext share_context, const EGLint* attrib_list); +EGLBoolean eglDestroyContext(EGLDisplay display, EGLContext context); +EGLBoolean eglMakeCurrent(EGLDisplay display, EGLSurface draw, EGLSurface read, EGLContext context); +EGLContext eglGetCurrentContext(void); +EGLSurface eglGetCurrentSurface(EGLint readdraw); +EGLBoolean eglSwapBuffers(EGLDisplay display, EGLSurface surface); +void* eglGetProcAddress(const char* function_name); diff --git a/external/android-emugl/host/libs/libOpenGLESDispatch/render_egl_extensions.entries b/external/android-emugl/host/libs/libOpenGLESDispatch/render_egl_extensions.entries new file mode 100644 index 0000000..cd6d32a --- /dev/null +++ b/external/android-emugl/host/libs/libOpenGLESDispatch/render_egl_extensions.entries @@ -0,0 +1,11 @@ +# The list of EGL extension functions used by libOpenglRender. +# This is only a subset of the full EGL API. + +!Render_EGL_extensions + +%#include +%#define EGL_EGLEXT_PROTOTYPES +%#include + +EGLImageKHR eglCreateImageKHR(EGLDisplay display, EGLContext context, EGLenum target, EGLClientBuffer buffer, const EGLint* attrib_list); +EGLBoolean eglDestroyImageKHR(EGLDisplay display, EGLImageKHR image); diff --git a/external/android-emugl/host/libs/libOpenglRender/Android.mk b/external/android-emugl/host/libs/libOpenglRender/Android.mk new file mode 100644 index 0000000..d98a714 --- /dev/null +++ b/external/android-emugl/host/libs/libOpenglRender/Android.mk @@ -0,0 +1,70 @@ +LOCAL_PATH := $(call my-dir) + +host_OS_SRCS := +host_common_LDLIBS := + +ifeq ($(BUILD_TARGET_OS),linux) + host_OS_SRCS = NativeSubWindow_x11.cpp + host_common_LDLIBS += -lX11 -lrt +endif + +ifeq ($(BUILD_TARGET_OS),darwin) + host_OS_SRCS = NativeSubWindow_cocoa.m + host_common_LDLIBS += -Wl,-framework,AppKit +endif + +ifeq ($(BUILD_TARGET_OS),windows) + host_OS_SRCS = NativeSubWindow_win32.cpp + host_common_LDLIBS += -lgdi32 +endif + +host_common_SRC_FILES := \ + $(host_OS_SRCS) \ + ColorBuffer.cpp \ + FbConfig.cpp \ + FrameBuffer.cpp \ + ReadBuffer.cpp \ + RenderContext.cpp \ + RenderControl.cpp \ + RenderServer.cpp \ + RenderThread.cpp \ + RenderThreadInfo.cpp \ + render_api.cpp \ + RenderWindow.cpp \ + SocketStream.cpp \ + TcpStream.cpp \ + TextureDraw.cpp \ + TextureResize.cpp \ + TimeUtils.cpp \ + WindowSurface.cpp \ + +ifeq ($(BUILD_TARGET_OS),windows) + host_common_SRC_FILES += Win32PipeStream.cpp + host_common_LDLIBS += -lws2_32 -lpsapi +else + host_common_SRC_FILES += UnixStream.cpp +endif + +### host libOpenglRender ################################################# +$(call emugl-begin-host-shared-library,lib$(BUILD_TARGET_SUFFIX)OpenglRender) + +$(call emugl-import,libGLESv1_dec libGLESv2_dec lib_renderControl_dec libOpenglCodecCommon) + +LOCAL_LDLIBS += $(host_common_LDLIBS) + +LOCAL_SRC_FILES := $(host_common_SRC_FILES) +$(call emugl-export,C_INCLUDES,$(EMUGL_PATH)/host/include) +$(call emugl-export,C_INCLUDES,$(LOCAL_PATH)) + +# use Translator's egl/gles headers +LOCAL_C_INCLUDES += $(EMUGL_PATH)/host/libs/Translator/include +LOCAL_C_INCLUDES += $(EMUGL_PATH)/host/libs/libOpenGLESDispatch + +LOCAL_STATIC_LIBRARIES += libemugl_common +LOCAL_STATIC_LIBRARIES += libOpenGLESDispatch + +LOCAL_SYMBOL_FILE := render_api.entries + +$(call emugl-export,CFLAGS,$(EMUGL_USER_CFLAGS)) + +$(call emugl-end-module) diff --git a/external/android-emugl/host/libs/libOpenglRender/CMakeLists.txt b/external/android-emugl/host/libs/libOpenglRender/CMakeLists.txt new file mode 100644 index 0000000..27846a1 --- /dev/null +++ b/external/android-emugl/host/libs/libOpenglRender/CMakeLists.txt @@ -0,0 +1,37 @@ +set(SOURCES + ColorBuffer.cpp + FbConfig.cpp + FrameBuffer.cpp + NativeSubWindow_mir.cpp + ReadBuffer.cpp + RenderContext.cpp + RenderControl.cpp + RenderServer.cpp + RenderThread.cpp + RenderThreadInfo.cpp + render_api.cpp + RenderWindow.cpp + SocketStream.cpp + TcpStream.cpp + TextureDraw.cpp + TextureResize.cpp + TimeUtils.cpp + UnixStream.cpp + WindowSurface.cpp) + +include_directories(BEFORE + ${MIRCLIENT_INCLUDE_DIRS}) + +add_library(OpenglRender ${SOURCES}) +target_link_libraries(OpenglRender + emugl_common + GLESv1_dec + GLESv2_dec + renderControl_dec + OpenGLESDispatch + OpenglCodecCommon + mir_support + ${EGL_LDFLAGS} + ${EGL_LIBRARIES} + ${MIRCLIENT_LDFLAGS} + ${MIRCLIENT_LIBRARIES}) diff --git a/external/android-emugl/host/libs/libOpenglRender/ColorBuffer.cpp b/external/android-emugl/host/libs/libOpenglRender/ColorBuffer.cpp new file mode 100644 index 0000000..95de4dd --- /dev/null +++ b/external/android-emugl/host/libs/libOpenglRender/ColorBuffer.cpp @@ -0,0 +1,396 @@ +/* +* Copyright (C) 2011 The Android Open Source Project +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ +#include "ColorBuffer.h" + +#include "DispatchTables.h" +#include "RenderThreadInfo.h" +#include "TextureDraw.h" +#include "TextureResize.h" + +#include "OpenGLESDispatch/EGLDispatch.h" + +#include + +namespace { + +// defines many types as 'void*' while they're really +// implemented as unsigned integers. These convenience template functions +// help casting between them safely without generating compiler warnings. +inline void* SafePointerFromUInt(unsigned int handle) { + return (void*)(uintptr_t)(handle); +} + +inline unsigned int SafeUIntFromPointer(const void* ptr) { +#if 1 + // Ignore the assert below to avoid crashing when running older + // system images, which might have buggy encoder libraries. Print + // an error message though. + if ((uintptr_t)(ptr) != (unsigned int)(uintptr_t)(ptr)) { + fprintf(stderr, "EmuGL:WARNING: bad generic pointer %p\n", ptr); + } +#else + // Assertion error if the pointer contains a value that does not fit + // in an unsigned integer! + assert((uintptr_t)(ptr) == (unsigned int)(uintptr_t)(ptr)); +#endif + return (unsigned int)(uintptr_t)(ptr); +} + +// Lazily create and bind a framebuffer object to the current host context. +// |fbo| is the address of the framebuffer object name. +// |tex| is the name of a texture that is attached to the framebuffer object +// on creation only. I.e. all rendering operations will target it. +// returns true in case of success, false on failure. +bool bindFbo(GLuint* fbo, GLuint tex) { + if (*fbo) { + // fbo already exist - just bind + s_gles2.glBindFramebuffer(GL_FRAMEBUFFER, *fbo); + return true; + } + + s_gles2.glGenFramebuffers(1, fbo); + s_gles2.glBindFramebuffer(GL_FRAMEBUFFER, *fbo); + s_gles2.glFramebufferTexture2D(GL_FRAMEBUFFER, + GL_COLOR_ATTACHMENT0_OES, + GL_TEXTURE_2D, tex, 0); + GLenum status = s_gles2.glCheckFramebufferStatus(GL_FRAMEBUFFER); + if (status != GL_FRAMEBUFFER_COMPLETE_OES) { + ERR("ColorBuffer::bindFbo: FBO not complete: %#x\n", status); + s_gles2.glBindFramebuffer(GL_FRAMEBUFFER, 0); + s_gles2.glDeleteFramebuffers(1, fbo); + *fbo = 0; + return false; + } + return true; +} + +void unbindFbo() { + s_gles2.glBindFramebuffer(GL_FRAMEBUFFER, 0); +} + +// Helper class to use a ColorBuffer::Helper context. +// Usage is pretty simple: +// +// { +// ScopedHelperContext context(m_helper); +// if (!context.isOk()) { +// return false; // something bad happened. +// } +// .... do something .... +// } // automatically calls m_helper->teardownContext(); +// +class ScopedHelperContext { +public: + ScopedHelperContext(ColorBuffer::Helper* helper) : mHelper(helper) { + if (!helper->setupContext()) { + mHelper = NULL; + } + } + + bool isOk() const { return mHelper != NULL; } + + ~ScopedHelperContext() { + release(); + } + + void release() { + if (mHelper) { + mHelper->teardownContext(); + mHelper = NULL; + } + } +private: + ColorBuffer::Helper* mHelper; +}; + +} // namespace + +// static +ColorBuffer* ColorBuffer::create(EGLDisplay p_display, + int p_width, + int p_height, + GLenum p_internalFormat, + bool has_eglimage_texture_2d, + Helper* helper) { + GLenum texInternalFormat = 0; + + printf("Creating new color buffer width %i height %i\n", p_width, p_height); + + switch (p_internalFormat) { + case GL_RGB: + case GL_RGB565_OES: + texInternalFormat = GL_RGB; + break; + + case GL_RGBA: + case GL_RGB5_A1_OES: + case GL_RGBA4_OES: + texInternalFormat = GL_RGBA; + break; + + default: + return NULL; + break; + } + + ScopedHelperContext context(helper); + if (!context.isOk()) { + return NULL; + } + + ColorBuffer *cb = new ColorBuffer(p_display, helper); + + s_gles2.glGenTextures(1, &cb->m_tex); + s_gles2.glBindTexture(GL_TEXTURE_2D, cb->m_tex); + + int nComp = (texInternalFormat == GL_RGB ? 3 : 4); + + char* zBuff = static_cast(::calloc(nComp * p_width * p_height, 1)); + s_gles2.glTexImage2D(GL_TEXTURE_2D, + 0, + texInternalFormat, + p_width, + p_height, + 0, + texInternalFormat, + GL_UNSIGNED_BYTE, + zBuff); + ::free(zBuff); + + s_gles2.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + s_gles2.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + s_gles2.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + s_gles2.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + + // + // create another texture for that colorbuffer for blit + // + s_gles2.glGenTextures(1, &cb->m_blitTex); + s_gles2.glBindTexture(GL_TEXTURE_2D, cb->m_blitTex); + s_gles2.glTexImage2D(GL_TEXTURE_2D, + 0, + texInternalFormat, + p_width, + p_height, + 0, + texInternalFormat, + GL_UNSIGNED_BYTE, + NULL); + + s_gles2.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + s_gles2.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + s_gles2.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + s_gles2.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + + cb->m_width = p_width; + cb->m_height = p_height; + cb->m_internalFormat = texInternalFormat; + + if (has_eglimage_texture_2d) { + cb->m_eglImage = s_egl.eglCreateImageKHR( + p_display, + s_egl.eglGetCurrentContext(), + EGL_GL_TEXTURE_2D_KHR, + (EGLClientBuffer)SafePointerFromUInt(cb->m_tex), + NULL); + + cb->m_blitEGLImage = s_egl.eglCreateImageKHR( + p_display, + s_egl.eglGetCurrentContext(), + EGL_GL_TEXTURE_2D_KHR, + (EGLClientBuffer)SafePointerFromUInt(cb->m_blitTex), + NULL); + } + + cb->m_resizer = new TextureResize(p_width, p_height); + + return cb; +} + +ColorBuffer::ColorBuffer(EGLDisplay display, Helper* helper) : + m_tex(0), + m_blitTex(0), + m_eglImage(NULL), + m_blitEGLImage(NULL), + m_fbo(0), + m_internalFormat(0), + m_display(display), + m_helper(helper) {} + +ColorBuffer::~ColorBuffer() { + ScopedHelperContext context(m_helper); + + if (m_blitEGLImage) { + s_egl.eglDestroyImageKHR(m_display, m_blitEGLImage); + } + if (m_eglImage) { + s_egl.eglDestroyImageKHR(m_display, m_eglImage); + } + + if (m_fbo) { + s_gles2.glDeleteFramebuffers(1, &m_fbo); + } + + GLuint tex[2] = {m_tex, m_blitTex}; + s_gles2.glDeleteTextures(2, tex); + + delete m_resizer; +} + +void ColorBuffer::readPixels(int x, + int y, + int width, + int height, + GLenum p_format, + GLenum p_type, + void* pixels) { + ScopedHelperContext context(m_helper); + if (!context.isOk()) { + return; + } + + if (bindFbo(&m_fbo, m_tex)) { + s_gles2.glReadPixels(x, y, width, height, p_format, p_type, pixels); + unbindFbo(); + } +} + +void ColorBuffer::subUpdate(int x, + int y, + int width, + int height, + GLenum p_format, + GLenum p_type, + void* pixels) { + ScopedHelperContext context(m_helper); + if (!context.isOk()) { + return; + } + + s_gles2.glBindTexture(GL_TEXTURE_2D, m_tex); + s_gles2.glPixelStorei(GL_UNPACK_ALIGNMENT, 1); + s_gles2.glTexSubImage2D( + GL_TEXTURE_2D, 0, x, y, width, height, p_format, p_type, pixels); +} + +bool ColorBuffer::blitFromCurrentReadBuffer() +{ + RenderThreadInfo *tInfo = RenderThreadInfo::get(); + if (!tInfo->currContext.Ptr()) { + // no Current context + return false; + } + + // Copy the content of the current read surface into m_blitEGLImage. + // This is done by creating a temporary texture, bind it to the EGLImage + // then call glCopyTexSubImage2D(). + GLuint tmpTex; + GLint currTexBind; + if (tInfo->currContext->isGL2()) { + s_gles2.glGetIntegerv(GL_TEXTURE_BINDING_2D, &currTexBind); + s_gles2.glGenTextures(1,&tmpTex); + s_gles2.glBindTexture(GL_TEXTURE_2D, tmpTex); + s_gles2.glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, m_blitEGLImage); + s_gles2.glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 0, 0, + m_width, m_height); + s_gles2.glDeleteTextures(1, &tmpTex); + s_gles2.glBindTexture(GL_TEXTURE_2D, currTexBind); + } + else { + s_gles1.glGetIntegerv(GL_TEXTURE_BINDING_2D, &currTexBind); + s_gles1.glGenTextures(1,&tmpTex); + s_gles1.glBindTexture(GL_TEXTURE_2D, tmpTex); + s_gles1.glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, m_blitEGLImage); + s_gles1.glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 0, 0, + m_width, m_height); + s_gles1.glDeleteTextures(1, &tmpTex); + s_gles1.glBindTexture(GL_TEXTURE_2D, currTexBind); + } + + ScopedHelperContext context(m_helper); + if (!context.isOk()) { + return false; + } + + if (!bindFbo(&m_fbo, m_tex)) { + return false; + } + + // Save current viewport and match it to the current colorbuffer size. + GLint vport[4] = { 0, }; + s_gles2.glGetIntegerv(GL_VIEWPORT, vport); + s_gles2.glViewport(0, 0, m_width, m_height); + + // render m_blitTex + m_helper->getTextureDraw()->draw(m_blitTex, 0., 0, 0); + + // Restore previous viewport. + s_gles2.glViewport(vport[0], vport[1], vport[2], vport[3]); + unbindFbo(); + + return true; +} + +bool ColorBuffer::bindToTexture() { + if (!m_eglImage) { + return false; + } + RenderThreadInfo *tInfo = RenderThreadInfo::get(); + if (!tInfo->currContext.Ptr()) { + return false; + } + if (tInfo->currContext->isGL2()) { + s_gles2.glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, m_eglImage); + } + else { + s_gles1.glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, m_eglImage); + } + return true; +} + +bool ColorBuffer::bindToRenderbuffer() { + if (!m_eglImage) { + return false; + } + RenderThreadInfo *tInfo = RenderThreadInfo::get(); + if (!tInfo->currContext.Ptr()) { + return false; + } + if (tInfo->currContext->isGL2()) { + s_gles2.glEGLImageTargetRenderbufferStorageOES(GL_RENDERBUFFER_OES, m_eglImage); + } + else { + s_gles1.glEGLImageTargetRenderbufferStorageOES(GL_RENDERBUFFER_OES, m_eglImage); + } + return true; +} + +bool ColorBuffer::post(float rotation, float dx, float dy) { + // NOTE: Do not call m_helper->setupContext() here! + return m_helper->getTextureDraw()->draw(m_resizer->update(m_tex), rotation, dx, dy); +} + +void ColorBuffer::readback(unsigned char* img) { + ScopedHelperContext context(m_helper); + if (!context.isOk()) { + return; + } + if (bindFbo(&m_fbo, m_tex)) { + s_gles2.glReadPixels( + 0, 0, m_width, m_height, GL_RGBA, GL_UNSIGNED_BYTE, img); + unbindFbo(); + } +} diff --git a/external/android-emugl/host/libs/libOpenglRender/ColorBuffer.h b/external/android-emugl/host/libs/libOpenglRender/ColorBuffer.h new file mode 100644 index 0000000..a9ce3c5 --- /dev/null +++ b/external/android-emugl/host/libs/libOpenglRender/ColorBuffer.h @@ -0,0 +1,161 @@ +/* +* Copyright (C) 2011 The Android Open Source Project +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ +#ifndef _LIBRENDER_COLORBUFFER_H +#define _LIBRENDER_COLORBUFFER_H + +#include +#include +#include +#include "emugl/common/smart_ptr.h" + +#include + + +class TextureDraw; +class TextureResize; + +// A class used to model a guest color buffer, and used to implement several +// related things: +// +// - Every gralloc native buffer with HW read or write requirements will +// allocate a host ColorBuffer instance. When gralloc_lock() is called, +// the guest will use ColorBuffer::readPixels() to read the current content +// of the buffer. When gralloc_unlock() is later called, it will call +// ColorBuffer::subUpdate() to send the updated pixels. +// +// - Every guest window EGLSurface is implemented by a host PBuffer +// (see WindowSurface.h) that can have a ColorBuffer instance attached to +// it (through WindowSurface::attachColorBuffer()). When such an attachment +// exists, WindowSurface::flushColorBuffer() will copy the PBuffer's +// pixel data into the ColorBuffer. The latter can then be displayed +// in the client's UI sub-window with ColorBuffer::post(). +// +// - Guest EGLImages are implemented as native gralloc buffers too. +// The guest glEGLImageTargetTexture2DOES() implementations will end up +// calling ColorBuffer::bindToTexture() to bind the current context's +// GL_TEXTURE_2D to the buffer. Similarly, the guest versions of +// glEGLImageTargetRenderbufferStorageOES() will end up calling +// ColorBuffer::bindToRenderbuffer(). +// +// This forces the implementation to use a host EGLImage to implement each +// ColorBuffer. +// +// As an additional twist. + +class ColorBuffer { +public: + // Helper interface class used during ColorBuffer operations. This is + // introduced to remove coupling from the FrameBuffer class implementation. + class Helper { + public: + Helper() {} + virtual ~Helper() {} + virtual bool setupContext() = 0; + virtual void teardownContext() = 0; + virtual TextureDraw* getTextureDraw() const = 0; + }; + + // Create a new ColorBuffer instance. + // |p_display| is the host EGLDisplay handle. + // |p_width| and |p_height| are the buffer's dimensions in pixels. + // |p_internalFormat| is the internal pixel format to use, valid values + // are: GL_RGB, GL_RGB565, GL_RGBA, GL_RGB5_A1_OES and GL_RGBA4_OES. + // Implementation is free to use something else though. + // |has_eglimage_texture_2d| should be true iff the display supports + // the EGL_KHR_gl_texture_2D_image extension. + // Returns NULL on failure. + static ColorBuffer* create(EGLDisplay p_display, + int p_width, + int p_height, + GLenum p_internalFormat, + bool has_eglimage_texture_2d, + Helper* helper); + + // Destructor. + ~ColorBuffer(); + + // Return ColorBuffer width and height in pixels + GLuint getWidth() const { return m_width; } + GLuint getHeight() const { return m_height; } + + // Read the ColorBuffer instance's pixel values into host memory. + void readPixels(int x, + int y, + int width, + int height, + GLenum p_format, + GLenum p_type, + void *pixels); + + // Update the ColorBuffer instance's pixel values from host memory. + void subUpdate(int x, + int y, + int width, + int height, + GLenum p_format, + GLenum p_type, + void *pixels); + + // Draw a ColorBuffer instance, i.e. blit it to the current guest + // framebuffer object / window surface. This doesn't display anything. + bool draw(); + + // Post this ColorBuffer to the host native sub-window. + // |rotation| is the rotation angle in degrees, clockwise in the GL + // coordinate space. + bool post(float rotation, float dx, float dy); + + // Bind the current context's EGL_TEXTURE_2D texture to this ColorBuffer's + // EGLImage. This is intended to implement glEGLImageTargetTexture2DOES() + // for all GLES versions. + bool bindToTexture(); + + // Bind the current context's EGL_RENDERBUFFER_OES render buffer to this + // ColorBuffer's EGLImage. This is intended to implement + // glEGLImageTargetRenderbufferStorageOES() for all GLES versions. + bool bindToRenderbuffer(); + + // Copy the content of the current context's read surface to this + // ColorBuffer. This is used from WindowSurface::flushColorBuffer(). + // Return true on success, false on failure (e.g. no current context). + bool blitFromCurrentReadBuffer(); + + // Read the content of the whole ColorBuffer as 32-bit RGBA pixels. + // |img| must be a buffer large enough (i.e. width * height * 4). + void readback(unsigned char* img); + +private: + ColorBuffer(); // no default constructor. + + explicit ColorBuffer(EGLDisplay display, Helper* helper); + +private: + GLuint m_tex; + GLuint m_blitTex; + EGLImageKHR m_eglImage; + EGLImageKHR m_blitEGLImage; + GLuint m_width; + GLuint m_height; + GLuint m_fbo; + GLenum m_internalFormat; + EGLDisplay m_display; + Helper* m_helper; + TextureResize * m_resizer; +}; + +typedef emugl::SmartPtr ColorBufferPtr; + +#endif diff --git a/external/android-emugl/host/libs/libOpenglRender/DispatchTables.h b/external/android-emugl/host/libs/libOpenglRender/DispatchTables.h new file mode 100644 index 0000000..8094285 --- /dev/null +++ b/external/android-emugl/host/libs/libOpenglRender/DispatchTables.h @@ -0,0 +1,22 @@ +/* +* Copyright (C) 2011-2015 The Android Open Source Project +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ +#pragma once + +#include "OpenGLESDispatch/GLESv1Dispatch.h" +#include "OpenGLESDispatch/GLESv2Dispatch.h" + +extern GLESv2Dispatch s_gles2; +extern GLESv1Dispatch s_gles1; diff --git a/external/android-emugl/host/libs/libOpenglRender/FbConfig.cpp b/external/android-emugl/host/libs/libOpenglRender/FbConfig.cpp new file mode 100644 index 0000000..a49a984 --- /dev/null +++ b/external/android-emugl/host/libs/libOpenglRender/FbConfig.cpp @@ -0,0 +1,275 @@ +// Copyright (C) 2015 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "FbConfig.h" + +#include "OpenGLESDispatch/EGLDispatch.h" + +#include +#include + +namespace { + +#define E(...) fprintf(stderr, __VA_ARGS__) + +const GLuint kConfigAttributes[] = { + EGL_DEPTH_SIZE, // must be first - see getDepthSize() + EGL_STENCIL_SIZE, // must be second - see getStencilSize() + EGL_RENDERABLE_TYPE,// must be third - see getRenderableType() + EGL_SURFACE_TYPE, // must be fourth - see getSurfaceType() + EGL_CONFIG_ID, // must be fifth - see chooseConfig() + EGL_BUFFER_SIZE, + EGL_ALPHA_SIZE, + EGL_BLUE_SIZE, + EGL_GREEN_SIZE, + EGL_RED_SIZE, + EGL_CONFIG_CAVEAT, + EGL_LEVEL, + EGL_MAX_PBUFFER_HEIGHT, + EGL_MAX_PBUFFER_PIXELS, + EGL_MAX_PBUFFER_WIDTH, + EGL_NATIVE_RENDERABLE, + EGL_NATIVE_VISUAL_ID, + EGL_NATIVE_VISUAL_TYPE, + EGL_SAMPLES, + EGL_SAMPLE_BUFFERS, + EGL_TRANSPARENT_TYPE, + EGL_TRANSPARENT_BLUE_VALUE, + EGL_TRANSPARENT_GREEN_VALUE, + EGL_TRANSPARENT_RED_VALUE, + EGL_BIND_TO_TEXTURE_RGB, + EGL_BIND_TO_TEXTURE_RGBA, + EGL_MIN_SWAP_INTERVAL, + EGL_MAX_SWAP_INTERVAL, + EGL_LUMINANCE_SIZE, + EGL_ALPHA_MASK_SIZE, + EGL_COLOR_BUFFER_TYPE, + //EGL_MATCH_NATIVE_PIXMAP, + EGL_CONFORMANT +}; + +const size_t kConfigAttributesLen = + sizeof(kConfigAttributes) / sizeof(kConfigAttributes[0]); + +bool isCompatibleHostConfig(EGLConfig config, EGLDisplay display) { + // Filter out configs which do not support pbuffers, since they + // are used to implement window surfaces. + EGLint surfaceType; + s_egl.eglGetConfigAttrib( + display, config, EGL_SURFACE_TYPE, &surfaceType); + if (!(surfaceType & EGL_PBUFFER_BIT)) { + return false; + } + + // Filter out configs that do not support RGB pixel values. + EGLint redSize = 0, greenSize = 0, blueSize = 0, alphaSize = 0; + s_egl.eglGetConfigAttrib( + display, config,EGL_RED_SIZE, &redSize); + s_egl.eglGetConfigAttrib( + display, config, EGL_GREEN_SIZE, &greenSize); + s_egl.eglGetConfigAttrib( + display, config, EGL_BLUE_SIZE, &blueSize); + s_egl.eglGetConfigAttrib( + display, config, EGL_ALPHA_SIZE, &alphaSize); + + if (!redSize || !greenSize || !blueSize || !alphaSize) { + return false; + } + + return true; +} + +} // namespace + +FbConfig::~FbConfig() { + delete [] mAttribValues; +} + +FbConfig::FbConfig(EGLConfig hostConfig, EGLDisplay hostDisplay) : + mEglConfig(hostConfig), mAttribValues(NULL) { + mAttribValues = new GLint[kConfigAttributesLen]; + for (size_t i = 0; i < kConfigAttributesLen; ++i) { + mAttribValues[i] = 0; + s_egl.eglGetConfigAttrib(hostDisplay, + hostConfig, + kConfigAttributes[i], + &mAttribValues[i]); + + // This implementation supports guest window surfaces by wrapping + // them around host Pbuffers, so always report it to the guest. + if (kConfigAttributes[i] == EGL_SURFACE_TYPE) { + mAttribValues[i] |= EGL_WINDOW_BIT; + } + } +} + +FbConfigList::FbConfigList(EGLDisplay display) : + mCount(0), mConfigs(NULL), mDisplay(display) { + if (display == EGL_NO_DISPLAY) { + E("%s: Invalid display value %p (EGL_NO_DISPLAY)\n", + __FUNCTION__, (void*)display); + return; + } + + EGLint numHostConfigs = 0; + if (!s_egl.eglGetConfigs(display, NULL, 0, &numHostConfigs)) { + E("%s: Could not get number of host EGL configs\n", __FUNCTION__); + return; + } + EGLConfig* hostConfigs = new EGLConfig[numHostConfigs]; + s_egl.eglGetConfigs(display, hostConfigs, numHostConfigs, &numHostConfigs); + + mConfigs = new FbConfig*[numHostConfigs]; + for (EGLint i = 0; i < numHostConfigs; ++i) { + // Filter out configs that are not compatible with our implementation. + if (!isCompatibleHostConfig(hostConfigs[i], display)) { + continue; + } + mConfigs[mCount] = new FbConfig(hostConfigs[i], display); + mCount++; + } + + delete [] hostConfigs; +} + +FbConfigList::~FbConfigList() { + for (int n = 0; n < mCount; ++n) { + delete mConfigs[n]; + } + delete [] mConfigs; +} + +int FbConfigList::chooseConfig(const EGLint* attribs, + EGLint* configs, + EGLint configsSize) const { + EGLint numHostConfigs = 0; + if (!s_egl.eglGetConfigs(mDisplay, NULL, 0, &numHostConfigs)) { + E("%s: Could not get number of host EGL configs\n", __FUNCTION__); + return 0; + } + + EGLConfig* matchedConfigs = new EGLConfig[numHostConfigs]; + + // If EGL_SURFACE_TYPE appears in |attribs|, the value passed to + // eglChooseConfig should be forced to EGL_PBUFFER_BIT because that's + // what it used by the current implementation, exclusively. This forces + // the rewrite of |attribs| into a new array. + bool hasSurfaceType = false; + bool mustReplaceSurfaceType = false; + int numAttribs = 0; + while (attribs[numAttribs] != EGL_NONE) { + if (attribs[numAttribs] == EGL_SURFACE_TYPE) { + hasSurfaceType = true; + if (attribs[numAttribs + 1] != EGL_PBUFFER_BIT) { + mustReplaceSurfaceType = true; + } + } + numAttribs += 2; + } + + EGLint* newAttribs = NULL; + + if (mustReplaceSurfaceType) { + // There is at least on EGL_SURFACE_TYPE in |attribs|. Copy the + // array and replace all values with EGL_PBUFFER_BIT + newAttribs = new GLint[numAttribs + 1]; + memcpy(newAttribs, attribs, numAttribs * sizeof(GLint)); + newAttribs[numAttribs] = EGL_NONE; + for (int n = 0; n < numAttribs; n += 2) { + if (newAttribs[n] == EGL_SURFACE_TYPE) { + newAttribs[n + 1] = EGL_PBUFFER_BIT; + } + } + } else if (!hasSurfaceType) { + // There is no EGL_SURFACE_TYPE in |attribs|, then add one entry + // with the value EGL_PBUFFER_BIT. + newAttribs = new GLint[numAttribs + 3]; + memcpy(newAttribs, attribs, numAttribs * sizeof(GLint)); + newAttribs[numAttribs] = EGL_SURFACE_TYPE; + newAttribs[numAttribs + 1] = EGL_PBUFFER_BIT; + newAttribs[numAttribs + 2] = EGL_NONE; + } + + if (!s_egl.eglChooseConfig(mDisplay, + newAttribs ? newAttribs : attribs, + matchedConfigs, + numHostConfigs, + &numHostConfigs)) { + numHostConfigs = 0; + } + + delete [] newAttribs; + + int result = 0; + for (int n = 0; n < numHostConfigs; ++n) { + // Don't count or write more than |configsSize| items if |configs| + // is not NULL. + if (configs && configsSize > 0 && result >= configsSize) { + break; + } + // Skip incompatible host configs. + if (!isCompatibleHostConfig(matchedConfigs[n], mDisplay)) { + continue; + } + // Find the FbConfig with the same EGL_CONFIG_ID + EGLint hostConfigId; + s_egl.eglGetConfigAttrib( + mDisplay, matchedConfigs[n], EGL_CONFIG_ID, &hostConfigId); + for (int k = 0; k < mCount; ++k) { + int guestConfigId = mConfigs[k]->getConfigId(); + if (guestConfigId == hostConfigId) { + // There is a match. Write it to |configs| if it is not NULL. + if (configs && result < configsSize) { + configs[result] = (uint32_t)k; + } + result ++; + break; + } + } + } + + delete [] matchedConfigs; + + return result; +} + + +void FbConfigList::getPackInfo(EGLint* numConfigs, + EGLint* numAttributes) const { + if (numConfigs) { + *numConfigs = mCount; + } + if (numAttributes) { + *numAttributes = static_cast(kConfigAttributesLen); + } +} + +EGLint FbConfigList::packConfigs(GLuint bufferByteSize, GLuint* buffer) const { + GLuint numAttribs = static_cast(kConfigAttributesLen); + GLuint kGLuintSize = static_cast(sizeof(GLuint)); + GLuint neededByteSize = (mCount + 1) * numAttribs * kGLuintSize; + if (!buffer || bufferByteSize < neededByteSize) { + return -neededByteSize; + } + // Write to the buffer the config attribute ids, followed for each one + // of the configs, their values. + memcpy(buffer, kConfigAttributes, kConfigAttributesLen * kGLuintSize); + + for (int i = 0; i < mCount; ++i) { + memcpy(buffer + (i + 1) * kConfigAttributesLen, + mConfigs[i]->mAttribValues, + kConfigAttributesLen * kGLuintSize); + } + return mCount; +} diff --git a/external/android-emugl/host/libs/libOpenglRender/FbConfig.h b/external/android-emugl/host/libs/libOpenglRender/FbConfig.h new file mode 100644 index 0000000..2a653e8 --- /dev/null +++ b/external/android-emugl/host/libs/libOpenglRender/FbConfig.h @@ -0,0 +1,163 @@ +// Copyright (C) 2015 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef _LIBRENDER_FB_CONFIG_H +#define _LIBRENDER_FB_CONFIG_H + +#include +#include + +#include + +// A class used to model a guest EGL config. +// This really wraps a host EGLConfig handle, and provides a few cached +// attributes that can be retrieved through direct accessors, like +// getDepthSize(). +// +// Each FbConfig is identified by a unique id which is its index in +// an FbConfigList instance (as declared below). It is not related to +// the host EGLConfig value or its EGL_CONFIG_ID. +// +// One doesn't create an FbConfig instance. Instead, create and initialize +// an FbConfigList from the host EGLDisplay, and use its size() and get() +// methods to access it. +class FbConfig { +public: + // Destructor + ~FbConfig(); + + // Retrieve host EGLConfig. + EGLConfig getEglConfig() const { return mEglConfig; } + + // Get depth size in bits. + GLuint getDepthSize() const { return getAttribValue(0); } + + // Get stencil size in bits. + GLuint getStencilSize() const { return getAttribValue(1); } + + // Get renderable type mask. + GLuint getRenderableType() const { return getAttribValue(2); } + + // Get surface type mask. + GLuint getSurfaceType() const { return getAttribValue(3); } + + // Get the EGL_CONFIG_ID value. This is the same as the one of the + // underlying host EGLConfig handle. + GLint getConfigId() const { return (GLint)getAttribValue(4); } + +private: + FbConfig(); + FbConfig(FbConfig& other); + + explicit FbConfig(EGLConfig hostConfig, EGLDisplay hostDisplay); + + friend class FbConfigList; + + GLuint getAttribValue(int n) const { + return mAttribValues ? mAttribValues[n] : 0U; + } + + EGLConfig mEglConfig; + GLint* mAttribValues; +}; + +// A class to model the list of FbConfig for a given EGLDisplay, this is +// built from the list of host EGLConfig handles, filtered to only accept +// configs that are useful by the rendering library (e.g. they must support +// PBuffers and RGB pixel values). +// +// Usage is the following: +// +// 1) Create new instance by passing host EGLDisplay value. +// +// 2) Call empty() to check that the list is not empty, which would indicate +// an error during creation. +// +// 3) FbConfig instances are identified by numbers in 0..(N-1) range, where +// N is the result of the size() method. +// +// 4) Convert an FbConfig id into an FbConfig instance with get(). +// +// 5) Use getPackInfo() and packConfigs() to retrieve information about +// available configs to the guest. +class FbConfigList { +public: + // Create a new list of FbConfig instance, by querying all compatible + // host configs from |display|. A compatible config is one that supports + // Pbuffers and RGB pixel values. + // + // After construction, call empty() to check if there are items. + // An empty list means there was an error during construction. + explicit FbConfigList(EGLDisplay display); + + // Destructor. + ~FbConfigList(); + + // Return true iff the list is empty. true means there was an error + // during construction. + bool empty() const { return mCount == 0; } + + // Return the number of FbConfig instances in the list. + // Each instance is identified by a number from 0 to N-1, + // where N is the result of this function. + size_t size() const { return static_cast(mCount); } + + // Retrieve the FbConfig instance associated with |guestId|, + // which must be an integer between 0 and |size() - 1|. Returns + // NULL in case of failure. + const FbConfig* get(int guestId) const { + if (guestId >= 0 && guestId < mCount) { + return mConfigs[guestId]; + } else { + return NULL; + } + } + + // Use |attribs| a list of EGL attribute name/values terminated by + // EGL_NONE, to select a set of matching FbConfig instances. + // + // On success, returns the number of matching instances. + // If |configs| is not NULL, it will be populated with the guest IDs + // of the matched FbConfig instances. + // + // |configsSize| is the number of entries in the |configs| array. The + // function will never write more than |configsSize| entries into + // |configsSize|. + EGLint chooseConfig(const EGLint* attribs, + EGLint* configs, + EGLint configsSize) const; + + // Retrieve information that can be sent to the guest before packed + // config list information. If |numConfigs| is NULL, then |*numConfigs| + // will be set on return to the number of config instances. + // If |numAttribs| is not NULL, then |*numAttribs| will be set on return + // to the number of attribute values cached by each FbConfig instance. + void getPackInfo(EGLint* mumConfigs, EGLint* numAttribs) const; + + // Write the full list information into an array of EGLuint items. + // |buffer| is the output buffer that will receive the data. + // |bufferByteSize| is teh buffer size in bytes. + // On success, this returns + EGLint packConfigs(GLuint bufferByteSize, GLuint* buffer) const; + +private: + FbConfigList(); + FbConfigList(const FbConfigList& other); + + int mCount; + FbConfig** mConfigs; + EGLDisplay mDisplay; +}; + +#endif // _LIBRENDER_FB_CONFIG_H diff --git a/external/android-emugl/host/libs/libOpenglRender/FrameBuffer.cpp b/external/android-emugl/host/libs/libOpenglRender/FrameBuffer.cpp new file mode 100644 index 0000000..a85f0d5 --- /dev/null +++ b/external/android-emugl/host/libs/libOpenglRender/FrameBuffer.cpp @@ -0,0 +1,1166 @@ +/* +* Copyright (C) 2011-2015 The Android Open Source Project +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +#include "FrameBuffer.h" + +#include "DispatchTables.h" +#include "NativeSubWindow.h" +#include "RenderThreadInfo.h" +#include "TimeUtils.h" +#include "gles2_dec.h" + +#include "OpenGLESDispatch/EGLDispatch.h" + +#include "emugl/common/logging.h" + +#include + +#include "mir_support/shared_state.h" + +#define MID_AUBERGINE(x) (x)*0.368627451f, (x)*0.152941176f, (x)*0.31372549f + +namespace { + +// Helper class to call the bind_locked() / unbind_locked() properly. +class ScopedBind { +public: + // Constructor will call bind_locked() on |fb|. + // Use isValid() to check for errors. + ScopedBind(FrameBuffer* fb) : mFb(fb) { + if (!mFb->bind_locked()) { + mFb = NULL; + } + } + + // Returns true if contruction bound the framebuffer context properly. + bool isValid() const { return mFb != NULL; } + + // Unbound the framebuffer explictly. This is also called by the + // destructor. + void release() { + if (mFb) { + mFb->unbind_locked(); + mFb = NULL; + } + } + + // Destructor will call release(). + ~ScopedBind() { + release(); + } + +private: + FrameBuffer* mFb; +}; + +// Implementation of a ColorBuffer::Helper instance that redirects calls +// to a FrameBuffer instance. +class ColorBufferHelper : public ColorBuffer::Helper { +public: + ColorBufferHelper(FrameBuffer* fb) : mFb(fb) {} + + virtual bool setupContext() { + return mFb->bind_locked(); + } + + virtual void teardownContext() { + mFb->unbind_locked(); + } + + virtual TextureDraw* getTextureDraw() const { + return mFb->getTextureDraw(); + } +private: + FrameBuffer* mFb; +}; + +} // namespace + +FrameBuffer *FrameBuffer::s_theFrameBuffer = NULL; +HandleType FrameBuffer::s_nextHandle = 0; + +static char* getGLES1ExtensionString(EGLDisplay p_dpy) +{ + EGLConfig config; + EGLSurface surface; + + static const GLint configAttribs[] = { + EGL_SURFACE_TYPE, EGL_PBUFFER_BIT, + EGL_RENDERABLE_TYPE, EGL_OPENGL_ES_BIT, + EGL_NONE + }; + + int n; + if (!s_egl.eglChooseConfig(p_dpy, configAttribs, + &config, 1, &n) || n == 0) { + ERR("%s: Could not find GLES 1.x config!\n", __FUNCTION__); + return NULL; + } + + DBG("%s: Found config %p\n", __FUNCTION__, (void*)config); + + static const EGLint pbufAttribs[] = { + EGL_WIDTH, 1, + EGL_HEIGHT, 1, + EGL_NONE + }; + + surface = s_egl.eglCreatePbufferSurface(p_dpy, config, pbufAttribs); + if (surface == EGL_NO_SURFACE) { + ERR("%s: Could not create GLES 1.x Pbuffer!\n", __FUNCTION__); + return NULL; + } + + static const GLint gles1ContextAttribs[] = { + EGL_CONTEXT_CLIENT_VERSION, 1, + EGL_NONE + }; + + EGLContext ctx = s_egl.eglCreateContext(p_dpy, config, + EGL_NO_CONTEXT, + gles1ContextAttribs); + if (ctx == EGL_NO_CONTEXT) { + ERR("%s: Could not create GLES 1.x Context!\n", __FUNCTION__); + s_egl.eglDestroySurface(p_dpy, surface); + return NULL; + } + + if (!s_egl.eglMakeCurrent(p_dpy, surface, surface, ctx)) { + ERR("%s: Could not make GLES 1.x context current!\n", __FUNCTION__); + s_egl.eglDestroySurface(p_dpy, surface); + s_egl.eglDestroyContext(p_dpy, ctx); + return NULL; + } + + // the string pointer may become invalid when the context is destroyed + const char* s = (const char*)s_gles1.glGetString(GL_EXTENSIONS); + char* extString = strdup(s ? s : ""); + + s_egl.eglMakeCurrent(p_dpy, NULL, NULL, NULL); + s_egl.eglDestroyContext(p_dpy, ctx); + s_egl.eglDestroySurface(p_dpy, surface); + + return extString; +} + +void FrameBuffer::finalize(){ + m_colorbuffers.clear(); + if (m_useSubWindow) { + removeSubWindow(); + } + m_windows.clear(); + m_contexts.clear(); + s_egl.eglMakeCurrent(m_eglDisplay, NULL, NULL, NULL); + s_egl.eglDestroyContext(m_eglDisplay, m_eglContext); + s_egl.eglDestroyContext(m_eglDisplay, m_pbufContext); + s_egl.eglDestroySurface(m_eglDisplay, m_pbufSurface); +} + +bool FrameBuffer::initialize(int width, int height, bool useSubWindow) +{ + GL_LOG("FrameBuffer::initialize"); + if (s_theFrameBuffer != NULL) { + return true; + } + + // + // allocate space for the FrameBuffer object + // + FrameBuffer *fb = new FrameBuffer(width, height, useSubWindow); + if (!fb) { + ERR("Failed to create fb\n"); + return false; + } + + // + // Initialize backend EGL display + // + mir::support::SharedState::get()->ensure_connection(); + fb->m_eglDisplay = s_egl.eglGetDisplay(mir::support::SharedState::get()->native_display()); + if (fb->m_eglDisplay == EGL_NO_DISPLAY) { + ERR("Failed to Initialize backend EGL display\n"); + delete fb; + return false; + } + + GL_LOG("call eglInitialize"); + if (!s_egl.eglInitialize(fb->m_eglDisplay, + &fb->m_caps.eglMajor, + &fb->m_caps.eglMinor)) { + ERR("Failed to eglInitialize\n"); + GL_LOG("Failed to eglInitialize"); + delete fb; + return false; + } + + DBG("egl: %d %d\n", fb->m_caps.eglMajor, fb->m_caps.eglMinor); + GL_LOG("egl: %d %d", fb->m_caps.eglMajor, fb->m_caps.eglMinor); + s_egl.eglBindAPI(EGL_OPENGL_ES_API); + + // + // if GLES2 plugin has loaded - try to make GLES2 context and + // get GLES2 extension string + // + char* gles1Extensions = NULL; + gles1Extensions = getGLES1ExtensionString(fb->m_eglDisplay); + if (!gles1Extensions) { + // Could not create GLES2 context - drop GL2 capability + ERR("Failed to obtain GLES 2.x extensions string!\n"); + delete fb; + return false; + } + + // + // Create EGL context for framebuffer post rendering. + // + GLint surfaceType = (useSubWindow ? EGL_WINDOW_BIT : 0) | EGL_PBUFFER_BIT; + const GLint configAttribs[] = { + EGL_RED_SIZE, 1, + EGL_GREEN_SIZE, 1, + EGL_BLUE_SIZE, 1, + EGL_SURFACE_TYPE, surfaceType, + EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT, + EGL_NONE + }; + + int n; + if (!s_egl.eglChooseConfig(fb->m_eglDisplay, configAttribs, + &fb->m_eglConfig, 1, &n)) { + ERR("Failed on eglChooseConfig\n"); + free(gles1Extensions); + delete fb; + return false; + } + + static const GLint glContextAttribs[] = { + EGL_CONTEXT_CLIENT_VERSION, 2, + EGL_NONE + }; + + GL_LOG("attempting to create egl context"); + fb->m_eglContext = s_egl.eglCreateContext(fb->m_eglDisplay, + fb->m_eglConfig, + EGL_NO_CONTEXT, + glContextAttribs); + if (fb->m_eglContext == EGL_NO_CONTEXT) { + ERR("Failed to create context 0x%x\n", s_egl.eglGetError()); + free(gles1Extensions); + delete fb; + return false; + } + + GL_LOG("attempting to create egl pbuffer context"); + // + // Create another context which shares with the eglContext to be used + // when we bind the pbuffer. That prevent switching drawable binding + // back and forth on framebuffer context. + // The main purpose of it is to solve a "blanking" behaviour we see on + // on Mac platform when switching binded drawable for a context however + // it is more efficient on other platforms as well. + // + fb->m_pbufContext = s_egl.eglCreateContext(fb->m_eglDisplay, + fb->m_eglConfig, + fb->m_eglContext, + glContextAttribs); + if (fb->m_pbufContext == EGL_NO_CONTEXT) { + ERR("Failed to create Pbuffer Context 0x%x\n", s_egl.eglGetError()); + free(gles1Extensions); + delete fb; + return false; + } + + GL_LOG("context creation successful"); + // + // create a 1x1 pbuffer surface which will be used for binding + // the FB context. + // The FB output will go to a subwindow, if one exist. + // + static const EGLint pbufAttribs[] = { + EGL_WIDTH, 1, + EGL_HEIGHT, 1, + EGL_NONE + }; + + fb->m_pbufSurface = s_egl.eglCreatePbufferSurface(fb->m_eglDisplay, + fb->m_eglConfig, + pbufAttribs); + if (fb->m_pbufSurface == EGL_NO_SURFACE) { + ERR("Failed to create pbuf surface for FB 0x%x\n", s_egl.eglGetError()); + free(gles1Extensions); + delete fb; + return false; + } + + GL_LOG("attempting to make context current"); + // Make the context current + ScopedBind bind(fb); + if (!bind.isValid()) { + ERR("Failed to make current\n"); + free(gles1Extensions); + delete fb; + return false; + } + GL_LOG("context-current successful"); + + // + // Initilize framebuffer capabilities + // + //const char* gles2Extensions = (const char *)s_gles2.glGetString(GL_EXTENSIONS); + bool has_gl_oes_image = false; + +// printf("GLES1 [%s]\n", gles1Extensions); +// printf("GLES2 [%s]\n", gles2Extensions); + + has_gl_oes_image = true; + + if (has_gl_oes_image) { + has_gl_oes_image &= strstr(gles1Extensions, "GL_OES_EGL_image") != NULL; + } + free((void*)gles1Extensions); + gles1Extensions = NULL; + + const char *eglExtensions = s_egl.eglQueryString(fb->m_eglDisplay, + EGL_EXTENSIONS); + + if (eglExtensions && has_gl_oes_image) { + fb->m_caps.has_eglimage_texture_2d = + strstr(eglExtensions, "EGL_KHR_gl_texture_2D_image") != NULL; + fb->m_caps.has_eglimage_renderbuffer = + strstr(eglExtensions, "EGL_KHR_gl_renderbuffer_image") != NULL; + } + else { + fb->m_caps.has_eglimage_texture_2d = false; + fb->m_caps.has_eglimage_renderbuffer = false; + } + + // + // Fail initialization if not all of the following extensions + // exist: + // EGL_KHR_gl_texture_2d_image + // GL_OES_EGL_IMAGE (by both GLES implementations [1 and 2]) + // + if (!fb->m_caps.has_eglimage_texture_2d) { + ERR("Failed: Missing egl_image related extension(s)\n"); + bind.release(); + delete fb; + return false; + } + + GL_LOG("host system has enough extensions"); + // + // Initialize set of configs + // + fb->m_configs = new FbConfigList(fb->m_eglDisplay); + if (fb->m_configs->empty()) { + ERR("Failed: Initialize set of configs\n"); + bind.release(); + delete fb; + return false; + } + + // + // Check that we have config for each GLES and GLES2 + // + size_t nConfigs = fb->m_configs->size(); + int nGLConfigs = 0; + int nGL2Configs = 0; + for (size_t i = 0; i < nConfigs; ++i) { + GLint rtype = fb->m_configs->get(i)->getRenderableType(); + if (0 != (rtype & EGL_OPENGL_ES_BIT)) { + nGLConfigs++; + } + if (0 != (rtype & EGL_OPENGL_ES2_BIT)) { + nGL2Configs++; + } + } + + // + // Fail initialization if no GLES configs exist + // + if (nGLConfigs == 0) { + bind.release(); + delete fb; + return false; + } + + // + // If no GLES2 configs exist - not GLES2 capability + // + if (nGL2Configs == 0) { + ERR("Failed: No GLES 2.x configs found!\n"); + bind.release(); + delete fb; + return false; + } + + GL_LOG("There are sufficient EGLconfigs available"); + + // + // Cache the GL strings so we don't have to think about threading or + // current-context when asked for them. + // + fb->m_glVendor = (const char*)s_gles2.glGetString(GL_VENDOR); + fb->m_glRenderer = (const char*)s_gles2.glGetString(GL_RENDERER); + fb->m_glVersion = (const char*)s_gles2.glGetString(GL_VERSION); + + fb->m_textureDraw = new TextureDraw(fb->m_eglDisplay); + if (!fb->m_textureDraw) { + ERR("Failed: creation of TextureDraw instance\n"); + bind.release(); + delete fb; + return false; + } + + // release the FB context + bind.release(); + + // + // Keep the singleton framebuffer pointer + // + s_theFrameBuffer = fb; + GL_LOG("basic EGL initialization successful"); + return true; +} + +FrameBuffer::FrameBuffer(int p_width, int p_height, bool useSubWindow) : + m_framebufferWidth(p_width), + m_framebufferHeight(p_height), + m_windowWidth(p_width), + m_windowHeight(p_height), + m_useSubWindow(useSubWindow), + m_configs(NULL), + m_eglDisplay(EGL_NO_DISPLAY), + m_colorBufferHelper(new ColorBufferHelper(this)), + m_eglSurface(EGL_NO_SURFACE), + m_eglContext(EGL_NO_CONTEXT), + m_pbufContext(EGL_NO_CONTEXT), + m_prevContext(EGL_NO_CONTEXT), + m_prevReadSurf(EGL_NO_SURFACE), + m_prevDrawSurf(EGL_NO_SURFACE), + m_subWin((EGLNativeWindowType)0), + m_textureDraw(NULL), + m_lastPostedColorBuffer(0), + m_zRot(0.0f), + m_px(0), + m_py(0), + m_eglContextInitialized(false), + m_statsNumFrames(0), + m_statsStartTime(0LL), + m_onPost(NULL), + m_onPostContext(NULL), + m_fbImage(NULL), + m_glVendor(NULL), + m_glRenderer(NULL), + m_glVersion(NULL) +{ + m_fpsStats = getenv("SHOW_FPS_STATS") != NULL; +} + +FrameBuffer::~FrameBuffer() { + delete m_textureDraw; + delete m_configs; + delete m_colorBufferHelper; + free(m_fbImage); +} + +void FrameBuffer::setPostCallback(OnPostFn onPost, void* onPostContext) +{ + emugl::Mutex::AutoLock mutex(m_lock); + m_onPost = onPost; + m_onPostContext = onPostContext; + if (m_onPost && !m_fbImage) { + m_fbImage = (unsigned char*)malloc(4 * m_framebufferWidth * m_framebufferHeight); + if (!m_fbImage) { + ERR("out of memory, cancelling OnPost callback"); + m_onPost = NULL; + m_onPostContext = NULL; + return; + } + } +} + +static void subWindowRepaint(void* param) { + auto fb = static_cast(param); + fb->repost(); +} + +bool FrameBuffer::setupSubWindow(FBNativeWindowType p_window, + int wx, + int wy, + int ww, + int wh, + int fbw, + int fbh, + float dpr, + float zRot) { + bool success = false; + + if (!m_useSubWindow) { + ERR("%s: Cannot create native sub-window in this configuration\n", + __FUNCTION__); + return false; + } + + m_lock.lock(); + + // If the subwindow doesn't exist, create it with the appropriate dimensions + if (!m_subWin) { + + // Create native subwindow for FB display output + m_x = wx; + m_y = wy; + m_windowWidth = ww; + m_windowHeight = wh; + + m_subWin = createSubWindow(p_window, m_x, m_y, + m_windowWidth, m_windowHeight, subWindowRepaint, this); + if (m_subWin) { + m_nativeWindow = p_window; + + // create EGLSurface from the generated subwindow + m_eglSurface = s_egl.eglCreateWindowSurface(m_eglDisplay, + m_eglConfig, + m_subWin, + NULL); + + if (m_eglSurface == EGL_NO_SURFACE) { + // NOTE: This can typically happen with software-only renderers like OSMesa. + destroySubWindow(m_subWin); + m_subWin = (EGLNativeWindowType)0; + } else { + m_px = 0; + m_py = 0; + + success = true; + } + } + } + + // At this point, if the subwindow doesn't exist, it is because it either couldn't be created + // in the first place or the EGLSurface couldn't be created. + if (m_subWin && bindSubwin_locked()) { + + // Only attempt to update window geometry if anything has actually changed. + if (!(m_x == wx && + m_y == wy && + m_windowWidth == ww && + m_windowHeight == wh)) { + + m_x = wx; + m_y = wy; + m_windowWidth = ww; + m_windowHeight = wh; + + success = ::moveSubWindow(m_nativeWindow, m_subWin, + m_x, m_y, m_windowWidth, m_windowHeight); + + // Otherwise, ensure that at least viewport parameters are properly updated. + } else { + success = true; + } + + if (success) { + // Subwin creation or movement was successful, + // update viewport and z rotation and draw + // the last posted color buffer. + s_gles2.glViewport(0, 0, fbw * dpr, fbh * dpr); + m_dpr = dpr; + m_zRot = zRot; + if (m_lastPostedColorBuffer) { + post(m_lastPostedColorBuffer, false); + } else { + s_gles2.glClearColor(MID_AUBERGINE(1.0), 1.0); + s_gles2.glClear(GL_COLOR_BUFFER_BIT | + GL_DEPTH_BUFFER_BIT | + GL_STENCIL_BUFFER_BIT); + s_egl.eglSwapBuffers(m_eglDisplay, m_eglSurface); + } + } + unbind_locked(); + } + + m_lock.unlock(); + return success; +} + +bool FrameBuffer::removeSubWindow() { + if (!m_useSubWindow) { + ERR("%s: Cannot remove native sub-window in this configuration\n", + __FUNCTION__); + return false; + } + bool removed = false; + m_lock.lock(); + if (m_subWin) { + s_egl.eglMakeCurrent(m_eglDisplay, NULL, NULL, NULL); + s_egl.eglDestroySurface(m_eglDisplay, m_eglSurface); + destroySubWindow(m_subWin); + + m_eglSurface = EGL_NO_SURFACE; + m_subWin = (EGLNativeWindowType)0; + removed = true; + } + m_lock.unlock(); + return removed; +} + +HandleType FrameBuffer::genHandle() +{ + HandleType id; + do { + id = ++s_nextHandle; + } while( id == 0 || + m_contexts.find(id) != m_contexts.end() || + m_windows.find(id) != m_windows.end() ); + + return id; +} + +HandleType FrameBuffer::createColorBuffer(int p_width, int p_height, + GLenum p_internalFormat) +{ + emugl::Mutex::AutoLock mutex(m_lock); + HandleType ret = 0; + + ColorBufferPtr cb(ColorBuffer::create( + getDisplay(), + p_width, + p_height, + p_internalFormat, + getCaps().has_eglimage_texture_2d, + m_colorBufferHelper)); + if (cb.Ptr() != NULL) { + ret = genHandle(); + m_colorbuffers[ret].cb = cb; + m_colorbuffers[ret].refcount = 1; + } + return ret; +} + +HandleType FrameBuffer::createRenderContext(int p_config, HandleType p_share, + bool p_isGL2) +{ + emugl::Mutex::AutoLock mutex(m_lock); + HandleType ret = 0; + + const FbConfig* config = getConfigs()->get(p_config); + if (!config) { + return ret; + } + + RenderContextPtr share(NULL); + if (p_share != 0) { + RenderContextMap::iterator s(m_contexts.find(p_share)); + if (s == m_contexts.end()) { + return ret; + } + share = (*s).second; + } + EGLContext sharedContext = + share.Ptr() ? share->getEGLContext() : EGL_NO_CONTEXT; + + RenderContextPtr rctx(RenderContext::create( + m_eglDisplay, config->getEglConfig(), sharedContext, p_isGL2)); + if (rctx.Ptr() != NULL) { + ret = genHandle(); + m_contexts[ret] = rctx; + RenderThreadInfo *tinfo = RenderThreadInfo::get(); + tinfo->m_contextSet.insert(ret); + } + return ret; +} + +HandleType FrameBuffer::createWindowSurface(int p_config, int p_width, int p_height) +{ + emugl::Mutex::AutoLock mutex(m_lock); + + HandleType ret = 0; + + const FbConfig* config = getConfigs()->get(p_config); + if (!config) { + return ret; + } + + WindowSurfacePtr win(WindowSurface::create( + getDisplay(), config->getEglConfig(), p_width, p_height)); + if (win.Ptr() != NULL) { + ret = genHandle(); + m_windows[ret] = std::pair(win,0); + RenderThreadInfo *tinfo = RenderThreadInfo::get(); + tinfo->m_windowSet.insert(ret); + } + + return ret; +} + +void FrameBuffer::drainRenderContext() +{ + emugl::Mutex::AutoLock mutex(m_lock); + RenderThreadInfo *tinfo = RenderThreadInfo::get(); + if (tinfo->m_contextSet.empty()) return; + for (std::set::iterator it = tinfo->m_contextSet.begin(); + it != tinfo->m_contextSet.end(); ++it) { + HandleType contextHandle = *it; + m_contexts.erase(contextHandle); + } + tinfo->m_contextSet.clear(); +} + +void FrameBuffer::drainWindowSurface() +{ + emugl::Mutex::AutoLock mutex(m_lock); + RenderThreadInfo *tinfo = RenderThreadInfo::get(); + if (tinfo->m_windowSet.empty()) return; + for (std::set::iterator it = tinfo->m_windowSet.begin(); + it != tinfo->m_windowSet.end(); ++it) { + HandleType windowHandle = *it; + if (m_windows.find(windowHandle) != m_windows.end()) { + HandleType oldColorBufferHandle = m_windows[windowHandle].second; + if (oldColorBufferHandle) { + ColorBufferMap::iterator cit(m_colorbuffers.find(oldColorBufferHandle)); + if (cit != m_colorbuffers.end()) { + if (--(*cit).second.refcount == 0) { m_colorbuffers.erase(cit); } + } + } + m_windows.erase(windowHandle); + } + } + tinfo->m_windowSet.clear(); +} + +void FrameBuffer::DestroyRenderContext(HandleType p_context) +{ + emugl::Mutex::AutoLock mutex(m_lock); + m_contexts.erase(p_context); + RenderThreadInfo *tinfo = RenderThreadInfo::get(); + if (tinfo->m_contextSet.empty()) return; + tinfo->m_contextSet.erase(p_context); +} + +void FrameBuffer::DestroyWindowSurface(HandleType p_surface) +{ + emugl::Mutex::AutoLock mutex(m_lock); + if (m_windows.find(p_surface) != m_windows.end()) { + m_windows.erase(p_surface); + RenderThreadInfo *tinfo = RenderThreadInfo::get(); + if (tinfo->m_windowSet.empty()) return; + tinfo->m_windowSet.erase(p_surface); + } +} + +int FrameBuffer::openColorBuffer(HandleType p_colorbuffer) +{ + emugl::Mutex::AutoLock mutex(m_lock); + ColorBufferMap::iterator c(m_colorbuffers.find(p_colorbuffer)); + if (c == m_colorbuffers.end()) { + // bad colorbuffer handle + ERR("FB: openColorBuffer cb handle %#x not found\n", p_colorbuffer); + return -1; + } + (*c).second.refcount++; + return 0; +} + +void FrameBuffer::closeColorBuffer(HandleType p_colorbuffer) +{ + emugl::Mutex::AutoLock mutex(m_lock); + ColorBufferMap::iterator c(m_colorbuffers.find(p_colorbuffer)); + if (c == m_colorbuffers.end()) { + // This is harmless: it is normal for guest system to issue + // closeColorBuffer command when the color buffer is already + // garbage collected on the host. (we dont have a mechanism + // to give guest a notice yet) + return; + } + if (--(*c).second.refcount == 0) { + m_colorbuffers.erase(c); + } +} + +bool FrameBuffer::flushWindowSurfaceColorBuffer(HandleType p_surface) +{ + emugl::Mutex::AutoLock mutex(m_lock); + + WindowSurfaceMap::iterator w( m_windows.find(p_surface) ); + if (w == m_windows.end()) { + ERR("FB::flushWindowSurfaceColorBuffer: window handle %#x not found\n", p_surface); + // bad surface handle + return false; + } + + WindowSurface* surface = (*w).second.first.Ptr(); + surface->flushColorBuffer(); + + return true; +} + +bool FrameBuffer::setWindowSurfaceColorBuffer(HandleType p_surface, + HandleType p_colorbuffer) +{ + emugl::Mutex::AutoLock mutex(m_lock); + + WindowSurfaceMap::iterator w( m_windows.find(p_surface) ); + if (w == m_windows.end()) { + // bad surface handle + ERR("%s: bad window surface handle %#x\n", __FUNCTION__, p_surface); + return false; + } + + ColorBufferMap::iterator c( m_colorbuffers.find(p_colorbuffer) ); + if (c == m_colorbuffers.end()) { + DBG("%s: bad color buffer handle %#x\n", __FUNCTION__, p_colorbuffer); + // bad colorbuffer handle + return false; + } + + (*w).second.first->setColorBuffer((*c).second.cb); + (*w).second.second = p_colorbuffer; + return true; +} + +void FrameBuffer::readColorBuffer(HandleType p_colorbuffer, + int x, int y, int width, int height, + GLenum format, GLenum type, void *pixels) +{ + emugl::Mutex::AutoLock mutex(m_lock); + + ColorBufferMap::iterator c( m_colorbuffers.find(p_colorbuffer) ); + if (c == m_colorbuffers.end()) { + // bad colorbuffer handle + return; + } + + (*c).second.cb->readPixels(x, y, width, height, format, type, pixels); +} + +bool FrameBuffer::updateColorBuffer(HandleType p_colorbuffer, + int x, int y, int width, int height, + GLenum format, GLenum type, void *pixels) +{ + emugl::Mutex::AutoLock mutex(m_lock); + + ColorBufferMap::iterator c( m_colorbuffers.find(p_colorbuffer) ); + if (c == m_colorbuffers.end()) { + // bad colorbuffer handle + return false; + } + + (*c).second.cb->subUpdate(x, y, width, height, format, type, pixels); + + return true; +} + +bool FrameBuffer::bindColorBufferToTexture(HandleType p_colorbuffer) +{ + emugl::Mutex::AutoLock mutex(m_lock); + + ColorBufferMap::iterator c( m_colorbuffers.find(p_colorbuffer) ); + if (c == m_colorbuffers.end()) { + // bad colorbuffer handle + return false; + } + + return (*c).second.cb->bindToTexture(); +} + +bool FrameBuffer::bindColorBufferToRenderbuffer(HandleType p_colorbuffer) +{ + emugl::Mutex::AutoLock mutex(m_lock); + + ColorBufferMap::iterator c( m_colorbuffers.find(p_colorbuffer) ); + if (c == m_colorbuffers.end()) { + // bad colorbuffer handle + return false; + } + + return (*c).second.cb->bindToRenderbuffer(); +} + +bool FrameBuffer::bindContext(HandleType p_context, + HandleType p_drawSurface, + HandleType p_readSurface) +{ + emugl::Mutex::AutoLock mutex(m_lock); + + WindowSurfacePtr draw(NULL), read(NULL); + RenderContextPtr ctx(NULL); + + // + // if this is not an unbind operation - make sure all handles are good + // + if (p_context || p_drawSurface || p_readSurface) { + RenderContextMap::iterator r( m_contexts.find(p_context) ); + if (r == m_contexts.end()) { + // bad context handle + return false; + } + + ctx = (*r).second; + WindowSurfaceMap::iterator w( m_windows.find(p_drawSurface) ); + if (w == m_windows.end()) { + // bad surface handle + return false; + } + draw = (*w).second.first; + + if (p_readSurface != p_drawSurface) { + WindowSurfaceMap::iterator w( m_windows.find(p_readSurface) ); + if (w == m_windows.end()) { + // bad surface handle + return false; + } + read = (*w).second.first; + } + else { + read = draw; + } + } + + if (!s_egl.eglMakeCurrent(m_eglDisplay, + draw ? draw->getEGLSurface() : EGL_NO_SURFACE, + read ? read->getEGLSurface() : EGL_NO_SURFACE, + ctx ? ctx->getEGLContext() : EGL_NO_CONTEXT)) { + ERR("eglMakeCurrent failed\n"); + return false; + } + + // + // Bind the surface(s) to the context + // + RenderThreadInfo *tinfo = RenderThreadInfo::get(); + WindowSurfacePtr bindDraw, bindRead; + if (draw.Ptr() == NULL && read.Ptr() == NULL) { + // Unbind the current read and draw surfaces from the context + bindDraw = tinfo->currDrawSurf; + bindRead = tinfo->currReadSurf; + } else { + bindDraw = draw; + bindRead = read; + } + + if (bindDraw.Ptr() != NULL && bindRead.Ptr() != NULL) { + if (bindDraw.Ptr() != bindRead.Ptr()) { + bindDraw->bind(ctx, WindowSurface::BIND_DRAW); + bindRead->bind(ctx, WindowSurface::BIND_READ); + } + else { + bindDraw->bind(ctx, WindowSurface::BIND_READDRAW); + } + } + + // + // update thread info with current bound context + // + tinfo->currContext = ctx; + tinfo->currDrawSurf = draw; + tinfo->currReadSurf = read; + if (ctx) { + if (ctx->isGL2()) tinfo->m_gl2Dec.setContextData(&ctx->decoderContextData()); + else tinfo->m_glDec.setContextData(&ctx->decoderContextData()); + } + else { + tinfo->m_glDec.setContextData(NULL); + tinfo->m_gl2Dec.setContextData(NULL); + } + return true; +} + +HandleType FrameBuffer::createClientImage(HandleType context, EGLenum target, GLuint buffer) +{ + RenderContextPtr ctx(NULL); + + if (context) { + RenderContextMap::iterator r( m_contexts.find(context) ); + if (r == m_contexts.end()) { + // bad context handle + return false; + } + + ctx = (*r).second; + } + + EGLContext eglContext = ctx ? ctx->getEGLContext() : EGL_NO_CONTEXT; + EGLImageKHR image = s_egl.eglCreateImageKHR( + m_eglDisplay, eglContext, target, + reinterpret_cast(buffer), NULL); + + return (HandleType)reinterpret_cast(image); +} + +EGLBoolean FrameBuffer::destroyClientImage(HandleType image) +{ + return s_egl.eglDestroyImageKHR(m_eglDisplay, + reinterpret_cast(image)); +} + +// +// The framebuffer lock should be held when calling this function ! +// +bool FrameBuffer::bind_locked() +{ + EGLContext prevContext = s_egl.eglGetCurrentContext(); + EGLSurface prevReadSurf = s_egl.eglGetCurrentSurface(EGL_READ); + EGLSurface prevDrawSurf = s_egl.eglGetCurrentSurface(EGL_DRAW); + + if (!s_egl.eglMakeCurrent(m_eglDisplay, m_pbufSurface, + m_pbufSurface, m_pbufContext)) { + ERR("eglMakeCurrent failed\n"); + return false; + } + + m_prevContext = prevContext; + m_prevReadSurf = prevReadSurf; + m_prevDrawSurf = prevDrawSurf; + return true; +} + +bool FrameBuffer::bindSubwin_locked() +{ + EGLContext prevContext = s_egl.eglGetCurrentContext(); + EGLSurface prevReadSurf = s_egl.eglGetCurrentSurface(EGL_READ); + EGLSurface prevDrawSurf = s_egl.eglGetCurrentSurface(EGL_DRAW); + + if (!s_egl.eglMakeCurrent(m_eglDisplay, m_eglSurface, + m_eglSurface, m_eglContext)) { + ERR("eglMakeCurrent failed\n"); + return false; + } + + // + // initialize GL state in eglContext if not yet initilaized + // + if (!m_eglContextInitialized) { + m_eglContextInitialized = true; + } + + m_prevContext = prevContext; + m_prevReadSurf = prevReadSurf; + m_prevDrawSurf = prevDrawSurf; + return true; +} + +bool FrameBuffer::unbind_locked() +{ + if (!s_egl.eglMakeCurrent(m_eglDisplay, m_prevDrawSurf, + m_prevReadSurf, m_prevContext)) { + return false; + } + + m_prevContext = EGL_NO_CONTEXT; + m_prevReadSurf = EGL_NO_SURFACE; + m_prevDrawSurf = EGL_NO_SURFACE; + return true; +} + +bool FrameBuffer::post(HandleType p_colorbuffer, bool needLock) +{ + if (needLock) { + m_lock.lock(); + } + bool ret = false; + + ColorBufferMap::iterator c( m_colorbuffers.find(p_colorbuffer) ); + if (c == m_colorbuffers.end()) { + goto EXIT; + } + + m_lastPostedColorBuffer = p_colorbuffer; + + if (m_subWin) { + // bind the subwindow eglSurface + if (!bindSubwin_locked()) { + ERR("FrameBuffer::post(): eglMakeCurrent failed\n"); + goto EXIT; + } + + // get the viewport + GLint vp[4]; + s_gles2.glGetIntegerv(GL_VIEWPORT, vp); + + // divide by device pixel ratio because windowing coordinates ignore DPR, + // but the framebuffer includes DPR + vp[2] = vp[2] / m_dpr; + vp[3] = vp[3] / m_dpr; + + // find the x and y values at the origin when "fully scrolled" + // multiply by 2 because the texture goes from -1 to 1, not 0 to 1 + float fx = 2.f * (vp[2] - m_windowWidth) / (float) vp[2]; + float fy = 2.f * (vp[3] - m_windowHeight) / (float) vp[3]; + + // finally, compute translation values + float dx = m_px * fx; + float dy = m_py * fy; + + // + // render the color buffer to the window + // + if (m_zRot != 0.0f) { + s_gles2.glClear(GL_COLOR_BUFFER_BIT); + } + ret = (*c).second.cb->post(m_zRot, dx, dy); + if (ret) { + s_egl.eglSwapBuffers(m_eglDisplay, m_eglSurface); + } + + // restore previous binding + unbind_locked(); + } else { + // If there is no sub-window, don't display anything, the client will + // rely on m_onPost to get the pixels instead. + ret = true; + } + + // + // output FPS statistics + // + if (m_fpsStats) { + long long currTime = GetCurrentTimeMS(); + m_statsNumFrames++; + if (currTime - m_statsStartTime >= 1000) { + float dt = (float)(currTime - m_statsStartTime) / 1000.0f; + printf("FPS: %5.3f\n", (float)m_statsNumFrames / dt); + m_statsStartTime = currTime; + m_statsNumFrames = 0; + } + } + + // + // Send framebuffer (without FPS overlay) to callback + // + if (m_onPost) { + (*c).second.cb->readback(m_fbImage); + m_onPost(m_onPostContext, + m_framebufferWidth, + m_framebufferHeight, + -1, + GL_RGBA, + GL_UNSIGNED_BYTE, + m_fbImage); + } + +EXIT: + if (needLock) { + m_lock.unlock(); + } + return ret; +} + +bool FrameBuffer::repost() { + if (m_lastPostedColorBuffer) { + return post(m_lastPostedColorBuffer); + } + return false; +} diff --git a/external/android-emugl/host/libs/libOpenglRender/FrameBuffer.h b/external/android-emugl/host/libs/libOpenglRender/FrameBuffer.h new file mode 100644 index 0000000..77a2705 --- /dev/null +++ b/external/android-emugl/host/libs/libOpenglRender/FrameBuffer.h @@ -0,0 +1,360 @@ +/* +* Copyright (C) 2011-2015 The Android Open Source Project +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ +#ifndef _LIBRENDER_FRAMEBUFFER_H +#define _LIBRENDER_FRAMEBUFFER_H + +#include "ColorBuffer.h" +#include "emugl/common/mutex.h" +#include "FbConfig.h" +#include "RenderContext.h" +#include "TextureDraw.h" +#include "WindowSurface.h" + +#include "OpenglRender/render_api.h" + +#include + +#include + +#include + +// Type of handles, a.k.a. "object names" in the GL specification. +// These are integers used to uniquely identify a resource of a given type. +typedef uint32_t HandleType; + +struct ColorBufferRef { + ColorBufferPtr cb; + uint32_t refcount; // number of client-side references +}; +typedef std::map RenderContextMap; +typedef std::map > WindowSurfaceMap; +typedef std::map ColorBufferMap; + +// A structure used to list the capabilities of the underlying EGL +// implementation that the FrameBuffer instance depends on. +// |has_eglimage_texture_2d| is true iff the EGL_KHR_gl_texture_2D_image +// extension is supported. +// |has_eglimage_renderbuffer| is true iff the EGL_KHR_gl_renderbuffer_image +// extension is supported. +// |eglMajor| and |eglMinor| are the major and minor version numbers of +// the underlying EGL implementation. +struct FrameBufferCaps { + bool has_eglimage_texture_2d; + bool has_eglimage_renderbuffer; + EGLint eglMajor; + EGLint eglMinor; +}; + +// The FrameBuffer class holds the global state of the emulation library on +// top of the underlying EGL/GLES implementation. It should probably be +// named "Display" instead of "FrameBuffer". +// +// There is only one global instance, that can be retrieved with getFB(), +// and which must be previously setup by calling initialize(). +// +class FrameBuffer { +public: + // Initialize the global instance. + // |width| and |height| are the dimensions of the emulator GPU display + // in pixels. |useSubWindow| is true to indicate that the caller + // will use setupSubWindow() to let EmuGL display the GPU content in its + // own sub-windows. If false, this means the caller will use + // setPostCallback() instead to retrieve the content. + // Returns true on success, false otherwise. + static bool initialize(int width, int height, bool useSubWindow); + + // Setup a sub-window to display the content of the emulated GPU + // on-top of an existing UI window. |p_window| is the platform-specific + // parent window handle. |wx|, |wy|, |ww| and |wh| are the + // dimensions in pixels of the sub-window, relative to the parent window's + // coordinate. |fbw| and |fbh| are the dimensions used to initialize + // the framebuffer, which may be different from the dimensions of the + // sub-window (in which case scaling will be applied automatically). + // |dpr| is the device pixel ratio of the monitor, which is needed for + // proper panning on high-density displays (like retina) + // |zRot| is a rotation angle in degrees, (clockwise in the Y-upwards GL + // coordinate space). + // + // If a sub-window already exists, this function updates the subwindow + // and framebuffer properties to match the given values. + // + // Return true on success, false otherwise. + // + // NOTE: This can return false for software-only EGL engines like OSMesa. + bool setupSubWindow(FBNativeWindowType p_window, + int wx, int wy, + int ww, int wh, + int fbw, int fbh, float dpr, float zRot); + + // Remove the sub-window created by setupSubWindow(), if any. + // Return true on success, false otherwise. + bool removeSubWindow(); + + // Finalize the instance. + void finalize(); + + // Return a pointer to the global instance. initialize() must be called + // previously, or this will return NULL. + static FrameBuffer *getFB() { return s_theFrameBuffer; } + + // Return the capabilities of the underlying display. + const FrameBufferCaps &getCaps() const { return m_caps; } + + // Return the emulated GPU display width in pixels. + int getWidth() const { return m_framebufferWidth; } + + // Return the emulated GPU display height in pixels. + int getHeight() const { return m_framebufferHeight; } + + // Return the list of configs available from this display. + const FbConfigList* getConfigs() const { return m_configs; } + + // Set a callback that will be called each time the emulated GPU content + // is updated. This can be relatively slow with host-based GPU emulation, + // so only do this when you need to. + void setPostCallback(OnPostFn onPost, void* onPostContext); + + // Retrieve the GL strings of the underlying EGL/GLES implementation. + // On return, |*vendor|, |*renderer| and |*version| will point to strings + // that are owned by the instance (and must not be freed by the caller). + void getGLStrings(const char** vendor, + const char** renderer, + const char** version) const { + *vendor = m_glVendor; + *renderer = m_glRenderer; + *version = m_glVersion; + } + + // Create a new RenderContext instance for this display instance. + // |p_config| is the index of one of the configs returned by getConfigs(). + // |p_share| is either EGL_NO_CONTEXT or the handle of a shared context. + // |p_isGL2| is true to create a GLES 2.x context, or false for a GLES 1.x + // one. + // Return a new handle value, which will be 0 in case of error. + HandleType createRenderContext( + int p_config, HandleType p_share, bool p_isGL2 = false); + + // Create a new WindowSurface instance from this display instance. + // |p_config| is the index of one of the configs returned by getConfigs(). + // |p_width| and |p_height| are the window dimensions in pixels. + // Return a new handle value, or 0 in case of error. + HandleType createWindowSurface(int p_config, int p_width, int p_height); + + // Create a new ColorBuffer instance from this display instance. + // |p_width| and |p_height| are its dimensions in pixels. + // |p_internalFormat| is the pixel format. See ColorBuffer::create() for + // list of valid values. Note that ColorBuffer instances are reference- + // counted. Use openColorBuffer / closeColorBuffer to operate on the + // internal count. + HandleType createColorBuffer( + int p_width, int p_height, GLenum p_internalFormat); + + // Call this function when a render thread terminates to destroy all + // the remaining contexts it created. Necessary to avoid leaking host + // contexts when a guest application crashes, for example. + void drainRenderContext(); + + // Call this function when a render thread terminates to destroy all + // remaining window surfqce it created. Necessary to avoid leaking + // host buffers when a guest application crashes, for example. + void drainWindowSurface(); + + // Destroy a given RenderContext instance. |p_context| is its handle + // value as returned by createRenderContext(). + void DestroyRenderContext(HandleType p_context); + + // Destroy a given WindowSurface instance. |p_surcace| is its handle + // value as returned by createWindowSurface(). + void DestroyWindowSurface(HandleType p_surface); + + // Increment the reference count associated with a given ColorBuffer + // instance. |p_colorbuffer| is its handle value as returned by + // createColorBuffer(). + int openColorBuffer(HandleType p_colorbuffer); + + // Decrement the reference count associated with a given ColorBuffer + // instance. |p_colorbuffer| is its handle value as returned by + // createColorBuffer(). Note that if the reference count reaches 0, + // the instance is destroyed automatically. + void closeColorBuffer(HandleType p_colorbuffer); + + // Equivalent for eglMakeCurrent() for the current display. + // |p_context|, |p_drawSurface| and |p_readSurface| are the handle values + // of the context, the draw surface and the read surface, respectively. + // Returns true on success, false on failure. + // Note: if all handle values are 0, this is an unbind operation. + bool bindContext(HandleType p_context, + HandleType p_drawSurface, + HandleType p_readSurface); + + // Attach a ColorBuffer to a WindowSurface instance. + // See the documentation for WindowSurface::setColorBuffer(). + // |p_surface| is the target WindowSurface's handle value. + // |p_colorbuffer| is the ColorBuffer handle value. + // Returns true on success, false otherwise. + bool setWindowSurfaceColorBuffer( + HandleType p_surface, HandleType p_colorbuffer); + + // Copy the content of a WindowSurface's Pbuffer to its attached + // ColorBuffer. See the documentation for WindowSurface::flushColorBuffer() + // |p_surface| is the target WindowSurface's handle value. + // Returns true on success, false on failure. + bool flushWindowSurfaceColorBuffer(HandleType p_surface); + + // Bind the current context's EGL_TEXTURE_2D texture to a ColorBuffer + // instance's EGLImage. This is intended to implement + // glEGLImageTargetTexture2DOES() for all GLES versions. + // |p_colorbuffer| is the ColorBuffer's handle value. + // Returns true on success, false on failure. + bool bindColorBufferToTexture(HandleType p_colorbuffer); + + // Bind the current context's EGL_RENDERBUFFER_OES render buffer to this + // ColorBuffer's EGLImage. This is intended to implement + // glEGLImageTargetRenderbufferStorageOES() for all GLES versions. + // |p_colorbuffer| is the ColorBuffer's handle value. + // Returns true on success, false on failure. + bool bindColorBufferToRenderbuffer(HandleType p_colorbuffer); + + // Read the content of a given ColorBuffer into client memory. + // |p_colorbuffer| is the ColorBuffer's handle value. Similar + // to glReadPixels(), this can be a slow operation. + // |x|, |y|, |width| and |height| are the position and dimensions of + // a rectangle whose pixel values will be transfered to the host. + // |format| indicates the format of the pixel data, e.g. GL_RGB or GL_RGBA. + // |type| is the type of pixel data, e.g. GL_UNSIGNED_BYTE. + // |pixels| is the address of a caller-provided buffer that will be filled + // with the pixel data. + void readColorBuffer(HandleType p_colorbuffer, + int x, int y, int width, int height, + GLenum format, GLenum type, void *pixels); + + // Update the content of a given ColorBuffer from client data. + // |p_colorbuffer| is the ColorBuffer's handle value. Similar + // to glReadPixels(), this can be a slow operation. + // |x|, |y|, |width| and |height| are the position and dimensions of + // a rectangle whose pixel values will be transfered to the GPU + // |format| indicates the format of the pixel data, e.g. GL_RGB or GL_RGBA. + // |type| is the type of pixel data, e.g. GL_UNSIGNED_BYTE. + // |pixels| is the address of a buffer containing the new pixel data. + // Returns true on success, false otherwise. + bool updateColorBuffer(HandleType p_colorbuffer, + int x, int y, int width, int height, + GLenum format, GLenum type, void *pixels); + + // Display the content of a given ColorBuffer into the framebuffer's + // sub-window. |p_colorbuffer| is a handle value. + // |needLock| is used to indicate whether the operation requires + // acquiring/releasing the FrameBuffer instance's lock. It should be + // false only when called internally. + bool post(HandleType p_colorbuffer, bool needLock = true); + + // Re-post the last ColorBuffer that was displayed through post(). + // This is useful if you detect that the sub-window content needs to + // be re-displayed for any reason. + bool repost(); + + // Return the host EGLDisplay used by this instance. + EGLDisplay getDisplay() const { return m_eglDisplay; } + + // Change the rotation of the displayed GPU sub-window. + void setDisplayRotation(float zRot) { + m_zRot = zRot; + repost(); + } + + // Changes what coordinate of this framebuffer will be displayed at the + // corner of the GPU sub-window. Specifically, |px| and |py| = 0 means + // align the bottom-left of the framebuffer with the bottom-left of the + // sub-window, and |px| and |py| = 1 means align the top right of the + // framebuffer with the top right of the sub-window. Intermediate values + // interpolate between these states. + void setDisplayTranslation(float px, float py) { + + // Sanity check the values to ensure they are between 0 and 1 + m_px = px > 1 ? 1 : (px < 0 ? 0 : px); + m_py = py > 1 ? 1 : (py < 0 ? 0 : py); + repost(); + } + + // Return a TextureDraw instance that can be used with this surfaces + // and windows created by this instance. + TextureDraw* getTextureDraw() const { return m_textureDraw; } + + HandleType createClientImage(HandleType context, EGLenum target, GLuint buffer); + EGLBoolean destroyClientImage(HandleType image); + + // Used internally. + bool bind_locked(); + bool unbind_locked(); + +private: + FrameBuffer(int p_width, int p_height, bool useSubWindow); + ~FrameBuffer(); + HandleType genHandle(); + + bool bindSubwin_locked(); + +private: + static FrameBuffer *s_theFrameBuffer; + static HandleType s_nextHandle; + int m_x; + int m_y; + int m_framebufferWidth; + int m_framebufferHeight; + int m_windowWidth; + int m_windowHeight; + float m_dpr; + bool m_useSubWindow; + emugl::Mutex m_lock; + FbConfigList* m_configs; + FBNativeWindowType m_nativeWindow; + FrameBufferCaps m_caps; + EGLDisplay m_eglDisplay; + RenderContextMap m_contexts; + WindowSurfaceMap m_windows; + ColorBufferMap m_colorbuffers; + ColorBuffer::Helper* m_colorBufferHelper; + + EGLSurface m_eglSurface; + EGLContext m_eglContext; + EGLSurface m_pbufSurface; + EGLContext m_pbufContext; + + EGLContext m_prevContext; + EGLSurface m_prevReadSurf; + EGLSurface m_prevDrawSurf; + EGLNativeWindowType m_subWin; + TextureDraw* m_textureDraw; + EGLConfig m_eglConfig; + HandleType m_lastPostedColorBuffer; + float m_zRot; + float m_px; + float m_py; + bool m_eglContextInitialized; + + int m_statsNumFrames; + long long m_statsStartTime; + bool m_fpsStats; + + OnPostFn m_onPost; + void* m_onPostContext; + unsigned char* m_fbImage; + + const char* m_glVendor; + const char* m_glRenderer; + const char* m_glVersion; +}; +#endif diff --git a/external/android-emugl/host/libs/libOpenglRender/NativeSubWindow.h b/external/android-emugl/host/libs/libOpenglRender/NativeSubWindow.h new file mode 100644 index 0000000..2c1402a --- /dev/null +++ b/external/android-emugl/host/libs/libOpenglRender/NativeSubWindow.h @@ -0,0 +1,70 @@ +/* +* Copyright (C) 2011 The Android Open Source Project +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ +#ifndef NATIVE_SUB_WINDOW_H +#define NATIVE_SUB_WINDOW_H + +#include "OpenglRender/render_api_platform_types.h" + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef void (*SubWindowRepaintCallback)(void*); + +// Create a new sub-window that will be used to display the content of the +// emulated GPU on top of the regular UI window. +// |p_window| is the platform-specific handle to the main UI window. +// |x|, |y| is the position sub-window relative to the top-left corner of the +// main window. +// |width| and |height| are the dimensions of the sub-window, as well as of +// the emulated framebuffer. +// |repaint_callback| may be invoked every time the window has to be repainted +// (such as receiving a WM_PAINT event on Windows). If the provided argument is +// NULL, nothing will be invoked. +// |repaint_callback_param| an additional parameter that will be passed to the +// repaint callback when/if it's invoked. +// On success, return a new platform-specific window handle, cast as an +// EGLNativeWindowType. Or 0/NULL in case of failure. +EGLNativeWindowType createSubWindow(FBNativeWindowType p_window, + int x, + int y, + int width, + int height, + SubWindowRepaintCallback repaint_callback, + void* repaint_callback_param); + +// Destroy a sub-window previously created through createSubWindow() above. +void destroySubWindow(EGLNativeWindowType win); + +// Moves a sub-window previously created through createSubWindow() above. +// |p_parent_window| is the platform-specific handle to the main UI window. +// |p_sub_window| is the platform-specific handle to the EGL subwindow. +// |x|,|y|,|width|,|height| are the new location and dimensions of the +// subwindow. +int moveSubWindow(FBNativeWindowType p_parent_window, + EGLNativeWindowType p_sub_window, + int x, + int y, + int width, + int height); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/external/android-emugl/host/libs/libOpenglRender/NativeSubWindow_cocoa.m b/external/android-emugl/host/libs/libOpenglRender/NativeSubWindow_cocoa.m new file mode 100644 index 0000000..54674b5 --- /dev/null +++ b/external/android-emugl/host/libs/libOpenglRender/NativeSubWindow_cocoa.m @@ -0,0 +1,101 @@ +/* +* Copyright (C) 2011 The Android Open Source Project +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ +#include "NativeSubWindow.h" +#include + +/* + * EmuGLView inherit from NSView and override the isOpaque + * method to return YES. That prevents drawing of underlying window/view + * when the view needs to be redrawn. + */ +@interface EmuGLView : NSView { +} @end + +@implementation EmuGLView + + - (BOOL)isOpaque { + return YES; + } + +@end + + +EGLNativeWindowType createSubWindow(FBNativeWindowType p_window, + int x, + int y, + int width, + int height, + SubWindowRepaintCallback repaint_callback, + void* repaint_callback_param) { + NSWindow* win = (NSWindow *)p_window; + if (!win) { + return NULL; + } + + /* (x,y) assume an upper-left origin, but Cocoa uses a lower-left origin */ + NSRect content_rect = [win contentRectForFrameRect:[win frame]]; + int cocoa_y = (int)content_rect.size.height - (y + height); + NSRect contentRect = NSMakeRect(x, cocoa_y, width, height); + + NSView *glView = [[EmuGLView alloc] initWithFrame:contentRect]; + if (glView) { + [glView setWantsBestResolutionOpenGLSurface:YES]; + [[win contentView] addSubview:glView]; + [win makeKeyAndOrderFront:nil]; + } + + return (EGLNativeWindowType)glView; +} + +void destroySubWindow(EGLNativeWindowType win) { + if(win){ + NSView *glView = (NSView *)win; + [glView removeFromSuperview]; + [glView release]; + } +} + +int moveSubWindow(FBNativeWindowType p_parent_window, + EGLNativeWindowType p_sub_window, + int x, + int y, + int width, + int height) { + NSWindow *win = (NSWindow *)p_parent_window; + if (!win) { + return 0; + } + + NSView *glView = (NSView *)p_sub_window; + if (!glView) { + return 0; + } + + /* The view must be removed from the hierarchy to be properly resized */ + [glView removeFromSuperview]; + + /* (x,y) assume an upper-left origin, but Cocoa uses a lower-left origin */ + NSRect content_rect = [win contentRectForFrameRect:[win frame]]; + int cocoa_y = (int)content_rect.size.height - (y + height); + NSRect newFrame = NSMakeRect(x, cocoa_y, width, height); + [glView setFrame:newFrame]; + + /* Re-add the sub-window to the view hierarchy */ + [[win contentView] addSubview:glView]; + [win makeKeyAndOrderFront:nil]; + + return 1; +} diff --git a/external/android-emugl/host/libs/libOpenglRender/NativeSubWindow_mir.cpp b/external/android-emugl/host/libs/libOpenglRender/NativeSubWindow_mir.cpp new file mode 100644 index 0000000..1bbf0b3 --- /dev/null +++ b/external/android-emugl/host/libs/libOpenglRender/NativeSubWindow_mir.cpp @@ -0,0 +1,201 @@ +/* +* Copyright (C) 2011 The Android Open Source Project +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +#define MIR_EGL_PLATFORM + +#include "mir_support/shared_state.h" + +#include "NativeSubWindow.h" + +#include "OpenglCodecCommon/ErrorLog.h" + +#include +#include +#include + +#include + +namespace { +std::map surfaces; + +static const MirDisplayOutput *find_active_output( + const MirDisplayConfiguration *conf) +{ + const MirDisplayOutput *output = NULL; + int d; + + for (d = 0; d < (int)conf->num_outputs; d++) + { + const MirDisplayOutput *out = conf->outputs + d; + + if (out->used && + out->connected && + out->num_modes && + out->current_mode < out->num_modes) + { + output = out; + break; + } + } + + return output; +} +} + +MirPixelFormat defaultPixelFormatFor(MirConnection *connection) +{ + MirPixelFormat format; + unsigned int nformats; + mir_connection_get_available_surface_formats(connection, &format, 1, &nformats); + return format; +} + +void handleSurfaceEvent(const MirSurfaceEvent *event) { + MirSurfaceAttrib attrib = mir_surface_event_get_attribute(event); + if (attrib != mir_surface_attrib_visibility) + return; + switch (mir_surface_event_get_attribute(event)) { + case mir_surface_visibility_exposed: + ERR("Surface exposed\n"); + break; + case mir_surface_visibility_occluded: + ERR("Surface occluded\n"); + break; + default: + break; + } +} + +void surfaceEventHandler(MirSurface *surface, MirEvent const *event, void *context) { + switch (mir_event_get_type(event)) { + case mir_event_type_input: + break; + case mir_event_type_surface: + handleSurfaceEvent(mir_event_get_surface_event(event)); + break; + case mir_event_type_resize: + { + MirResizeEvent const *resize_event = mir_event_get_resize_event(event); + ERR("Resized to %dx%d\n", + mir_resize_event_get_width(resize_event), + mir_resize_event_get_height(resize_event)); + } + break; + case mir_event_type_close_surface: + break; + case mir_event_type_surface_output: + { + MirSurfaceOutputEvent const *output_event = mir_event_get_surface_output_event(event); + ERR("dpi %i output id %d\n", + mir_surface_output_event_get_dpi(output_event), + mir_surface_output_event_get_output_id(output_event)); + } + break; + default: + break; + } +} + +EGLNativeWindowType createSubWindow(FBNativeWindowType p_window, + int x, + int y, + int width, + int height, + SubWindowRepaintCallback repaint_callback, + void* repaint_callback_param) { + + (void) p_window; + (void) x; + (void) y; + (void) width; + (void) height; + (void) repaint_callback; + (void) repaint_callback_param; + + mir::support::SharedState::get()->ensure_connection(); + + const auto pixel_format = defaultPixelFormatFor(mir::support::SharedState::get()->connection()); + + ERR("Selected pixel format %d\n", pixel_format); + + MirDisplayConfiguration* display_config = + mir_connection_create_display_config(mir::support::SharedState::get()->connection()); + + const MirDisplayOutput *output = find_active_output(display_config); + if (!output) + throw new std::runtime_error("Failed to find active output display"); + + const MirDisplayMode *mode = &output->modes[output->current_mode]; + + auto spec = mir_connection_create_spec_for_normal_surface( + mir::support::SharedState::get()->connection(), + mode->vertical_resolution, + mode->horizontal_resolution, + pixel_format); + + mir_surface_spec_set_name(spec, "anbox"); + mir_surface_spec_set_event_handler(spec, surfaceEventHandler, nullptr); + ERR("Selecting output id %d", output->output_id); + mir_surface_spec_set_fullscreen_on_output(spec, output->output_id); + mir_surface_spec_set_buffer_usage(spec, mir_buffer_usage_hardware); + + mir_display_config_destroy(display_config); + + auto surface = mir_surface_create_sync(spec); + mir_surface_spec_release(spec); + + if (!mir_surface_is_valid(surface)) { + ERR("surface error: %s\n", mir_surface_get_error_message(surface)); + throw std::runtime_error("Failed to create a Mir surface"); + } + + MirSurfaceParameters parameters; + mir_surface_get_parameters(surface, ¶meters); + ERR("width %i height %i output id %i\n", + parameters.width, + parameters.height, + parameters.output_id); + + auto surface_buffer_stream = mir_surface_get_buffer_stream(surface); + auto native_window = reinterpret_cast( + mir_buffer_stream_get_egl_native_window(surface_buffer_stream)); + + surfaces.insert({ native_window, surface }); + + return native_window; +} + +void destroySubWindow(EGLNativeWindowType win) { + if (surfaces.find(win) == surfaces.end()) + return; + + auto surface = surfaces[win]; + surfaces.erase(win); + + mir_surface_release_sync(surface); +} + +int moveSubWindow(FBNativeWindowType p_parent_window, + EGLNativeWindowType p_sub_window, + int x, + int y, + int width, + int height) { + + ERR("%s: Not implemented", __func__); + + return true; +} diff --git a/external/android-emugl/host/libs/libOpenglRender/NativeSubWindow_null.cpp b/external/android-emugl/host/libs/libOpenglRender/NativeSubWindow_null.cpp new file mode 100644 index 0000000..46bee10 --- /dev/null +++ b/external/android-emugl/host/libs/libOpenglRender/NativeSubWindow_null.cpp @@ -0,0 +1,39 @@ +/* +* Copyright (C) 2011 The Android Open Source Project +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +#include "NativeSubWindow.h" + +EGLNativeWindowType createSubWindow(FBNativeWindowType p_window, + int x, + int y, + int width, + int height, + SubWindowRepaintCallback repaint_callback, + void* repaint_callback_param) { + return NULL; +} + +void destroySubWindow(EGLNativeWindowType win) { +} + +int moveSubWindow(FBNativeWindowType p_parent_window, + EGLNativeWindowType p_sub_window, + int x, + int y, + int width, + int height) { + return false; +} diff --git a/external/android-emugl/host/libs/libOpenglRender/NativeSubWindow_win32.cpp b/external/android-emugl/host/libs/libOpenglRender/NativeSubWindow_win32.cpp new file mode 100644 index 0000000..344459a --- /dev/null +++ b/external/android-emugl/host/libs/libOpenglRender/NativeSubWindow_win32.cpp @@ -0,0 +1,90 @@ +/* +* Copyright (C) 2011 The Android Open Source Project +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ +#include "NativeSubWindow.h" + +struct SubWindowUserData { + SubWindowRepaintCallback repaint_callback; + void* repaint_callback_param; +}; + +static LRESULT CALLBACK subWindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { + if (uMsg == WM_PAINT) { + auto user_data = + (SubWindowUserData*)GetWindowLongPtr(hwnd, GWLP_USERDATA); + if (user_data && user_data->repaint_callback) { + user_data->repaint_callback(user_data->repaint_callback_param); + } + } else if (uMsg == WM_NCDESTROY) { + SubWindowUserData* user_data = + (SubWindowUserData*)GetWindowLongPtr(hwnd, GWLP_USERDATA); + delete user_data; + } + return DefWindowProc(hwnd, uMsg, wParam, lParam); +} + +EGLNativeWindowType createSubWindow(FBNativeWindowType p_window, + int x, int y,int width, int height, + SubWindowRepaintCallback repaint_callback, + void* repaint_callback_param){ + static const char className[] = "subWin"; + + WNDCLASS wc = {}; + if (!GetClassInfo(GetModuleHandle(NULL), className, &wc)) { + wc.style = CS_OWNDC | CS_HREDRAW | CS_VREDRAW;// redraw if size changes + wc.lpfnWndProc = &subWindowProc; // points to window procedure + wc.cbWndExtra = sizeof(void*) ; // save extra window memory + wc.lpszClassName = className; // name of window class + RegisterClass(&wc); + } + + EGLNativeWindowType ret = CreateWindowEx( + WS_EX_NOPARENTNOTIFY, // do not bother our parent window + className, + "sub", + WS_CHILD|WS_DISABLED, + x,y,width,height, + p_window, + NULL, + NULL, + NULL); + + auto user_data = new SubWindowUserData(); + user_data->repaint_callback = repaint_callback; + user_data->repaint_callback_param = repaint_callback_param; + + SetWindowLongPtr(ret, GWLP_USERDATA, (LONG_PTR)user_data); + ShowWindow(ret, SW_SHOW); + return ret; +} + +void destroySubWindow(EGLNativeWindowType win){ + PostMessage(win, WM_CLOSE, 0, 0); +} + +int moveSubWindow(FBNativeWindowType p_parent_window, + EGLNativeWindowType p_sub_window, + int x, + int y, + int width, + int height) { + BOOL ret = MoveWindow(p_sub_window, + x, + y, + width, + height, + TRUE); + return ret; +} diff --git a/external/android-emugl/host/libs/libOpenglRender/NativeSubWindow_x11.cpp b/external/android-emugl/host/libs/libOpenglRender/NativeSubWindow_x11.cpp new file mode 100644 index 0000000..be2d4d3 --- /dev/null +++ b/external/android-emugl/host/libs/libOpenglRender/NativeSubWindow_x11.cpp @@ -0,0 +1,105 @@ +/* +* Copyright (C) 2011 The Android Open Source Project +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ +#include "NativeSubWindow.h" + +#include + +static Bool WaitForMapNotify(Display *d, XEvent *e, char *arg) { + if (e->type == MapNotify && e->xmap.window == (Window)arg) { + return 1; + } + return 0; +} + +static Bool WaitForConfigureNotify(Display *d, XEvent *e, char *arg) { + if (e->type == ConfigureNotify && e->xmap.window == (Window)arg) { + return 1; + } + return 0; +} + +static Display *s_display = NULL; + +EGLNativeWindowType createSubWindow(FBNativeWindowType p_window, + int x, + int y, + int width, + int height, + SubWindowRepaintCallback repaint_callback, + void* repaint_callback_param) { + // The call to this function is protected by a lock + // in FrameBuffer so it is safe to check and initialize s_display here + if (!s_display) { + s_display = XOpenDisplay(NULL); + } + + XSetWindowAttributes wa; + wa.event_mask = StructureNotifyMask; + wa.override_redirect = True; + Window win = XCreateWindow(s_display, + p_window, + x, + y, + width, + height, + 0, + CopyFromParent, + CopyFromParent, + CopyFromParent, + CWEventMask, + &wa); + XMapWindow(s_display,win); + XSetWindowBackground(s_display, win, BlackPixel(s_display, 0)); + XEvent e; + XIfEvent(s_display, &e, WaitForMapNotify, (char *)win); + return win; +} + +void destroySubWindow(EGLNativeWindowType win) { + if (!s_display) { + return; + } + XDestroyWindow(s_display, win); +} + +int moveSubWindow(FBNativeWindowType p_parent_window, + EGLNativeWindowType p_sub_window, + int x, + int y, + int width, + int height) { + // This value is set during create, so if it is still null, simply + // return because the global state is corrupted + if (!s_display) { + return false; + } + + // This prevents flicker on resize. + XSetWindowBackgroundPixmap(s_display, p_sub_window, None); + + int ret = XMoveResizeWindow( + s_display, + p_sub_window, + x, + y, + width, + height); + + XEvent e; + XIfEvent(s_display, &e, WaitForConfigureNotify, (char *)p_sub_window); + + return ret; +} diff --git a/external/android-emugl/host/libs/libOpenglRender/ReadBuffer.cpp b/external/android-emugl/host/libs/libOpenglRender/ReadBuffer.cpp new file mode 100644 index 0000000..5db9948 --- /dev/null +++ b/external/android-emugl/host/libs/libOpenglRender/ReadBuffer.cpp @@ -0,0 +1,74 @@ +/* +* Copyright (C) 2011 The Android Open Source Project +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ +#include "ReadBuffer.h" +#include +#include +#include +#include "ErrorLog.h" + +ReadBuffer::ReadBuffer(size_t bufsize) +{ + m_size = bufsize; + m_buf = (unsigned char*)malloc(m_size*sizeof(unsigned char)); + m_validData = 0; + m_readPtr = m_buf; +} + +ReadBuffer::~ReadBuffer() +{ + free(m_buf); +} + +int ReadBuffer::getData(IOStream *stream) +{ + if(stream == NULL) + return -1; + if ((m_validData > 0) && (m_readPtr > m_buf)) { + memmove(m_buf, m_readPtr, m_validData); + } + // get fresh data into the buffer; + size_t len = m_size - m_validData; + if (len==0) { + //we need to inc our buffer + size_t new_size = m_size*2; + unsigned char* new_buf; + if (new_size < m_size) { // overflow check + new_size = INT_MAX; + } + + new_buf = (unsigned char*)realloc(m_buf, new_size); + if (!new_buf) { + ERR("Failed to alloc %zu bytes for ReadBuffer\n", new_size); + return -1; + } + m_size = new_size; + m_buf = new_buf; + len = m_size - m_validData; + } + m_readPtr = m_buf; + if (NULL != stream->read(m_buf + m_validData, &len)) { + m_validData += len; + return len; + } + return -1; +} + +void ReadBuffer::consume(size_t amount) +{ + assert(amount <= m_validData); + m_validData -= amount; + m_readPtr += amount; +} diff --git a/external/android-emugl/host/libs/libOpenglRender/ReadBuffer.h b/external/android-emugl/host/libs/libOpenglRender/ReadBuffer.h new file mode 100644 index 0000000..484b6c1 --- /dev/null +++ b/external/android-emugl/host/libs/libOpenglRender/ReadBuffer.h @@ -0,0 +1,35 @@ +/* +* Copyright (C) 2011 The Android Open Source Project +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ +#ifndef _READ_BUFFER_H +#define _READ_BUFFER_H + +#include "IOStream.h" + +class ReadBuffer { +public: + ReadBuffer(size_t bufSize); + ~ReadBuffer(); + int getData(IOStream *stream); // get fresh data from the stream + unsigned char *buf() { return m_readPtr; } // return the next read location + size_t validData() { return m_validData; } // return the amount of valid data in readptr + void consume(size_t amount); // notify that 'amount' data has been consumed; +private: + unsigned char *m_buf; + unsigned char *m_readPtr; + size_t m_size; + size_t m_validData; +}; +#endif diff --git a/external/android-emugl/host/libs/libOpenglRender/RenderContext.cpp b/external/android-emugl/host/libs/libOpenglRender/RenderContext.cpp new file mode 100644 index 0000000..59faa4b --- /dev/null +++ b/external/android-emugl/host/libs/libOpenglRender/RenderContext.cpp @@ -0,0 +1,49 @@ +/* +* Copyright (C) 2011 The Android Open Source Project +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ +#include "RenderContext.h" + +#include "OpenGLESDispatch/EGLDispatch.h" + +RenderContext* RenderContext::create(EGLDisplay display, + EGLConfig config, + EGLContext sharedContext, + bool isGl2) { + const EGLint contextAttribs[] = { + EGL_CONTEXT_CLIENT_VERSION, isGl2 ? 2 : 1, + EGL_NONE + }; + EGLContext context = s_egl.eglCreateContext( + display, config, sharedContext, contextAttribs); + if (context == EGL_NO_CONTEXT) { + return NULL; + } + + return new RenderContext(display, context, isGl2); +} + +RenderContext::RenderContext(EGLDisplay display, + EGLContext context, + bool isGl2) : + mDisplay(display), + mContext(context), + mIsGl2(isGl2), + mContextData() {} + +RenderContext::~RenderContext() { + if (mContext != EGL_NO_CONTEXT) { + s_egl.eglDestroyContext(mDisplay, mContext); + } +} diff --git a/external/android-emugl/host/libs/libOpenglRender/RenderContext.h b/external/android-emugl/host/libs/libOpenglRender/RenderContext.h new file mode 100644 index 0000000..4182996 --- /dev/null +++ b/external/android-emugl/host/libs/libOpenglRender/RenderContext.h @@ -0,0 +1,69 @@ +/* +* Copyright (C) 2011 The Android Open Source Project +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ +#ifndef _LIBRENDER_RENDER_CONTEXT_H +#define _LIBRENDER_RENDER_CONTEXT_H + +#include "emugl/common/smart_ptr.h" +#include "GLDecoderContextData.h" + +#include + +// A class used to model a guest EGLContext. This simply wraps a host +// EGLContext, associated with an GLDecoderContextData instance that is +// used to store copies of guest-side arrays. +class RenderContext { +public: + // Create a new RenderContext instance. + // |display| is the host EGLDisplay handle. + // |config| is the host EGLConfig to use. + // |sharedContext| is either EGL_NO_CONTEXT of a host EGLContext handle. + // |isGl2| is true iff the new context will be used with GLESv2, or + // GLESv1 otherwise. + static RenderContext *create(EGLDisplay display, + EGLConfig config, + EGLContext sharedContext, + bool isGL2 = false); + + // Destructor. + ~RenderContext(); + + // Retrieve host EGLContext value. + EGLContext getEGLContext() const { return mContext; } + + // Return true iff this is a GLESv2 context. + bool isGL2() const { return mIsGl2; } + + // Retrieve GLDecoderContextData instance reference for this + // RenderContext instance. + GLDecoderContextData& decoderContextData() { return mContextData; } + +private: + RenderContext(); + + RenderContext(EGLDisplay display, + EGLContext context, + bool isGl2); + +private: + EGLDisplay mDisplay; + EGLContext mContext; + bool mIsGl2; + GLDecoderContextData mContextData; +}; + +typedef emugl::SmartPtr RenderContextPtr; + +#endif // _LIBRENDER_RENDER_CONTEXT_H diff --git a/external/android-emugl/host/libs/libOpenglRender/RenderControl.cpp b/external/android-emugl/host/libs/libOpenglRender/RenderControl.cpp new file mode 100644 index 0000000..c9d6953 --- /dev/null +++ b/external/android-emugl/host/libs/libOpenglRender/RenderControl.cpp @@ -0,0 +1,417 @@ +/* +* Copyright (C) 2011 The Android Open Source Project +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ +#include "RenderControl.h" + +#include "DispatchTables.h" +#include "FbConfig.h" +#include "FrameBuffer.h" +#include "RenderThreadInfo.h" +#include "ChecksumCalculatorThreadInfo.h" + +#include "OpenGLESDispatch/EGLDispatch.h" + +static const GLint rendererVersion = 1; + +static GLint rcGetRendererVersion() +{ + return rendererVersion; +} + +static EGLint rcGetEGLVersion(EGLint* major, EGLint* minor) +{ + FrameBuffer *fb = FrameBuffer::getFB(); + if (!fb) { + return EGL_FALSE; + } + *major = (EGLint)fb->getCaps().eglMajor; + *minor = (EGLint)fb->getCaps().eglMinor; + + return EGL_TRUE; +} + +static EGLint rcQueryEGLString(EGLenum name, void* buffer, EGLint bufferSize) +{ + FrameBuffer *fb = FrameBuffer::getFB(); + if (!fb) { + return 0; + } + + const char *str = s_egl.eglQueryString(fb->getDisplay(), name); + if (!str) { + return 0; + } + + int len = strlen(str) + 1; + if (!buffer || len > bufferSize) { + return -len; + } + + strcpy((char *)buffer, str); + return len; +} + +static EGLint rcGetGLString(EGLenum name, void* buffer, EGLint bufferSize) +{ + RenderThreadInfo *tInfo = RenderThreadInfo::get(); + const char *str = NULL; + int len = 0; + if (tInfo && tInfo->currContext.Ptr()) { + if (tInfo->currContext->isGL2()) { + str = (const char *)s_gles2.glGetString(name); + } + else { + str = (const char *)s_gles1.glGetString(name); + } + if (str) { + len = strlen(str) + 1; + } + } + + // We add the maximum supported GL protocol number into GL_EXTENSIONS + const char* glProtocolStr = NULL; + if (name == GL_EXTENSIONS) { + glProtocolStr = ChecksumCalculatorThreadInfo::getMaxVersionString(); + if (len==0) len = 1; // the last byte + len += strlen(glProtocolStr) + 1; + } + + if (!buffer || len > bufferSize) { + return -len; + } + + if (name == GL_EXTENSIONS) { + snprintf((char *)buffer, bufferSize, "%s%s ", str ? str : "", glProtocolStr); + } else if (str) { + strcpy((char *)buffer, str); + } else { + if (bufferSize >= 1) { + ((char*)buffer)[0] = '\0'; + } + len = 0; + } + return len; +} + +static EGLint rcGetNumConfigs(uint32_t* p_numAttribs) +{ + int numConfigs = 0, numAttribs = 0; + + FrameBuffer::getFB()->getConfigs()->getPackInfo(&numConfigs, &numAttribs); + if (p_numAttribs) { + *p_numAttribs = static_cast(numAttribs); + } + return numConfigs; +} + +static EGLint rcGetConfigs(uint32_t bufSize, GLuint* buffer) +{ + GLuint bufferSize = (GLuint)bufSize; + return FrameBuffer::getFB()->getConfigs()->packConfigs(bufferSize, buffer); +} + +static EGLint rcChooseConfig(EGLint *attribs, + uint32_t attribs_size, + uint32_t *configs, + uint32_t configs_size) +{ + FrameBuffer *fb = FrameBuffer::getFB(); + if (!fb || attribs_size==0) { + return 0; + } + + return fb->getConfigs()->chooseConfig( + attribs, (EGLint*)configs, (EGLint)configs_size); +} + +static EGLint rcGetFBParam(EGLint param) +{ + FrameBuffer *fb = FrameBuffer::getFB(); + if (!fb) { + return 0; + } + + EGLint ret = 0; + + switch(param) { + case FB_WIDTH: + ret = fb->getWidth(); + break; + case FB_HEIGHT: + ret = fb->getHeight(); + break; + case FB_XDPI: + ret = 72; // XXX: should be implemented + break; + case FB_YDPI: + ret = 72; // XXX: should be implemented + break; + case FB_FPS: + ret = 60; + break; + case FB_MIN_SWAP_INTERVAL: + ret = 1; // XXX: should be implemented + break; + case FB_MAX_SWAP_INTERVAL: + ret = 1; // XXX: should be implemented + break; + default: + break; + } + + return ret; +} + +static uint32_t rcCreateContext(uint32_t config, + uint32_t share, uint32_t glVersion) +{ + FrameBuffer *fb = FrameBuffer::getFB(); + if (!fb) { + return 0; + } + + // To make it consistent with the guest, create GLES2 context when GL + // version==2 or 3 + HandleType ret = fb->createRenderContext(config, share, glVersion == 2 || glVersion == 3); + return ret; +} + +static void rcDestroyContext(uint32_t context) +{ + FrameBuffer *fb = FrameBuffer::getFB(); + if (!fb) { + return; + } + + fb->DestroyRenderContext(context); +} + +static uint32_t rcCreateWindowSurface(uint32_t config, + uint32_t width, uint32_t height) +{ + FrameBuffer *fb = FrameBuffer::getFB(); + if (!fb) { + return 0; + } + + return fb->createWindowSurface(config, width, height); +} + +static void rcDestroyWindowSurface(uint32_t windowSurface) +{ + FrameBuffer *fb = FrameBuffer::getFB(); + if (!fb) { + return; + } + + fb->DestroyWindowSurface( windowSurface ); +} + +static uint32_t rcCreateColorBuffer(uint32_t width, + uint32_t height, GLenum internalFormat) +{ + FrameBuffer *fb = FrameBuffer::getFB(); + if (!fb) { + return 0; + } + + return fb->createColorBuffer(width, height, internalFormat); +} + +static int rcOpenColorBuffer2(uint32_t colorbuffer) +{ + FrameBuffer *fb = FrameBuffer::getFB(); + if (!fb) { + return -1; + } + return fb->openColorBuffer( colorbuffer ); +} + +// Deprecated, kept for compatibility with old system images only. +// Use rcOpenColorBuffer2 instead. +static void rcOpenColorBuffer(uint32_t colorbuffer) +{ + (void) rcOpenColorBuffer2(colorbuffer); +} + +static void rcCloseColorBuffer(uint32_t colorbuffer) +{ + FrameBuffer *fb = FrameBuffer::getFB(); + if (!fb) { + return; + } + fb->closeColorBuffer( colorbuffer ); +} + +static int rcFlushWindowColorBuffer(uint32_t windowSurface) +{ + FrameBuffer *fb = FrameBuffer::getFB(); + if (!fb) { + return -1; + } + if (!fb->flushWindowSurfaceColorBuffer(windowSurface)) { + return -1; + } + return 0; +} + +static void rcSetWindowColorBuffer(uint32_t windowSurface, + uint32_t colorBuffer) +{ + FrameBuffer *fb = FrameBuffer::getFB(); + if (!fb) { + return; + } + fb->setWindowSurfaceColorBuffer(windowSurface, colorBuffer); +} + +static EGLint rcMakeCurrent(uint32_t context, + uint32_t drawSurf, uint32_t readSurf) +{ + FrameBuffer *fb = FrameBuffer::getFB(); + if (!fb) { + return EGL_FALSE; + } + + bool ret = fb->bindContext(context, drawSurf, readSurf); + + return (ret ? EGL_TRUE : EGL_FALSE); +} + +static void rcFBPost(uint32_t colorBuffer) +{ + FrameBuffer *fb = FrameBuffer::getFB(); + if (!fb) { + return; + } + + fb->post(colorBuffer); +} + +static void rcFBSetSwapInterval(EGLint interval) +{ + // XXX: TBD - should be implemented +} + +static void rcBindTexture(uint32_t colorBuffer) +{ + FrameBuffer *fb = FrameBuffer::getFB(); + if (!fb) { + return; + } + + fb->bindColorBufferToTexture(colorBuffer); +} + +static void rcBindRenderbuffer(uint32_t colorBuffer) +{ + FrameBuffer *fb = FrameBuffer::getFB(); + if (!fb) { + return; + } + + fb->bindColorBufferToRenderbuffer(colorBuffer); +} + +static EGLint rcColorBufferCacheFlush(uint32_t colorBuffer, + EGLint postCount, int forRead) +{ + // XXX: TBD - should be implemented + return 0; +} + +static void rcReadColorBuffer(uint32_t colorBuffer, + GLint x, GLint y, + GLint width, GLint height, + GLenum format, GLenum type, void* pixels) +{ + FrameBuffer *fb = FrameBuffer::getFB(); + if (!fb) { + return; + } + + fb->readColorBuffer(colorBuffer, x, y, width, height, format, type, pixels); +} + +static int rcUpdateColorBuffer(uint32_t colorBuffer, + GLint x, GLint y, + GLint width, GLint height, + GLenum format, GLenum type, void* pixels) +{ + FrameBuffer *fb = FrameBuffer::getFB(); + if (!fb) { + return -1; + } + + fb->updateColorBuffer(colorBuffer, x, y, width, height, format, type, pixels); + return 0; +} + +static uint32_t rcCreateClientImage(uint32_t context, EGLenum target, GLuint buffer) +{ + FrameBuffer *fb = FrameBuffer::getFB(); + if (!fb) { + return 0; + } + + return fb->createClientImage(context, target, buffer); +} + +static int rcDestroyClientImage(uint32_t image) +{ + FrameBuffer *fb = FrameBuffer::getFB(); + if (!fb) { + return 0; + } + + return fb->destroyClientImage(image); +} + +static void rcSelectChecksumCalculator(uint32_t protocol, uint32_t reserved) { + ChecksumCalculatorThreadInfo::setVersion(protocol); +} + +void initRenderControlContext(renderControl_decoder_context_t *dec) +{ + dec->rcGetRendererVersion = rcGetRendererVersion; + dec->rcGetEGLVersion = rcGetEGLVersion; + dec->rcQueryEGLString = rcQueryEGLString; + dec->rcGetGLString = rcGetGLString; + dec->rcGetNumConfigs = rcGetNumConfigs; + dec->rcGetConfigs = rcGetConfigs; + dec->rcChooseConfig = rcChooseConfig; + dec->rcGetFBParam = rcGetFBParam; + dec->rcCreateContext = rcCreateContext; + dec->rcDestroyContext = rcDestroyContext; + dec->rcCreateWindowSurface = rcCreateWindowSurface; + dec->rcDestroyWindowSurface = rcDestroyWindowSurface; + dec->rcCreateColorBuffer = rcCreateColorBuffer; + dec->rcOpenColorBuffer = rcOpenColorBuffer; + dec->rcCloseColorBuffer = rcCloseColorBuffer; + dec->rcSetWindowColorBuffer = rcSetWindowColorBuffer; + dec->rcFlushWindowColorBuffer = rcFlushWindowColorBuffer; + dec->rcMakeCurrent = rcMakeCurrent; + dec->rcFBPost = rcFBPost; + dec->rcFBSetSwapInterval = rcFBSetSwapInterval; + dec->rcBindTexture = rcBindTexture; + dec->rcBindRenderbuffer = rcBindRenderbuffer; + dec->rcColorBufferCacheFlush = rcColorBufferCacheFlush; + dec->rcReadColorBuffer = rcReadColorBuffer; + dec->rcUpdateColorBuffer = rcUpdateColorBuffer; + dec->rcOpenColorBuffer2 = rcOpenColorBuffer2; + dec->rcCreateClientImage = rcCreateClientImage; + dec->rcDestroyClientImage = rcDestroyClientImage; + dec->rcSelectChecksumCalculator = rcSelectChecksumCalculator; +} diff --git a/external/android-emugl/host/libs/libOpenglRender/RenderControl.h b/external/android-emugl/host/libs/libOpenglRender/RenderControl.h new file mode 100644 index 0000000..87fe1e0 --- /dev/null +++ b/external/android-emugl/host/libs/libOpenglRender/RenderControl.h @@ -0,0 +1,23 @@ +/* +* Copyright (C) 2011 The Android Open Source Project +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ +#ifndef _RENDER_CONTROL_H +#define _RENDER_CONTROL_H + +#include "renderControl_dec.h" + +void initRenderControlContext(renderControl_decoder_context_t *dec); + +#endif diff --git a/external/android-emugl/host/libs/libOpenglRender/RenderServer.cpp b/external/android-emugl/host/libs/libOpenglRender/RenderServer.cpp new file mode 100644 index 0000000..bb09a40 --- /dev/null +++ b/external/android-emugl/host/libs/libOpenglRender/RenderServer.cpp @@ -0,0 +1,170 @@ +/* +* Copyright (C) 2011 The Android Open Source Project +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ +#include "RenderServer.h" + +#include "RenderThread.h" +#include "TcpStream.h" +#ifndef _WIN32 +#include "UnixStream.h" +#include +#include +#endif +#ifdef _WIN32 +#include "Win32PipeStream.h" +#endif + +#include "OpenglRender/render_api.h" + +#include + +#include + +typedef std::set RenderThreadsSet; + +RenderServer::RenderServer() : + m_lock(), + m_listenSock(NULL), + m_exiting(false) +{ +} + +RenderServer::~RenderServer() +{ + delete m_listenSock; +} + + +extern "C" int gRendererStreamMode; + +RenderServer *RenderServer::create(char* addr, size_t addrLen) +{ + RenderServer *server = new RenderServer(); + if (!server) { + return NULL; + } + + if (gRendererStreamMode == RENDER_API_STREAM_MODE_TCP) { + server->m_listenSock = new TcpStream(); + } else { +#ifdef _WIN32 + server->m_listenSock = new Win32PipeStream(); +#else + server->m_listenSock = new UnixStream(); +#endif + } + + char addrstr[SocketStream::MAX_ADDRSTR_LEN]; + if (server->m_listenSock->listen(addrstr) < 0) { + ERR("RenderServer::create failed to listen\n"); + delete server; + return NULL; + } + + size_t len = strlen(addrstr) + 1; + if (len > addrLen) { + ERR("RenderServer address name too big for provided buffer: %zu > %zu\n", + len, addrLen); + delete server; + return NULL; + } + memcpy(addr, addrstr, len); + + return server; +} + +intptr_t RenderServer::main() +{ + RenderThreadsSet threads; + +#ifndef _WIN32 + sigset_t set; + sigfillset(&set); + pthread_sigmask(SIG_SETMASK, &set, NULL); +#endif + + while(1) { + SocketStream *stream = m_listenSock->accept(); + if (!stream) { + fprintf(stderr,"Error accepting gles connection, ignoring.\n"); + continue; + } + + unsigned int clientFlags; + if (!stream->readFully(&clientFlags, sizeof(unsigned int))) { + fprintf(stderr,"Error reading clientFlags\n"); + delete stream; + continue; + } + + DBG("RenderServer: Got new stream!\n"); + + // check if we have been requested to exit while waiting on accept + if ((clientFlags & IOSTREAM_CLIENT_EXIT_SERVER) != 0) { + m_exiting = true; + delete stream; + break; + } + + RenderThread *rt = RenderThread::create(stream, &m_lock); + if (!rt) { + fprintf(stderr,"Failed to create RenderThread\n"); + delete stream; + } else if (!rt->start()) { + fprintf(stderr,"Failed to start RenderThread\n"); + delete rt; + delete stream; + } + + // + // remove from the threads list threads which are + // no longer running + // + for (RenderThreadsSet::iterator n,t = threads.begin(); + t != threads.end(); + t = n) { + // first find next iterator + n = t; + n++; + + // delete and erase the current iterator + // if thread is no longer running + if ((*t)->isFinished()) { + delete (*t); + threads.erase(t); + } + } + + // if the thread has been created and started, insert it to the list + if (rt) { + threads.insert(rt); + DBG("Started new RenderThread\n"); + } + } + + // + // Wait for all threads to finish + // + for (RenderThreadsSet::iterator t = threads.begin(); + t != threads.end(); + t++) { + (*t)->forceStop(); + (*t)->wait(NULL); + delete (*t); + } + threads.clear(); + + return 0; +} diff --git a/external/android-emugl/host/libs/libOpenglRender/RenderServer.h b/external/android-emugl/host/libs/libOpenglRender/RenderServer.h new file mode 100644 index 0000000..8be8a17 --- /dev/null +++ b/external/android-emugl/host/libs/libOpenglRender/RenderServer.h @@ -0,0 +1,42 @@ +/* +* Copyright (C) 2011 The Android Open Source Project +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ +#ifndef _LIB_OPENGL_RENDER_RENDER_SERVER_H +#define _LIB_OPENGL_RENDER_RENDER_SERVER_H + +#include "SocketStream.h" +#include "emugl/common/mutex.h" +#include "emugl/common/thread.h" + +class RenderServer : public emugl::Thread +{ +public: + static RenderServer *create(char* addr, size_t addrLen); + virtual ~RenderServer(); + + virtual intptr_t main(); + + bool isExiting() const { return m_exiting; } + +private: + RenderServer(); + +private: + emugl::Mutex m_lock; + SocketStream *m_listenSock; + bool m_exiting; +}; + +#endif diff --git a/external/android-emugl/host/libs/libOpenglRender/RenderThread.cpp b/external/android-emugl/host/libs/libOpenglRender/RenderThread.cpp new file mode 100644 index 0000000..b1f0bbd --- /dev/null +++ b/external/android-emugl/host/libs/libOpenglRender/RenderThread.cpp @@ -0,0 +1,165 @@ +/* +* Copyright (C) 2011 The Android Open Source Project +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ +#include "RenderThread.h" + +#include "FrameBuffer.h" +#include "ReadBuffer.h" +#include "RenderControl.h" +#include "RenderThreadInfo.h" +#include "TimeUtils.h" + +#include "OpenGLESDispatch/EGLDispatch.h" +#include "OpenGLESDispatch/GLESv2Dispatch.h" +#include "OpenGLESDispatch/GLESv1Dispatch.h" +#include "../../../shared/OpenglCodecCommon/ChecksumCalculatorThreadInfo.h" + +#define STREAM_BUFFER_SIZE 4*1024*1024 + +RenderThread::RenderThread(IOStream *stream, emugl::Mutex *lock) : + emugl::Thread(), + m_lock(lock), + m_stream(stream) {} + +RenderThread::~RenderThread() { + delete m_stream; +} + +// static +RenderThread* RenderThread::create(IOStream *stream, emugl::Mutex *lock) { + return new RenderThread(stream, lock); +} + +void RenderThread::forceStop() { + m_stream->forceStop(); +} + +intptr_t RenderThread::main() { + RenderThreadInfo tInfo; + ChecksumCalculatorThreadInfo tChecksumInfo; + + // + // initialize decoders + // + tInfo.m_glDec.initGL(gles1_dispatch_get_proc_func, NULL); + tInfo.m_gl2Dec.initGL(gles2_dispatch_get_proc_func, NULL); + initRenderControlContext(&tInfo.m_rcDec); + + ReadBuffer readBuf(STREAM_BUFFER_SIZE); + + int stats_totalBytes = 0; + long long stats_t0 = GetCurrentTimeMS(); + + // + // open dump file if RENDER_DUMP_DIR is defined + // + const char *dump_dir = getenv("RENDERER_DUMP_DIR"); + FILE *dumpFP = NULL; + if (dump_dir) { + size_t bsize = strlen(dump_dir) + 32; + char *fname = new char[bsize]; + snprintf(fname,bsize,"%s/stream_%p", dump_dir, this); + dumpFP = fopen(fname, "wb"); + if (!dumpFP) { + fprintf(stderr,"Warning: stream dump failed to open file %s\n",fname); + } + delete [] fname; + } + + while (1) { + + int stat = readBuf.getData(m_stream); + if (stat <= 0) { + break; + } + + // + // log received bandwidth statistics + // + stats_totalBytes += readBuf.validData(); + long long dt = GetCurrentTimeMS() - stats_t0; + if (dt > 1000) { + float dts = (float)dt / 1000.0f; + printf("Used Bandwidth %5.3f MB/s\n", ((float)stats_totalBytes / dts) / (1024.0f*1024.0f)); + stats_totalBytes = 0; + stats_t0 = GetCurrentTimeMS(); + } + + // + // dump stream to file if needed + // + if (dumpFP) { + int skip = readBuf.validData() - stat; + fwrite(readBuf.buf()+skip, 1, readBuf.validData()-skip, dumpFP); + fflush(dumpFP); + } + + bool progress; + do { + progress = false; + + m_lock->lock(); + // + // try to process some of the command buffer using the GLESv1 decoder + // + size_t last = tInfo.m_glDec.decode(readBuf.buf(), readBuf.validData(), m_stream); + if (last > 0) { + progress = true; + readBuf.consume(last); + } + + // + // try to process some of the command buffer using the GLESv2 decoder + // + last = tInfo.m_gl2Dec.decode(readBuf.buf(), readBuf.validData(), m_stream); + if (last > 0) { + progress = true; + readBuf.consume(last); + } + + // + // try to process some of the command buffer using the + // renderControl decoder + // + last = tInfo.m_rcDec.decode(readBuf.buf(), readBuf.validData(), m_stream); + if (last > 0) { + readBuf.consume(last); + progress = true; + } + + m_lock->unlock(); + + } while( progress ); + + } + + if (dumpFP) { + fclose(dumpFP); + } + + // + // Release references to the current thread's context/surfaces if any + // + FrameBuffer::getFB()->bindContext(0, 0, 0); + if (tInfo.currContext || tInfo.currDrawSurf || tInfo.currReadSurf) { + fprintf(stderr, "ERROR: RenderThread exiting with current context/surfaces\n"); + } + + FrameBuffer::getFB()->drainWindowSurface(); + + FrameBuffer::getFB()->drainRenderContext(); + + return 0; +} diff --git a/external/android-emugl/host/libs/libOpenglRender/RenderThread.h b/external/android-emugl/host/libs/libOpenglRender/RenderThread.h new file mode 100644 index 0000000..7487764 --- /dev/null +++ b/external/android-emugl/host/libs/libOpenglRender/RenderThread.h @@ -0,0 +1,58 @@ +/* +* Copyright (C) 2011 The Android Open Source Project +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ +#ifndef _LIB_OPENGL_RENDER_RENDER_THREAD_H +#define _LIB_OPENGL_RENDER_RENDER_THREAD_H + +#include "IOStream.h" + +#include "emugl/common/mutex.h" +#include "emugl/common/thread.h" + +// A class used to model a thread of the RenderServer. Each one of them +// handles a single guest client / protocol byte stream. +class RenderThread : public emugl::Thread { +public: + // Create a new RenderThread instance. + // |stream| is an input stream that will be read from the thread, + // and deleted by it when it exits. + // |mutex| is a pointer to a shared mutex used to serialize + // decoding operations between all threads. + // TODO(digit): Why is this needed here? Shouldn't this be handled + // by the decoders themselves or at a lower-level? + static RenderThread* create(IOStream* stream, emugl::Mutex* mutex); + + // Destructor. + virtual ~RenderThread(); + + // Returns true iff the thread has finished. + // Note that this also means that the thread's stack has been + bool isFinished() { return tryWait(NULL); } + + // Force a thread to stop. + void forceStop(); + +private: + RenderThread(); // No default constructor + + RenderThread(IOStream* stream, emugl::Mutex* mutex); + + virtual intptr_t main(); + + emugl::Mutex* m_lock; + IOStream* m_stream; +}; + +#endif diff --git a/external/android-emugl/host/libs/libOpenglRender/RenderThreadInfo.cpp b/external/android-emugl/host/libs/libOpenglRender/RenderThreadInfo.cpp new file mode 100644 index 0000000..e88069d --- /dev/null +++ b/external/android-emugl/host/libs/libOpenglRender/RenderThreadInfo.cpp @@ -0,0 +1,43 @@ +/* +* Copyright (C) 2011 The Android Open Source Project +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +#include "RenderThreadInfo.h" + +#include "emugl/common/lazy_instance.h" +#include "emugl/common/thread_store.h" + +namespace { + +class ThreadInfoStore : public ::emugl::ThreadStore { +public: + ThreadInfoStore() : ::emugl::ThreadStore(NULL) {} +}; + +} // namespace + +static ::emugl::LazyInstance s_tls = LAZY_INSTANCE_INIT; + +RenderThreadInfo::RenderThreadInfo() { + s_tls->set(this); +} + +RenderThreadInfo::~RenderThreadInfo() { + s_tls->set(NULL); +} + +RenderThreadInfo* RenderThreadInfo::get() { + return static_cast(s_tls->get()); +} diff --git a/external/android-emugl/host/libs/libOpenglRender/RenderThreadInfo.h b/external/android-emugl/host/libs/libOpenglRender/RenderThreadInfo.h new file mode 100644 index 0000000..fd15433 --- /dev/null +++ b/external/android-emugl/host/libs/libOpenglRender/RenderThreadInfo.h @@ -0,0 +1,60 @@ +/* +* Copyright (C) 2011 The Android Open Source Project +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ +#ifndef _LIB_OPENGL_RENDER_THREAD_INFO_H +#define _LIB_OPENGL_RENDER_THREAD_INFO_H + +#include "RenderContext.h" +#include "WindowSurface.h" +#include "GLESv1Decoder.h" +#include "GLESv2Decoder.h" +#include "renderControl_dec.h" + +#include + +typedef uint32_t HandleType; +typedef std::set ThreadContextSet; +typedef std::set WindowSurfaceSet; + +// A class used to model the state of each RenderThread related +struct RenderThreadInfo { + // Create new instance. Only call this once per thread. + // Future callls to get() will return this instance until + // it is destroyed. + RenderThreadInfo(); + + // Destructor. + ~RenderThreadInfo(); + + // Return the current thread's instance, if any, or NULL. + static RenderThreadInfo* get(); + + // Current EGL context, draw surface and read surface. + RenderContextPtr currContext; + WindowSurfacePtr currDrawSurf; + WindowSurfacePtr currReadSurf; + + // Decoder states. + GLESv1Decoder m_glDec; + GLESv2Decoder m_gl2Dec; + renderControl_decoder_context_t m_rcDec; + + // all the contexts that are created by this render thread + ThreadContextSet m_contextSet; + // all the window surfaces that are created by this render thread + WindowSurfaceSet m_windowSet; +}; + +#endif diff --git a/external/android-emugl/host/libs/libOpenglRender/RenderWindow.cpp b/external/android-emugl/host/libs/libOpenglRender/RenderWindow.cpp new file mode 100644 index 0000000..bbfaa5e --- /dev/null +++ b/external/android-emugl/host/libs/libOpenglRender/RenderWindow.cpp @@ -0,0 +1,454 @@ +// Copyright 2014-2015 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "RenderWindow.h" + +#include "emugl/common/logging.h" +#include "emugl/common/message_channel.h" +#include "emugl/common/mutex.h" +#include "emugl/common/thread.h" +#include "FrameBuffer.h" + +#include +#include +#ifndef _WIN32 +#include +#include +#endif + +#define DEBUG 0 + +#if DEBUG +# define D(...) my_debug(__PRETTY_FUNCTION__, __LINE__, __VA_ARGS__) +#else +# define D(...) ((void)0) +#endif + +namespace { + +#if DEBUG +void my_debug(const char* function, int line, const char* format, ...) { + static ::emugl::Mutex mutex; + va_list args; + va_start(args, format); + mutex.lock(); + fprintf(stderr, "%s:%d:", function, line); + vfprintf(stderr, format, args); + mutex.unlock(); + va_end(args); +} +#endif + +// List of possible commands to send to the render window thread from +// the main one. +enum Command { + CMD_INITIALIZE, + CMD_SET_POST_CALLBACK, + CMD_SETUP_SUBWINDOW, + CMD_REMOVE_SUBWINDOW, + CMD_SET_ROTATION, + CMD_SET_TRANSLATION, + CMD_REPAINT, + CMD_FINALIZE, +}; + +} // namespace + +// A single message sent from the main thread to the render window thread. +// |cmd| determines which fields are valid to read. +struct RenderWindowMessage { + Command cmd; + union { + // CMD_INITIALIZE + struct { + int width; + int height; + bool useSubWindow; + } init; + + // CMD_SET_POST_CALLBACK + struct { + OnPostFn on_post; + void* on_post_context; + } set_post_callback; + + // CMD_SETUP_SUBWINDOW + struct { + FBNativeWindowType parent; + int wx; + int wy; + int ww; + int wh; + int fbw; + int fbh; + float dpr; + float rotation; + } subwindow; + + // CMD_SET_TRANSLATION; + struct { + float px; + float py; + } trans; + + // CMD_SET_ROTATION + float rotation; + + // result of operations. + bool result; + }; + + // Process the current message, and updates its |result| field. + // Returns true on success, or false on failure. + bool process() const { + const RenderWindowMessage& msg = *this; + FrameBuffer* fb; + bool result = false; + switch (msg.cmd) { + case CMD_INITIALIZE: + D("CMD_INITIALIZE w=%d h=%d\n", msg.init.width, msg.init.height); + GL_LOG("RenderWindow: CMD_INITIALIZE w=%d h=%d", + msg.init.width, msg.init.height); + result = FrameBuffer::initialize(msg.init.width, + msg.init.height, + msg.init.useSubWindow); + break; + + case CMD_FINALIZE: + D("CMD_FINALIZE\n"); + // this command may be issued even when frame buffer is not + // yet created (e.g. if CMD_INITIALIZE failed), + // so make sure we check if it is there before finalizing + if (const auto fb = FrameBuffer::getFB()) { + fb->finalize(); + } + result = true; + break; + + case CMD_SET_POST_CALLBACK: + D("CMD_SET_POST_CALLBACK\n"); + fb = FrameBuffer::getFB(); + fb->setPostCallback(msg.set_post_callback.on_post, + msg.set_post_callback.on_post_context); + result = true; + break; + + case CMD_SETUP_SUBWINDOW: + D("CMD_SETUP_SUBWINDOW: parent=%p wx=%d wy=%d ww=%d wh=%d fbw=%d fbh=%d dpr=%f rotation=%f\n", + (void*)msg.subwindow.parent, + msg.subwindow.wx, + msg.subwindow.wy, + msg.subwindow.ww, + msg.subwindow.wh, + msg.subwindow.fbw, + msg.subwindow.fbh, + msg.subwindow.dpr, + msg.subwindow.rotation); + result = FrameBuffer::getFB()->setupSubWindow( + msg.subwindow.parent, + msg.subwindow.wx, + msg.subwindow.wy, + msg.subwindow.ww, + msg.subwindow.wh, + msg.subwindow.fbw, + msg.subwindow.fbh, + msg.subwindow.dpr, + msg.subwindow.rotation); + break; + + case CMD_REMOVE_SUBWINDOW: + D("CMD_REMOVE_SUBWINDOW\n"); + result = FrameBuffer::getFB()->removeSubWindow(); + break; + + case CMD_SET_ROTATION: + D("CMD_SET_ROTATION rotation=%f\n", msg.rotation); + fb = FrameBuffer::getFB(); + if (fb) { + fb->setDisplayRotation(msg.rotation); + result = true; + } + break; + + case CMD_SET_TRANSLATION: + D("CMD_SET_TRANSLATION translation=%f,%f\n", msg.trans.px, msg.trans.py); + fb = FrameBuffer::getFB(); + if (fb) { + fb->setDisplayTranslation(msg.trans.px, msg.trans.py); + result = true; + } + break; + + case CMD_REPAINT: + D("CMD_REPAINT\n"); + fb = FrameBuffer::getFB(); + if (fb) { + fb->repost(); + result = true; + } + break; + + default: + ; + } + return result; + } +}; + +// Simple synchronization structure used to exchange data between the +// main and render window threads. Usage is the following: +// +// The main thread does the following in a loop: +// +// canWriteCmd.wait() +// updates |message| by writing a new |cmd| value and appropriate +// parameters. +// canReadCmd.signal() +// canReadResult.wait() +// reads |message.result| +// canWriteResult.signal() +// +// The render window thread will do the following: +// +// canReadCmd.wait() +// reads |message.cmd| and acts upon it. +// canWriteResult.wait() +// writes |message.result| +// canReadResult.signal() +// canWriteCmd.signal() +// +class RenderWindowChannel { +public: + RenderWindowChannel() : mIn(), mOut() {} + ~RenderWindowChannel() {} + + // Send a message from the main thread. + // Note that the content of |msg| is copied into the channel. + // Returns with the command's result (true or false). + bool sendMessageAndGetResult(const RenderWindowMessage& msg) { + D("msg.cmd=%d\n", msg.cmd); + mIn.send(msg); + D("waiting for result\n"); + bool result = false; + mOut.receive(&result); + D("result=%s\n", result ? "success" : "failure"); + return result; + } + + // Receive a message from the render window thread. + // On exit, |*msg| gets a copy of the message. The caller + // must always call sendResult() after processing the message. + void receiveMessage(RenderWindowMessage* msg) { + D("entering\n"); + mIn.receive(msg); + D("message cmd=%d\n", msg->cmd); + } + + // Send result from the render window thread to the main one. + // Must always be called after receiveMessage(). + void sendResult(bool result) { + D("waiting to send result (%s)\n", result ? "success" : "failure"); + mOut.send(result); + D("result sent\n"); + } + +private: + emugl::MessageChannel mIn; + emugl::MessageChannel mOut; +}; + +namespace { + +// This class implements the window render thread. +// Its purpose is to listen for commands from the main thread in a loop, +// process them, then return a boolean result for each one of them. +// +// The thread ends with a CMD_FINALIZE. +// +class RenderWindowThread : public emugl::Thread { +public: + RenderWindowThread(RenderWindowChannel* channel) : mChannel(channel) {} + + virtual intptr_t main() { + D("Entering render window thread thread\n"); +#ifndef _WIN32 + sigset_t set; + sigfillset(&set); + pthread_sigmask(SIG_SETMASK, &set, NULL); +#endif + bool running = true; + while (running) { + RenderWindowMessage msg; + + D("Waiting for message from main thread\n"); + mChannel->receiveMessage(&msg); + + bool result = msg.process(); + if (msg.cmd == CMD_FINALIZE) { + running = false; + } + + D("Sending result (%s) to main thread\n", result ? "success" : "failure"); + mChannel->sendResult(result); + } + D("Exiting thread\n"); + return 0; + } + +private: + RenderWindowChannel* mChannel; +}; + +} // namespace + +RenderWindow::RenderWindow(int width, + int height, + bool use_thread, + bool use_sub_window) : + mValid(false), + mHasSubWindow(false), + mThread(NULL), + mChannel(NULL) { + if (use_thread) { + mChannel = new RenderWindowChannel(); + mThread = new RenderWindowThread(mChannel); + mThread->start(); + } + + RenderWindowMessage msg; + msg.cmd = CMD_INITIALIZE; + msg.init.width = width; + msg.init.height = height; + msg.init.useSubWindow = use_sub_window; + mValid = processMessage(msg); +} + +RenderWindow::~RenderWindow() { + D("Entering\n"); + removeSubWindow(); + D("Sending CMD_FINALIZE\n"); + RenderWindowMessage msg; + msg.cmd = CMD_FINALIZE; + (void) processMessage(msg); + + if (mThread) { + mThread->wait(NULL); + delete mThread; + delete mChannel; + } +} + +bool RenderWindow::getHardwareStrings(const char** vendor, + const char** renderer, + const char** version) { + D("Entering\n"); + // TODO(digit): Move this to render window thread. + FrameBuffer* fb = FrameBuffer::getFB(); + if (!fb) { + D("No framebuffer!\n"); + return false; + } + fb->getGLStrings(vendor, renderer, version); + D("Exiting vendor=[%s] renderer=[%s] version=[%s]\n", + *vendor, *renderer, *version); + + return true; +} + +void RenderWindow::setPostCallback(OnPostFn onPost, void* onPostContext) { + D("Entering\n"); + RenderWindowMessage msg; + msg.cmd = CMD_SET_POST_CALLBACK; + msg.set_post_callback.on_post = onPost; + msg.set_post_callback.on_post_context = onPostContext; + (void) processMessage(msg); + D("Exiting\n"); +} + +bool RenderWindow::setupSubWindow(FBNativeWindowType window, + int wx, + int wy, + int ww, + int wh, + int fbw, + int fbh, + float dpr, + float zRot) { + D("Entering mHasSubWindow=%s\n", mHasSubWindow ? "true" : "false"); + + RenderWindowMessage msg; + msg.cmd = CMD_SETUP_SUBWINDOW; + msg.subwindow.parent = window; + msg.subwindow.wx = wx; + msg.subwindow.wy = wy; + msg.subwindow.ww = ww; + msg.subwindow.wh = wh; + msg.subwindow.fbw = fbw; + msg.subwindow.fbh = fbh; + msg.subwindow.dpr = dpr; + msg.subwindow.rotation = zRot; + + mHasSubWindow = processMessage(msg); + D("Exiting mHasSubWindow=%s\n", mHasSubWindow ? "true" : "false"); + return mHasSubWindow; +} + +bool RenderWindow::removeSubWindow() { + D("Entering mHasSubWindow=%s\n", mHasSubWindow ? "true" : "false"); + if (!mHasSubWindow) { + return false; + } + mHasSubWindow = false; + + RenderWindowMessage msg; + msg.cmd = CMD_REMOVE_SUBWINDOW; + bool result = processMessage(msg); + D("Exiting result=%s\n", result ? "success" : "failure"); + return result; +} + +void RenderWindow::setRotation(float zRot) { + D("Entering rotation=%f\n", zRot); + RenderWindowMessage msg; + msg.cmd = CMD_SET_ROTATION; + msg.rotation = zRot; + (void) processMessage(msg); + D("Exiting\n"); +} + +void RenderWindow::setTranslation(float px, float py) { + D("Entering translation=%f,%f\n", px, py); + RenderWindowMessage msg; + msg.cmd = CMD_SET_TRANSLATION; + msg.trans.px = px; + msg.trans.py = py; + (void) processMessage(msg); + D("Exiting\n"); +} + +void RenderWindow::repaint() { + D("Entering\n"); + RenderWindowMessage msg; + msg.cmd = CMD_REPAINT; + (void) processMessage(msg); + D("Exiting\n"); +} + +bool RenderWindow::processMessage(const RenderWindowMessage& msg) { + if (mChannel) { + return mChannel->sendMessageAndGetResult(msg); + } else { + return msg.process(); + } +} diff --git a/external/android-emugl/host/libs/libOpenglRender/RenderWindow.h b/external/android-emugl/host/libs/libOpenglRender/RenderWindow.h new file mode 100644 index 0000000..ab03619 --- /dev/null +++ b/external/android-emugl/host/libs/libOpenglRender/RenderWindow.h @@ -0,0 +1,135 @@ +// Copyright 2014-2015 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef ANDROID_EMUGL_LIBRENDER_RENDER_WINDOW_H +#define ANDROID_EMUGL_LIBRENDER_RENDER_WINDOW_H + +#include "OpenglRender/render_api.h" + +namespace emugl { +class Thread; +} // namespace emugl + +class RenderWindowChannel; +struct RenderWindowMessage; + +// Helper class used to manage the sub-window that displays the emulated GPU +// output. To use it, do the following: +// +// 1) Create a new instance, passing the size of the emulated accelerated +// framebuffer in pixels you need. +// +// 2) Check isValid() after construction. If false, the library could not +// initialize the class properly, and one should abort. +// +// 3) Optional: call setPostCallback() to specify a callback function which +// will be called everytime a new frame is drawn. +// +// 4) Call setupSubWindow() to setup a new sub-window within the UI window. +// One can call removeSubWindow() to remove it, and one can call +// setupSubWindow() + removeSubWindow() any number of time (e.g. for +// changing the position / rotation of the subwindow). +// +// 5) Optional: call setRotation() to only change the display rotation of +// the sub-window content. +// +// 6) Call repaint() to force a repaint(). +// +class RenderWindow { +public: + // Create new instance. |width| and |height| are the dimensions of the + // emulated accelerated framebuffer. |use_thread| can be true to force + // the use of a separate thread, which might be required on some platforms + // to avoid GL-realted corruption issues in the main window. Call + // isValid() after construction to verify that it worked properly. + // + // |use_sub_window| is true if the client will call setupSubWindow(), + // and false if it will call setPostCallback(). + // + // Note that this call doesn't display anything, it just initializes + // the library, use setupSubWindow() to display something. + RenderWindow(int width, int height, bool use_thread, bool use_sub_window); + + // Destructor. This will automatically call removeSubWindow() is needed. + ~RenderWindow(); + + // Returns true if the RenderWindow instance is valid, which really + // means that the constructor succeeded. + bool isValid() const { return mValid; } + + // Return misc. GL strings to the caller. On success, return true and sets + // |*vendor| to the GL vendor string, |*renderer| to the GL renderer one, + // and |*version| to the GL version one. On failure, return false. + bool getHardwareStrings(const char** vendor, + const char** renderer, + const char** version); + + // Specify a function that will be called everytime a new frame is + // displayed. This is relatively slow but allows one to capture the + // output. + void setPostCallback(OnPostFn onPost, void* onPostContext); + + // Start displaying the emulated framebuffer using a sub-window of a + // parent |window| id. |wx|, |wy|, |ww| and |wh| are the position + // and dimension of the sub-window, relative to its parent. + // |fbw| and |fbh| are the dimensions of the underlying guest framebuffer. + // |dpr| is the device pixel ratio for the monitor, which is required for + // higher-density displays (such as retina). + // |rotation| is a clockwise-rotation for the content. Only multiples of + // 90. are accepted. Returns true on success, false otherwise. + // + // If the subwindow already exists, this function will update + // the dimensions of the subwindow, backing framebuffer, and rendering + // pipeline to reflect the new values. + // + // One can call removeSubWindow() to remove the sub-window. + bool setupSubWindow(FBNativeWindowType window, + int wx, + int wy, + int ww, + int wh, + int fbw, + int fbh, + float dpr, + float rotation); + + // Remove the sub-window created by calling setupSubWindow(). + // Note that this doesn't discard the content of the emulated framebuffer, + // it just hides it from the main window. Returns true on success, false + // otherwise. + bool removeSubWindow(); + + // Change the display rotation on the fly. |zRot| is a clockwise rotation + // angle in degrees. Only multiples of 90. are accepted. + void setRotation(float zRot); + + // Change the display translation. |px|,|py| are numbers between 0 and 1, + // with (0,0) indicating "align the bottom left of the framebuffer with the + // bottom left of the subwindow", and (1,1) indicating "align the top right of + // the framebuffer with the top right of the subwindow." + void setTranslation(float px, float py); + + // Force a repaint of the whole content into the sub-window. + void repaint(); + +private: + bool processMessage(const RenderWindowMessage& msg); + + bool mValid; + bool mHasSubWindow; + emugl::Thread* mThread; + RenderWindowChannel* mChannel; +}; + +#endif // ANDROID_EMUGL_LIBRENDER_RENDER_WINDOW_H diff --git a/external/android-emugl/host/libs/libOpenglRender/SocketStream.cpp b/external/android-emugl/host/libs/libOpenglRender/SocketStream.cpp new file mode 100644 index 0000000..3121e81 --- /dev/null +++ b/external/android-emugl/host/libs/libOpenglRender/SocketStream.cpp @@ -0,0 +1,183 @@ +/* +* Copyright (C) 2011 The Android Open Source Project +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ +#include "SocketStream.h" +#include +#include +#include +#include +#include + +#ifndef _WIN32 +#include +#include +#include +#else +#include +#endif + +SocketStream::SocketStream(size_t bufSize) : + IOStream(bufSize), + m_sock(-1), + m_bufsize(bufSize), + m_buf(NULL) +{ +} + +SocketStream::SocketStream(int sock, size_t bufSize) : + IOStream(bufSize), + m_sock(sock), + m_bufsize(bufSize), + m_buf(NULL) +{ +} + +SocketStream::~SocketStream() +{ + if (m_sock >= 0) { + forceStop(); +#ifndef _WIN32 + if(close(m_sock) < 0) + perror("Closing SocketStream failed"); +#endif + // DBG("SocketStream::~close @ %d \n", m_sock); + m_sock = -1; + } + if (m_buf != NULL) { + free(m_buf); + m_buf = NULL; + } +} + + +void *SocketStream::allocBuffer(size_t minSize) +{ + size_t allocSize = (m_bufsize < minSize ? minSize : m_bufsize); + if (!m_buf) { + m_buf = (unsigned char *)malloc(allocSize); + } + else if (m_bufsize < allocSize) { + unsigned char *p = (unsigned char *)realloc(m_buf, allocSize); + if (p != NULL) { + m_buf = p; + m_bufsize = allocSize; + } else { + ERR("%s: realloc (%zu) failed\n", __FUNCTION__, allocSize); + free(m_buf); + m_buf = NULL; + m_bufsize = 0; + } + } + + return m_buf; +}; + +int SocketStream::commitBuffer(size_t size) +{ + return writeFully(m_buf, size); +} + +int SocketStream::writeFully(const void* buffer, size_t size) +{ + if (!valid()) return -1; + + size_t res = size; + int retval = 0; + + while (res > 0) { + ssize_t stat = ::send(m_sock, (const char *)buffer + (size - res), res, 0); + if (stat < 0) { + if (errno != EINTR) { + retval = stat; + ERR("%s: failed: %s\n", __FUNCTION__, strerror(errno)); + break; + } + } else { + res -= stat; + } + } + return retval; +} + +const unsigned char *SocketStream::readFully(void *buf, size_t len) +{ + if (!valid()) return NULL; + if (!buf) { + return NULL; // do not allow NULL buf in that implementation + } + size_t res = len; + while (res > 0) { + ssize_t stat = ::recv(m_sock, (char *)(buf) + len - res, res, 0); + if (stat > 0) { + res -= stat; + continue; + } + if (stat == 0 || errno != EINTR) { // client shutdown or error + return NULL; + } + } + return (const unsigned char *)buf; +} + +const unsigned char *SocketStream::read( void *buf, size_t *inout_len) +{ + if (!valid()) return NULL; + if (!buf) { + return NULL; // do not allow NULL buf in that implementation + } + + int n; + do { + n = this->recv(buf, *inout_len); + } while( n < 0 && errno == EINTR ); + + if (n > 0) { + *inout_len = n; + return (const unsigned char *)buf; + } + + return NULL; +} + +int SocketStream::recv(void *buf, size_t len) +{ + if (!valid()) return int(ERR_INVALID_SOCKET); + int res = 0; + while(true) { + res = ::recv(m_sock, (char *)buf, len, 0); + if (res < 0) { + if (errno == EINTR) { + continue; + } + } + break; + } + return res; +} + +void SocketStream::forceStop() { + // Shutdown socket to force read/write errors. +#ifdef _WIN32 + ::shutdown(m_sock, SD_BOTH); + // As documented by programmers on MSDN, shutdown implementation in Windows does + // NOT result to unblocking threads that are blocked on a recv on the socket + // being shut down. The only way to actually implement this behavior (expected + // by this forceStop() implementation) is to rudely close the socket. + ::closesocket(m_sock); + m_sock = -1; +#else + ::shutdown(m_sock, SHUT_RDWR); +#endif +} diff --git a/external/android-emugl/host/libs/libOpenglRender/SocketStream.h b/external/android-emugl/host/libs/libOpenglRender/SocketStream.h new file mode 100644 index 0000000..2a9cf13 --- /dev/null +++ b/external/android-emugl/host/libs/libOpenglRender/SocketStream.h @@ -0,0 +1,53 @@ +/* +* Copyright (C) 2011 The Android Open Source Project +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ +#ifndef __SOCKET_STREAM_H +#define __SOCKET_STREAM_H + +#include +#include "IOStream.h" + +class SocketStream : public IOStream { +public: + typedef enum { ERR_INVALID_SOCKET = -1000 } SocketStreamError; + static const size_t MAX_ADDRSTR_LEN = 256; + + explicit SocketStream(size_t bufsize = 10000); + virtual ~SocketStream(); + + virtual int listen(char addrstr[MAX_ADDRSTR_LEN]) = 0; + virtual SocketStream *accept() = 0; + virtual int connect(const char* addr) = 0; + + virtual void *allocBuffer(size_t minSize); + virtual int commitBuffer(size_t size); + virtual const unsigned char *readFully(void *buf, size_t len); + virtual const unsigned char *read(void *buf, size_t *inout_len); + + bool valid() { return m_sock >= 0; } + virtual int recv(void *buf, size_t len); + virtual int writeFully(const void *buf, size_t len); + + virtual void forceStop(); + +protected: + int m_sock; + size_t m_bufsize; + unsigned char *m_buf; + + SocketStream(int sock, size_t bufSize); +}; + +#endif /* __SOCKET_STREAM_H */ diff --git a/external/android-emugl/host/libs/libOpenglRender/TcpStream.cpp b/external/android-emugl/host/libs/libOpenglRender/TcpStream.cpp new file mode 100644 index 0000000..ba355ab --- /dev/null +++ b/external/android-emugl/host/libs/libOpenglRender/TcpStream.cpp @@ -0,0 +1,78 @@ +/* +* Copyright (C) 2011 The Android Open Source Project +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ +#include "TcpStream.h" +#include "emugl/common/sockets.h" + +#include +#include +#include +#include +#include + +#ifndef _WIN32 +#include +#include +#else +#include +#endif + +#define LISTEN_BACKLOG 4 + +TcpStream::TcpStream(size_t bufSize) : SocketStream(bufSize) {} + +TcpStream::TcpStream(int sock, size_t bufSize) : + SocketStream(sock, bufSize) { + // disable Nagle algorithm to improve bandwidth of small + // packets which are quite common in our implementation. + emugl::socketTcpDisableNagle(sock); +} + +int TcpStream::listen(char addrstr[MAX_ADDRSTR_LEN]) { + m_sock = emugl::socketTcpLoopbackServer(0, SOCK_STREAM); + if (!valid()) + return int(ERR_INVALID_SOCKET); + + int port = emugl::socketGetPort(m_sock); + if (port < 0) { + ::close(m_sock); + return int(ERR_INVALID_SOCKET); + } + + snprintf(addrstr, MAX_ADDRSTR_LEN - 1, "%hu", port); + addrstr[MAX_ADDRSTR_LEN-1] = '\0'; + + return 0; +} + +SocketStream * TcpStream::accept() { + int clientSock = emugl::socketAccept(m_sock); + if (clientSock < 0) + return NULL; + + return new TcpStream(clientSock, m_bufsize); +} + +int TcpStream::connect(const char* addr) { + int port = atoi(addr); + m_sock = emugl::socketTcpLoopbackClient(port, SOCK_STREAM); + return valid() ? 0 : -1; +} + +int TcpStream::connect(const char* hostname, unsigned short port) +{ + m_sock = emugl::socketTcpClient(hostname, port, SOCK_STREAM); + return valid() ? 0 : -1; +} diff --git a/external/android-emugl/host/libs/libOpenglRender/TcpStream.h b/external/android-emugl/host/libs/libOpenglRender/TcpStream.h new file mode 100644 index 0000000..f5b462b --- /dev/null +++ b/external/android-emugl/host/libs/libOpenglRender/TcpStream.h @@ -0,0 +1,32 @@ +/* +* Copyright (C) 2011 The Android Open Source Project +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ +#ifndef __TCP_STREAM_H +#define __TCP_STREAM_H + +#include "SocketStream.h" + +class TcpStream : public SocketStream { +public: + explicit TcpStream(size_t bufsize = 10000); + virtual int listen(char addrstr[MAX_ADDRSTR_LEN]); + virtual SocketStream *accept(); + virtual int connect(const char* addr); + int connect(const char* hostname, unsigned short port); +private: + TcpStream(int sock, size_t bufSize); +}; + +#endif diff --git a/external/android-emugl/host/libs/libOpenglRender/TextureDraw.cpp b/external/android-emugl/host/libs/libOpenglRender/TextureDraw.cpp new file mode 100644 index 0000000..af6d2aa --- /dev/null +++ b/external/android-emugl/host/libs/libOpenglRender/TextureDraw.cpp @@ -0,0 +1,287 @@ +// Copyright (C) 2015 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "TextureDraw.h" + +#include "DispatchTables.h" + +#include +#include +#include + +#include +#define ERR(...) fprintf(stderr, __VA_ARGS__) + +// M_PI isn't defined in C++ (when strict ISO compliance is enabled) +#ifndef M_PI +#define M_PI 3.14159265358979323846264338327 +#endif + +namespace { + +// Helper function to create a new shader. +// |shaderType| is the shader type (e.g. GL_VERTEX_SHADER). +// |shaderText| is a 0-terminated C string for the shader source to use. +// On success, return the handle of the new compiled shader, or 0 on failure. +GLuint createShader(GLint shaderType, const char* shaderText) { + // Create new shader handle and attach source. + GLuint shader = s_gles2.glCreateShader(shaderType); + if (!shader) { + return 0; + } + const GLchar* text = static_cast(shaderText); + const GLint textLen = ::strlen(shaderText); + s_gles2.glShaderSource(shader, 1, &text, &textLen); + + // Compiler the shader. + GLint success; + s_gles2.glCompileShader(shader); + s_gles2.glGetShaderiv(shader, GL_COMPILE_STATUS, &success); + if (success == GL_FALSE) { + s_gles2.glDeleteShader(shader); + return 0; + } + + return shader; +} + +// No scaling / projection since we want to fill the whole viewport with +// the texture, hence a trivial vertex shader that only supports clockwise +// rotation. +const char kVertexShaderSource[] = + "#version 100\n" + "const int _translator_gl_MaxVertexUniformVectors = 256;\n" + "const int _translator_gl_MaxFragmentUniformVectors = 256;\n" + "const int _translator_gl_MaxVaryingVectors = 15;\n" + "#define gl_MaxVertexUniformVectors _translator_gl_MaxVertexUniformVectors\n" + "#define gl_MaxFragmentUniformVectors _translator_gl_MaxFragmentUniformVectors\n" + "#define gl_MaxVaryingVectors _translator_gl_MaxVaryingVectors\n" + "#line 1\n" + "attribute vec4 position;\n" + "attribute vec2 inCoord;\n" + "varying lowp vec2 outCoord;\n" + "uniform float rotation;\n" + "uniform vec2 translation;\n" + + "void main(void) {\n" + " float cs = cos(rotation);\n" + " float sn = sin(rotation);\n" + " gl_Position.x = position.x * cs + position.y * sn - translation.x;\n" + " gl_Position.y = position.y * cs - position.x * sn - translation.y;\n" + " gl_Position.zw = position.zw;\n" + " outCoord = inCoord;\n" + "}\n"; + +// Similarly, just interpolate texture coordinates. +const char kFragmentShaderSource[] = + "#version 100\n" + "const int _translator_gl_MaxVertexUniformVectors = 256;\n" + "const int _translator_gl_MaxFragmentUniformVectors = 256;\n" + "const int _translator_gl_MaxVaryingVectors = 15;\n" + "#define gl_MaxVertexUniformVectors _translator_gl_MaxVertexUniformVectors\n" + "#define gl_MaxFragmentUniformVectors _translator_gl_MaxFragmentUniformVectors\n" + "#define gl_MaxVaryingVectors _translator_gl_MaxVaryingVectors\n" + "#line 1\n" + "varying lowp vec2 outCoord;\n" + "uniform sampler2D texture;\n" + + "void main(void) {\n" + " gl_FragColor = texture2D(texture, outCoord);\n" + "}\n"; + +// Hard-coded arrays of vertex information. +struct Vertex { + float pos[3]; + float coord[2]; +}; + +const Vertex kVertices[] = { + {{ +1, -1, +0 }, { +1, +1 }}, + {{ +1, +1, +0 }, { +1, +0 }}, + {{ -1, +1, +0 }, { +0, +0 }}, + {{ -1, -1, +0 }, { +0, +1 }}, +}; + +const GLubyte kIndices[] = { 0, 1, 2, 2, 3, 0 }; +const GLint kIndicesLen = sizeof(kIndices) / sizeof(kIndices[0]); + +} // namespace + +TextureDraw::TextureDraw(EGLDisplay display) : + mDisplay(display), + mVertexShader(0), + mFragmentShader(0), + mProgram(0), + mPositionSlot(-1), + mInCoordSlot(-1), + mTextureSlot(-1), + mRotationSlot(-1), + mTranslationSlot(-1) { + // Create shaders and program. + mVertexShader = createShader(GL_VERTEX_SHADER, kVertexShaderSource); + mFragmentShader = createShader(GL_FRAGMENT_SHADER, kFragmentShaderSource); + + mProgram = s_gles2.glCreateProgram(); + s_gles2.glAttachShader(mProgram, mVertexShader); + s_gles2.glAttachShader(mProgram, mFragmentShader); + + GLint success; + s_gles2.glLinkProgram(mProgram); + s_gles2.glGetProgramiv(mProgram, GL_LINK_STATUS, &success); + if (success == GL_FALSE) { + GLchar messages[256]; + s_gles2.glGetProgramInfoLog( + mProgram, sizeof(messages), 0, &messages[0]); + ERR("%s: Could not create/link program: %s\n", __FUNCTION__, messages); + s_gles2.glDeleteProgram(mProgram); + mProgram = 0; + return; + } + + s_gles2.glUseProgram(mProgram); + + // Retrieve attribute/uniform locations. + mPositionSlot = s_gles2.glGetAttribLocation(mProgram, "position"); + s_gles2.glEnableVertexAttribArray(mPositionSlot); + + mInCoordSlot = s_gles2.glGetAttribLocation(mProgram, "inCoord"); + s_gles2.glEnableVertexAttribArray(mInCoordSlot); + + mRotationSlot = s_gles2.glGetUniformLocation(mProgram, "rotation"); + mTranslationSlot = s_gles2.glGetUniformLocation(mProgram, "translation"); + mTextureSlot = s_gles2.glGetUniformLocation(mProgram, "texture"); + +#if 0 + printf("SLOTS position=%d inCoord=%d texture=%d rotation=%d\n", + mPositionSlot, mInCoordSlot, mTextureSlot, mRotationSlot); +#endif + + // Create vertex and index buffers. + s_gles2.glGenBuffers(1, &mVertexBuffer); + s_gles2.glBindBuffer(GL_ARRAY_BUFFER, mVertexBuffer); + s_gles2.glBufferData( + GL_ARRAY_BUFFER, sizeof(kVertices), kVertices, GL_STATIC_DRAW); + + s_gles2.glGenBuffers(1, &mIndexBuffer); + s_gles2.glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, mIndexBuffer); + s_gles2.glBufferData(GL_ELEMENT_ARRAY_BUFFER, + sizeof(kIndices), + kIndices, + GL_STATIC_DRAW); +} + +bool TextureDraw::draw(GLuint texture, float rotation, float dx, float dy) { + if (!mProgram) { + ERR("%s: no program\n", __FUNCTION__); + return false; + } + + // TODO(digit): Save previous program state. + + GLenum err; + + s_gles2.glUseProgram(mProgram); + err = s_gles2.glGetError(); + if (err != GL_NO_ERROR) { + ERR("%s: Could not use program error=0x%x\n", + __FUNCTION__, err); + } + + + // Setup the |position| attribute values. + s_gles2.glBindBuffer(GL_ARRAY_BUFFER, mVertexBuffer); + err = s_gles2.glGetError(); + if (err != GL_NO_ERROR) { + ERR("%s: Could not bind GL_ARRAY_BUFFER error=0x%x\n", + __FUNCTION__, err); + } + + s_gles2.glEnableVertexAttribArray(mPositionSlot); + s_gles2.glVertexAttribPointer(mPositionSlot, + 3, + GL_FLOAT, + GL_FALSE, + sizeof(Vertex), + 0); + err = s_gles2.glGetError(); + if (err != GL_NO_ERROR) { + ERR("%s: Could glVertexAttribPointer with mPositionSlot error=0x%x\n", + __FUNCTION__, err); + } + + // Setup the |inCoord| attribute values. + s_gles2.glEnableVertexAttribArray(mInCoordSlot); + s_gles2.glVertexAttribPointer(mInCoordSlot, + 2, + GL_FLOAT, + GL_FALSE, + sizeof(Vertex), + reinterpret_cast( + static_cast( + sizeof(float) * 3))); + + // setup the |texture| uniform value. + s_gles2.glActiveTexture(GL_TEXTURE0); + s_gles2.glBindTexture(GL_TEXTURE_2D, texture); + s_gles2.glUniform1i(mTextureSlot, 0); + + // setup the |rotation| uniform value. + s_gles2.glUniform1f(mRotationSlot, rotation * M_PI / 180.); + s_gles2.glUniform2f(mTranslationSlot, dx, dy); + +#if 1 + // Validate program, just to be sure. + s_gles2.glValidateProgram(mProgram); + GLint validState = 0; + s_gles2.glGetProgramiv(mProgram, GL_VALIDATE_STATUS, &validState); + if (validState == GL_FALSE) { + GLchar messages[256]; + s_gles2.glGetProgramInfoLog( + mProgram, sizeof(messages), 0, &messages[0]); + ERR("%s: Could not run program: %s\n", __FUNCTION__, messages); + return false; + } +#endif + + // Do the rendering. + s_gles2.glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, mIndexBuffer); + err = s_gles2.glGetError(); + if (err != GL_NO_ERROR) { + ERR("%s: Could not glBindBuffer(GL_ELEMENT_ARRAY_BUFFER) error=0x%x\n", + __FUNCTION__, err); + } + + s_gles2.glDrawElements(GL_TRIANGLES, kIndicesLen, GL_UNSIGNED_BYTE, 0); + err = s_gles2.glGetError(); + if (err != GL_NO_ERROR) { + ERR("%s: Could not glDrawElements() error=0x%x\n", + __FUNCTION__, err); + } + + // TODO(digit): Restore previous program state. + + return true; +} + +TextureDraw::~TextureDraw() { + s_gles2.glDeleteBuffers(1, &mIndexBuffer); + s_gles2.glDeleteBuffers(1, &mVertexBuffer); + + if (mFragmentShader) { + s_gles2.glDeleteShader(mFragmentShader); + } + if (mVertexShader) { + s_gles2.glDeleteShader(mVertexShader); + } +} diff --git a/external/android-emugl/host/libs/libOpenglRender/TextureDraw.h b/external/android-emugl/host/libs/libOpenglRender/TextureDraw.h new file mode 100644 index 0000000..ee9371f --- /dev/null +++ b/external/android-emugl/host/libs/libOpenglRender/TextureDraw.h @@ -0,0 +1,61 @@ +// Copyright (C) 2015 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef TEXTURE_DRAW_H +#define TEXTURE_DRAW_H + +#include +#include + +// Helper class used to draw a simple texture to the current framebuffer. +// Usage is pretty simple: +// +// 1) Create a TextureDraw instance, passing the current EGLDisplay to it. +// +// 2) Each time you want to draw a texture, call draw(texture, rotation), +// where |texture| is the name of a GLES 2.x texture object, and +// |rotation| is an angle in degrees describing the clockwise rotation +// in the GL y-upwards coordinate space. This function fills the whole +// framebuffer with texture content. +// +class TextureDraw { +public: + // Create a new instance. + TextureDraw(EGLDisplay display); + + // Destructor + ~TextureDraw(); + + // Fill the current framebuffer with the content of |texture|, which must + // be the name of a GLES 2.x texture object. |rotationDegrees| is a + // clockwise rotation angle in degrees (clockwise in the GL Y-upwards + // coordinate space). |dx,dy| is the translation of the image towards the + // origin. + bool draw(GLuint texture, float rotationDegrees, float dx, float dy); + +private: + EGLDisplay mDisplay; + GLuint mVertexShader; + GLuint mFragmentShader; + GLuint mProgram; + GLint mPositionSlot; + GLint mInCoordSlot; + GLint mTextureSlot; + GLint mRotationSlot; + GLint mTranslationSlot; + GLuint mVertexBuffer; + GLuint mIndexBuffer; +}; + +#endif // TEXTURE_DRAW_H diff --git a/external/android-emugl/host/libs/libOpenglRender/TextureResize.cpp b/external/android-emugl/host/libs/libOpenglRender/TextureResize.cpp new file mode 100644 index 0000000..e09b939 --- /dev/null +++ b/external/android-emugl/host/libs/libOpenglRender/TextureResize.cpp @@ -0,0 +1,344 @@ +/* +* Copyright (C) 2015 The Android Open Source Project +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ +#include "TextureResize.h" + +#include "DispatchTables.h" + +#include +#include +#include +#include + +#define ERR(...) fprintf(stderr, __VA_ARGS__) +#define MAX_FACTOR_POWER 4 + +static const char kCommonShaderSource[] = + "precision mediump float;\n" + "varying vec2 vUV00, vUV01;\n" + "#if FACTOR > 2\n" + "varying vec2 vUV02, vUV03;\n" + "#if FACTOR > 4\n" + "varying vec2 vUV04, vUV05, vUV06, vUV07;\n" + "#if FACTOR > 8\n" + "varying vec2 vUV08, vUV09, vUV10, vUV11, vUV12, vUV13, vUV14, vUV15;\n" + "#endif\n" + "#endif\n" + "#endif\n"; + +static const char kVertexShaderSource[] = + "attribute vec2 aPosition;\n" + + "void main() {\n" + " gl_Position = vec4(aPosition, 0, 1);\n" + " vec2 uv = ((aPosition + 1.0) / 2.0) + 0.5 / kDimension;\n" + " vUV00 = uv;\n" + " #ifdef HORIZONTAL\n" + " vUV01 = uv + vec2( 1.0 / kDimension.x, 0);\n" + " #if FACTOR > 2\n" + " vUV02 = uv + vec2( 2.0 / kDimension.x, 0);\n" + " vUV03 = uv + vec2( 3.0 / kDimension.x, 0);\n" + " #if FACTOR > 4\n" + " vUV04 = uv + vec2( 4.0 / kDimension.x, 0);\n" + " vUV05 = uv + vec2( 5.0 / kDimension.x, 0);\n" + " vUV06 = uv + vec2( 6.0 / kDimension.x, 0);\n" + " vUV07 = uv + vec2( 7.0 / kDimension.x, 0);\n" + " #if FACTOR > 8\n" + " vUV08 = uv + vec2( 8.0 / kDimension.x, 0);\n" + " vUV09 = uv + vec2( 9.0 / kDimension.x, 0);\n" + " vUV10 = uv + vec2(10.0 / kDimension.x, 0);\n" + " vUV11 = uv + vec2(11.0 / kDimension.x, 0);\n" + " vUV12 = uv + vec2(12.0 / kDimension.x, 0);\n" + " vUV13 = uv + vec2(13.0 / kDimension.x, 0);\n" + " vUV14 = uv + vec2(14.0 / kDimension.x, 0);\n" + " vUV15 = uv + vec2(15.0 / kDimension.x, 0);\n" + " #endif\n" // FACTOR > 8 + " #endif\n" // FACTOR > 4 + " #endif\n" // FACTOR > 2 + + " #else\n" + " vUV01 = uv + vec2(0, 1.0 / kDimension.y);\n" + " #if FACTOR > 2\n" + " vUV02 = uv + vec2(0, 2.0 / kDimension.y);\n" + " vUV03 = uv + vec2(0, 3.0 / kDimension.y);\n" + " #if FACTOR > 4\n" + " vUV04 = uv + vec2(0, 4.0 / kDimension.y);\n" + " vUV05 = uv + vec2(0, 5.0 / kDimension.y);\n" + " vUV06 = uv + vec2(0, 6.0 / kDimension.y);\n" + " vUV07 = uv + vec2(0, 7.0 / kDimension.y);\n" + " #if FACTOR > 8\n" + " vUV08 = uv + vec2(0, 8.0 / kDimension.y);\n" + " vUV09 = uv + vec2(0, 9.0 / kDimension.y);\n" + " vUV10 = uv + vec2(0, 10.0 / kDimension.y);\n" + " vUV11 = uv + vec2(0, 11.0 / kDimension.y);\n" + " vUV12 = uv + vec2(0, 12.0 / kDimension.y);\n" + " vUV13 = uv + vec2(0, 13.0 / kDimension.y);\n" + " vUV14 = uv + vec2(0, 14.0 / kDimension.y);\n" + " vUV15 = uv + vec2(0, 15.0 / kDimension.y);\n" + " #endif\n" // FACTOR > 8 + " #endif\n" // FACTOR > 4 + " #endif\n" // FACTOR > 2 + " #endif\n" // HORIZONTAL/VERTICAL + "}\n"; + +const char kFragmentShaderSource[] = + "uniform sampler2D uTexture;\n" + + "vec4 read(vec2 uv) {\n" + " vec4 r = texture2D(uTexture, uv);\n" + " #ifdef HORIZONTAL\n" + " r.rgb = pow(r.rgb, vec3(2.2));\n" + " #endif\n" + " return r;\n" + "}\n" + + "void main() {\n" + " vec4 sum = read(vUV00) + read(vUV01);\n" + " #if FACTOR > 2\n" + " sum += read(vUV02) + read(vUV03);\n" + " #if FACTOR > 4\n" + " sum += read(vUV04) + read(vUV05) + read(vUV06) + read(vUV07);\n" + " #if FACTOR > 8\n" + " sum += read(vUV08) + read(vUV09) + read(vUV10) + read(vUV11) +" + " read(vUV12) + read(vUV13) + read(vUV14) + read(vUV15);\n" + " #endif\n" + " #endif\n" + " #endif\n" + " sum /= float(FACTOR);\n" + " #ifdef VERTICAL\n" + " sum.rgb = pow(sum.rgb, vec3(1.0 / 2.2));\n" + " #endif\n" + " gl_FragColor = sum;\n" + "}\n"; + +static const float kVertexData[] = {-1, -1, 3, -1, -1, 3}; + +static void detachShaders(GLuint program) { + GLuint shaders[2] = {}; + GLsizei count = 0; + s_gles2.glGetAttachedShaders(program, 2, &count, shaders); + if (s_gles2.glGetError() == GL_NO_ERROR) { + for (GLsizei i = 0; i < count; i++) { + s_gles2.glDetachShader(program, shaders[i]); + s_gles2.glDeleteShader(shaders[i]); + } + } +} + +static GLuint createShader(GLenum type, const std::initializer_list& source) { + GLint success, infoLength; + + GLuint shader = s_gles2.glCreateShader(type); + if (shader) { + s_gles2.glShaderSource(shader, source.size(), source.begin(), nullptr); + s_gles2.glCompileShader(shader); + s_gles2.glGetShaderiv(shader, GL_COMPILE_STATUS, &success); + if (success == GL_FALSE) { + s_gles2.glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &infoLength); + std::string infoLog(infoLength + 1, '\0'); + s_gles2.glGetShaderInfoLog(shader, infoLength, nullptr, &infoLog[0]); + ERR("%s shader compile failed:\n%s\n", + (type == GL_VERTEX_SHADER) ? "Vertex" : "Fragment", + infoLog.c_str()); + s_gles2.glDeleteShader(shader); + shader = 0; + } + } + return shader; +} + +static void attachShaders(TextureResize::Framebuffer* fb, const char* factorDefine, + const char* dimensionDefine, GLuint width, GLuint height) { + + std::ostringstream dimensionConst; + dimensionConst << "const vec2 kDimension = vec2(" << width << ", " << height << ");\n"; + + GLuint vShader = createShader(GL_VERTEX_SHADER, { + factorDefine, dimensionDefine, + kCommonShaderSource, dimensionConst.str().c_str(), kVertexShaderSource + }); + GLuint fShader = createShader(GL_FRAGMENT_SHADER, { + factorDefine, dimensionDefine, kCommonShaderSource, kFragmentShaderSource + }); + + if (!vShader || !fShader) { + return; + } + + s_gles2.glAttachShader(fb->program, vShader); + s_gles2.glAttachShader(fb->program, fShader); + s_gles2.glLinkProgram(fb->program); + + s_gles2.glUseProgram(fb->program); + fb->aPosition = s_gles2.glGetAttribLocation(fb->program, "aPosition"); + fb->uTexture = s_gles2.glGetUniformLocation(fb->program, "uTexture"); +} + +TextureResize::TextureResize(GLuint width, GLuint height) : + mWidth(width), + mHeight(height), + mFactor(1), + mFBWidth({0,}), + mFBHeight({0,}) { + s_gles2.glGenTextures(1, &mFBWidth.texture); + s_gles2.glBindTexture(GL_TEXTURE_2D, mFBWidth.texture); + s_gles2.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + s_gles2.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + s_gles2.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + s_gles2.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + + s_gles2.glGenTextures(1, &mFBHeight.texture); + s_gles2.glBindTexture(GL_TEXTURE_2D, mFBHeight.texture); + s_gles2.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + s_gles2.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + s_gles2.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + s_gles2.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + + s_gles2.glGenFramebuffers(1, &mFBWidth.framebuffer); + s_gles2.glGenFramebuffers(1, &mFBHeight.framebuffer); + + mFBWidth.program = s_gles2.glCreateProgram(); + mFBHeight.program = s_gles2.glCreateProgram(); + + s_gles2.glGenBuffers(1, &mVertexBuffer); + s_gles2.glBindBuffer(GL_ARRAY_BUFFER, mVertexBuffer); + s_gles2.glBufferData(GL_ARRAY_BUFFER, sizeof(kVertexData), kVertexData, GL_STATIC_DRAW); +} + +TextureResize::~TextureResize() { + GLuint fb[2] = {mFBWidth.framebuffer, mFBHeight.framebuffer}; + s_gles2.glDeleteFramebuffers(2, fb); + + GLuint tex[2] = {mFBWidth.texture, mFBHeight.texture}; + s_gles2.glDeleteTextures(2, tex); + + s_gles2.glDeleteProgram(mFBWidth.program); + s_gles2.glDeleteProgram(mFBHeight.program); + + s_gles2.glDeleteBuffers(1, &mVertexBuffer); +} + +GLuint TextureResize::update(GLuint texture) { + // Store the viewport. The viewport is clobbered due to the framebuffers. + GLint vport[4] = { 0, }; + s_gles2.glGetIntegerv(GL_VIEWPORT, vport); + + // Correctly deal with rotated screens. + GLint tWidth = vport[2], tHeight = vport[3]; + if ((mWidth < mHeight) != (tWidth < tHeight)) { + std::swap(tWidth, tHeight); + } + + // Compute the scaling factor needed to get an image just larger than the target viewport. + unsigned int factor = 1; + for (int i = 0, w = mWidth / 2, h = mHeight / 2; + i < MAX_FACTOR_POWER && w >= tWidth && h >= tHeight; + i++, w /= 2, h /= 2, factor *= 2) { + } + + // No resizing needed. + if (factor == 1) { + return texture; + } + + s_gles2.glGetError(); // Clear any GL errors. + setupFramebuffers(factor); + resize(texture); + s_gles2.glViewport(vport[0], vport[1], vport[2], vport[3]); // Restore the viewport. + + // If there was an error while resizing, just use the unscaled texture. + GLenum error = s_gles2.glGetError(); + if (error != GL_NO_ERROR) { + ERR("GL error while resizing: 0x%x (ignored)\n", error); + return texture; + } + + return mFBHeight.texture; +} + +void TextureResize::setupFramebuffers(unsigned int factor) { + if (factor == mFactor) { + // The factor hasn't changed, no need to update the framebuffers. + return; + } + + // Update the framebuffer sizes to match the new factor. + s_gles2.glBindTexture(GL_TEXTURE_2D, mFBWidth.texture); + s_gles2.glTexImage2D( + GL_TEXTURE_2D, 0, GL_RGBA, mWidth / factor, mHeight, 0, GL_RGBA, GL_FLOAT, nullptr); + s_gles2.glBindFramebuffer(GL_FRAMEBUFFER, mFBWidth.framebuffer); + s_gles2.glFramebufferTexture2D( + GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, mFBWidth.texture, 0); + + s_gles2.glBindTexture(GL_TEXTURE_2D, mFBHeight.texture); + s_gles2.glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, mWidth / factor, mHeight / factor, 0, GL_RGBA, + GL_UNSIGNED_BYTE, nullptr); + s_gles2.glBindFramebuffer(GL_FRAMEBUFFER, mFBHeight.framebuffer); + s_gles2.glFramebufferTexture2D( + GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, mFBHeight.texture, 0); + + // Update the shaders to the new factor. First detach the old shaders... + detachShaders(mFBWidth.program); + detachShaders(mFBHeight.program); + + // ... then attach the new ones. + std::ostringstream factorDefine; + factorDefine << "#define FACTOR " << factor << "\n"; + attachShaders(&mFBWidth, factorDefine.str().c_str(), "#define HORIZONTAL\n", mWidth, mHeight); + attachShaders(&mFBHeight, factorDefine.str().c_str(), "#define VERTICAL\n", mWidth, mHeight); + + mFactor = factor; +} + +void TextureResize::resize(GLuint texture) { + s_gles2.glBindBuffer(GL_ARRAY_BUFFER, mVertexBuffer); + s_gles2.glActiveTexture(GL_TEXTURE0); + + // First scale the horizontal dimension by rendering the input texture to a scaled framebuffer. + s_gles2.glBindFramebuffer(GL_FRAMEBUFFER, mFBWidth.framebuffer); + s_gles2.glViewport(0, 0, mWidth / mFactor, mHeight); + s_gles2.glUseProgram(mFBWidth.program); + s_gles2.glEnableVertexAttribArray(mFBWidth.aPosition); + s_gles2.glVertexAttribPointer(mFBWidth.aPosition, 2, GL_FLOAT, GL_FALSE, 0, 0); + s_gles2.glBindTexture(GL_TEXTURE_2D, texture); + + // Store the current texture filters and set to nearest for scaling. + GLint mag_filter, min_filter; + s_gles2.glGetTexParameteriv(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, &mag_filter); + s_gles2.glGetTexParameteriv(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, &min_filter); + s_gles2.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + s_gles2.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + s_gles2.glUniform1i(mFBWidth.uTexture, 0); + s_gles2.glDrawArrays(GL_TRIANGLES, 0, sizeof(kVertexData) / (2 * sizeof(float))); + + // Restore the previous texture filters. + s_gles2.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, mag_filter); + s_gles2.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, min_filter); + + // Secondly, scale the vertical dimension using the second framebuffer. + s_gles2.glBindFramebuffer(GL_FRAMEBUFFER, mFBHeight.framebuffer); + s_gles2.glViewport(0, 0, mWidth / mFactor, mHeight / mFactor); + s_gles2.glUseProgram(mFBHeight.program); + s_gles2.glEnableVertexAttribArray(mFBHeight.aPosition); + s_gles2.glVertexAttribPointer(mFBHeight.aPosition, 2, GL_FLOAT, GL_FALSE, 0, 0); + s_gles2.glBindTexture(GL_TEXTURE_2D, mFBWidth.texture); + s_gles2.glUniform1i(mFBHeight.uTexture, 0); + s_gles2.glDrawArrays(GL_TRIANGLES, 0, sizeof(kVertexData) / (2 * sizeof(float))); + + // Clear the bindings. + s_gles2.glBindBuffer(GL_ARRAY_BUFFER, 0); + s_gles2.glBindFramebuffer(GL_FRAMEBUFFER, 0); + s_gles2.glBindTexture(GL_TEXTURE_2D, 0); +} diff --git a/external/android-emugl/host/libs/libOpenglRender/TextureResize.h b/external/android-emugl/host/libs/libOpenglRender/TextureResize.h new file mode 100644 index 0000000..3dd9d31 --- /dev/null +++ b/external/android-emugl/host/libs/libOpenglRender/TextureResize.h @@ -0,0 +1,51 @@ +/* +* Copyright (C) 2015 The Android Open Source Project +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ +#ifndef _LIBRENDER_TEXTURERESIZE_H +#define _LIBRENDER_TEXTURERESIZE_H + +#include + +class TextureResize { +public: + TextureResize(GLuint width, GLuint height); + ~TextureResize(); + + // Scales the given texture for the current viewport and returns the scaled + // texture. May return the input if no scaling is required. + GLuint update(GLuint texture); + + struct Framebuffer { + GLuint texture; + GLuint framebuffer; + GLuint program; + GLuint aPosition; + GLuint uTexture; + }; + +private: + void setupFramebuffers(unsigned int factor); + void resize(GLuint texture); + +private: + GLuint mWidth; + GLuint mHeight; + unsigned int mFactor; + Framebuffer mFBWidth; + Framebuffer mFBHeight; + GLuint mVertexBuffer; +}; + +#endif diff --git a/external/android-emugl/host/libs/libOpenglRender/TimeUtils.cpp b/external/android-emugl/host/libs/libOpenglRender/TimeUtils.cpp new file mode 100644 index 0000000..50aeb03 --- /dev/null +++ b/external/android-emugl/host/libs/libOpenglRender/TimeUtils.cpp @@ -0,0 +1,69 @@ +/* +* Copyright (C) 2011 The Android Open Source Project +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ +#include "TimeUtils.h" + +#ifdef _WIN32 +#include +#include +#include +#elif defined(__linux__) +#include +#include +#include +#include +#else +#include +#include +#endif + +long long GetCurrentTimeMS() +{ +#ifdef _WIN32 + static LARGE_INTEGER freq; + static bool bNotInit = true; + if ( bNotInit ) { + bNotInit = (QueryPerformanceFrequency( &freq ) == FALSE); + } + LARGE_INTEGER currVal; + QueryPerformanceCounter( &currVal ); + + return currVal.QuadPart / (freq.QuadPart / 1000); + +#elif defined(__linux__) + + struct timespec now; + clock_gettime(CLOCK_MONOTONIC, &now); + long long iDiff = (now.tv_sec * 1000LL) + now.tv_nsec/1000000LL; + return iDiff; + +#else /* Others, e.g. OS X */ + + struct timeval now; + gettimeofday(&now, NULL); + long long iDiff = (now.tv_sec * 1000LL) + now.tv_usec/1000LL; + return iDiff; + +#endif +} + +void TimeSleepMS(int p_mili) +{ +#ifdef _WIN32 + Sleep(p_mili); +#else + usleep(p_mili * 1000); +#endif +} diff --git a/external/android-emugl/host/libs/libOpenglRender/TimeUtils.h b/external/android-emugl/host/libs/libOpenglRender/TimeUtils.h new file mode 100644 index 0000000..bc4fd1c --- /dev/null +++ b/external/android-emugl/host/libs/libOpenglRender/TimeUtils.h @@ -0,0 +1,22 @@ +/* +* Copyright (C) 2011 The Android Open Source Project +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ +#ifndef _TIME_UTILS_H +#define _TIME_UTILS_H + +long long GetCurrentTimeMS(); +void TimeSleepMS(int p_mili); + +#endif diff --git a/external/android-emugl/host/libs/libOpenglRender/UnixStream.cpp b/external/android-emugl/host/libs/libOpenglRender/UnixStream.cpp new file mode 100644 index 0000000..49b02fe --- /dev/null +++ b/external/android-emugl/host/libs/libOpenglRender/UnixStream.cpp @@ -0,0 +1,174 @@ +/* +* Copyright (C) 2011 The Android Open Source Project +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ +#include "UnixStream.h" + +#include "emugl/common/sockets.h" + +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +/* Not all systems define PATH_MAX, those who don't generally don't + * have a limit on the maximum path size, so use a value that is + * large enough for our very limited needs. + */ +#ifndef PATH_MAX +#define PATH_MAX 128 +#endif + +UnixStream::UnixStream(size_t bufSize) : + SocketStream(bufSize), + bound_socket_path(NULL) +{ +} + +UnixStream::UnixStream(int sock, size_t bufSize) : + SocketStream(sock, bufSize), + bound_socket_path(NULL) +{ +} + +UnixStream::~UnixStream() +{ + if (bound_socket_path != NULL) { + int ret = 0; + do { + ret = unlink(bound_socket_path); + } while (ret < 0 && errno == EINTR); + if(ret != 0) { + ERR("Failed to unlink UNIX socket at \"%s\"\n", bound_socket_path); + perror("UNIX socket could not be unlinked"); + } + free(bound_socket_path); + } +} + +/* Initialize a sockaddr_un with the appropriate values corresponding + * to a given 'virtual port'. Returns 0 on success, -1 on error. + */ +static int +make_unix_path(char *path, size_t pathlen, int port_number) +{ + char tmp[PATH_MAX]; // temp directory + int ret = 0; + + // First, create user-specific temp directory if needed + const char* user = getenv("XDG_RUNTIME_DIR"); + if (user != NULL) { + struct stat st; + snprintf(tmp, sizeof(tmp), "%s/anbox", user); + do { + ret = ::lstat(tmp, &st); + } while (ret < 0 && errno == EINTR); + + if (ret < 0 && errno == ENOENT) { + do { + ret = ::mkdir(tmp, 0766); + } while (ret < 0 && errno == EINTR); + if (ret < 0) { + ERR("Could not create temp directory: %s", tmp); + user = NULL; // will fall-back to /tmp + } + } + else if (ret < 0) { + user = NULL; // will fallback to /tmp + } + } + + if (user == NULL) { // fallback to /tmp in case of error + snprintf(tmp, sizeof(tmp), "/tmp"); + } + + // Now, initialize it properly + snprintf(path, pathlen, "%s/qemu-gles-%d", tmp, port_number); + + // If the emulator is killed, it can leave the socket file behind. + // Since the filename has PID in it, we can be sure that this socket + // is not supposed to be here and delete it, to prevent EADDRINUSE + // later in bind() + if (::access(path, F_OK) == 0) { + ret = ::remove(path); + if (ret < 0) { + ERR("Failed to remove stale socket file at %s: %s\n", path, strerror(errno)); + } else { + DBG("Stale socket file at %s was removed.\n", path); + } + } + + return 0; +} + + +int UnixStream::listen(char addrstr[MAX_ADDRSTR_LEN]) +{ + if (make_unix_path(addrstr, MAX_ADDRSTR_LEN, getpid()) < 0) { + return -1; + } + + m_sock = emugl::socketLocalServer(addrstr, SOCK_STREAM); + + if (!valid()) + return int(ERR_INVALID_SOCKET); + + bound_socket_path = strdup(addrstr); + if(bound_socket_path == NULL) { + ERR("WARNING: UNIX socket at \"%s\" should be manually removed \n", + addrstr); + return -1; + } + + return 0; +} + +SocketStream * UnixStream::accept() +{ + int clientSock = -1; + + while (true) { + struct sockaddr_un addr; + socklen_t len = sizeof(addr); + clientSock = ::accept(m_sock, (sockaddr *)&addr, &len); + // DBG("UnixStream::accept @ %d \n", clientSock); + + if (clientSock < 0 && errno == EINTR) { + continue; + } + break; + } + + UnixStream *clientStream = NULL; + + if (clientSock >= 0) { + clientStream = new UnixStream(clientSock, m_bufsize); + } + return clientStream; +} + +int UnixStream::connect(const char* addr) +{ + m_sock = emugl::socketLocalClient(addr, SOCK_STREAM); + // DBG("UnixStream::connect @ %d \n", m_sock); + if (!valid()) return -1; + + return 0; +} diff --git a/external/android-emugl/host/libs/libOpenglRender/UnixStream.h b/external/android-emugl/host/libs/libOpenglRender/UnixStream.h new file mode 100644 index 0000000..5e2025d --- /dev/null +++ b/external/android-emugl/host/libs/libOpenglRender/UnixStream.h @@ -0,0 +1,33 @@ +/* +* Copyright (C) 2011 The Android Open Source Project +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ +#ifndef __UNIX_STREAM_H +#define __UNIX_STREAM_H + +#include "SocketStream.h" + +class UnixStream : public SocketStream { +public: + explicit UnixStream(size_t bufsize = 10000); + ~UnixStream(); + virtual int listen(char addrstr[MAX_ADDRSTR_LEN]); + virtual SocketStream *accept(); + virtual int connect(const char* addr); +private: + char *bound_socket_path; + UnixStream(int sock, size_t bufSize); +}; + +#endif diff --git a/external/android-emugl/host/libs/libOpenglRender/Win32PipeStream.cpp b/external/android-emugl/host/libs/libOpenglRender/Win32PipeStream.cpp new file mode 100644 index 0000000..81ee202 --- /dev/null +++ b/external/android-emugl/host/libs/libOpenglRender/Win32PipeStream.cpp @@ -0,0 +1,241 @@ +/* +* Copyright (C) 2011 The Android Open Source Project +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ +#include "Win32PipeStream.h" + +#include +#include +#include +#include +#include +#include + +#ifndef _WIN32 +#error ONLY BUILD THIS SOURCE FILE FOR WINDOWS! +#endif + +/* The official documentation states that the name of a given named + * pipe cannot be more than 256 characters long. + */ +#define NAMED_PIPE_MAX 256 + +Win32PipeStream::Win32PipeStream(size_t bufSize) : + SocketStream(bufSize), + m_pipe(INVALID_HANDLE_VALUE) +{ +} + +Win32PipeStream::Win32PipeStream(HANDLE pipe, size_t bufSize) : + SocketStream(-1, bufSize), + m_pipe(pipe) +{ +} + +Win32PipeStream::~Win32PipeStream() +{ + if (m_pipe != INVALID_HANDLE_VALUE) { + CloseHandle(m_pipe); + m_pipe = INVALID_HANDLE_VALUE; + } +} + +/* Initialize the pipe name corresponding to a given port + */ +static void +make_pipe_name(char *path, size_t pathlen, int port_number) +{ + snprintf(path, pathlen, "\\\\.\\pipe\\qemu-gles-%d", port_number); +} + + +/* Technical note: Named pipes work differently from BSD Sockets. + * One does not create/bind a pipe, and collect a new handle each + * time a client connects with accept(). + * + * Instead, the server creates a new pipe instance each time it wants + * to get a new client connection, then calls ConnectNamedPipe() to + * wait for a connection. + * + * So listen() is a no-op, and accept() really creates the pipe handle. + * + * Also, connect() must create a pipe handle with CreateFile() and + * wait for a server instance with WaitNamedPipe() + */ +int Win32PipeStream::listen(char addrstr[MAX_ADDRSTR_LEN]) +{ + m_port = GetCurrentProcessId(); + make_pipe_name(addrstr, MAX_ADDRSTR_LEN, m_port); + return 0; +} + +SocketStream * Win32PipeStream::accept() +{ + char path[NAMED_PIPE_MAX+1]; + SocketStream* clientStream; + HANDLE pipe; + + make_pipe_name(path, sizeof(path), m_port); + + pipe = ::CreateNamedPipe( + path, // pipe name + PIPE_ACCESS_DUPLEX, // read-write access + PIPE_TYPE_BYTE | // byte-oriented writes + PIPE_READMODE_BYTE | // byte-oriented reads + PIPE_WAIT, // blocking operations + PIPE_UNLIMITED_INSTANCES, // no limit on clients + 4096, // input buffer size + 4096, // output buffer size + 0, // client time-out + NULL); // default security attributes + + if (pipe == INVALID_HANDLE_VALUE) { + ERR("%s: CreateNamedPipe failed %d\n", __FUNCTION__, (int)GetLastError()); + return NULL; + } + + // Stupid Win32 API design: If a client is already connected, then + // ConnectNamedPipe will return 0, and GetLastError() will return + // ERROR_PIPE_CONNECTED. This is not an error! It just means that the + // function didn't have to wait. + // + if (::ConnectNamedPipe(pipe, NULL) == 0 && GetLastError() != ERROR_PIPE_CONNECTED) { + ERR("%s: ConnectNamedPipe failed: %d\n", __FUNCTION__, (int)GetLastError()); + CloseHandle(pipe); + return NULL; + } + + clientStream = new Win32PipeStream(pipe, m_bufsize); + return clientStream; +} + +int Win32PipeStream::connect(const char* addr) +{ + HANDLE pipe; + int tries = 10; + + /* We're going to loop in order to wait for the pipe server to + * be setup properly. + */ + for (; tries > 0; tries--) { + pipe = ::CreateFile( + addr, // pipe name + GENERIC_READ | GENERIC_WRITE, // read & write + 0, // no sharing + NULL, // default security attrs + OPEN_EXISTING, // open existing pipe + 0, // default attributes + NULL); // no template file + + /* If we have a valid pipe handle, break from the loop */ + if (pipe != INVALID_HANDLE_VALUE) { + break; + } + + /* We can get here if the pipe is busy, i.e. if the server hasn't + * create a new pipe instance to service our request. In which case + * GetLastError() will return ERROR_PIPE_BUSY. + * + * If so, then use WaitNamedPipe() to wait for a decent time + * to try again. + */ + if (GetLastError() != ERROR_PIPE_BUSY) { + /* Not ERROR_PIPE_BUSY */ + ERR("%s: CreateFile failed: %d\n", __FUNCTION__, (int)GetLastError()); + errno = EINVAL; + return -1; + } + + /* Wait for 5 seconds */ + if ( !WaitNamedPipe(addr, 5000) ) { + ERR("%s: WaitNamedPipe failed: %d\n", __FUNCTION__, (int)GetLastError()); + errno = EINVAL; + return -1; + } + } + + m_pipe = pipe; + return 0; +} + +/* Special buffer methods, since we can't use socket functions here */ + +int Win32PipeStream::commitBuffer(size_t size) +{ + if (m_pipe == INVALID_HANDLE_VALUE) + return -1; + + size_t res = size; + int retval = 0; + + while (res > 0) { + DWORD written; + if (! ::WriteFile(m_pipe, (const char *)m_buf + (size - res), res, &written, NULL)) { + retval = -1; + ERR("%s: failed: %d\n", __FUNCTION__, (int)GetLastError()); + break; + } + res -= written; + } + return retval; +} + +const unsigned char *Win32PipeStream::readFully(void *buf, size_t len) +{ + if (m_pipe == INVALID_HANDLE_VALUE) + return NULL; + + if (!buf) { + return NULL; // do not allow NULL buf in that implementation + } + + size_t res = len; + while (res > 0) { + DWORD readcount = 0; + if (! ::ReadFile(m_pipe, (char *)buf + (len - res), res, &readcount, NULL) || readcount == 0) { + errno = (int)GetLastError(); + return NULL; + } + res -= readcount; + } + return (const unsigned char *)buf; +} + +const unsigned char *Win32PipeStream::read( void *buf, size_t *inout_len) +{ + size_t len = *inout_len; + DWORD readcount; + + if (m_pipe == INVALID_HANDLE_VALUE) + return NULL; + + if (!buf) { + return NULL; // do not allow NULL buf in that implementation + } + + if (!::ReadFile(m_pipe, (char *)buf, len, &readcount, NULL)) { + errno = (int)GetLastError(); + return NULL; + } + + *inout_len = (size_t)readcount; + return (const unsigned char *)buf; +} + +void Win32PipeStream::forceStop() +{ + HANDLE handle = m_pipe; + m_pipe = INVALID_HANDLE_VALUE; + CloseHandle(handle); +} diff --git a/external/android-emugl/host/libs/libOpenglRender/Win32PipeStream.h b/external/android-emugl/host/libs/libOpenglRender/Win32PipeStream.h new file mode 100644 index 0000000..be44074 --- /dev/null +++ b/external/android-emugl/host/libs/libOpenglRender/Win32PipeStream.h @@ -0,0 +1,42 @@ +/* +* Copyright (C) 2011 The Android Open Source Project +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ +#ifndef __WIN32_PIPE_STREAM_H +#define __WIN32_PIPE_STREAM_H + +#include "SocketStream.h" +#include + +class Win32PipeStream : public SocketStream { +public: + explicit Win32PipeStream(size_t bufsize = 10000); + virtual ~Win32PipeStream(); + virtual int listen(char addrstr[MAX_ADDRSTR_LEN]); + virtual SocketStream *accept(); + virtual int connect(const char* addr); + + virtual int commitBuffer(size_t size); + virtual const unsigned char *readFully(void *buf, size_t len); + virtual const unsigned char *read(void *buf, size_t *inout_len); + virtual void forceStop(); + +private: + Win32PipeStream(HANDLE pipe, size_t bufSize); + HANDLE m_pipe; + int m_port; +}; + + +#endif diff --git a/external/android-emugl/host/libs/libOpenglRender/WindowSurface.cpp b/external/android-emugl/host/libs/libOpenglRender/WindowSurface.cpp new file mode 100644 index 0000000..553b109 --- /dev/null +++ b/external/android-emugl/host/libs/libOpenglRender/WindowSurface.cpp @@ -0,0 +1,187 @@ +/* +* Copyright (C) 2011 The Android Open Source Project +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ +#include "WindowSurface.h" + +#include "ErrorLog.h" +#include "FbConfig.h" + +#include "OpenGLESDispatch/EGLDispatch.h" + +#include + +#include +#include + + +WindowSurface::WindowSurface(EGLDisplay display, + EGLConfig config) : + mSurface(NULL), + mAttachedColorBuffer(NULL), + mReadContext(NULL), + mDrawContext(NULL), + mWidth(0), + mHeight(0), + mConfig(config), + mDisplay(display) {} + +WindowSurface::~WindowSurface() { + if (mSurface) { + s_egl.eglDestroySurface(mDisplay, mSurface); + } +} + +WindowSurface *WindowSurface::create(EGLDisplay display, + EGLConfig config, + int p_width, + int p_height) { + // allocate space for the WindowSurface object + WindowSurface *win = new WindowSurface(display, config); + if (!win) { + return NULL; + } + + // Create a pbuffer to be used as the egl surface + // for that window. + if (!win->resize(p_width, p_height)) { + delete win; + return NULL; + } + + return win; +} + + +void WindowSurface::setColorBuffer(ColorBufferPtr p_colorBuffer) { + mAttachedColorBuffer = p_colorBuffer; + + // resize the window if the attached color buffer is of different + // size. + unsigned int cbWidth = mAttachedColorBuffer->getWidth(); + unsigned int cbHeight = mAttachedColorBuffer->getHeight(); + + if (cbWidth != mWidth || cbHeight != mHeight) { + resize(cbWidth, cbHeight); + } +} + +void WindowSurface::bind(RenderContextPtr p_ctx, BindType p_bindType) { + if (p_bindType == BIND_READ) { + mReadContext = p_ctx; + } else if (p_bindType == BIND_DRAW) { + mDrawContext = p_ctx; + } else if (p_bindType == BIND_READDRAW) { + mReadContext = p_ctx; + mDrawContext = p_ctx; + } +} + +bool WindowSurface::flushColorBuffer() { + if (!mAttachedColorBuffer.Ptr()) { + return true; + } + if (!mWidth || !mHeight) { + return false; + } + + if (mAttachedColorBuffer->getWidth() != mWidth || + mAttachedColorBuffer->getHeight() != mHeight) { + // XXX: should never happen - how this needs to be handled? + fprintf(stderr, "Dimensions do not match\n"); + return false; + } + + if (!mDrawContext.Ptr()) { + fprintf(stderr, "Draw context is NULL\n"); + return false; + } + + // Make the surface current + EGLContext prevContext = s_egl.eglGetCurrentContext(); + EGLSurface prevReadSurf = s_egl.eglGetCurrentSurface(EGL_READ); + EGLSurface prevDrawSurf = s_egl.eglGetCurrentSurface(EGL_DRAW); + + if (!s_egl.eglMakeCurrent(mDisplay, + mSurface, + mSurface, + mDrawContext->getEGLContext())) { + fprintf(stderr, "Error making draw context current\n"); + return false; + } + + mAttachedColorBuffer->blitFromCurrentReadBuffer(); + + // restore current context/surface + s_egl.eglMakeCurrent(mDisplay, prevDrawSurf, prevReadSurf, prevContext); + + return true; +} + +bool WindowSurface::resize(unsigned int p_width, unsigned int p_height) +{ + if (mSurface && mWidth == p_width && mHeight == p_height) { + // no need to resize + return true; + } + + EGLContext prevContext = s_egl.eglGetCurrentContext(); + EGLSurface prevReadSurf = s_egl.eglGetCurrentSurface(EGL_READ); + EGLSurface prevDrawSurf = s_egl.eglGetCurrentSurface(EGL_DRAW); + EGLSurface prevPbuf = mSurface; + bool needRebindContext = mSurface && + (prevReadSurf == mSurface || + prevDrawSurf == mSurface); + + if (needRebindContext) { + s_egl.eglMakeCurrent( + mDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); + } + + // + // Destroy previous surface + // + if (mSurface) { + s_egl.eglDestroySurface(mDisplay, mSurface); + mSurface = NULL; + } + + // + // Create pbuffer surface. + // + const EGLint pbufAttribs[5] = { + EGL_WIDTH, (EGLint) p_width, EGL_HEIGHT, (EGLint) p_height, EGL_NONE, + }; + + mSurface = s_egl.eglCreatePbufferSurface(mDisplay, + mConfig, + pbufAttribs); + if (mSurface == EGL_NO_SURFACE) { + fprintf(stderr, "Renderer error: failed to create/resize pbuffer!!\n"); + return false; + } + + mWidth = p_width; + mHeight = p_height; + + if (needRebindContext) { + s_egl.eglMakeCurrent( + mDisplay, + (prevDrawSurf == prevPbuf) ? mSurface : prevDrawSurf, + (prevReadSurf == prevPbuf) ? mSurface : prevReadSurf, + prevContext); + } + + return true; +} diff --git a/external/android-emugl/host/libs/libOpenglRender/WindowSurface.h b/external/android-emugl/host/libs/libOpenglRender/WindowSurface.h new file mode 100644 index 0000000..5e302be --- /dev/null +++ b/external/android-emugl/host/libs/libOpenglRender/WindowSurface.h @@ -0,0 +1,103 @@ +/* +* Copyright (C) 2011 The Android Open Source Project +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ +#ifndef _LIBRENDER_WINDOW_SURFACE_H +#define _LIBRENDER_WINDOW_SURFACE_H + +#include "ColorBuffer.h" +#include "RenderContext.h" + +#include "emugl/common/smart_ptr.h" + +#include +#include + +// A class used to model a guest-side window surface. The implementation +// uses a host Pbuffer to act as the EGL rendering surface instead. +class WindowSurface { +public: + // Create a new WindowSurface instance. + // |display| is the host EGLDisplay value. + // |config| is the host EGLConfig value. + // |width| and |height| are the initial size of the Pbuffer. + // Return a new WindowSurface instance on success, or NULL on failure. + static WindowSurface* create(EGLDisplay display, + EGLConfig config, + int width, + int height); + + // Destructor. + ~WindowSurface(); + + // Retrieve the host EGLSurface of the WindowSurface's Pbuffer. + EGLSurface getEGLSurface() const { return mSurface; } + + // Attach a ColorBuffer to this WindowSurface. + // Once attached, calling flushColorBuffer() will copy the Pbuffer's + // pixels to the color buffer. + // + // IMPORTANT: This automatically resizes the Pbuffer's to the ColorBuffer's + // dimensions. Potentially losing pixel values in the process. + void setColorBuffer(ColorBufferPtr p_colorBuffer); + + // Copy the Pbuffer's pixels to the attached color buffer. + // Returns true on success, or false on error (e.g. if there is no + // attached color buffer). + bool flushColorBuffer(); + + // Used by bind() below. + enum BindType { + BIND_READ, + BIND_DRAW, + BIND_READDRAW + }; + + // TODO(digit): What is this used for exactly? For example, the + // mReadContext is never used by this class. The mDrawContext is only + // used temporarily during flushColorBuffer() operation, and could be + // passed as a parameter to the function instead. Maybe this is only used + // to increment reference counts on the smart pointers. + // + // Bind a context to the WindowSurface (huh? Normally you would bind a + // surface to the context, not the other way around) + // + // |p_ctx| is a RenderContext pointer. + // |p_bindType| is the type of bind. For BIND_READ, this assigns |p_ctx| + // to mReadContext, for BIND_DRAW, it assigns it to mDrawContext, and for + // for BIND_READDRAW, it assigns it to both. + void bind(RenderContextPtr p_ctx, BindType p_bindType); + +private: + WindowSurface(); + WindowSurface(const WindowSurface& other); + + explicit WindowSurface(EGLDisplay display, EGLConfig config); + + bool resize(unsigned int p_width, unsigned int p_height); + +private: + EGLSurface mSurface; + ColorBufferPtr mAttachedColorBuffer; + RenderContextPtr mReadContext; + RenderContextPtr mDrawContext; + GLuint mWidth; + GLuint mHeight; + EGLConfig mConfig; + EGLDisplay mDisplay; +}; + +typedef emugl::SmartPtr WindowSurfacePtr; + +#endif // _LIBRENDER_WINDOW_SURFACE_H diff --git a/external/android-emugl/host/libs/libOpenglRender/render_api.cpp b/external/android-emugl/host/libs/libOpenglRender/render_api.cpp new file mode 100644 index 0000000..970477c --- /dev/null +++ b/external/android-emugl/host/libs/libOpenglRender/render_api.cpp @@ -0,0 +1,338 @@ +/* +* Copyright (C) 2011-2015 The Android Open Source Project +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ +#include "OpenglRender/render_api.h" + +#include "IOStream.h" +#include "RenderServer.h" +#include "RenderWindow.h" +#include "TimeUtils.h" + +#include "TcpStream.h" +#ifdef _WIN32 +#include "Win32PipeStream.h" +#else +#include "UnixStream.h" +#endif + +#include "DispatchTables.h" + +#include "OpenGLESDispatch/EGLDispatch.h" +#include "OpenGLESDispatch/GLESv1Dispatch.h" +#include "OpenGLESDispatch/GLESv2Dispatch.h" + +#include "emugl/common/crash_reporter.h" +#include "emugl/common/logging.h" + +#include + +GLESv2Dispatch s_gles2; +GLESv1Dispatch s_gles1; +static RenderServer* s_renderThread = NULL; +static char s_renderAddr[256]; + +static RenderWindow* s_renderWindow = NULL; + +static IOStream *createRenderThread(int p_stream_buffer_size, + unsigned int clientFlags); + +RENDER_APICALL int RENDER_APIENTRY initLibrary(void) +{ + // + // Load EGL Plugin + // + if (!init_egl_dispatch()) { + // Failed to load EGL + printf("Failed to init_egl_dispatch\n"); + return false; + } + + // + // Load GLES Plugin + // + if (!gles1_dispatch_init(&s_gles1)) { + // Failed to load GLES + ERR("Failed to gles1_dispatch_init\n"); + return false; + } + + /* failure to init the GLES2 dispatch table is not fatal */ + if (!gles2_dispatch_init(&s_gles2)) { + ERR("Failed to gles2_dispatch_init\n"); + return false; + } + + return true; +} + +RENDER_APICALL int RENDER_APIENTRY initOpenGLRenderer( + int width, int height, bool useSubWindow, char* addr, size_t addrLen, + emugl_logger_struct logfuncs, emugl_crash_func_t crashfunc) { + set_emugl_crash_reporter(crashfunc); + set_emugl_logger(logfuncs.coarse); + set_emugl_cxt_logger(logfuncs.fine); + // + // Fail if renderer is already initialized + // + if (s_renderThread) { + return false; + } + + // kUseThread is used to determine whether the RenderWindow should use + // a separate thread to manage its subwindow GL/GLES context. + // For now, this feature is disabled entirely for the following + // reasons: + // + // - It must be disabled on Windows at all times, otherwise the main window becomes + // unresponsive after a few seconds of user interaction (e.g. trying to + // move it over the desktop). Probably due to the subtle issues around + // input on this platform (input-queue is global, message-queue is + // per-thread). Also, this messes considerably the display of the + // main window when running the executable under Wine. + // + // - On Linux/XGL and OSX/Cocoa, this used to be necessary to avoid corruption + // issues with the GL state of the main window when using the SDL UI. + // After the switch to Qt, this is no longer necessary and may actually cause + // undesired interactions between the UI thread and the RenderWindow thread: + // for example, in a multi-monitor setup the context might be recreated when + // dragging the window between monitors, triggering a Qt-specific callback + // in the context of RenderWindow thread, which will become blocked on the UI + // thread, which may in turn be blocked on something else. + bool kUseThread = false; + + // + // initialize the renderer and listen to connections + // on a thread in the current process. + // + s_renderWindow = new RenderWindow(width, height, kUseThread, useSubWindow); + if (!s_renderWindow) { + ERR("Could not create rendering window class"); + GL_LOG("Could not create rendering window class"); + return false; + } + if (!s_renderWindow->isValid()) { + ERR("Could not initialize emulated framebuffer\n"); + delete s_renderWindow; + s_renderWindow = NULL; + return false; + } + + s_renderThread = RenderServer::create(addr, addrLen); + if (!s_renderThread) { + return false; + } + strncpy(s_renderAddr, addr, sizeof(s_renderAddr)); + + s_renderThread->start(); + + GL_LOG("OpenGL renderer initialized successfully"); + return true; +} + +RENDER_APICALL void RENDER_APIENTRY setPostCallback( + OnPostFn onPost, void* onPostContext) { + if (s_renderWindow) { + s_renderWindow->setPostCallback(onPost, onPostContext); + } else { + ERR("Calling setPostCallback() before creating render window!"); + } +} + +RENDER_APICALL void RENDER_APIENTRY getHardwareStrings( + const char** vendor, + const char** renderer, + const char** version) { + if (s_renderWindow && + s_renderWindow->getHardwareStrings(vendor, renderer, version)) { + return; + } + *vendor = *renderer = *version = NULL; +} + +RENDER_APICALL int RENDER_APIENTRY stopOpenGLRenderer(void) +{ + bool ret = false; + + // open a dummy connection to the renderer to make it + // realize the exit request. + // (send the exit request in clientFlags) + IOStream *dummy = createRenderThread(8, IOSTREAM_CLIENT_EXIT_SERVER); + if (!dummy) return false; + + if (s_renderThread) { + // wait for the thread to exit + ret = s_renderThread->wait(NULL); + + delete s_renderThread; + s_renderThread = NULL; + } + + if (s_renderWindow != NULL) { + delete s_renderWindow; + s_renderWindow = NULL; + } + + delete dummy; + + return ret; +} + +RENDER_APICALL bool RENDER_APIENTRY showOpenGLSubwindow( + FBNativeWindowType window_id, + int wx, + int wy, + int ww, + int wh, + int fbw, + int fbh, + float dpr, + float zRot) +{ + RenderWindow* window = s_renderWindow; + + if (window) { + return window->setupSubWindow(window_id,wx,wy,ww,wh,fbw,fbh,dpr,zRot); + } + // XXX: should be implemented by sending the renderer process + // a request + ERR("%s not implemented for separate renderer process !!!\n", + __FUNCTION__); + return false; +} + +RENDER_APICALL bool RENDER_APIENTRY destroyOpenGLSubwindow(void) +{ + RenderWindow* window = s_renderWindow; + + if (window) { + return window->removeSubWindow(); + } + + // XXX: should be implemented by sending the renderer process + // a request + ERR("%s not implemented for separate renderer process !!!\n", + __FUNCTION__); + return false; +} + +RENDER_APICALL void RENDER_APIENTRY setOpenGLDisplayRotation(float zRot) +{ + RenderWindow* window = s_renderWindow; + + if (window) { + window->setRotation(zRot); + return; + } + // XXX: should be implemented by sending the renderer process + // a request + ERR("%s not implemented for separate renderer process !!!\n", + __FUNCTION__); +} + +RENDER_APICALL void RENDER_APIENTRY setOpenGLDisplayTranslation(float px, float py) +{ + RenderWindow* window = s_renderWindow; + + if (window) { + window->setTranslation(px, py); + return; + } + // XXX: should be implemented by sending the renderer process + // a request + ERR("%s not implemented for separate renderer process !!!\n", + __FUNCTION__); +} + +RENDER_APICALL void RENDER_APIENTRY repaintOpenGLDisplay(void) +{ + RenderWindow* window = s_renderWindow; + + if (window) { + window->repaint(); + return; + } + // XXX: should be implemented by sending the renderer process + // a request + ERR("%s not implemented for separate renderer process !!!\n", + __FUNCTION__); +} + + +/* NOTE: For now, always use TCP mode by default, until the emulator + * has been updated to support Unix and Win32 pipes + */ +#define DEFAULT_STREAM_MODE RENDER_API_STREAM_MODE_TCP + +int gRendererStreamMode = DEFAULT_STREAM_MODE; + +IOStream *createRenderThread(int p_stream_buffer_size, unsigned int clientFlags) +{ + SocketStream* stream = NULL; + + if (gRendererStreamMode == RENDER_API_STREAM_MODE_TCP) { + stream = new TcpStream(p_stream_buffer_size); + } else { +#ifdef _WIN32 + stream = new Win32PipeStream(p_stream_buffer_size); +#else /* !_WIN32 */ + stream = new UnixStream(p_stream_buffer_size); +#endif + } + + if (!stream) { + ERR("createRenderThread failed to create stream\n"); + return NULL; + } + if (stream->connect(s_renderAddr) < 0) { + ERR("createRenderThread failed to connect\n"); + delete stream; + return NULL; + } + + // + // send clientFlags to the renderer + // + unsigned int *pClientFlags = + (unsigned int *)stream->allocBuffer(sizeof(unsigned int)); + *pClientFlags = clientFlags; + stream->commitBuffer(sizeof(unsigned int)); + + return stream; +} + +RENDER_APICALL int RENDER_APIENTRY setStreamMode(int mode) +{ + switch (mode) { + case RENDER_API_STREAM_MODE_DEFAULT: + mode = DEFAULT_STREAM_MODE; + break; + + case RENDER_API_STREAM_MODE_TCP: + break; + +#ifndef _WIN32 + case RENDER_API_STREAM_MODE_UNIX: + break; +#else /* _WIN32 */ + case RENDER_API_STREAM_MODE_PIPE: + break; +#endif /* _WIN32 */ + default: + // Invalid stream mode + return false; + } + gRendererStreamMode = mode; + return true; +} diff --git a/external/android-emugl/host/libs/libOpenglRender/render_api.entries b/external/android-emugl/host/libs/libOpenglRender/render_api.entries new file mode 100644 index 0000000..1e57a15 --- /dev/null +++ b/external/android-emugl/host/libs/libOpenglRender/render_api.entries @@ -0,0 +1,129 @@ +!render_api + +%#include "OpenglRender/render_api_platform_types.h" +% +%#include +%#include +%#include +% +%/* list of constants to be passed to setStreamMode */ +%#define RENDER_API_STREAM_MODE_DEFAULT 0 +%#define RENDER_API_STREAM_MODE_TCP 1 +%#define RENDER_API_STREAM_MODE_UNIX 2 +%#define RENDER_API_STREAM_MODE_PIPE 3 +% +%typedef void (*OnPostFn)(void* context, int width, int height, int ydir, +% int format, int type, unsigned char* pixels); + +# Initialize the library and tries to load the corresponding EGL/GLES +# translation libraries. Must be called before anything else to ensure that +# everything works. Returns 0 on success, error code otherwise. +# If it returns an error, you cannot use the library at all. +int initLibrary(void); + +# Change the stream mode. This must be called before initOpenGLRenderer() +# |mode| is one of STREAM_DEFAULT, STREAM_UNIX, STREAM_TCP or STREAM_PIPE. +int setStreamMode(int mode); + +# initOpenGLRenderer - initialize the OpenGL renderer process. +# +# width and height are the framebuffer dimensions that will be reported to the +# guest display driver. +# +# useSubWindow is true to indicate that createOpenGLSubWindow() will be called +# later. If false, only setPostCallback() is supported. +# +# addr is a buffer of addrLen bytes that will receive the address that clients +# should connect to. The interpretation depends on the transport: +# - TCP: The buffer contains the port number as a string. The server is +# listening only on the loopback address. +# - Win32 and UNIX named pipes: The buffer contains the full path clients +# should connect to. +# +# This function is *NOT* thread safe and should be called first +# to initialize the renderer after initLibrary(). +int initOpenGLRenderer(int width, int height, bool useSubWindow, char* addr, size_t addrLen, emugl_logger_struct logfuncs); + +# getHardwareStrings - describe the GPU hardware and driver. +# The underlying GL's vendor/renderer/version strings are returned to the +# caller. The pointers become invalid after a call to stopOpenGLRenderer(). +void getHardwareStrings(const char** vendor, const char** renderer, const char** version); + +# A per-frame callback can be registered with setPostCallback(); to remove it +# pass NULL for both parameters. While a callback is registered, the renderer +# will call it just before each new frame is displayed, providing a copy of +# the framebuffer contents. +# +# The callback will be called from one of the renderer's threads, so will +# probably need synchronization on any data structures it modifies. The +# pixels buffer may be overwritten as soon as the callback returns; if it +# needs the pixels afterwards it must copy them. +# +# The pixels buffer is intentionally not const: the callback may modify the +# data without copying to another buffer if it wants, e.g. in-place RGBA to +# RGB conversion, or in-place y-inversion. +# +# Parameters are: +# context The pointer optionally provided when the callback was +# registered. The client can use this to pass whatever +# information it wants to the callback. +# width, height Dimensions of the image, in pixels. Rows are tightly +# packed; there is no inter-row padding. +# ydir Indicates row order: 1 means top-to-bottom order, -1 means +# bottom-to-top order. +# format, type Format and type GL enums, as used in glTexImage2D() or +# glReadPixels(), describing the pixel format. +# pixels The framebuffer image. +# +# In the first implementation, ydir is always -1 (bottom to top), format and +# type are always GL_RGBA and GL_UNSIGNED_BYTE, and the width and height will +# always be the same as the ones passed to initOpenGLRenderer(). +void setPostCallback(OnPostFn onPost, void* onPostContext); + +# showOpenGLSubwindow - +# Create or modify a native subwindow which is a child of 'window' +# to be used for framebuffer display. If a subwindow already exists, +# its properties will be updated to match the given parameters. +# wx,wy is the top left corner of the rendering subwindow. +# ww,wh are the dimensions of the rendering subwindow. +# fbw,fbh are the dimensions of the underlying guest framebuffer. +# dpr is the device pixel ratio, which is needed for higher density +# displays like retina. +# zRot is the rotation to apply on the framebuffer display image. +# +# Return true on success, false on failure, which can happen when using +# a software-only renderer like OSMesa. In this case, the client should +# call setPostCallback to get the content of each new frame when it is +# posted, and will be responsible for displaying it. +bool showOpenGLSubwindow(FBNativeWindowType window, int wx, int wy, int ww, int wh, int fbw, int fbh, float dpr, float zRot); + +# destroyOpenGLSubwindow - +# destroys the created native subwindow. Once destroyed, +# Framebuffer content will not be visible until a new +# subwindow will be created. +# Return true on success, false otherwise. +bool destroyOpenGLSubwindow(void); + +# setOpenGLDisplayRotation - +# set the framebuffer display image rotation in units +# of degrees around the z axis +void setOpenGLDisplayRotation(float zRot); + +# setOpenGLDisplayTranslation +# change what coordinate of the guest framebuffer will be displayed at the +# corner of the GPU sub-window. Specifically, |px| and |py| = 0 means +# align the bottom-left of the framebuffer with the bottom-left of the +# sub-window, and |px| and |py| = 1 means align the top right of the +# framebuffer with the top right of the sub-window. Intermediate values +# interpolate between these states. +void setOpenGLDisplayTranslation(float px, float py); + +# repaintOpenGLDisplay - +# causes the OpenGL subwindow to get repainted with the +# latest framebuffer content. +void repaintOpenGLDisplay(void); + +# stopOpenGLRenderer - stops the OpenGL renderer process. +# This functions is#NOT* thread safe and should be called +# only if previous initOpenGLRenderer has returned true. +int stopOpenGLRenderer(void); diff --git a/external/android-emugl/host/libs/mir_support/CMakeLists.txt b/external/android-emugl/host/libs/mir_support/CMakeLists.txt new file mode 100644 index 0000000..ed29cd9 --- /dev/null +++ b/external/android-emugl/host/libs/mir_support/CMakeLists.txt @@ -0,0 +1,10 @@ +set(SOURCES + shared_state.cpp) + +include_directories(BEFORE + ${MIRCLIENT_INCLUDE_DIRS}) + +add_library(mir_support ${SOURCES}) +target_link_libraries(mir_support + ${MIRCLIENT_LDFLAGS} + ${MIRCLIENT_LIBRARIES}) diff --git a/external/android-emugl/host/libs/mir_support/shared_state.cpp b/external/android-emugl/host/libs/mir_support/shared_state.cpp new file mode 100644 index 0000000..3f4ad1f --- /dev/null +++ b/external/android-emugl/host/libs/mir_support/shared_state.cpp @@ -0,0 +1,74 @@ +/* + * Copyright (C) 2016 Simon Fels + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 3, as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranties of + * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + * + */ + +#include "mir_support/shared_state.h" + +namespace mir { +namespace support { + +std::shared_ptr SharedState::get() { + static auto instance = std::make_shared(); + return instance; +} + +SharedState::SharedState() : + connection_(nullptr) { +} + +SharedState::~SharedState() { + release_connection(); +} + +void SharedState::ensure_connection() { + if (connection_) + return; + + auto xdg_runtime_dir = ::getenv("XDG_RUNTIME_DIR"); + if (!xdg_runtime_dir) + throw std::runtime_error("Failed to find XDG_RUNTIME_DIR"); + + std::string socket_path = xdg_runtime_dir; + socket_path += "/mir_socket"; + + connection_ = mir_connect_sync(socket_path.c_str(), "anbox"); + if (!mir_connection_is_valid(connection_)) { + std::string msg; + msg += "Failed to connect with Mir server: "; + msg += mir_connection_get_error_message(connection_); + msg += "\n"; + throw std::runtime_error(msg.c_str()); + } +} + +void SharedState::release_connection() { + if (!connection_) + return; + + mir_connection_release(connection_); + connection_ = nullptr; +} + +MirConnection* SharedState::connection() const { + return connection_; +} + +EGLNativeDisplayType SharedState::native_display() const { + return mir_connection_get_egl_native_display(connection_); +} + +} // namespace support +} // namespace mir diff --git a/external/android-emugl/host/libs/mir_support/shared_state.h b/external/android-emugl/host/libs/mir_support/shared_state.h new file mode 100644 index 0000000..f57771e --- /dev/null +++ b/external/android-emugl/host/libs/mir_support/shared_state.h @@ -0,0 +1,50 @@ +/* + * Copyright (C) 2016 Simon Fels + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 3, as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranties of + * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + * + */ + +#ifndef MIR_SUPPORT_SHARED_STATE_H_ +#define MIR_SUPPORT_SHARED_STATE_H_ + +#define MIR_EGL_PLATFORM + +#include + +#include + +#include + +namespace mir { +namespace support { +class SharedState { +public: + static std::shared_ptr get(); + + SharedState(); + ~SharedState(); + + void ensure_connection(); + void release_connection(); + + MirConnection* connection() const; + EGLNativeDisplayType native_display() const; + +private: + MirConnection *connection_; +}; +} // namespace support +} // namespace mir + +#endif diff --git a/external/android-emugl/host/libs/renderControl_dec/Android.mk b/external/android-emugl/host/libs/renderControl_dec/Android.mk new file mode 100644 index 0000000..64c9507 --- /dev/null +++ b/external/android-emugl/host/libs/renderControl_dec/Android.mk @@ -0,0 +1,10 @@ +LOCAL_PATH := $(call my-dir) + + +### host library ############################################ +$(call emugl-begin-host-static-library,lib_renderControl_dec) +$(call emugl-import,libOpenglCodecCommon) +$(call emugl-gen-decoder,$(LOCAL_PATH),renderControl) +# For renderControl_types.h +$(call emugl-export,C_INCLUDES,$(LOCAL_PATH)) +$(call emugl-end-module) diff --git a/external/android-emugl/host/libs/renderControl_dec/CMakeLists.txt b/external/android-emugl/host/libs/renderControl_dec/CMakeLists.txt new file mode 100644 index 0000000..dad6019 --- /dev/null +++ b/external/android-emugl/host/libs/renderControl_dec/CMakeLists.txt @@ -0,0 +1,14 @@ +set(GENERATED_SOURCES + renderControl_dec.cpp + renderControl_server_context.cpp) + +add_custom_command( + OUTPUT ${GENERATED_SOURCES} + POST_BUILD + COMMAND ${CMAKE_BINARY_DIR}/external/android-emugl/host/tools/emugen/emugen + -D ${CMAKE_CURRENT_BINARY_DIR} renderControl + WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} + DEPENDS emugen) + +add_library(renderControl_dec ${GENERATED_SOURCES}) +target_link_libraries(renderControl_dec OpenglCodecCommon) diff --git a/external/android-emugl/host/libs/renderControl_dec/renderControl.attrib b/external/android-emugl/host/libs/renderControl_dec/renderControl.attrib new file mode 100644 index 0000000..0afa9d3 --- /dev/null +++ b/external/android-emugl/host/libs/renderControl_dec/renderControl.attrib @@ -0,0 +1,44 @@ +GLOBAL + base_opcode 10000 + encoder_headers "glUtils.h" + +rcGetEGLVersion + dir major out + len major sizeof(EGLint) + dir minor out + len minor sizeof(EGLint) + +rcQueryEGLString + dir buffer out + len buffer bufferSize + +rcGetGLString + dir buffer out + len buffer bufferSize + +rcGetNumConfigs + dir numAttribs out + len numAttribs sizeof(uint32_t) + +rcGetConfigs + dir buffer out + len buffer bufSize + +rcChooseConfig + dir attribs in + len attribs attribs_size + dir configs out + var_flag configs nullAllowed + len configs configs_size*sizeof(uint32_t) + +rcReadColorBuffer + dir pixels out + len pixels (((glUtilsPixelBitSize(format, type) * width) >> 3) * height) + +rcUpdateColorBuffer + dir pixels in + len pixels (((glUtilsPixelBitSize(format, type) * width) >> 3) * height) + var_flag pixels isLarge + +rcCloseColorBuffer + flag flushOnEncode diff --git a/external/android-emugl/host/libs/renderControl_dec/renderControl.in b/external/android-emugl/host/libs/renderControl_dec/renderControl.in new file mode 100644 index 0000000..2cc4031 --- /dev/null +++ b/external/android-emugl/host/libs/renderControl_dec/renderControl.in @@ -0,0 +1,29 @@ +GL_ENRTY(GLint, rcGetRendererVersion) +GL_ENTRY(EGLint, rcGetEGLVersion, EGLint *major, EGLint *minor) +GL_ENTRY(EGLint, rcQueryEGLString, EGLenum name, void *buffer, EGLint bufferSize) +GL_ENTRY(EGLint, rcGetGLString, EGLenum name, void *buffer, EGLint bufferSize) +GL_ENTRY(EGLint, rcGetNumConfigs, uint32_t *numAttribs) +GL_ENTRY(EGLint, rcGetConfigs, uint32_t bufSize, GLuint *buffer) +GL_ENTRY(EGLint, rcChooseConfig, EGLint *attribs, uint32_t attribs_size, uint32_t *configs, uint32_t configs_size) +GL_ENTRY(EGLint, rcGetFBParam, EGLint param) +GL_ENTRY(uint32_t, rcCreateContext, uint32_t config, uint32_t share, uint32_t glVersion) +GL_ENTRY(void, rcDestroyContext, uint32_t context) +GL_ENTRY(uint32_t, rcCreateWindowSurface, uint32_t config, uint32_t width, uint32_t height) +GL_ENTRY(void, rcDestroyWindowSurface, uint32_t windowSurface) +GL_ENTRY(uint32_t, rcCreateColorBuffer, uint32_t width, uint32_t height, GLenum internalFormat) +GL_ENTRY(void, rcOpenColorBuffer, uint32_t colorbuffer) +GL_ENTRY(void, rcCloseColorBuffer, uint32_t colorbuffer) +GL_ENTRY(void, rcSetWindowColorBuffer, uint32_t windowSurface, uint32_t colorBuffer) +GL_ENTRY(int, rcFlushWindowColorBuffer, uint32_t windowSurface) +GL_ENTRY(EGLint, rcMakeCurrent, uint32_t context, uint32_t drawSurf, uint32_t readSurf) +GL_ENTRY(void, rcFBPost, uint32_t colorBuffer) +GL_ENTRY(void, rcFBSetSwapInterval, EGLint interval) +GL_ENTRY(void, rcBindTexture, uint32_t colorBuffer) +GL_ENTRY(void, rcBindRenderbuffer, uint32_t colorBuffer) +GL_ENTRY(EGLint, rcColorBufferCacheFlush, uint32_t colorbuffer, EGLint postCount,int forRead) +GL_ENTRY(void, rcReadColorBuffer, uint32_t colorbuffer, GLint x, GLint y, GLint width, GLint height, GLenum format, GLenum type, void *pixels) +GL_ENTRY(int, rcUpdateColorBuffer, uint32_t colorbuffer, GLint x, GLint y, GLint width, GLint height, GLenum format, GLenum type, void *pixels) +GL_ENTRY(int, rcOpenColorBuffer2, uint32_t colorbuffer) +GL_ENTRY(uint32_t, rcCreateClientImage, uint32_t context, EGLenum target, GLuint buffer) +GL_ENTRY(int, rcDestroyClientImage, uint32_t image) +GL_ENTRY(void, rcSelectChecksumCalculator, uint32_t newProtocol, uint32_t reserved) diff --git a/external/android-emugl/host/libs/renderControl_dec/renderControl.types b/external/android-emugl/host/libs/renderControl_dec/renderControl.types new file mode 100644 index 0000000..2b38470 --- /dev/null +++ b/external/android-emugl/host/libs/renderControl_dec/renderControl.types @@ -0,0 +1,11 @@ +uint32_t 32 0x%08x +EGLint 32 0x%08x +GLint 32 0x%08x +GLuint 32 0x%08x +GLenum 32 0x%08x +EGLenum 32 0x%08x +uint32_t* 32 0x%08x +EGLint* 32 0x%08x +GLint* 32 0x%08x +GLuint* 32 0x%08x +void* 32 0x%08x diff --git a/external/android-emugl/host/libs/renderControl_dec/renderControl_types.h b/external/android-emugl/host/libs/renderControl_dec/renderControl_types.h new file mode 100644 index 0000000..da215bb --- /dev/null +++ b/external/android-emugl/host/libs/renderControl_dec/renderControl_types.h @@ -0,0 +1,28 @@ +/* +* Copyright 2011 The Android Open Source Project +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +#include +#include +#include "glUtils.h" + +// values for 'param' argument of rcGetFBParam +#define FB_WIDTH 1 +#define FB_HEIGHT 2 +#define FB_XDPI 3 +#define FB_YDPI 4 +#define FB_FPS 5 +#define FB_MIN_SWAP_INTERVAL 6 +#define FB_MAX_SWAP_INTERVAL 7 diff --git a/external/android-emugl/host/tools/CMakeLists.txt b/external/android-emugl/host/tools/CMakeLists.txt new file mode 100644 index 0000000..01d9785 --- /dev/null +++ b/external/android-emugl/host/tools/CMakeLists.txt @@ -0,0 +1 @@ +add_subdirectory(emugen) diff --git a/external/android-emugl/host/tools/emugen/Android.mk b/external/android-emugl/host/tools/emugen/Android.mk new file mode 100644 index 0000000..78d17d6 --- /dev/null +++ b/external/android-emugl/host/tools/emugen/Android.mk @@ -0,0 +1,31 @@ +# Determine if the emugen build needs to be builts from +# sources. +LOCAL_PATH:=$(call my-dir) + +$(call emugl-begin-host-executable,emugen) + +LOCAL_SRC_FILES := \ + ApiGen.cpp \ + EntryPoint.cpp \ + main.cpp \ + Parser.cpp \ + strUtils.cpp \ + TypeFactory.cpp \ + +LOCAL_HOST_BUILD := true +LOCAL_INSTALL := false + +$(call emugl-end-module) + +# The location of the emugen host tool that is used to generate wire +# protocol encoders/ decoders. This variable is used by other emugl modules. +EMUGL_EMUGEN := $(LOCAL_BUILT_MODULE) + +$(call emugl-begin-host-executable,emugen_unittests) +LOCAL_SRC_FILES := \ + Parser.cpp \ + Parser_unittest.cpp +LOCAL_HOST_BUILD := true +LOCAL_INSTALL := false +$(call emugl-import,libemugl_gtest_host) +$(call emugl-end-module) diff --git a/external/android-emugl/host/tools/emugen/ApiGen.cpp b/external/android-emugl/host/tools/emugen/ApiGen.cpp new file mode 100644 index 0000000..7e87c07 --- /dev/null +++ b/external/android-emugl/host/tools/emugen/ApiGen.cpp @@ -0,0 +1,1323 @@ +/* +* Copyright (C) 2011 The Android Open Source Project +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ +#include "ApiGen.h" +#include "EntryPoint.h" +#include +#include +#include "strUtils.h" +#include +#include + +/* Define this to 1 to enable support for the 'isLarge' variable flag + * that instructs the encoder to send large data buffers by a direct + * write through the pipe (i.e. without copying it into a temporary + * buffer. This has definite performance benefits when using a QEMU Pipe. + * + * Set to 0 otherwise. + */ +#define WITH_LARGE_SUPPORT 1 + +// Set to 1 to ensure buffers passed to/from EGL/GL are properly aligned. +// This prevents crashes with certain backends (e.g. OSMesa). +#define USE_ALIGNED_BUFFERS 1 + +EntryPoint * ApiGen::findEntryByName(const std::string & name) +{ + EntryPoint * entry = NULL; + + size_t n = this->size(); + for (size_t i = 0; i < n; i++) { + if (at(i).name() == name) { + entry = &(at(i)); + break; + } + } + return entry; +} + +void ApiGen::printHeader(FILE *fp) const +{ + fprintf(fp, "// Generated Code - DO NOT EDIT !!\n"); + fprintf(fp, "// generated by 'emugen'\n"); +} + +int ApiGen::genProcTypes(const std::string &filename, SideType side) +{ + FILE *fp = fopen(filename.c_str(), "wt"); + if (fp == NULL) { + perror(filename.c_str()); + return -1; + } + printHeader(fp); + + const char* basename = m_basename.c_str(); + + fprintf(fp, "#ifndef __%s_%s_proc_t_h\n", basename, sideString(side)); + fprintf(fp, "#define __%s_%s_proc_t_h\n", basename, sideString(side)); + fprintf(fp, "\n\n"); + fprintf(fp, "\n#include \"%s_types.h\"\n",basename); + fprintf(fp, "\n#include \"emugl/common/logging.h\"\n"); + fprintf(fp, "#ifndef %s_APIENTRY\n",basename); + fprintf(fp, "#define %s_APIENTRY \n",basename); + fprintf(fp, "#endif\n"); + + + for (size_t i = 0; i < size(); i++) { + EntryPoint *e = &at(i); + + fprintf(fp, "typedef "); + e->retval().printType(fp); + fprintf(fp, " (%s_APIENTRY *%s_%s_proc_t) (", basename, e->name().c_str(), sideString(side)); + if (side == CLIENT_SIDE) { fprintf(fp, "void * ctx"); } + if (e->customDecoder() && side == SERVER_SIDE) { fprintf(fp, "void *ctx"); } + + VarsArray & evars = e->vars(); + size_t n = evars.size(); + + for (size_t j = 0; j < n; j++) { + if (!evars[j].isVoid()) { + if (j != 0 || side == CLIENT_SIDE || (side == SERVER_SIDE && e->customDecoder())) fprintf(fp, ", "); + evars[j].printType(fp); + } + } + fprintf(fp, ");\n"); + } + fprintf(fp, "\n\n#endif\n"); + return 0; +} + +int ApiGen::genFuncTable(const std::string &filename, SideType side) +{ + FILE *fp = fopen(filename.c_str(), "wt"); + if (fp == NULL) { + perror(filename.c_str()); + return -1; + } + printHeader(fp); + + fprintf(fp, "#ifndef __%s_%s_ftable_t_h\n", m_basename.c_str(), sideString(side)); + fprintf(fp, "#define __%s_%s_ftable_t_h\n", m_basename.c_str(), sideString(side)); + fprintf(fp, "\n\n"); + fprintf(fp, "static const struct _%s_funcs_by_name {\n", m_basename.c_str()); + fprintf(fp, + "\tconst char *name;\n" \ + "\tvoid *proc;\n" \ + "} %s_funcs_by_name[] = {\n", m_basename.c_str()); + + + for (size_t i = 0; i < size(); i++) { + EntryPoint *e = &at(i); + if (e->notApi()) continue; + fprintf(fp, "\t{\"%s\", (void*)%s},\n", e->name().c_str(), e->name().c_str()); + } + fprintf(fp, "};\n"); + fprintf(fp, "static const int %s_num_funcs = sizeof(%s_funcs_by_name) / sizeof(struct _%s_funcs_by_name);\n", + m_basename.c_str(), m_basename.c_str(), m_basename.c_str()); + fprintf(fp, "\n\n#endif\n"); + return 0; +} + +int ApiGen::genContext(const std::string & filename, SideType side) +{ + FILE *fp = fopen(filename.c_str(), "wt"); + if (fp == NULL) { + perror(filename.c_str()); + return -1; + } + printHeader(fp); + + fprintf(fp, "#ifndef __%s_%s_context_t_h\n", m_basename.c_str(), sideString(side)); + fprintf(fp, "#define __%s_%s_context_t_h\n", m_basename.c_str(), sideString(side)); + + fprintf(fp, "\n#include \"%s_%s_proc.h\"\n", + m_basename.c_str(), + side == CLIENT_SIDE ? "client" : "server"); + fprintf(fp, "\n#include \"%s_types.h\"\n", m_basename.c_str()); + + StringVec & contextHeaders = side == CLIENT_SIDE ? m_clientContextHeaders : m_serverContextHeaders; + for (size_t i = 0; i < contextHeaders.size(); i++) { + fprintf(fp, "#include %s\n", contextHeaders[i].c_str()); + } + fprintf(fp, "\n"); + + fprintf(fp, "\nstruct %s_%s_context_t {\n\n", + m_basename.c_str(), sideString(side)); + + // API entry points + for (size_t i = 0; i < size(); i++) { + EntryPoint *e = &at(i); + fprintf(fp, "\t%s_%s_proc_t %s;\n", e->name().c_str(), sideString(side), e->name().c_str()); + } + + // virtual destructor + fprintf(fp, "\t virtual ~%s_%s_context_t() {}\n", m_basename.c_str(), sideString(side)); + // accessor + if (side == CLIENT_SIDE || side == WRAPPER_SIDE) { + fprintf(fp, "\n\ttypedef %s_%s_context_t *CONTEXT_ACCESSOR_TYPE(void);\n", + m_basename.c_str(), sideString(side)); + fprintf(fp, "\tstatic void setContextAccessor(CONTEXT_ACCESSOR_TYPE *f);\n"); + } + + // init function + fprintf(fp, "\tint initDispatchByName( void *(*getProc)(const char *name, void *userData), void *userData);\n"); + + //client site set error virtual func + if (side == CLIENT_SIDE) { + fprintf(fp, "\tvirtual void setError(unsigned int error){ (void)error; };\n"); + fprintf(fp, "\tvirtual unsigned int getError(){ return 0; };\n"); + } + + fprintf(fp, "};\n"); + + fprintf(fp, "\n#endif\n"); + fclose(fp); + return 0; +} + +int ApiGen::genEntryPoints(const std::string & filename, SideType side) +{ + + if (side != CLIENT_SIDE && side != WRAPPER_SIDE) { + fprintf(stderr, "Entry points are only defined for Client and Wrapper components\n"); + return -999; + } + + + FILE *fp = fopen(filename.c_str(), "wt"); + if (fp == NULL) { + perror(filename.c_str()); + return errno; + } + + printHeader(fp); + fprintf(fp, "#include \n"); + fprintf(fp, "#include \n"); + fprintf(fp, "#include \"%s_%s_context.h\"\n", m_basename.c_str(), sideString(side)); + fprintf(fp, "\n"); + + fprintf(fp, "#ifndef GL_TRUE\n"); + fprintf(fp, "extern \"C\" {\n"); + + for (size_t i = 0; i < size(); i++) { + fprintf(fp, "\t"); at(i).print(fp, false); fprintf(fp, ";\n"); + } + fprintf(fp, "};\n\n"); + fprintf(fp, "#endif\n"); + + fprintf(fp, "#ifndef GET_CONTEXT\n"); + fprintf(fp, "static %s_%s_context_t::CONTEXT_ACCESSOR_TYPE *getCurrentContext = NULL;\n", + m_basename.c_str(), sideString(side)); + + fprintf(fp, + "void %s_%s_context_t::setContextAccessor(CONTEXT_ACCESSOR_TYPE *f) { getCurrentContext = f; }\n", + m_basename.c_str(), sideString(side)); + fprintf(fp, "#define GET_CONTEXT %s_%s_context_t * ctx = getCurrentContext()\n", + m_basename.c_str(), sideString(side)); + fprintf(fp, "#endif\n\n"); + + + for (size_t i = 0; i < size(); i++) { + EntryPoint *e = &at(i); + e->print(fp); + fprintf(fp, "{\n"); + fprintf(fp, "\tGET_CONTEXT;\n"); + + bool shouldReturn = !e->retval().isVoid(); + bool shouldCallWithContext = (side == CLIENT_SIDE); + //param check + if (shouldCallWithContext) { + for (size_t j=0; jvars().size(); j++) { + if (e->vars()[j].paramCheckExpression() != "") + fprintf(fp, "\t%s\n", e->vars()[j].paramCheckExpression().c_str()); + } + } + fprintf(fp, "\t%sctx->%s(%s", + shouldReturn ? "return " : "", + e->name().c_str(), + shouldCallWithContext ? "ctx" : ""); + size_t nvars = e->vars().size(); + + for (size_t j = 0; j < nvars; j++) { + if (!e->vars()[j].isVoid()) { + fprintf(fp, "%s %s", + j != 0 || shouldCallWithContext ? "," : "", + e->vars()[j].name().c_str()); + } + } + fprintf(fp, ");\n"); + fprintf(fp, "}\n\n"); + } + fclose(fp); + return 0; +} + + +int ApiGen::genOpcodes(const std::string &filename) +{ + FILE *fp = fopen(filename.c_str(), "wt"); + if (fp == NULL) { + perror(filename.c_str()); + return errno; + } + + printHeader(fp); + fprintf(fp, "#ifndef __GUARD_%s_opcodes_h_\n", m_basename.c_str()); + fprintf(fp, "#define __GUARD_%s_opcodes_h_\n\n", m_basename.c_str()); + for (size_t i = 0; i < size(); i++) { + fprintf(fp, "#define OP_%s \t\t\t\t\t%u\n", at(i).name().c_str(), (unsigned int)i + m_baseOpcode); + } + fprintf(fp, "#define OP_last \t\t\t\t\t%u\n", (unsigned int)size() + m_baseOpcode); + fprintf(fp,"\n\n#endif\n"); + fclose(fp); + return 0; + +} +int ApiGen::genAttributesTemplate(const std::string &filename ) +{ + FILE *fp = fopen(filename.c_str(), "wt"); + if (fp == NULL) { + perror(filename.c_str()); + return -1; + } + + for (size_t i = 0; i < size(); i++) { + if (at(i).hasPointers()) { + fprintf(fp, "#"); + at(i).print(fp); + fprintf(fp, "%s\n\n", at(i).name().c_str()); + } + } + fclose(fp); + return 0; +} + +int ApiGen::genEncoderHeader(const std::string &filename) +{ + FILE *fp = fopen(filename.c_str(), "wt"); + if (fp == NULL) { + perror(filename.c_str()); + return -1; + } + + printHeader(fp); + std::string classname = m_basename + "_encoder_context_t"; + + fprintf(fp, "\n#ifndef GUARD_%s\n", classname.c_str()); + fprintf(fp, "#define GUARD_%s\n\n", classname.c_str()); + + fprintf(fp, "#include \"IOStream.h\"\n"); + fprintf(fp, "#include \"ChecksumCalculator.h\"\n"); + fprintf(fp, "#include \"%s_%s_context.h\"\n\n\n", m_basename.c_str(), sideString(CLIENT_SIDE)); + + for (size_t i = 0; i < m_encoderHeaders.size(); i++) { + fprintf(fp, "#include %s\n", m_encoderHeaders[i].c_str()); + } + fprintf(fp, "\n"); + + fprintf(fp, "struct %s : public %s_%s_context_t {\n\n", + classname.c_str(), m_basename.c_str(), sideString(CLIENT_SIDE)); + fprintf(fp, "\tIOStream *m_stream;\n"); + fprintf(fp, "\tChecksumCalculator *m_checksumCalculator;\n\n"); + + fprintf(fp, "\t%s(IOStream *stream, ChecksumCalculator *checksumCalculator);\n", classname.c_str()); + fprintf(fp, "};\n\n"); + + fprintf(fp, "#endif // GUARD_%s", classname.c_str()); + + fclose(fp); + return 0; +} + +// Format the byte length expression for a given variable into a user-provided buffer +// If the variable type is not a pointer, this is simply its size as a decimal constant +// If the variable is a pointer, this will be an expression provided by the .attrib file +// through the 'len' attribute. +// +// Returns 1 if the variable is a pointer, 0 otherwise +// +static int getVarEncodingSizeExpression(Var& var, EntryPoint* e, char* buff, size_t bufflen) +{ + int ret = 0; + if (!var.isPointer()) { + snprintf(buff, bufflen, "%u", (unsigned int) var.type()->bytes()); + } else { + ret = 1; + const char* lenExpr = var.lenExpression().c_str(); + const char* varname = var.name().c_str(); + if (e != NULL && lenExpr[0] == '\0') { + fprintf(stderr, "%s: data len is undefined for '%s'\n", + e->name().c_str(), varname); + } + if (var.nullAllowed()) { + snprintf(buff, bufflen, "((%s != NULL) ? %s : 0)", varname, lenExpr); + } else { + snprintf(buff, bufflen, "%s", lenExpr); + } + } + return ret; +} + +static int writeVarEncodingSize(Var& var, FILE* fp) +{ + int ret = 0; + if (!var.isPointer()) { + fprintf(fp, "%u", (unsigned int) var.type()->bytes()); + } else { + ret = 1; + fprintf(fp, "__size_%s", var.name().c_str()); + } + return ret; +} + + + +static void writeVarEncodingExpression(Var& var, FILE* fp) +{ + const char* varname = var.name().c_str(); + + if (var.isPointer()) { + // encode a pointer header + fprintf(fp, "\t*(unsigned int *)(ptr) = __size_%s; ptr += 4;\n", varname); + + Var::PointerDir dir = var.pointerDir(); + if (dir == Var::POINTER_INOUT || dir == Var::POINTER_IN) { + if (var.nullAllowed()) { + fprintf(fp, "\tif (%s != NULL) ", varname); + } else { + fprintf(fp, "\t"); + } + + if (var.packExpression().size() != 0) { + fprintf(fp, "%s;", var.packExpression().c_str()); + } else { + fprintf(fp, "memcpy(ptr, %s, __size_%s);", + varname, varname); + } + + fprintf(fp, "ptr += __size_%s;\n", varname); + } + } else { + // encode a non pointer variable + if (!var.isVoid()) { + fprintf(fp, "\t\tmemcpy(ptr, &%s, %u); ptr += %u;\n", + varname, + (unsigned) var.type()->bytes(), + (unsigned) var.type()->bytes()); + } + } +} + +#if WITH_LARGE_SUPPORT +static void writeVarLargeEncodingExpression(Var& var, FILE* fp) +{ + const char* varname = var.name().c_str(); + + fprintf(fp, "\tstream->writeFully(&__size_%s,4);\n", varname); + fprintf(fp, "\tif (useChecksum) checksumCalculator->addBuffer(&__size_%s,4);\n", varname); + if (var.nullAllowed()) { + fprintf(fp, "\tif (%s != NULL) {\n", varname); + } + if (var.writeExpression() != "") { + fprintf(fp, "%s", var.writeExpression().c_str()); + } else { + fprintf(fp, "\t\tstream->writeFully(%s, __size_%s);\n", varname, varname); + fprintf(fp, "\t\tif (useChecksum) checksumCalculator->addBuffer(%s, __size_%s);\n", varname, varname); + } + if (var.nullAllowed()) fprintf(fp, "\t}\n"); +} +#endif /* WITH_LARGE_SUPPORT */ + +static void writeEncodingChecksumValidatorOnReturn(const char* funcName, FILE* fp) { + fprintf(fp, "\tif (useChecksum) {\n" + "\t\tstd::unique_ptr checksumBuf(new unsigned char[checksumSize]);\n" + "\t\tstream->readback(checksumBuf.get(), checksumSize);\n" + "\t\tif (!checksumCalculator->validate(checksumBuf.get(), checksumSize)) {\n" + "\t\t\tALOGE(\"%s: GL communication error, please report this issue to b.android.com.\\n\");\n" + "\t\t\tabort();\n" + "\t\t}\n" + "\t}\n", + funcName + ); +} + +int ApiGen::genEncoderImpl(const std::string &filename) +{ + FILE *fp = fopen(filename.c_str(), "wt"); + if (fp == NULL) { + perror(filename.c_str()); + return -1; + } + + printHeader(fp); + fprintf(fp, "\n\n"); + fprintf(fp, "#include \n"); + fprintf(fp, "#include \n"); + fprintf(fp, "#include \"%s_opcodes.h\"\n\n", m_basename.c_str()); + fprintf(fp, "#include \"%s_enc.h\"\n\n\n", m_basename.c_str()); + fprintf(fp, "#include \n\n"); + fprintf(fp, "namespace {\n\n"); + + // unsupport printout + fprintf(fp, + "void enc_unsupported()\n" + "{\n" + "\tALOGE(\"Function is unsupported\\n\");\n" + "}\n\n"); + + // entry points; + std::string classname = m_basename + "_encoder_context_t"; + + size_t n = size(); + for (size_t i = 0; i < n; i++) { + EntryPoint *e = &at(i); + + if (e->unsupported()) continue; + + + e->print(fp, true, "_enc", /* classname + "::" */"", "void *self"); + fprintf(fp, "{\n"); + +// fprintf(fp, "\n\tDBG(\">>>> %s\\n\");\n", e->name().c_str()); + fprintf(fp, "\n\t%s *ctx = (%s *)self;\n", + classname.c_str(), + classname.c_str()); + fprintf(fp, "\tIOStream *stream = ctx->m_stream;\n" + "\tChecksumCalculator *checksumCalculator = ctx->m_checksumCalculator;\n" + "\tbool useChecksum = checksumCalculator->getVersion() > 0;\n\n"); + VarsArray & evars = e->vars(); + size_t maxvars = evars.size(); + size_t j; + + char buff[256]; + + // Define the __size_XXX variables that contain the size of data + // associated with pointers. + for (j = 0; j < maxvars; j++) { + Var& var = evars[j]; + + if (!var.isPointer()) + continue; + + const char* varname = var.name().c_str(); + fprintf(fp, "\tconst unsigned int __size_%s = ", varname); + + getVarEncodingSizeExpression(var, e, buff, sizeof(buff)); + fprintf(fp, "%s;\n", buff); + } + + bool hasLargeFields = false; +#if WITH_LARGE_SUPPORT + // We need to take care of 'isLarge' variable in a special way + // Anything before an isLarge variable can be packed into a single + // buffer, which is then commited. Each isLarge variable is a pointer + // to data that can be written to directly through the pipe, which + // will be instant when using a QEMU pipe + + size_t nvars = 0; + size_t npointers = 0; + + // First, compute the total size, 8 bytes for the opcode + payload size (without checksum) + fprintf(fp, "\t unsigned char *ptr;\n"); + fprintf(fp, "\t unsigned char *buf;\n"); + fprintf(fp, "\t const size_t sizeWithoutChecksum = 8"); + + for (j = 0; j < maxvars; j++) { + fprintf(fp, " + "); + npointers += writeVarEncodingSize(evars[j], fp); + } + if (npointers > 0) { + fprintf(fp, " + %zu*4", npointers); + } + fprintf(fp, ";\n"); + + // Then, size of the checksum string + fprintf(fp, "\t const size_t checksumSize = checksumCalculator->checksumByteSize();\n"); + + // And, size of the whole thing + fprintf(fp, "\t const size_t totalSize = sizeWithoutChecksum + checksumSize;\n"); + + // We need to divide the packet into fragments. Each fragment contains + // either copied arguments to a temporary buffer, or direct writes for + // large variables. + // + // The first fragment must also contain the opcode+payload_size+checksum_size + // + nvars = 0; + while (nvars < maxvars || maxvars == 0) { + + // Skip over non-large fields + for (j = nvars; j < maxvars; j++) { + if (evars[j].isLarge()) + break; + } + + // Write a fragment if needed. + if (nvars == 0 || j > nvars) { + const char* plus = ""; + + if (nvars == 0 && j == maxvars) { + // Simple shortcut for the common case where we don't have large variables; + fprintf(fp, "\tbuf = stream->alloc(totalSize);\n"); + + } else { + hasLargeFields = true; + // allocate buffer from the stream until the first large variable + fprintf(fp, "\tbuf = stream->alloc("); + plus = ""; + + if (nvars == 0) { + fprintf(fp,"8"); plus = " + "; + } + if (j > nvars) { + npointers = 0; + for (j = nvars; j < maxvars && !evars[j].isLarge(); j++) { + fprintf(fp, "%s", plus); plus = " + "; + npointers += writeVarEncodingSize(evars[j], fp); + } + if (npointers > 0) { + fprintf(fp, "%s%zu*4", plus, npointers); plus = " + "; + } + } + fprintf(fp,");\n"); + } + fprintf(fp, "\tptr = buf;\n"); + + // encode packet header if needed. + if (nvars == 0) { + fprintf(fp, "\tint tmp = OP_%s;memcpy(ptr, &tmp, 4); ptr += 4;\n", e->name().c_str()); + fprintf(fp, "\tmemcpy(ptr, &totalSize, 4); ptr += 4;\n\n"); + } + + if (maxvars == 0) { + fprintf(fp, "\n\tif (useChecksum) checksumCalculator->addBuffer(buf, ptr-buf);\n"); + break; + } + + // encode non-large fields in this fragment + for (j = nvars; j < maxvars && !evars[j].isLarge(); j++) { + writeVarEncodingExpression(evars[j],fp); + } + + fprintf(fp, "\n\tif (useChecksum) checksumCalculator->addBuffer(buf, ptr-buf);\n"); + // Ensure the fragment is commited if it is followed by a large variable + if (j < maxvars) { + fprintf(fp, "\tstream->flush();\n"); + } + } + + // If we have one or more large variables, write them directly. + // As size + data + for ( ; j < maxvars && evars[j].isLarge(); j++) { + writeVarLargeEncodingExpression(evars[j], fp); + } + + nvars = j; + } + +#else /* !WITH_LARGE_SUPPORT */ + size_t nvars = evars.size(); + size_t npointers = 0; + fprintf(fp, "\t const size_t sizeWithoutChecksum = 8"); + for (size_t j = 0; j < nvars; j++) { + npointers += getVarEncodingSizeExpression(evars[j],e,buff,sizeof(buff)); + fprintf(fp, " + %s", buff); + } + fprintf(fp, " + %u * 4;\n", (unsigned int) npointers); + // Size of checksum + fprintf(fp, "\t const size_t checksumSize = checksumCalculator->checksumByteSize();\n"); + // Size of the whole thing + fprintf(fp, "\t const size_t totalSize = sizeWithoutChecksum + checksumSize;\n"); + + // allocate buffer from the stream; + fprintf(fp, "\t unsigned char *ptr = stream->alloc(sizeWithoutChecksum);\n\n"); + + // encode into the stream; + fprintf(fp, "\tint tmp = OP_%s; memcpy(ptr, &tmp, 4); ptr += 4;\n", e->name().c_str()); + fprintf(fp, "\tmemcpy(ptr, &sizeWithoutChecksum, 4); ptr += 4;\n\n"); + + // out variables + for (size_t j = 0; j < nvars; j++) { + writeVarEncodingExpression(evars[j], fp); + } +#endif /* !WITH_LARGE_SUPPORT */ + + // checksum + if (hasLargeFields) { + fprintf(fp, "\tbuf = stream->alloc(checksumSize);\n"); + fprintf(fp, "\tif (useChecksum) checksumCalculator->writeChecksum(buf, checksumSize);\n\n"); + } else { + fprintf(fp, "\tif (useChecksum) checksumCalculator->writeChecksum(ptr, checksumSize); ptr += checksumSize;\n\n"); + } + + // in variables; + bool hasReadbackChecksum = false; + for (size_t j = 0; j < nvars; j++) { + if (evars[j].isPointer()) { + Var::PointerDir dir = evars[j].pointerDir(); + if (dir == Var::POINTER_INOUT || dir == Var::POINTER_OUT) { + const char* varname = evars[j].name().c_str(); + const char* indent = "\t"; + if (evars[j].nullAllowed()) { + fprintf(fp, "\tif (%s != NULL) {\n",varname); + indent = "\t\t"; + } + fprintf(fp, "%sstream->readback(%s, __size_%s);\n", + indent, varname, varname); + fprintf(fp, "%sif (useChecksum) checksumCalculator->addBuffer(%s, __size_%s);\n", + indent, varname, varname); + if (evars[j].nullAllowed()) { + fprintf(fp, "\t}\n"); + } + hasReadbackChecksum = true; + } + } + } +//XXX fprintf(fp, "\n\tDBG(\"<<<< %s\\n\");\n", e->name().c_str()); + + // todo - return value for pointers + if (e->retval().isPointer()) { + fprintf(stderr, "WARNING: %s : return value of pointer is unsupported\n", + e->name().c_str()); + if (e->flushOnEncode()) { + fprintf(fp, "\tstream->flush();\n"); + } + fprintf(fp, "\t return NULL;\n"); + } else if (e->retval().type()->name() != "void") { + fprintf(fp, "\n\t%s retval;\n", e->retval().type()->name().c_str()); + fprintf(fp, "\tstream->readback(&retval, %u);\n",(unsigned) e->retval().type()->bytes()); + fprintf(fp, "\tif (useChecksum) checksumCalculator->addBuffer(&retval, %u);\n", + (unsigned) e->retval().type()->bytes()); + writeEncodingChecksumValidatorOnReturn(e->name().c_str(), fp); + fprintf(fp, "\treturn retval;\n"); + } else { + if (e->flushOnEncode()) fprintf(fp, "\tstream->flush();\n"); + if (hasReadbackChecksum) writeEncodingChecksumValidatorOnReturn(e->name().c_str(), fp); + } + fprintf(fp, "}\n\n"); + } + + fprintf(fp, "} // namespace\n\n"); + + // constructor + fprintf(fp, "%s::%s(IOStream *stream, ChecksumCalculator *checksumCalculator)\n{\n", classname.c_str(), classname.c_str()); + fprintf(fp, "\tm_stream = stream;\n"); + fprintf(fp, "\tm_checksumCalculator = checksumCalculator;\n\n"); + + for (size_t i = 0; i < n; i++) { + EntryPoint *e = &at(i); + if (e->unsupported()) { + fprintf(fp, + "\tthis->%s = (%s_%s_proc_t) &enc_unsupported;\n", + e->name().c_str(), + e->name().c_str(), + sideString(CLIENT_SIDE)); + } else { + fprintf(fp, + "\tthis->%s = &%s_enc;\n", + e->name().c_str(), + e->name().c_str()); + } + } + fprintf(fp, "}\n\n"); + + fclose(fp); + return 0; +} + + +int ApiGen::genDecoderHeader(const std::string &filename) +{ + FILE *fp = fopen(filename.c_str(), "wt"); + if (fp == NULL) { + perror(filename.c_str()); + return -1; + } + + printHeader(fp); + std::string classname = m_basename + "_decoder_context_t"; + + fprintf(fp, "\n#ifndef GUARD_%s\n", classname.c_str()); + fprintf(fp, "#define GUARD_%s\n\n", classname.c_str()); + + fprintf(fp, "#include \"IOStream.h\" \n"); + fprintf(fp, "#include \"%s_%s_context.h\"\n\n\n", m_basename.c_str(), sideString(SERVER_SIDE)); + + for (size_t i = 0; i < m_decoderHeaders.size(); i++) { + fprintf(fp, "#include %s\n", m_decoderHeaders[i].c_str()); + } + fprintf(fp, "\n"); + + fprintf(fp, "struct %s : public %s_%s_context_t {\n\n", + classname.c_str(), m_basename.c_str(), sideString(SERVER_SIDE)); + fprintf(fp, "\tsize_t decode(void *buf, size_t bufsize, IOStream *stream);\n"); + fprintf(fp, "\n};\n\n"); + fprintf(fp, "#endif // GUARD_%s\n", classname.c_str()); + + fclose(fp); + return 0; +} + +int ApiGen::genContextImpl(const std::string &filename, SideType side) +{ + FILE *fp = fopen(filename.c_str(), "wt"); + if (fp == NULL) { + perror(filename.c_str()); + return -1; + } + printHeader(fp); + + std::string classname = m_basename + "_" + sideString(side) + "_context_t"; + size_t n = size(); + fprintf(fp, "\n\n#include \n"); + fprintf(fp, "#include \"%s_%s_context.h\"\n\n\n", m_basename.c_str(), sideString(side)); + fprintf(fp, "#include \n\n"); + + fprintf(fp, "int %s::initDispatchByName(void *(*getProc)(const char *, void *userData), void *userData)\n{\n", classname.c_str()); + for (size_t i = 0; i < n; i++) { + EntryPoint *e = &at(i); + fprintf(fp, "\t%s = (%s_%s_proc_t) getProc(\"%s\", userData);\n", + e->name().c_str(), + e->name().c_str(), + sideString(side), + e->name().c_str()); + } + fprintf(fp, "\treturn 0;\n"); + fprintf(fp, "}\n\n"); + fclose(fp); + return 0; +} + +int ApiGen::genDecoderImpl(const std::string &filename) +{ + FILE *fp = fopen(filename.c_str(), "wt"); + if (fp == NULL) { + perror(filename.c_str()); + return -1; + } + + printHeader(fp); + + std::string classname = m_basename + "_decoder_context_t"; + + size_t n = size(); + + fprintf(fp, "\n\n#include \n"); + fprintf(fp, "#include \"%s_opcodes.h\"\n\n", m_basename.c_str()); + fprintf(fp, "#include \"%s_dec.h\"\n\n\n", m_basename.c_str()); + fprintf(fp, "#include \"ProtocolUtils.h\"\n\n"); + fprintf(fp, "#include \"ChecksumCalculatorThreadInfo.h\"\n\n"); + fprintf(fp, "#include \n\n"); + fprintf(fp, "typedef unsigned int tsize_t; // Target \"size_t\", which is 32-bit for now. It may or may not be the same as host's size_t when emugen is compiled.\n\n"); + + // helper macros + fprintf(fp, + "#ifdef OPENGL_DEBUG_PRINTOUT\n" + "# define DEBUG(...) do { if (emugl_cxt_logger) { emugl_cxt_logger(__VA_ARGS__); } } while(0)\n" + "#else\n" + "# define DEBUG(...) ((void)0)\n" + "#endif\n\n"); + + fprintf(fp, + "#ifdef CHECK_GLERROR\n" + "# define SET_LASTCALL(name) sprintf(lastCall, #name)\n" + "#else\n" + "# define SET_LASTCALL(name) ((void)0)\n" + "#endif\n\n"); + + // helper templates + fprintf(fp, "using namespace emugl;\n\n"); + + // decoder switch; + fprintf(fp, "size_t %s::decode(void *buf, size_t len, IOStream *stream)\n{\n", classname.c_str()); + fprintf(fp, + " \n\ +\tsize_t pos = 0;\n\ +\tif (len < 8) return pos; \n\ +\tunsigned char *ptr = (unsigned char *)buf;\n\ +\tbool unknownOpcode = false; \n\ +#ifdef CHECK_GL_ERROR \n\ +\tchar lastCall[256] = {0}; \n\ +#endif \n\ +\twhile ((len - pos >= 8) && !unknownOpcode) { \n\ +\t\tuint32_t opcode = *(uint32_t *)ptr; \n\ +\t\tsize_t packetLen = *(uint32_t *)(ptr + 4);\n\ +\t\tif (len - pos < packetLen) return pos; \n\ +\t\tbool useChecksum = ChecksumCalculatorThreadInfo::getVersion() > 0;\n\ +\t\tsize_t checksumSize = 0;\n\ +\t\tif (useChecksum) {\n\ +\t\t\tchecksumSize = ChecksumCalculatorThreadInfo::checksumByteSize();\n\ +\t\t}\n\ +\t\tswitch(opcode) {\n"); + + for (size_t f = 0; f < n; f++) { + enum Pass_t { + PASS_FIRST = 0, + PASS_VariableDeclarations = PASS_FIRST, + PASS_Protocol, + PASS_TmpBuffAlloc, + PASS_MemAlloc, + PASS_DebugPrint, + PASS_FunctionCall, + PASS_FlushOutput, + PASS_Epilog, + PASS_LAST }; + EntryPoint *e = &at(f); + + // construct a printout string; + std::string printString = ""; + for (size_t i = 0; i < e->vars().size(); i++) { + Var *v = &e->vars()[i]; + if (!v->isVoid()) printString += (v->isPointer() ? "%p(%u)" : v->type()->printFormat()) + " "; + } + printString += ""; + // TODO - add for return value; + + fprintf(fp, "\t\tcase OP_%s: {\n", e->name().c_str()); + + bool totalTmpBuffExist = false; + std::string totalTmpBuffOffset = "0"; + std::string *tmpBufOffset = new std::string[e->vars().size()]; + + // construct retval type string + std::string retvalType; + if (!e->retval().isVoid()) { + retvalType = e->retval().type()->name(); + } + + for (int pass = PASS_FIRST; pass < PASS_LAST; pass++) { + if (pass == PASS_FunctionCall && + !e->retval().isVoid() && + !e->retval().isPointer()) { + fprintf(fp, "\t\t\t*(%s *)(&tmpBuf[%s]) = ", retvalType.c_str(), + totalTmpBuffOffset.c_str()); + } + + + if (pass == PASS_FunctionCall) { + fprintf(fp, "\t\t\tthis->%s(", e->name().c_str()); + if (e->customDecoder()) { + fprintf(fp, "this"); // add a context to the call + } + } else if (pass == PASS_DebugPrint) { + fprintf(fp, + "\t\t\tDEBUG(\"%s(%%p): %s(%s)\\n\", stream", + m_basename.c_str(), + e->name().c_str(), + printString.c_str()); + if (e->vars().size() > 0 && !e->vars()[0].isVoid()) { + fprintf(fp, ","); + } + } + + std::string varoffset = "8"; // skip the header + VarsArray & evars = e->vars(); + // allocate memory for out pointers; + for (size_t j = 0; j < evars.size(); j++) { + Var *v = & evars[j]; + if (v->isVoid()) { + continue; + } + const char* var_name = v->name().c_str(); + const char* var_type_name = v->type()->name().c_str(); + const unsigned var_type_bytes = v->type()->bytes(); + + if ((pass == PASS_FunctionCall) && + (j != 0 || e->customDecoder())) { + fprintf(fp, ", "); + } + if (pass == PASS_DebugPrint && j != 0) { + fprintf(fp, ", "); + } + + if (!v->isPointer()) { + if (pass == PASS_VariableDeclarations) { + fprintf(fp, + "\t\t\t%s var_%s = Unpack<%s,uint%u_t>(ptr + %s);\n", + var_type_name, + var_name, + var_type_name, + var_type_bytes * 8U, + varoffset.c_str()); + } + + if (pass == PASS_FunctionCall || + pass == PASS_DebugPrint) { + fprintf(fp, "var_%s", var_name); + } + varoffset += " + " + toString(var_type_bytes); + continue; + } + + if (pass == PASS_VariableDeclarations) { + fprintf(fp, + "\t\t\tuint32_t size_%s __attribute__((unused)) = Unpack(ptr + %s);\n", + var_name, + varoffset.c_str()); + } + + if (v->pointerDir() == Var::POINTER_IN || + v->pointerDir() == Var::POINTER_INOUT) { + if (pass == PASS_VariableDeclarations) { +#if USE_ALIGNED_BUFFERS + fprintf(fp, + "\t\t\tInputBuffer inptr_%s(ptr + %s + 4, size_%s);\n", + var_name, + varoffset.c_str(), + var_name); + } + if (pass == PASS_FunctionCall) { + if (v->nullAllowed()) { + fprintf(fp, + "size_%s == 0 ? NULL : (%s)(inptr_%s.get())", + var_name, + var_type_name, + var_name); + } else { + fprintf(fp, + "(%s)(inptr_%s.get())", + var_type_name, + var_name); + } + } else if (pass == PASS_DebugPrint) { + fprintf(fp, + "(%s)(inptr_%s.get()), size_%s", + var_type_name, + var_name, + var_name); + } +#else // !USE_ALIGNED_BUFFERS + fprintf(fp, + "unsigned char *inptr_%s = (ptr + %s + 4);\n", + var_name, + varoffset.c_str()); + } + if (pass == PASS_FunctionCall) { + if (v->nullAllowed()) { + fprintf(fp, + "size_%s == 0 ? NULL : (%s)(inptr_%s)", + var_name, + var_type_name, + var_name); + } else { + fprintf(fp, + "(%s)(inptr_%s)", + var_type_name, + var_name); + } + } else if (pass == PASS_DebugPrint) { + fprintf(fp, + "(%s)(inptr_%s), size_%s", + var_type_name, + var_name, + var_name); + } +#endif // !USE_ALIGNED_BUFFERS + varoffset += " + 4 + size_"; + varoffset += var_name; + } else { // out pointer; + if (pass == PASS_TmpBuffAlloc) { + if (!totalTmpBuffExist) { + fprintf(fp, + "\t\t\tsize_t totalTmpSize = size_%s;\n", + var_name); + } else { + fprintf(fp, + "\t\t\ttotalTmpSize += size_%s;\n", + var_name); + } + tmpBufOffset[j] = totalTmpBuffOffset; + totalTmpBuffOffset += " + size_"; + totalTmpBuffOffset += var_name; + totalTmpBuffExist = true; + } else if (pass == PASS_MemAlloc) { +#if USE_ALIGNED_BUFFERS + fprintf(fp, + "\t\t\tOutputBuffer outptr_%s(&tmpBuf[%s], size_%s);\n", + var_name, + tmpBufOffset[j].c_str(), + var_name); + } else if (pass == PASS_FunctionCall) { + if (v->nullAllowed()) { + fprintf(fp, + "size_%s == 0 ? NULL : (%s)(outptr_%s.get())", + var_name, + var_type_name, + var_name); + } else { + fprintf(fp, + "(%s)(outptr_%s.get())", + var_type_name, + var_name); + } + } else if (pass == PASS_DebugPrint) { + fprintf(fp, + "(%s)(outptr_%s.get()), size_%s", + var_type_name, + var_name, + var_name); + } + if (pass == PASS_FlushOutput) { + fprintf(fp, + "\t\t\toutptr_%s.flush();\n", + var_name); + } +#else // !USE_ALIGNED_BUFFERS + fprintf(fp, + "\t\t\tunsigned char *outptr_%s = &tmpBuf[%s];\n", + var_name, + tmpBufOffset[j].c_str()); + fprintf(fp, + "\t\t\tmemset(outptr_%s, 0, %s);\n", + var_name, + toString(v->type()->bytes()).c_str()); + } else if (pass == PASS_FunctionCall) { + if (v->nullAllowed()) { + fprintf(fp, + "size_%s == 0 ? NULL : (%s)(outptr_%s)", + var_name, + var_type_name, + var_name); + } else { + fprintf(fp, + "(%s)(outptr_%s)", + var_type_name, + var_name); + } + } else if (pass == PASS_DebugPrint) { + fprintf(fp, + "(%s)(outptr_%s), size_%s", + var_type_name, + var_name, + varoffset.c_str()); + } +#endif // !USE_ALIGNED_BUFFERS + varoffset += " + 4"; + } + } + + if (pass == PASS_Protocol) { + fprintf(fp, + "\t\t\tif (useChecksum) {\n" + "\t\t\t\tChecksumCalculatorThreadInfo::validOrDie(ptr, %s, " + "ptr + %s, checksumSize, " + "\n\t\t\t\t\t\"%s::decode," + " OP_%s: GL checksumCalculator failure\\n\");\n" + "\t\t\t}\n", + varoffset.c_str(), + varoffset.c_str(), + varoffset.c_str(), + classname.c_str(), + e->name().c_str() + ); + + varoffset += " + 4"; + } + + if (pass == PASS_FunctionCall || + pass == PASS_DebugPrint) { + fprintf(fp, ");\n"); + } + + if (pass == PASS_TmpBuffAlloc) { + if (!e->retval().isVoid() && !e->retval().isPointer()) { + if (!totalTmpBuffExist) + fprintf(fp, + "\t\t\tsize_t totalTmpSize = sizeof(%s);\n", + retvalType.c_str()); + else + fprintf(fp, + "\t\t\ttotalTmpSize += sizeof(%s);\n", + retvalType.c_str()); + + totalTmpBuffExist = true; + } + if (totalTmpBuffExist) { + fprintf(fp, + "\t\t\ttotalTmpSize += checksumSize;\n" + "\t\t\tunsigned char *tmpBuf = stream->alloc(totalTmpSize);\n"); + } + } + + if (pass == PASS_Epilog) { + // send back out pointers data as well as retval + if (totalTmpBuffExist) { + fprintf(fp, + "\t\t\tif (useChecksum) {\n" + "\t\t\t\tChecksumCalculatorThreadInfo::writeChecksum(" + "&tmpBuf[0], totalTmpSize - checksumSize, " + "&tmpBuf[totalTmpSize - checksumSize], checksumSize);\n" + "\t\t\t}\n" + "\t\t\tstream->flush();\n"); + } + } + + } // pass; + fprintf(fp, "\t\t\tSET_LASTCALL(\"%s\");\n", e->name().c_str()); + fprintf(fp, "\t\t\tbreak;\n"); + fprintf(fp, "\t\t}\n"); + + delete [] tmpBufOffset; + } + fprintf(fp, "\t\t\tdefault:\n"); + fprintf(fp, "\t\t\t\tunknownOpcode = true;\n"); + fprintf(fp, "\t\t} //switch\n"); + if (strstr(m_basename.c_str(), "gl")) { + fprintf(fp, "#ifdef CHECK_GL_ERROR\n"); + fprintf(fp, "\tint err = lastCall[0] ? this->glGetError() : GL_NO_ERROR;\n"); + fprintf(fp, "\tif (err) fprintf(stderr, \"%s Error: 0x%%X in %%s\\n\", err, lastCall);\n", m_basename.c_str()); + fprintf(fp, "#endif\n"); + } + + fprintf(fp, "\t\tif (!unknownOpcode) {\n"); + fprintf(fp, "\t\t\tpos += packetLen;\n"); + fprintf(fp, "\t\t\tptr += packetLen;\n"); + fprintf(fp, "\t\t}\n"); + fprintf(fp, "\t} // while\n"); + fprintf(fp, "\treturn pos;\n"); + fprintf(fp, "}\n"); + + fclose(fp); + return 0; +} + +int ApiGen::readSpec(const std::string & filename) +{ + FILE *specfp = fopen(filename.c_str(), "rt"); + if (specfp == NULL) { + return -1; + } + + char line[1000]; + unsigned int lc = 0; + while (fgets(line, sizeof(line), specfp) != NULL) { + lc++; + EntryPoint ref; + if (ref.parse(lc, std::string(line))) { + push_back(ref); + updateMaxEntryPointsParams(ref.vars().size()); + } + } + fclose(specfp); + return 0; +} + +int ApiGen::readAttributes(const std::string & attribFilename) +{ + enum { ST_NAME, ST_ATT } state; + + FILE *fp = fopen(attribFilename.c_str(), "rt"); + if (fp == NULL) { + perror(attribFilename.c_str()); + return -1; + } + char buf[1000]; + + state = ST_NAME; + EntryPoint *currentEntry = NULL; + size_t lc = 0; + bool globalAttributes = false; + while (fgets(buf, sizeof(buf), fp) != NULL) { + lc++; + std::string line(buf); + if (line.size() == 0) continue; // could that happen? + + if (line.at(0) == '#') continue; // comment + + size_t first = line.find_first_not_of(" \t\n"); + if (state == ST_ATT && (first == std::string::npos || first == 0)) state = ST_NAME; + + line = trim(line); + if (line.size() == 0 || line.at(0) == '#') continue; + + switch(state) { + case ST_NAME: + if (line == "GLOBAL") { + globalAttributes = true; + } else { + globalAttributes = false; + currentEntry = findEntryByName(line); + if (currentEntry == NULL) { + fprintf(stderr, "WARNING: %u: attribute of non existant entry point %s\n", (unsigned int)lc, line.c_str()); + } + } + state = ST_ATT; + break; + case ST_ATT: + if (globalAttributes) { + setGlobalAttribute(line, lc); + } else if (currentEntry != NULL) { + currentEntry->setAttribute(line, lc); + } + break; + } + } + return 0; +} + + +int ApiGen::setGlobalAttribute(const std::string & line, size_t lc) +{ + size_t pos = 0; + size_t last; + std::string token = getNextToken(line, pos, &last, WHITESPACE); + pos = last; + + if (token == "base_opcode") { + std::string str = getNextToken(line, pos, &last, WHITESPACE); + if (str.size() == 0) { + fprintf(stderr, "line %u: missing value for base_opcode\n", (unsigned) lc); + } else { + setBaseOpcode(atoi(str.c_str())); + } + } else if (token == "encoder_headers") { + std::string str = getNextToken(line, pos, &last, WHITESPACE); + pos = last; + while (str.size() != 0) { + encoderHeaders().push_back(str); + str = getNextToken(line, pos, &last, WHITESPACE); + pos = last; + } + } else if (token == "client_context_headers") { + std::string str = getNextToken(line, pos, &last, WHITESPACE); + pos = last; + while (str.size() != 0) { + clientContextHeaders().push_back(str); + str = getNextToken(line, pos, &last, WHITESPACE); + pos = last; + } + } else if (token == "server_context_headers") { + std::string str = getNextToken(line, pos, &last, WHITESPACE); + pos = last; + while (str.size() != 0) { + serverContextHeaders().push_back(str); + str = getNextToken(line, pos, &last, WHITESPACE); + pos = last; + } + } else if (token == "decoder_headers") { + std::string str = getNextToken(line, pos, &last, WHITESPACE); + pos = last; + while (str.size() != 0) { + decoderHeaders().push_back(str); + str = getNextToken(line, pos, &last, WHITESPACE); + pos = last; + } + } + else { + fprintf(stderr, "WARNING: %u : unknown global attribute %s\n", (unsigned int)lc, line.c_str()); + } + + return 0; +} diff --git a/external/android-emugl/host/tools/emugen/ApiGen.h b/external/android-emugl/host/tools/emugen/ApiGen.h new file mode 100644 index 0000000..8ba18af --- /dev/null +++ b/external/android-emugl/host/tools/emugen/ApiGen.h @@ -0,0 +1,97 @@ +/* +* Copyright (C) 2011 The Android Open Source Project +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ +#ifndef __API_GEN_H_ +#define __API_GEN_H_ + +#include +#include +#include "EntryPoint.h" + + +class ApiGen : public std::vector { + +public: + typedef std::vector StringVec; + typedef enum { CLIENT_SIDE, SERVER_SIDE, WRAPPER_SIDE } SideType; + + ApiGen(const std::string & basename) : + m_basename(basename), + m_maxEntryPointsParams(0), + m_baseOpcode(0) + { } + virtual ~ApiGen() {} + int readSpec(const std::string & filename); + int readAttributes(const std::string & attribFilename); + size_t maxEntryPointsParams() { return m_maxEntryPointsParams; } + void updateMaxEntryPointsParams(size_t val) { + if (m_maxEntryPointsParams == 0 || val > m_maxEntryPointsParams) m_maxEntryPointsParams = val; + } + int baseOpcode() { return m_baseOpcode; } + void setBaseOpcode(int base) { m_baseOpcode = base; } + + const char *sideString(SideType side) { + const char *retval; + switch(side) { + case CLIENT_SIDE: + retval = "client"; + break; + case SERVER_SIDE: + retval = "server"; + break; + case WRAPPER_SIDE: + retval = "wrapper"; + break; + default: + retval = "unknown"; + } + return retval; + } + + StringVec & clientContextHeaders() { return m_clientContextHeaders; } + StringVec & encoderHeaders() { return m_encoderHeaders; } + StringVec & serverContextHeaders() { return m_serverContextHeaders; } + StringVec & decoderHeaders() { return m_decoderHeaders; } + + EntryPoint * findEntryByName(const std::string & name); + int genOpcodes(const std::string &filename); + int genAttributesTemplate(const std::string &filename); + int genProcTypes(const std::string &filename, SideType side); + int genFuncTable(const std::string &filename, SideType side); + + int genContext(const std::string &filename, SideType side); + int genContextImpl(const std::string &filename, SideType side); + + int genEntryPoints(const std::string &filename, SideType side); + + int genEncoderHeader(const std::string &filename); + int genEncoderImpl(const std::string &filename); + + int genDecoderHeader(const std::string &filename); + int genDecoderImpl(const std::string &filename); + +protected: + virtual void printHeader(FILE *fp) const; + std::string m_basename; + StringVec m_clientContextHeaders; + StringVec m_encoderHeaders; + StringVec m_serverContextHeaders; + StringVec m_decoderHeaders; + size_t m_maxEntryPointsParams; // record the maximum number of parameters in the entry points; + int m_baseOpcode; + int setGlobalAttribute(const std::string & line, size_t lc); +}; + +#endif diff --git a/external/android-emugl/host/tools/emugen/CMakeLists.txt b/external/android-emugl/host/tools/emugen/CMakeLists.txt new file mode 100644 index 0000000..dbdca59 --- /dev/null +++ b/external/android-emugl/host/tools/emugen/CMakeLists.txt @@ -0,0 +1,9 @@ +set(SOURCES + ApiGen.cpp + EntryPoint.cpp + main.cpp + Parser.cpp + strUtils.cpp + TypeFactory.cpp) + +add_executable(emugen ${SOURCES}) diff --git a/external/android-emugl/host/tools/emugen/EntryPoint.cpp b/external/android-emugl/host/tools/emugen/EntryPoint.cpp new file mode 100644 index 0000000..cc3bef1 --- /dev/null +++ b/external/android-emugl/host/tools/emugen/EntryPoint.cpp @@ -0,0 +1,349 @@ +/* +* Copyright (C) 2011 The Android Open Source Project +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ +#include "EntryPoint.h" + +#include "Parser.h" +#include "TypeFactory.h" +#include "strUtils.h" + +#include +#include + +#include + +EntryPoint::EntryPoint() +{ + reset(); +} + +EntryPoint::~EntryPoint() +{ +} + +void EntryPoint::reset() +{ + m_unsupported = false; + m_customDecoder = false; + m_notApi = false; + m_flushOnEncode = false; + m_vars.empty(); +} + +// return true for valid line (need to get into the entry points list) +bool EntryPoint::parse(unsigned int lc, const std::string & str) +{ + size_t pos, last; + std::string field; + + reset(); + std::string linestr = trim(str); + + if (linestr.size() == 0) return false; + if (linestr.at(0) == '#') return false; + + // skip PREFIX + field = getNextToken(linestr, 0, &last, "("); + pos = last + 1; + // return type + field = getNextToken(linestr, pos, &last, ",)"); + + std::string error; + std::string retTypeName; + if (!parseTypeDeclaration(field, &retTypeName, &error)) { + fprintf(stderr, + "line: %d: Parsing error in field <%s>: %s\n", + lc, + field.c_str(), + error.c_str()); + return false; + } + pos = last + 1; + const VarType *theType = TypeFactory::instance()->getVarTypeByName(retTypeName); + if (theType->name() == "UNKNOWN") { + fprintf(stderr, "UNKNOWN retval: %s\n", linestr.c_str()); + } + + m_retval.init(std::string(""), + theType, + std::string(""), + Var::POINTER_OUT, + std::string(""), + std::string("")); + + // function name + m_name = getNextToken(linestr, pos, &last, ",)"); + pos = last + 1; + + // parameters; + int nvars = 0; + while (pos < linestr.size() - 1) { + field = getNextToken(linestr, pos, &last, ",)"); + if (field == "void") { + // 'void' is used as a special case for functions that don't take + // parameters at all. + break; + } + std::string vartype, varname; + if (!parseParameterDeclaration(field, &vartype, &varname, &error)) { + fprintf(stderr, + "line: %d: Parsing error in field <%s>\n", + lc, + field.c_str(), + error.c_str()); + return false; + } + nvars++; + const VarType *v = TypeFactory::instance()->getVarTypeByName(vartype); + if (v->id() == 0) { + fprintf(stderr, "%d: Unknown type: %s\n", lc, vartype.c_str()); + } else { + if (varname == "" && + !(v->name() == "void" && !v->isPointer())) { + std::ostringstream oss; + oss << "var" << nvars; + varname = oss.str(); + } + + m_vars.push_back(Var(varname, v, std::string(""), Var::POINTER_IN, "", "")); + } + pos = last + 1; + } + return true; +} + +void EntryPoint::print(FILE *fp, bool newline, + const std::string & name_suffix, + const std::string & name_prefix, + const std::string & ctx_param ) const +{ + fprintf(fp, "%s %s%s%s(", + m_retval.type()->name().c_str(), + name_prefix.c_str(), + m_name.c_str(), + name_suffix.c_str()); + + if (ctx_param != "") fprintf(fp, "%s ", ctx_param.c_str()); + + for (size_t i = 0; i < m_vars.size(); i++) { + if (m_vars[i].isVoid()) continue; + if (i != 0 || ctx_param != "") fprintf(fp, ", "); + fprintf(fp, "%s %s", m_vars[i].type()->name().c_str(), + m_vars[i].name().c_str()); + } + fprintf(fp, ")%s", newline? "\n" : ""); +} + +Var * EntryPoint::var(const std::string & name) +{ + Var *v = NULL; + for (size_t i = 0; i < m_vars.size(); i++) { + if (m_vars[i].name() == name) { + v = &m_vars[i]; + break; + } + } + return v; +} + +bool EntryPoint::hasPointers() +{ + bool pointers = false; + if (m_retval.isPointer()) pointers = true; + if (!pointers) { + for (size_t i = 0; i < m_vars.size(); i++) { + if (m_vars[i].isPointer()) { + pointers = true; + break; + } + } + } + return pointers; +} + +int EntryPoint::setAttribute(const std::string &line, size_t lc) +{ + size_t pos = 0; + size_t last; + std::string token = getNextToken(line, 0, &last, WHITESPACE); + + if (token == "len") { + pos = last; + std::string varname = getNextToken(line, pos, &last, WHITESPACE); + + if (varname.size() == 0) { + fprintf(stderr, "ERROR: %u: Missing variable name in 'len' attribute\n", (unsigned int)lc); + return -1; + } + Var * v = var(varname); + if (v == NULL) { + fprintf(stderr, "ERROR: %u: variable %s is not a parameter of %s\n", + (unsigned int)lc, varname.c_str(), name().c_str()); + return -2; + } + // set the size expression into var + pos = last; + v->setLenExpression(line.substr(pos)); + } else if (token == "param_check") { + pos = last; + std::string varname = getNextToken(line, pos, &last, WHITESPACE); + + if (varname.size() == 0) { + fprintf(stderr, "ERROR: %u: Missing variable name in 'param_check' attribute\n", (unsigned int)lc); + return -1; + } + Var * v = var(varname); + if (v == NULL) { + fprintf(stderr, "ERROR: %u: variable %s is not a parameter of %s\n", + (unsigned int)lc, varname.c_str(), name().c_str()); + return -2; + } + // set the size expression into var + pos = last; + v->setParamCheckExpression(line.substr(pos)); + + } else if (token == "dir") { + pos = last; + std::string varname = getNextToken(line, pos, &last, WHITESPACE); + if (varname.size() == 0) { + fprintf(stderr, "ERROR: %u: Missing variable name in 'dir' attribute\n", (unsigned int)lc); + return -1; + } + Var * v = var(varname); + if (v == NULL) { + fprintf(stderr, "ERROR: %u: variable %s is not a parameter of %s\n", + (unsigned int)lc, varname.c_str(), name().c_str()); + return -2; + } + + pos = last; + std::string pointerDirStr = getNextToken(line, pos, &last, WHITESPACE); + if (pointerDirStr.size() == 0) { + fprintf(stderr, "ERROR: %u: missing pointer directions\n", (unsigned int)lc); + return -3; + } + + if (pointerDirStr == "out") { + v->setPointerDir(Var::POINTER_OUT); + } else if (pointerDirStr == "inout") { + v->setPointerDir(Var::POINTER_INOUT); + } else if (pointerDirStr == "in") { + v->setPointerDir(Var::POINTER_IN); + } else { + fprintf(stderr, "ERROR: %u: unknow pointer direction %s\n", (unsigned int)lc, pointerDirStr.c_str()); + } + } else if (token == "var_flag") { + pos = last; + std::string varname = getNextToken(line, pos, &last, WHITESPACE); + if (varname.size() == 0) { + fprintf(stderr, "ERROR: %u: Missing variable name in 'var_flag' attribute\n", (unsigned int)lc); + return -1; + } + Var * v = var(varname); + if (v == NULL) { + fprintf(stderr, "ERROR: %u: variable %s is not a parameter of %s\n", + (unsigned int)lc, varname.c_str(), name().c_str()); + return -2; + } + int count = 0; + for (;;) { + pos = last; + std::string flag = getNextToken(line, pos, &last, WHITESPACE); + if (flag.size() == 0) { + if (count == 0) { + fprintf(stderr, "ERROR: %u: missing flag\n", (unsigned int) lc); + return -3; + } + break; + } + count++; + + if (flag == "nullAllowed") { + if (v->isPointer()) { + v->setNullAllowed(true); + } else { + fprintf(stderr, "WARNING: %u: setting nullAllowed for non-pointer variable %s\n", + (unsigned int) lc, v->name().c_str()); + } + } else if (flag == "isLarge") { + if (v->isPointer()) { + v->setIsLarge(true); + } else { + fprintf(stderr, "WARNING: %u: setting isLarge flag for a non-pointer variable %s\n", + (unsigned int) lc, v->name().c_str()); + } + } else { + fprintf(stderr, "WARNING: %u: unknow flag %s\n", (unsigned int)lc, flag.c_str()); + } + } + } else if (token == "custom_pack") { + pos = last; + std::string varname = getNextToken(line, pos, &last, WHITESPACE); + + if (varname.size() == 0) { + fprintf(stderr, "ERROR: %u: Missing variable name in 'custom_pack' attribute\n", (unsigned int)lc); + return -1; + } + Var * v = var(varname); + if (v == NULL) { + fprintf(stderr, "ERROR: %u: variable %s is not a parameter of %s\n", + (unsigned int)lc, varname.c_str(), name().c_str()); + return -2; + } + // set the size expression into var + pos = last; + v->setPackExpression(line.substr(pos)); + } else if (token == "custom_write") { + pos = last; + std::string varname = getNextToken(line, pos, &last, WHITESPACE); + + if (varname.size() == 0) { + fprintf(stderr, "ERROR: %u: Missing variable name in 'custom_write' attribute\n", (unsigned int)lc); + return -1; + } + Var * v = var(varname); + if (v == NULL) { + fprintf(stderr, "ERROR: %u: variable %s is not a parameter of %s\n", + (unsigned int)lc, varname.c_str(), name().c_str()); + return -2; + } + // set the size expression into var + pos = last; + v->setWriteExpression(line.substr(pos)); + } else if (token == "flag") { + pos = last; + std::string flag = getNextToken(line, pos, &last, WHITESPACE); + if (flag.size() == 0) { + fprintf(stderr, "ERROR: %u: missing flag\n", (unsigned int) lc); + return -4; + } + + if (flag == "unsupported") { + setUnsupported(true); + } else if (flag == "custom_decoder") { + setCustomDecoder(true); + } else if (flag == "not_api") { + setNotApi(true); + } else if (flag == "flushOnEncode") { + setFlushOnEncode(true); + } else { + fprintf(stderr, "WARNING: %u: unknown flag %s\n", (unsigned int)lc, flag.c_str()); + } + } else { + fprintf(stderr, "WARNING: %u: unknown attribute %s\n", (unsigned int)lc, token.c_str()); + } + + return 0; +} diff --git a/external/android-emugl/host/tools/emugen/EntryPoint.h b/external/android-emugl/host/tools/emugen/EntryPoint.h new file mode 100644 index 0000000..1061d25 --- /dev/null +++ b/external/android-emugl/host/tools/emugen/EntryPoint.h @@ -0,0 +1,70 @@ +/* +* Copyright (C) 2011 The Android Open Source Project +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ +#ifndef __EntryPoint__H__ +#define __EntryPoint__H__ + +#include +#include +#include + +#include "Var.h" + +//--------------------------------------------------- + +typedef std::vector VarsArray; + +class EntryPoint { +public: + EntryPoint(); + virtual ~EntryPoint(); + bool parse(unsigned int lc, const std::string & str); + void reset(); // reset the class to empty; + void print(FILE *fp = stdout, bool newline = true, + const std::string & name_suffix = std::string(""), + const std::string & name_prefix = std::string(""), + const std::string & ctx_param = std::string("")) const; + const std::string & name() const { return m_name; } + VarsArray & vars() { return m_vars; } + Var & retval() { return m_retval; } + Var * var(const std::string & name); + bool hasPointers(); + bool unsupported() const { return m_unsupported; } + void setUnsupported(bool state) { m_unsupported = state; } + bool customDecoder() { return m_customDecoder; } + void setCustomDecoder(bool state) { m_customDecoder = state; } + bool notApi() const { return m_notApi; } + void setNotApi(bool state) { m_notApi = state; } + bool flushOnEncode() const { return m_flushOnEncode; } + void setFlushOnEncode(bool state) { m_flushOnEncode = state; } + int setAttribute(const std::string &line, size_t lc); + +private: + enum { PR_RETVAL = 0, PR_NAME, PR_VARS, PR_DONE } prState; + std::string m_name; + Var m_retval; + VarsArray m_vars; + bool m_unsupported; + bool m_customDecoder; + bool m_notApi; + bool m_flushOnEncode; + + void err(unsigned int lc, const char *msg) { + fprintf(stderr, "line %d: %s\n", lc, msg); + } +}; + + +#endif diff --git a/external/android-emugl/host/tools/emugen/Parser.cpp b/external/android-emugl/host/tools/emugen/Parser.cpp new file mode 100644 index 0000000..0839f20 --- /dev/null +++ b/external/android-emugl/host/tools/emugen/Parser.cpp @@ -0,0 +1,191 @@ +/* +* Copyright 2014 The Android Open Source Project +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ +#include "Parser.h" + +#include + +#define WHITESPACE " \t\n" + +// Parse the |input| string as a list of type-specific tokens. +// This tokenizes the input, using whitespace as separators and '*' as +// a single token too. On success, return true and sets |*out| to the +// list of tokens. On failure, return false. +// +// Example: 'const char**foo' -> ['const', 'char', '*', '*', 'foo'] +// +static bool parseTypeTokens(const std::string& input, + std::vector* out, + std::string* error) { + out->clear(); + size_t pos = 0U; + + // Parse all tokens in the input, treat '*' as a single token. + // I.e. + for (;;) { + // skip leading whitespace. + pos = input.find_first_not_of(WHITESPACE, pos); + if (pos == std::string::npos) { + break; // end of parse. + } + + // If this is a star, ensure it follows a type name. + // otherwise treat it as part of the final type. + if (input[pos] == '*') { + out->push_back(std::string("*")); + pos += 1U; + continue; + } + + // find end of type/token. + size_t end = input.find_first_of(WHITESPACE "*", pos); + if (end == std::string::npos) { + end = input.size(); + } + + std::string str = input.substr(pos, end - pos); + if (str.size() == 0) { + // Sanity check: should not happen. + if (error != NULL) { + *error = "Unexpected empty token !?"; + } + return false; + } + + out->push_back(str); + pos = end; + } + + if (error != NULL) { + // Sanity check: require non-empty input + if (out->empty()) { + *error = "Empty parameter declaration!"; + return false; + } + + // Sanity check: There must be base type name before any '*' + for (size_t n = 0; n < out->size(); ++n) { + std::string& token = (*out)[n]; + if (token == "*") { + *error = "Unexpected '*' before type name"; + return false; + } else if (token != "const") { + break; + } + } + } + + return true; +} + +// Given |tokens|, an input vector of strings, join the first |count| items +// into a normalized type string, and return it. +static std::string buildTypeString(const std::vector& tokens, + size_t count) { + std::string result; + + for (size_t n = 0; n < count; ++n) { + const std::string& token = tokens[n]; + if (n > 0 && token != "*") { + result.append(" "); + } + result.append(token); + } + return result; +} + + +std::string normalizeTypeDeclaration(const std::string& input) { + std::vector tokens; + if (!parseTypeTokens(input, &tokens, NULL)) { + return ""; + } + return buildTypeString(tokens, tokens.size()); +} + +bool parseTypeDeclaration(const std::string& input, + std::string* typeName, + std::string* error) { + // The type name can be made of several tokens, e.g. 'unsigned int' + // use an array to store them, and a count variable. Each item can be + // one of '*', 'const' or a type name component (e.g. 'struct', 'unsigned') + std::vector tokens; + + if (!parseTypeTokens(input, &tokens, error)) { + return false; + } + + // Sanity check, there must be a least one non-special tokens. + size_t nonSpecialCount = 0; + for (size_t n = 0; n < tokens.size(); ++n) { + if (tokens[n] != "*" && tokens[n] != "const") { + nonSpecialCount++; + } + } + if (nonSpecialCount == 0) { + *error = "Missing type name"; + return false; + } + // Build the type name from all tokens before it. + *typeName = buildTypeString(tokens, tokens.size()); + return true; +} + + +bool parseParameterDeclaration(const std::string& param, + std::string* typeName, + std::string* variableName, + std::string* error) { + std::vector tokens; + + if (!parseTypeTokens(param, &tokens, error)) { + return false; + } + + // Sanity check, there must be a least two non-special tokens. + size_t nonSpecialCount = 0; + for (size_t n = 0; n < tokens.size(); ++n) { + if (tokens[n] != "*" && tokens[n] != "const") { + nonSpecialCount++; + } + } + if (nonSpecialCount == 0) { + *error = "Missing type name"; + return false; + } + if (nonSpecialCount == 1) { + *error = "Missing variable name"; + return false; + } + + // Sanity check: variable name must not be followed by 'const' or '*' + const std::string& lastToken = tokens[tokens.size() - 1U]; + if (lastToken == "*") { + *error = "Extra '*' after variable name"; + return false; + } + if (lastToken == "const") { + *error = "Extra 'const' after variable name"; + return false; + } + + // Extract the variable name as the last token. + if (variableName) { + *variableName = lastToken; + } + // Build the type name from all tokens before it. + *typeName = buildTypeString(tokens, tokens.size() - 1U); + return true; +} diff --git a/external/android-emugl/host/tools/emugen/Parser.h b/external/android-emugl/host/tools/emugen/Parser.h new file mode 100644 index 0000000..f62c076 --- /dev/null +++ b/external/android-emugl/host/tools/emugen/Parser.h @@ -0,0 +1,68 @@ +/* +* Copyright 2014 The Android Open Source Project +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ +#ifndef PARSER_H +#define PARSER_H + +#include + +// Normalize a type declaration. This gets rid of leading/trailing whitespace +// as well as ensure there is a single space separating all input tokens, +// with the exception of '*' which is treated specially by never being +// prepended with a space. +// |input| is the input type declaration. Return normalized form. +// Note that this doesn't try to validate the input. +std::string normalizeTypeDeclaration(const std::string& input); + +// Parse a return type declaration. |input| is the type declaration +// (e.g. 'const char**'). On success return true and sets |*typeName| to +// the appropriate normalized type string. On failure, return false and +// sets |*error| with a human-friendly message explaining the reason for +// failure. +// +// Note that the returned type string is normalized, see comment for +// parseParameterDeclaration() for examples. +// +// NOTE: This does not support declarations of arrays or functions! +// +bool parseTypeDeclaration(const std::string& input, + std::string* typeName, + std::string* error); + +// Parse a function parameter declaration and extract the type and variable +// name from it. |param| is the individual parameter declaration from the +// function's signature (e.g. 'const char *items') +// +// On success, returns true and sets |*vartype| and |*varname| to the +// appropriate type name and variable name. |varname| can be NULL if the caller +// isn't interested in the variable name. +// +// On failure, return false and sets |*error| to a human-friendly message +// explaining the reason for failure. +// +// Note that the returned type name is normalized with regards to whitespace +// and star location, e.g.: +// +// const void *items -> 'const void*' +// char* *items -> 'char**' +// const void * const * items -> 'const void* const*' +// unsigned int *const data -> 'unsigned int* const' +// +bool parseParameterDeclaration(const std::string& param, + std::string* typeName, + std::string* variableName, + std::string* error); + +#endif // PARSER_H diff --git a/external/android-emugl/host/tools/emugen/Parser_unittest.cpp b/external/android-emugl/host/tools/emugen/Parser_unittest.cpp new file mode 100644 index 0000000..904b247 --- /dev/null +++ b/external/android-emugl/host/tools/emugen/Parser_unittest.cpp @@ -0,0 +1,120 @@ +/* +* Copyright 2014 The Android Open Source Project +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ +#include "Parser.h" + +#include + +#define ARRAYLEN(x) (sizeof(x) / sizeof(x[0])) + +TEST(ParserTest, normalizeTypeDeclaration) { + static const struct { + const char* expected; + const char* input; + } kData[] = { + { "char", "char" }, + { "const unsigned int", " const unsigned\tint\n" }, + { "char* const**", "char *const* *" }, + }; + const size_t kDataSize = ARRAYLEN(kData); + for (size_t n = 0; n < kDataSize; ++n) { + std::string result; + std::string text = "When parsing '"; + text += kData[n].input; + text += "'"; + + result = normalizeTypeDeclaration(kData[n].input); + EXPECT_STREQ(kData[n].expected, result.c_str()) << text; + } +} + +TEST(ParserTest, parseTypeDeclaration) { + static const struct { + const char* input; + bool expected; + const char* expectedType; + const char* expectedError; + } kData[] = { + { "const", false, NULL, "Missing type name" }, + { "const const", false, NULL, "Missing type name" }, + { "foo", true, "foo", NULL }, + { "void", true, "void", NULL }, + { "const foo", true, "const foo", NULL }, + { "foo *", true, "foo*", NULL }, + { "char foo", true, "char foo", NULL }, + { "\tunsigned \t int\n", true, "unsigned int", NULL }, + { "const * char", false, NULL, "Unexpected '*' before type name" }, + { "const char * ", true, "const char*", NULL }, + { "const void*const * *", true, "const void* const**", NULL }, + }; + const size_t kDataSize = ARRAYLEN(kData); + for (size_t n = 0; n < kDataSize; ++n) { + std::string varname, vartype, error; + std::string text = "When parsing '"; + text += kData[n].input; + text += "'"; + + EXPECT_EQ(kData[n].expected, + parseTypeDeclaration(kData[n].input, + &vartype, + &error)) << text; + if (kData[n].expected) { + EXPECT_STREQ(kData[n].expectedType, vartype.c_str()) << text; + } else { + EXPECT_STREQ(kData[n].expectedError, error.c_str()) << text; + } + } +} + +TEST(ParserTest, parseParameterDeclaration) { + static const struct { + const char* input; + bool expected; + const char* expectedType; + const char* expectedVariable; + const char* expectedError; + } kData[] = { + { "foo", false, NULL, NULL, "Missing variable name" }, + { "const", false, NULL, NULL, "Missing type name" }, + { "const foo", false, NULL, NULL, "Missing variable name" }, + { "const const", false, NULL, NULL, "Missing type name" }, + { "char foo", true, "char", "foo", NULL }, + { "unsigned int\t bar\n", true, "unsigned int", "bar", NULL }, + { "const * char foo", false, NULL, NULL, "Unexpected '*' before type name" }, + { "const char * foo", true, "const char*", "foo", NULL }, + { "const void*const *data", true, "const void* const*", "data", NULL }, + { "char foo const", false, NULL, NULL, "Extra 'const' after variable name" }, + { "int bar*", false, NULL, NULL, "Extra '*' after variable name" }, + }; + const size_t kDataSize = ARRAYLEN(kData); + for (size_t n = 0; n < kDataSize; ++n) { + std::string varname, vartype, error; + std::string text = "When parsing '"; + text += kData[n].input; + text += "'"; + + EXPECT_EQ(kData[n].expected, + parseParameterDeclaration(kData[n].input, + &vartype, + &varname, + &error)) << text; + if (kData[n].expected) { + EXPECT_STREQ(kData[n].expectedType, vartype.c_str()) << text; + EXPECT_STREQ(kData[n].expectedVariable, varname.c_str()) << text; + } else { + EXPECT_STREQ(kData[n].expectedError, error.c_str()) << text; + } + } +} diff --git a/external/android-emugl/host/tools/emugen/README b/external/android-emugl/host/tools/emugen/README new file mode 100644 index 0000000..0fe85f9 --- /dev/null +++ b/external/android-emugl/host/tools/emugen/README @@ -0,0 +1,332 @@ +Introduction: +------------- +The emugen tool is a tool to generate a wire protocol implementation +based on provided API. The tool generates c++ encoder code that takes +API calls and encodes them into the wire and decoder code that decodes +the wire stream and calls server matching API. +The emugen tool includes additional functionality that enables to +generate an wrapper library. The wrapper library provides entry points +for the specified API, where each entry routes the call via a dispatch +table. The dispatch table may be initialized as required by a specific application. + +The following paragraphs includes the following: + * Wire Protocol Description + * Input files description & format + * Generated code description. + + +Note: In this document, the caller is referred to as Encoder or Client +and the callee is referred to as the Decoder or Server. These terms +are used interchangeably by the context. + + + +Wire Protocol packet structure: +------------------------------- +A general Encoder->Decoder packet is structured as following: +struct Packet { + unsigned int opcode; + unsigned int packet_len; + … parameter 1 + … parameter 2 +}; +A general Decoder->Encoder reply is expected to be received in the +context of the ‘call’ that triggered it, thus it includes the reply +data only, with no context headers. In precise term terms, a reply +packet will look like: + +struct { + ...// reply data +}; + +consider the following function call: +int foo(int p1, short s1) +will be encoded into : +{ + 101, // foo opcode + 14, // sizeof(opcode) + sizeof(packet_len) + sizeof(int) + sizeof(short) + p1, // 4 bytes + s1 // 2 bytes +} + +Since ‘foo’ returns value, the caller is expected to read back the return packet from the server->client stream. The return value in this example is in thus the return packet is: +{ + int retval; +} + + +Pointer decoding: +---------------- +The wire protocol also allows exchanging of pointer data +(arrays). Pointers are defined with directions: + + in : Data is sent from the caller to the calle + out: Data is sent from the callee to the caller + in_out: data is sent from the caller and return in place. + +‘in’ and ‘in_out’ encoded with their len: +{ + unsinged int pointer_data_len; + unsigned char data[pointer_data_len];… // pointer data +} + +‘out’ pointers are encoded by data length only: +{ +unsigned int pointer_data_len; +} + +‘out’ and ‘in_out’ pointer’s data is returned in the return +packet. For example, consider the following call: + +int foo(int n, int *ptr); // assume that ‘data’ is in_out pointer which contains ‘n’ ints + +The caller packet will have the following form: +{ + 101, // foo opcode + xx, sizeof(opcode) + sizeof(datalen) + sizeof(int) + sizeof(unsigned int) + n * sizeof(int); + n, // the n parameter + n * sizeof(int), // size of the data in ptr + … // n* sizeof(int) bytes +} + +The return packet is; +{ + …. // n * sizeof(int) bytes of data return in ptr + retval // sizeof(int) - the return value of the function; +} + +Endianess +--------- +The Wire protocol is designed to impose minimum overhead on the client +side. Thus, the data endianness that is sent across the wire is +determined by the ‘client’ side. It is up to the server side to +determine the client endianess and marshal the packets as required. + + + +Emugen input files - protocol specification +------------------------------------------- +The protocol generated by emugen consists of two input files: + +1. basename.in - A sepcification of the protocol RPC procedures. This +part of the specification is expected to be generated automatically +from c/c++ header files or similar. + +‘basename’ is the basename for the protocol and will be used to prefix +the files that are generated for this protocol. A line in the .in +file has the following format: + +[prefix](retvalType, FuncName, [param name],...) +where + retvalType - The function return value type + FuncName - function name + mandatory parameter type + [param name] - optional parameter name +Examples: +GL_ENTRY(void, glVertex1f, float v) +XXX(int *, foo, int n, float, short) +XXX(void, glFlush, void) + +Note: Empty lines in the file are ignored. A line starts with # is a comment + +2. basename.attrib - Attributes information of the API. +This file includes additional flags, pointers datalen information and +global attributes of the protocol. For uptodate format of the file, +please refer to the specification file in the project source +tree. The format of the .attrib file is described below. + +3. basename.types - Types information + +This files describes the types that are described by the API. A type +is defined as follows: + +where: + is the name of the type as described in the API + 0, 8, 16, 32 sizes are accepted + a string to format the value of the type, as +acceted by printf(3) + true or false string species whether the type should be +treated as a pointer. + +example: +GLint 32 %d false +GLint* 32 %p true +GLptr 32 %p true + +Encoder generated code files +---------------------------- +In order to generate the encoder files, one should run the ‘emugen’ +tool as follows: + +emugen -i -E +where: + containes the api specification files (basename.in + basename.attrib) + - a directory name to generate the encoder output files + basename - The basename for the api. + +Assuming the basename is ‘api’, The following files are generated: + +api_opcodes.h - defines the protocol opcodes. The first opcode value +is 0, unless defined otherwise in the .attrib file + +api_entry.cpp - defines entry points for the functions that are +defined by the protocol. this File also includes a function call +‘setContextAccessor(void *(*f)()). This function should be used to +provide a callback function that is used by the functions to access +the encoder context. For example, such callback could fetch the +context from a Thread Local Storage (TLS) location. + +api_client_proc.h - type defintions for the protocol procedures. + +api_client_context.h - defines the client side dispatch table data +structure that stores the encoding functions. This data structure also +includes ‘accessors’ methods such that library user can override +default entries for special case handling. + +api_client_context.cpp - defines an initialization function for +dispatch table + +api_enc.h - This header file defines the encoder data strcuture. The +encoder data structure inherits its functionality from the +‘client_context’ class above and adds encoding and streaming +functionality. + +api_enc.cpp - Encoder implementation. + +Decoder generated files +----------------------- +In order to generate the decoder files, one should run the ‘emugen’ +tool as follows: +emugen -i -D basename +where: + containes the api specification files (basename.in + basename.attrib) + - a directory name to generate the decoder output files + basename - The basename for the api. + +With resepct to the example above, Emugen will generate the following +files: + +api_opcodes.h - Protocol opcodes + +api_server_proc.h - type definitions for the server side procedures + +api_server_context.h - dispatch table the decoder functions + +api_server_context.cpp - dispatch table initialization function +api_dec.h - Decoder header file + +api_dec.cpp - Decoder implementation. In addtion, this file includes +an intiailization function that uses a user provided callback to +initialize the API server implementation. An example for such +initialization is loading a set of functions from a shared library +module. + +Wrapper generated files +----------------------- +In order to generate a wrapper library files, one should run the +'emugen' tool as follows: + +emugen -i -W basename +where: + containes the api specification files (basename.in + basename.attrib) + - a directory name to generate the wrapper output files + basename - The basename for the api. + +With resepct to the example above, Emugen will generate the following +files: + +api_wrapper_proc.h - type definitions for the wrapper procedures + +api_wrapper_context.h - dispatch table the wrapper functions + +api_wrapper_context.cpp - dispatch table initialization function +api_wrapper_entry.cpp - entry points for the API + + +.attrib file format description: +------------------------------- +The .attrib file is an input file to emugen and is used to provide + additional information that is required for the code generation. +The file format is as follows: + +a line that starts with # is ignored (comment) +a empty line just whitespace of (" " "\t" "\n") is ignored. + +The file is divided into 'sections', each describes a specific API +function call. A section starts with the name of the function in +column 0. + +A section that starts with the reserved word 'GLOBAL' provides global +attributes. + +below are few sections examples: + +GLOBAL + encoder_headers string.h kuku.h + +glVertex3fv + len data (size) +glTexImage2D + len pixels (pixels == NULL? 0 : (format_pixel_size(internalformat) * width * height * type_size(type))) + + +Global section flags description: + +base_opcode + set the base opcode value for this api + format: base_opcode 100 + +encoder_headers + a list of headers that will be included in the encoder header file + format: encoder_headers "kuku.h" + +client_context_headers + a list of headers that will be included in the client context header file + format: client_context_headers "kuku.h" + +decoder_headers + a list of headers that will be included in the decoder header file + format: decoder_headers "kuku.h" + +server_context_headers + a list of headers that will be included in the server context header file + format: server_context_headers "kuku.h" + + +Entry point flags description: + + len + desciption : provide an expression to calcualte an expression data len + format: len + +custom_pack + description: provide an expression to pack data into the stream. + format: custom_pack + The stream is represented by a (unsigned char *)ptr. The expression may also refer + to other function parameters. In addition, the expression may refer to 'void *self' which + is the encoding context as provided by the caller. + + dir + description : set a pointer direction (in - for data that goes + to the codec, out from data that returns from the codec. + format: dir <[in | out | inout]> + + var_flag + description : set variable flags + format: var_flag < nullAllowed | isLarge | ... > + + nullAllowed -> for pointer variables, indicates that NULL is a valid value + isLarge -> for pointer variables, indicates that the data should be sent without an intermediate copy + + flag + description: set entry point flag; + format: flag < unsupported | ... > + supported flags are: + unsupported - The encoder side implementation is pointed to "unsuppored reporting function". + custom_decoder - The decoder is expected to be provided with + custom implementation. The call to the + deocder function includes a pointer to the + context + not_api - the function is not native gl api + + diff --git a/external/android-emugl/host/tools/emugen/TypeFactory.cpp b/external/android-emugl/host/tools/emugen/TypeFactory.cpp new file mode 100644 index 0000000..1e0cc96 --- /dev/null +++ b/external/android-emugl/host/tools/emugen/TypeFactory.cpp @@ -0,0 +1,157 @@ +/* +* Copyright (C) 2011 The Android Open Source Project +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ +#include "TypeFactory.h" + +#include "Parser.h" +#include "VarType.h" +#include "strUtils.h" + +#include +#include + +#include +#include + + +TypeFactory * TypeFactory::m_instance = NULL; + +typedef std::map TypeMap; +static TypeMap g_varMap; +static bool g_initialized = false; +static int g_typeId = 0; + + +#define ADD_TYPE(name, size, printformat,ispointer) \ + g_varMap.insert(std::pair(name, VarType(g_typeId++, name, (size + 7) >> 3, printformat , ispointer))); + +void TypeFactory::initBaseTypes() +{ + g_initialized = true; + ADD_TYPE("UNKNOWN", 0, "0x%x", false); + ADD_TYPE("void", 0, "0x%x", false); + ADD_TYPE("char", 8, "%c", false); + ADD_TYPE("int", 32, "%d", false); + ADD_TYPE("float", 32, "%d", false); + ADD_TYPE("short", 16, "%d", false); +} + +int TypeFactory::initFromFile(const std::string &filename) +{ + if (!g_initialized) { + initBaseTypes(); + } + + FILE *fp = fopen(filename.c_str(), "rt"); + if (fp == NULL) { + perror(filename.c_str()); + return -1; + } + char line[1000]; + int lc = 0; + while(fgets(line, sizeof(line), fp) != NULL) { + lc++; + std::string str = trim(line); + if (str.size() == 0 || str.at(0) == '#') { + continue; + } + size_t pos = 0, last; + std::string name; + name = getNextToken(str, pos, &last, WHITESPACE); + name = normalizeTypeDeclaration(name); + if (name.size() == 0) { + fprintf(stderr, "Error: %d : missing type name\n", lc); + return -2; + } + pos = last + 1; + std::string size; + size = getNextToken(str, pos, &last, WHITESPACE); + if (size.size() == 0) { + fprintf(stderr, "Error: %d : missing type width\n", lc); + return -2; + } + pos = last + 1; + std::string printString; + printString = getNextToken(str, pos, &last, WHITESPACE); + if (printString.size() == 0) { + fprintf(stderr, "Error: %d : missing print-string\n", lc); + return -2; + } + + // The ispointer definition is optional since we can just + // look at the type name, and determine it is a pointer if + // it ends with '*'. + bool isPointer = (name[name.size() - 1U] == '*'); + + pos = last + 1; + std::string pointerDef; + pointerDef = getNextToken(str, pos, &last, WHITESPACE); + if (pointerDef.size() != 0) { + // Just a little sanity check. + if (std::string("true")==pointerDef) { + if (!isPointer) { + fprintf(stderr, "Error: %d: invalid isPointer definition: 'true' but name does not end with '*'!\n", lc); + return -2; + } + } else if (std::string("false")==pointerDef) { + if (isPointer) { + fprintf(stderr, "Error: %d: invalid isPointer definition: 'false' but name does end with '*'!\n", lc); + return -2; + } + } else { + fprintf(stderr, "Error: %d : invalid isPointer definition, must be either \"true\" or \"false\"\n", lc); + return -2; + } + } + + size_t bitSize = atoi(size.c_str()); + size_t byteSize = (bitSize + 7) >> 3; + + if (getVarTypeByName(name)->id() != 0) { + fprintf(stderr, + "Warining: %d : type %s is already known, definition in line %d is taken\n", + lc, name.c_str(), lc); + } + g_varMap.insert(std::pair( + name, VarType(g_typeId++, + name, + byteSize, + printString, + isPointer))); + std::string constName = "const " + name; + g_varMap.insert(std::pair( + constName, VarType(g_typeId++, + constName, + byteSize, + printString, + isPointer))); //add a const type + } + g_initialized = true; + return 0; +} + + +const VarType * TypeFactory::getVarTypeByName(const std::string & type) +{ + if (!g_initialized) { + initBaseTypes(); + } + TypeMap::iterator i = g_varMap.find(type); + if (i == g_varMap.end()) { + i = g_varMap.find("UNKNOWN"); + } + return &(i->second); +} + diff --git a/external/android-emugl/host/tools/emugen/TypeFactory.h b/external/android-emugl/host/tools/emugen/TypeFactory.h new file mode 100644 index 0000000..deee2ca --- /dev/null +++ b/external/android-emugl/host/tools/emugen/TypeFactory.h @@ -0,0 +1,37 @@ +/* +* Copyright (C) 2011 The Android Open Source Project +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ +#ifndef __TYPE__FACTORY__H__ +#define __TYPE__FACTORY__H__ + +#include +#include "VarType.h" + +class TypeFactory { +public: + static TypeFactory *instance() { + if (m_instance == NULL) { + m_instance = new TypeFactory; + } + return m_instance; + } + const VarType * getVarTypeByName(const std::string &type); + int initFromFile(const std::string &filename); +private: + static TypeFactory *m_instance; + void initBaseTypes(); + TypeFactory() {} +}; +#endif diff --git a/external/android-emugl/host/tools/emugen/Var.h b/external/android-emugl/host/tools/emugen/Var.h new file mode 100644 index 0000000..322c66a --- /dev/null +++ b/external/android-emugl/host/tools/emugen/Var.h @@ -0,0 +1,110 @@ +/* +* Copyright (C) 2011 The Android Open Source Project +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ +#ifndef __VAR__H__ +#define __VAR__H__ + +#include "VarType.h" +#include +#include + +class Var { +public: + // pointer data direction - from the client point of view. + typedef enum { POINTER_OUT = 0x1, POINTER_IN = 0x2, POINTER_INOUT = 0x3 } PointerDir; + Var() : + m_name(""), + m_type(NULL), + m_lenExpression(""), + m_pointerDir(POINTER_IN), + m_nullAllowed(false), + m_isLarge(false), + m_packExpression(""), + m_writeExpression(""), + m_paramCheckExpression("") + + { + } + + Var(const std::string & name, + const VarType * vartype, + const std::string & lenExpression, + PointerDir dir, + const std::string &packExpression, + const std::string &writeExpression) : + m_name(name), + m_type(const_cast(vartype)), + m_lenExpression(lenExpression), + m_pointerDir(dir), + m_nullAllowed(false), + m_isLarge(false), + m_packExpression(packExpression), + m_writeExpression(writeExpression), + m_paramCheckExpression("") + { + } + + void init(const std::string name, const VarType * vartype, + std::string lenExpression, + PointerDir dir, + std::string packExpression, + std::string writeExpression) { + m_name = name; + m_type = vartype; + m_lenExpression = lenExpression; + m_packExpression = packExpression; + m_writeExpression = writeExpression; + m_pointerDir = dir; + m_nullAllowed = false; + m_isLarge = false; + + } + + const std::string & name() const { return m_name; } + const VarType * type() const { return m_type; } + bool isPointer() const { return m_type->isPointer(); } + bool isVoid() const { return ((m_type->bytes() == 0) && (!m_type->isPointer())); } + const std::string & lenExpression() const { return m_lenExpression; } + const std::string & packExpression() const { return(m_packExpression); } + const std::string & writeExpression() const { return(m_writeExpression); } + const std::string & paramCheckExpression() const { return m_paramCheckExpression; } + void setLenExpression(const std::string & lenExpression) { m_lenExpression = lenExpression; } + void setPackExpression(const std::string & packExpression) { m_packExpression = packExpression; } + void setWriteExpression(const std::string & writeExpression) { m_writeExpression = writeExpression; } + void setParamCheckExpression(const std::string & paramCheckExpression) { m_paramCheckExpression = paramCheckExpression; } + void setPointerDir(PointerDir dir) { m_pointerDir = dir; } + PointerDir pointerDir() { return m_pointerDir; } + void setNullAllowed(bool state) { m_nullAllowed = state; } + void setIsLarge(bool state) { m_isLarge = state; } + bool nullAllowed() const { return m_nullAllowed; } + bool isLarge() const { return m_isLarge; } + void printType(FILE *fp) { fprintf(fp, "%s", m_type->name().c_str()); } + void printTypeName(FILE *fp) { printType(fp); fprintf(fp, " %s", m_name.c_str()); } + +private: + std::string m_name; + const VarType * m_type; + bool m_pointer; // is this variable a pointer; + std::string m_lenExpression; // an expression to calcualte a pointer data size + PointerDir m_pointerDir; + bool m_nullAllowed; + bool m_isLarge; + std::string m_packExpression; // an expression to pack data into the stream + std::string m_writeExpression; // an expression to write data into the stream + std::string m_paramCheckExpression; //an expression to check parameter value + +}; + +#endif diff --git a/external/android-emugl/host/tools/emugen/VarType.h b/external/android-emugl/host/tools/emugen/VarType.h new file mode 100644 index 0000000..b6937a0 --- /dev/null +++ b/external/android-emugl/host/tools/emugen/VarType.h @@ -0,0 +1,62 @@ +/* +* Copyright (C) 2011 The Android Open Source Project +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ +#ifndef __VARTYPE__H__ +#define __VARTYPE__H__ + +#include + +// VarType models the types of values used on the wire protocol by +// both encoders and decoders. Each type is identified by a unique id, +// and a name, and provides a size in bytes for the values, a printf-like +// formatter string, and a flag telling if the value corresponds to a +// pointer. +class VarType { +public: + VarType() : + m_id(0), + m_name("default_constructed"), + m_byteSize(0), + m_printFormat("0x%x"), + m_isPointer(false) {} + + VarType(size_t id, + const std::string& name, + size_t byteSize, + const std::string& printFormat, + bool isPointer) : + m_id(id), + m_name(name), + m_byteSize(byteSize), + m_printFormat(printFormat), + m_isPointer(isPointer) {} + + ~VarType() {} + + size_t id() const { return m_id; } + const std::string& name() const { return m_name; } + size_t bytes() const { return m_byteSize; } + const std::string& printFormat() const { return m_printFormat; } + bool isPointer() const { return m_isPointer; } + +private: + size_t m_id; + std::string m_name; + size_t m_byteSize; + std::string m_printFormat; + bool m_isPointer; +}; + +#endif diff --git a/external/android-emugl/host/tools/emugen/errors.h b/external/android-emugl/host/tools/emugen/errors.h new file mode 100644 index 0000000..d09c292 --- /dev/null +++ b/external/android-emugl/host/tools/emugen/errors.h @@ -0,0 +1,24 @@ +/* +* Copyright (C) 2011 The Android Open Source Project +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ +#ifndef _ERRORS_H_ +#define _ERRORS_H_ + +#define BAD_USAGE -1 +#define BAD_SPEC_FILE -2 +#define BAD_TYPES_FILE -3 +#define BAD_ATTRIBUTES_FILE -4 + +#endif diff --git a/external/android-emugl/host/tools/emugen/getopt.c b/external/android-emugl/host/tools/emugen/getopt.c new file mode 100644 index 0000000..3523538 --- /dev/null +++ b/external/android-emugl/host/tools/emugen/getopt.c @@ -0,0 +1,76 @@ +#include "getopt.h" + +#include +#include + +#define _getprogname() nargv[0] + +int opterr = 1; +int optind = 1; +int optopt = 0; +char* optarg; + +int getopt(int argc, char* const argv[], const char* ostr) { + static const char kEmpty[] = ""; + static const char* place = kEmpty; + if (!*place) { + if (optind >= argc) + return -1; + + const char* arg = argv[optind]; + if (arg[0] != '-') { + // Not an option. + return -1; + } + if (arg[1] == '-' && !arg[2]) { + // '--' -> end of options. + return -1; + } + if (!arg[1]) { + // Single '-', If the program wants it, treat it as an option. + // Otherwise, it's the end of options. + if (!strchr(ostr, '-')) { + return -1; + } + optopt = '-'; + place = arg + 1; + } else { + optopt = arg[1]; + place = arg + 2; + } + }; + + char* oindex = strchr(ostr, optopt); + if (!oindex) { + // Unsupported option. + (void)fprintf(stderr, "%s: illegal option -- %c\n", argv[0]); + return '?'; + } + if (oindex[1] != ':') { + // No argument needed. + optarg = NULL; + if (!*place) + optind++; + return optopt; + } + + // This option needs an argument. Either after the option character, + // or the argument that follows. + if (*place) { + optarg = (char *)place; + } else if (argc > ++optind) { + optarg = (char *)argv[optind]; + } else if (oindex[2] == ':') { + // Optional argument is missing. + place = kEmpty; + optarg = NULL; + return optopt; + } else { + // Missing argument. + place = kEmpty; + (void)fprintf(stderr, "%s: option requires an argument --%c\n", + argv[0], optopt); + return ':'; + } + return optopt; +} diff --git a/external/android-emugl/host/tools/emugen/getopt.h b/external/android-emugl/host/tools/emugen/getopt.h new file mode 100644 index 0000000..cc04850 --- /dev/null +++ b/external/android-emugl/host/tools/emugen/getopt.h @@ -0,0 +1,18 @@ +#ifndef GETOPT_H +#define GETOPT_H + +#ifdef __cplusplus +extern "C" { +#endif + +extern int optind; +extern char* optarg; +extern int optopt; + +int getopt(int argc, char* const argv[], const char* ostr); + +#ifdef __cplusplus +} +#endif + +#endif // GETOPT_H diff --git a/external/android-emugl/host/tools/emugen/main.cpp b/external/android-emugl/host/tools/emugen/main.cpp new file mode 100644 index 0000000..5408617 --- /dev/null +++ b/external/android-emugl/host/tools/emugen/main.cpp @@ -0,0 +1,164 @@ +/* +* Copyright (C) 2011 The Android Open Source Project +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ +#include +#include +#include "errors.h" +#include "EntryPoint.h" +#include "strUtils.h" +#include "ApiGen.h" +#include "TypeFactory.h" +#include "getopt.h" + +const std::string SPEC_EXTENSION = std::string(".in"); +const std::string ATTRIB_EXTENSION = std::string(".attrib"); +const std::string TYPES_EXTENTION = std::string(".types"); + + +void usage(const char *filename) +{ + fprintf(stderr, "Usage: %s [options] \n", filename); + fprintf(stderr, "\t-h: This message\n"); + fprintf(stderr, "\t-E : generate encoder into dir\n"); + fprintf(stderr, "\t-D : generate decoder into dir\n"); + fprintf(stderr, "\t-i: input dir, local directory by default\n"); + fprintf(stderr, "\t-T : generate attribute template into the input directory\n\t\tno other files are generated\n"); + fprintf(stderr, "\t-W : generate wrapper into dir\n"); +} + +int main(int argc, char *argv[]) +{ + std::string encoderDir = ""; + std::string decoderDir = ""; + std::string wrapperDir = ""; + std::string inDir = "."; + bool generateAttributesTemplate = false; + + int c; + while((c = getopt(argc, argv, "TE:D:i:hW:")) != -1) { + switch(c) { + case 'W': + wrapperDir = std::string(optarg); + break; + case 'T': + generateAttributesTemplate = true; + break; + case 'h': + usage(argv[0]); + exit(0); + break; + case 'E': + encoderDir = std::string(optarg); + break; + case 'D': + decoderDir = std::string(optarg); + break; + case 'i': + inDir = std::string(optarg); + break; + case ':': + fprintf(stderr, "Missing argument !!\n"); + // fall through + default: + usage(argv[0]); + exit(0); + } + } + + if (optind >= argc) { + fprintf(stderr, "Usage: %s [options] \n", argv[0]); + return BAD_USAGE; + } + + if (encoderDir.size() == 0 && + decoderDir.size() == 0 && + generateAttributesTemplate == false && + wrapperDir.size() == 0) { + fprintf(stderr, "No output specified - aborting\n"); + return BAD_USAGE; + } + + std::string baseName = std::string(argv[optind]); + ApiGen apiEntries(baseName); + + // init types; + std::string typesFilename = inDir + "/" + baseName + TYPES_EXTENTION; + + if (TypeFactory::instance()->initFromFile(typesFilename) < 0) { + fprintf(stderr, "missing or error reading types file: %s...ignored\n", typesFilename.c_str()); + } + + std::string filename = inDir + "/" + baseName + SPEC_EXTENSION; + if (apiEntries.readSpec(filename) < 0) { + perror(filename.c_str()); + return BAD_SPEC_FILE; + } + + + if (generateAttributesTemplate) { + apiEntries.genAttributesTemplate(inDir + "/" + baseName + ATTRIB_EXTENSION); + exit(0); + } + + std::string attribFileName = inDir + "/" + baseName + ATTRIB_EXTENSION; + if (apiEntries.readAttributes(attribFileName) < 0) { + perror(attribFileName.c_str()); + fprintf(stderr, "failed to parse attributes\n"); + exit(1); + } + + if (encoderDir.size() != 0) { + + apiEntries.genOpcodes(encoderDir + "/" + baseName + "_opcodes.h"); + apiEntries.genContext(encoderDir + "/" + baseName + "_client_context.h", ApiGen::CLIENT_SIDE); + apiEntries.genContextImpl(encoderDir + "/" + baseName + "_client_context.cpp", ApiGen::CLIENT_SIDE); + + apiEntries.genProcTypes(encoderDir + "/" + baseName + "_client_proc.h", ApiGen::CLIENT_SIDE); + apiEntries.genFuncTable(encoderDir + "/" + baseName + "_ftable.h", ApiGen::CLIENT_SIDE); + + apiEntries.genEntryPoints(encoderDir + "/" + baseName + "_entry.cpp", ApiGen::CLIENT_SIDE); + apiEntries.genEncoderHeader(encoderDir + "/" + baseName + "_enc.h"); + apiEntries.genEncoderImpl(encoderDir + "/" + baseName + "_enc.cpp"); + } + + if (decoderDir.size() != 0) { + apiEntries.genOpcodes(decoderDir + "/" + baseName + "_opcodes.h"); + apiEntries.genProcTypes(decoderDir + "/" + baseName + "_server_proc.h", ApiGen::SERVER_SIDE); + apiEntries.genContext(decoderDir + "/" + baseName + "_server_context.h", ApiGen::SERVER_SIDE); + apiEntries.genContextImpl(decoderDir + "/" + baseName + "_server_context.cpp", ApiGen::SERVER_SIDE); + apiEntries.genDecoderHeader(decoderDir + "/" + baseName + "_dec.h"); + apiEntries.genDecoderImpl(decoderDir + "/" + baseName + "_dec.cpp"); + } + + if (wrapperDir.size() != 0) { + apiEntries.genProcTypes(wrapperDir + "/" + baseName + "_wrapper_proc.h", ApiGen::WRAPPER_SIDE); + apiEntries.genContext(wrapperDir + "/" + baseName + "_wrapper_context.h", ApiGen::WRAPPER_SIDE); + apiEntries.genContextImpl(wrapperDir + "/" + baseName + "_wrapper_context.cpp", ApiGen::WRAPPER_SIDE); + apiEntries.genEntryPoints(wrapperDir + "/" + baseName + "_wrapper_entry.cpp", ApiGen::WRAPPER_SIDE); + } + +#ifdef DEBUG_DUMP + int withPointers = 0; + printf("%d functions found\n", int(apiEntries.size())); + for (int i = 0; i < apiEntries.size(); i++) { + if (apiEntries[i].hasPointers()) { + withPointers++; + apiEntries[i].print(); + } + } + fprintf(stdout, "%d entries has poitners\n", withPointers); +#endif + +} diff --git a/external/android-emugl/host/tools/emugen/strUtils.cpp b/external/android-emugl/host/tools/emugen/strUtils.cpp new file mode 100644 index 0000000..357054b --- /dev/null +++ b/external/android-emugl/host/tools/emugen/strUtils.cpp @@ -0,0 +1,49 @@ +/* +* Copyright (C) 2011 The Android Open Source Project +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ +#include "strUtils.h" + +using namespace std; + + +std::string getNextToken(const std::string & str, size_t pos, size_t * last, const std::string & delim) +{ + if (str.size() == 0 || pos >= str.size()) return ""; + + pos = str.find_first_not_of(WHITESPACE, pos); + if (pos == std::string::npos) return ""; + + *last = str.find_first_of(delim, pos); + if (*last == std::string::npos) *last = str.size(); + std::string retval = str.substr(pos, *last - pos); + retval = trim(retval); + return retval; +} + + +std::string trim(const string & str) +{ + string result; + string::size_type start = str.find_first_not_of(WHITESPACE, 0); + string::size_type end = str.find_last_not_of(WHITESPACE); + if (start == string::npos || end == string::npos) { + result = string(""); + } else { + result = str.substr(start, end - start + 1); + } + return result; +} + + diff --git a/external/android-emugl/host/tools/emugen/strUtils.h b/external/android-emugl/host/tools/emugen/strUtils.h new file mode 100644 index 0000000..3fa0908 --- /dev/null +++ b/external/android-emugl/host/tools/emugen/strUtils.h @@ -0,0 +1,33 @@ +/* +* Copyright (C) 2011 The Android Open Source Project +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ +#ifndef STR_UTILS_H_ +#define STR_UTILS_H_ + +#include +#include + +#define WHITESPACE " \t\n" + +std::string trim(const std::string & str); +std::string getNextToken(const std::string & str, size_t pos, size_t * last, const std::string & delim); +template std::string inline toString(const T& t) { + std::stringstream ss; + ss << t; + return ss.str(); + +} + +#endif diff --git a/external/android-emugl/host/tools/emugen/tests/run-tests.sh b/external/android-emugl/host/tools/emugen/tests/run-tests.sh new file mode 100755 index 0000000..67409ed --- /dev/null +++ b/external/android-emugl/host/tools/emugen/tests/run-tests.sh @@ -0,0 +1,129 @@ +#!/bin/sh + +# Copyright 2014 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +set -e + +export LANG=C +export LC_ALL=C + +PROGDIR=$(dirname "$0") +PROGNAME=$(basename "$0") + +fatal () { + echo "ERROR: $@" + exit 1 +} + +OPT_EMUGEN= +OPT_HELP= +OPT_OUT_DIR= +OPT_TOOL= + +for OPT; do + OPTARG=$(expr "x$OPT" : "x[^=]*=\\(.*\\)" || true) + case $OPT in + --help|-h|-?) + OPT_HELP=true + ;; + --emugen=*) + OPT_EMUGEN=$OPTARG + ;; + --out-dir=*) + OPT_OUT_DIR=$OPTARG + ;; + --tool=*) + OPT_TOOL=$OPTARG + ;; + -*) + fatal "Invalid option '$OPT', see --help." + ;; + *) + fatal "This script doesn't take arguments, see --help." + ;; + esac +done + +if [ "$OPT_HELP" ]; then + cat </input, and uses them as input to 'emugen'. It then +compares the output to t./expected/ content. + +Valid options: + --help|-h|-? Print this help. + --out-dir= Generate outputs into . + --emugen= Emugen program path, if not in path. + --tool= Launch visual diff tool in case of differences. +EOF + exit 0 +fi + +# Find emugen program +EMUGEN= +if [ "$OPT_EMUGEN" ]; then + EMUGEN=$OPT_EMUGEN +else + EMUGEN=$(which emugen 2>/dev/null || true) + if [ -z "$EMUGEN" ]; then + fatal "Cannot find 'emugen' program in PATH, use --emugen= option." + fi + echo "Auto-config: --emugen=$EMUGEN" +fi +if [ ! -f "$EMUGEN" ]; then + fatal "Emugen program doesn't exist: $EMUGEN" +fi + +# Create output directory. +OUT_DIR= +if [ "$OPT_OUT_DIR" ]; then + OUT_DIR=$OPT_OUT_DIR +else + OUT_DIR=/tmp/$USER-emugen-testing + echo "Auto-config: --out-dir=$OUT_DIR" +fi +mkdir -p "$OUT_DIR" && rm -rf "$OUT_DIR/emugen" + +OUT_DIR=$OUT_DIR/emugen + +# Find test directories +TEST_DIRS=$(cd "$PROGDIR" && find . -name "t.*" | sed -e 's|^\./||') +for TEST_DIR in $TEST_DIRS; do + IN=$PROGDIR/$TEST_DIR/input + PREFIXES=$(cd $IN && find . -name "*.in" | sed -e 's|^\./||g' -e 's|\.in$||g') + OUT=$OUT_DIR/$TEST_DIR + mkdir -p "$OUT/encoder" + mkdir -p "$OUT/decoder" + mkdir -p "$OUT/wrapper" + for PREFIX in $PREFIXES; do + echo "Processing $IN/foo.*" + $EMUGEN -i "$PROGDIR/$TEST_DIR/input" -D "$OUT/decoder" -E "$OUT/encoder" -W "$OUT/wrapper" $PREFIX + done + if ! diff -qr "$PROGDIR/$TEST_DIR/expected" "$OUT"; then + if [ "$OPT_TOOL" ]; then + $OPT_TOOL "$PROGDIR/$TEST_DIR/expected" "$OUT" + else + echo "ERROR: Invalid differences between actual and expected output!" + diff -burN "$PROGDIR/$TEST_DIR/expected" "$OUT" + exit 1 + fi + fi +done + +echo "All good!" +exit 0 diff --git a/external/android-emugl/host/tools/emugen/tests/t.001/expected/decoder/foo_dec.cpp b/external/android-emugl/host/tools/emugen/tests/t.001/expected/decoder/foo_dec.cpp new file mode 100644 index 0000000..8122de0 --- /dev/null +++ b/external/android-emugl/host/tools/emugen/tests/t.001/expected/decoder/foo_dec.cpp @@ -0,0 +1,128 @@ +// Generated Code - DO NOT EDIT !! +// generated by 'emugen' + + +#include +#include "foo_opcodes.h" + +#include "foo_dec.h" + + +#include "ProtocolUtils.h" + +#include "ChecksumCalculatorThreadInfo.h" + +#include + +typedef unsigned int tsize_t; // Target "size_t", which is 32-bit for now. It may or may not be the same as host's size_t when emugen is compiled. + +#ifdef OPENGL_DEBUG_PRINTOUT +# define DEBUG(...) do { if (emugl_cxt_logger) { emugl_cxt_logger(__VA_ARGS__); } } while(0) +#else +# define DEBUG(...) ((void)0) +#endif + +#ifdef CHECK_GLERROR +# define SET_LASTCALL(name) sprintf(lastCall, #name) +#else +# define SET_LASTCALL(name) ((void)0) +#endif + +using namespace emugl; + +size_t foo_decoder_context_t::decode(void *buf, size_t len, IOStream *stream) +{ + + size_t pos = 0; + if (len < 8) return pos; + unsigned char *ptr = (unsigned char *)buf; + bool unknownOpcode = false; +#ifdef CHECK_GL_ERROR + char lastCall[256] = {0}; +#endif + while ((len - pos >= 8) && !unknownOpcode) { + uint32_t opcode = *(uint32_t *)ptr; + size_t packetLen = *(uint32_t *)(ptr + 4); + if (len - pos < packetLen) return pos; + bool useChecksum = ChecksumCalculatorThreadInfo::getVersion() > 0; + size_t checksumSize = 0; + if (useChecksum) { + checksumSize = ChecksumCalculatorThreadInfo::checksumByteSize(); + } + switch(opcode) { + case OP_fooAlphaFunc: { + FooInt var_func = Unpack(ptr + 8); + FooFloat var_ref = Unpack(ptr + 8 + 4); + if (useChecksum) { + ChecksumCalculatorThreadInfo::validOrDie(ptr, 8 + 4 + 4, ptr + 8 + 4 + 4, checksumSize, + "8 + 4 + 4::decode, OP_foo_decoder_context_t: GL checksumCalculator failure\n"); + } + DEBUG("foo(%p): fooAlphaFunc(%d %f )\n", stream,var_func, var_ref); + this->fooAlphaFunc(var_func, var_ref); + SET_LASTCALL("fooAlphaFunc"); + break; + } + case OP_fooIsBuffer: { + uint32_t size_stuff __attribute__((unused)) = Unpack(ptr + 8); + InputBuffer inptr_stuff(ptr + 8 + 4, size_stuff); + if (useChecksum) { + ChecksumCalculatorThreadInfo::validOrDie(ptr, 8 + 4 + size_stuff, ptr + 8 + 4 + size_stuff, checksumSize, + "8 + 4 + size_stuff::decode, OP_foo_decoder_context_t: GL checksumCalculator failure\n"); + } + size_t totalTmpSize = sizeof(FooBoolean); + totalTmpSize += checksumSize; + unsigned char *tmpBuf = stream->alloc(totalTmpSize); + DEBUG("foo(%p): fooIsBuffer(%p(%u) )\n", stream,(void*)(inptr_stuff.get()), size_stuff); + *(FooBoolean *)(&tmpBuf[0]) = this->fooIsBuffer((void*)(inptr_stuff.get())); + if (useChecksum) { + ChecksumCalculatorThreadInfo::writeChecksum(&tmpBuf[0], totalTmpSize - checksumSize, &tmpBuf[totalTmpSize - checksumSize], checksumSize); + } + stream->flush(); + SET_LASTCALL("fooIsBuffer"); + break; + } + case OP_fooUnsupported: { + uint32_t size_params __attribute__((unused)) = Unpack(ptr + 8); + InputBuffer inptr_params(ptr + 8 + 4, size_params); + if (useChecksum) { + ChecksumCalculatorThreadInfo::validOrDie(ptr, 8 + 4 + size_params, ptr + 8 + 4 + size_params, checksumSize, + "8 + 4 + size_params::decode, OP_foo_decoder_context_t: GL checksumCalculator failure\n"); + } + DEBUG("foo(%p): fooUnsupported(%p(%u) )\n", stream,(void*)(inptr_params.get()), size_params); + this->fooUnsupported((void*)(inptr_params.get())); + SET_LASTCALL("fooUnsupported"); + break; + } + case OP_fooDoEncoderFlush: { + FooInt var_param = Unpack(ptr + 8); + if (useChecksum) { + ChecksumCalculatorThreadInfo::validOrDie(ptr, 8 + 4, ptr + 8 + 4, checksumSize, + "8 + 4::decode, OP_foo_decoder_context_t: GL checksumCalculator failure\n"); + } + DEBUG("foo(%p): fooDoEncoderFlush(%d )\n", stream,var_param); + this->fooDoEncoderFlush(var_param); + SET_LASTCALL("fooDoEncoderFlush"); + break; + } + case OP_fooTakeConstVoidPtrConstPtr: { + uint32_t size_param __attribute__((unused)) = Unpack(ptr + 8); + InputBuffer inptr_param(ptr + 8 + 4, size_param); + if (useChecksum) { + ChecksumCalculatorThreadInfo::validOrDie(ptr, 8 + 4 + size_param, ptr + 8 + 4 + size_param, checksumSize, + "8 + 4 + size_param::decode, OP_foo_decoder_context_t: GL checksumCalculator failure\n"); + } + DEBUG("foo(%p): fooTakeConstVoidPtrConstPtr(%p(%u) )\n", stream,(const void* const*)(inptr_param.get()), size_param); + this->fooTakeConstVoidPtrConstPtr((const void* const*)(inptr_param.get())); + SET_LASTCALL("fooTakeConstVoidPtrConstPtr"); + break; + } + default: + unknownOpcode = true; + } //switch + if (!unknownOpcode) { + pos += packetLen; + ptr += packetLen; + } + } // while + return pos; +} diff --git a/external/android-emugl/host/tools/emugen/tests/t.001/expected/decoder/foo_dec.h b/external/android-emugl/host/tools/emugen/tests/t.001/expected/decoder/foo_dec.h new file mode 100644 index 0000000..3646de5 --- /dev/null +++ b/external/android-emugl/host/tools/emugen/tests/t.001/expected/decoder/foo_dec.h @@ -0,0 +1,18 @@ +// Generated Code - DO NOT EDIT !! +// generated by 'emugen' + +#ifndef GUARD_foo_decoder_context_t +#define GUARD_foo_decoder_context_t + +#include "IOStream.h" +#include "foo_server_context.h" + + + +struct foo_decoder_context_t : public foo_server_context_t { + + size_t decode(void *buf, size_t bufsize, IOStream *stream); + +}; + +#endif // GUARD_foo_decoder_context_t diff --git a/external/android-emugl/host/tools/emugen/tests/t.001/expected/decoder/foo_opcodes.h b/external/android-emugl/host/tools/emugen/tests/t.001/expected/decoder/foo_opcodes.h new file mode 100644 index 0000000..7219caa --- /dev/null +++ b/external/android-emugl/host/tools/emugen/tests/t.001/expected/decoder/foo_opcodes.h @@ -0,0 +1,14 @@ +// Generated Code - DO NOT EDIT !! +// generated by 'emugen' +#ifndef __GUARD_foo_opcodes_h_ +#define __GUARD_foo_opcodes_h_ + +#define OP_fooAlphaFunc 200 +#define OP_fooIsBuffer 201 +#define OP_fooUnsupported 202 +#define OP_fooDoEncoderFlush 203 +#define OP_fooTakeConstVoidPtrConstPtr 204 +#define OP_last 205 + + +#endif diff --git a/external/android-emugl/host/tools/emugen/tests/t.001/expected/decoder/foo_server_context.cpp b/external/android-emugl/host/tools/emugen/tests/t.001/expected/decoder/foo_server_context.cpp new file mode 100644 index 0000000..22ff47f --- /dev/null +++ b/external/android-emugl/host/tools/emugen/tests/t.001/expected/decoder/foo_server_context.cpp @@ -0,0 +1,20 @@ +// Generated Code - DO NOT EDIT !! +// generated by 'emugen' + + +#include +#include "foo_server_context.h" + + +#include + +int foo_server_context_t::initDispatchByName(void *(*getProc)(const char *, void *userData), void *userData) +{ + fooAlphaFunc = (fooAlphaFunc_server_proc_t) getProc("fooAlphaFunc", userData); + fooIsBuffer = (fooIsBuffer_server_proc_t) getProc("fooIsBuffer", userData); + fooUnsupported = (fooUnsupported_server_proc_t) getProc("fooUnsupported", userData); + fooDoEncoderFlush = (fooDoEncoderFlush_server_proc_t) getProc("fooDoEncoderFlush", userData); + fooTakeConstVoidPtrConstPtr = (fooTakeConstVoidPtrConstPtr_server_proc_t) getProc("fooTakeConstVoidPtrConstPtr", userData); + return 0; +} + diff --git a/external/android-emugl/host/tools/emugen/tests/t.001/expected/decoder/foo_server_context.h b/external/android-emugl/host/tools/emugen/tests/t.001/expected/decoder/foo_server_context.h new file mode 100644 index 0000000..b2d6537 --- /dev/null +++ b/external/android-emugl/host/tools/emugen/tests/t.001/expected/decoder/foo_server_context.h @@ -0,0 +1,22 @@ +// Generated Code - DO NOT EDIT !! +// generated by 'emugen' +#ifndef __foo_server_context_t_h +#define __foo_server_context_t_h + +#include "foo_server_proc.h" + +#include "foo_types.h" + + +struct foo_server_context_t { + + fooAlphaFunc_server_proc_t fooAlphaFunc; + fooIsBuffer_server_proc_t fooIsBuffer; + fooUnsupported_server_proc_t fooUnsupported; + fooDoEncoderFlush_server_proc_t fooDoEncoderFlush; + fooTakeConstVoidPtrConstPtr_server_proc_t fooTakeConstVoidPtrConstPtr; + virtual ~foo_server_context_t() {} + int initDispatchByName( void *(*getProc)(const char *name, void *userData), void *userData); +}; + +#endif diff --git a/external/android-emugl/host/tools/emugen/tests/t.001/expected/decoder/foo_server_proc.h b/external/android-emugl/host/tools/emugen/tests/t.001/expected/decoder/foo_server_proc.h new file mode 100644 index 0000000..bf7ff53 --- /dev/null +++ b/external/android-emugl/host/tools/emugen/tests/t.001/expected/decoder/foo_server_proc.h @@ -0,0 +1,21 @@ +// Generated Code - DO NOT EDIT !! +// generated by 'emugen' +#ifndef __foo_server_proc_t_h +#define __foo_server_proc_t_h + + + +#include "foo_types.h" + +#include "emugl/common/logging.h" +#ifndef foo_APIENTRY +#define foo_APIENTRY +#endif +typedef void (foo_APIENTRY *fooAlphaFunc_server_proc_t) (FooInt, FooFloat); +typedef FooBoolean (foo_APIENTRY *fooIsBuffer_server_proc_t) (void*); +typedef void (foo_APIENTRY *fooUnsupported_server_proc_t) (void*); +typedef void (foo_APIENTRY *fooDoEncoderFlush_server_proc_t) (FooInt); +typedef void (foo_APIENTRY *fooTakeConstVoidPtrConstPtr_server_proc_t) (const void* const*); + + +#endif diff --git a/external/android-emugl/host/tools/emugen/tests/t.001/expected/encoder/foo_client_context.cpp b/external/android-emugl/host/tools/emugen/tests/t.001/expected/encoder/foo_client_context.cpp new file mode 100644 index 0000000..f09e881 --- /dev/null +++ b/external/android-emugl/host/tools/emugen/tests/t.001/expected/encoder/foo_client_context.cpp @@ -0,0 +1,20 @@ +// Generated Code - DO NOT EDIT !! +// generated by 'emugen' + + +#include +#include "foo_client_context.h" + + +#include + +int foo_client_context_t::initDispatchByName(void *(*getProc)(const char *, void *userData), void *userData) +{ + fooAlphaFunc = (fooAlphaFunc_client_proc_t) getProc("fooAlphaFunc", userData); + fooIsBuffer = (fooIsBuffer_client_proc_t) getProc("fooIsBuffer", userData); + fooUnsupported = (fooUnsupported_client_proc_t) getProc("fooUnsupported", userData); + fooDoEncoderFlush = (fooDoEncoderFlush_client_proc_t) getProc("fooDoEncoderFlush", userData); + fooTakeConstVoidPtrConstPtr = (fooTakeConstVoidPtrConstPtr_client_proc_t) getProc("fooTakeConstVoidPtrConstPtr", userData); + return 0; +} + diff --git a/external/android-emugl/host/tools/emugen/tests/t.001/expected/encoder/foo_client_context.h b/external/android-emugl/host/tools/emugen/tests/t.001/expected/encoder/foo_client_context.h new file mode 100644 index 0000000..d63e835 --- /dev/null +++ b/external/android-emugl/host/tools/emugen/tests/t.001/expected/encoder/foo_client_context.h @@ -0,0 +1,27 @@ +// Generated Code - DO NOT EDIT !! +// generated by 'emugen' +#ifndef __foo_client_context_t_h +#define __foo_client_context_t_h + +#include "foo_client_proc.h" + +#include "foo_types.h" + + +struct foo_client_context_t { + + fooAlphaFunc_client_proc_t fooAlphaFunc; + fooIsBuffer_client_proc_t fooIsBuffer; + fooUnsupported_client_proc_t fooUnsupported; + fooDoEncoderFlush_client_proc_t fooDoEncoderFlush; + fooTakeConstVoidPtrConstPtr_client_proc_t fooTakeConstVoidPtrConstPtr; + virtual ~foo_client_context_t() {} + + typedef foo_client_context_t *CONTEXT_ACCESSOR_TYPE(void); + static void setContextAccessor(CONTEXT_ACCESSOR_TYPE *f); + int initDispatchByName( void *(*getProc)(const char *name, void *userData), void *userData); + virtual void setError(unsigned int error){ (void)error; }; + virtual unsigned int getError(){ return 0; }; +}; + +#endif diff --git a/external/android-emugl/host/tools/emugen/tests/t.001/expected/encoder/foo_client_proc.h b/external/android-emugl/host/tools/emugen/tests/t.001/expected/encoder/foo_client_proc.h new file mode 100644 index 0000000..6cf72d5 --- /dev/null +++ b/external/android-emugl/host/tools/emugen/tests/t.001/expected/encoder/foo_client_proc.h @@ -0,0 +1,21 @@ +// Generated Code - DO NOT EDIT !! +// generated by 'emugen' +#ifndef __foo_client_proc_t_h +#define __foo_client_proc_t_h + + + +#include "foo_types.h" + +#include "emugl/common/logging.h" +#ifndef foo_APIENTRY +#define foo_APIENTRY +#endif +typedef void (foo_APIENTRY *fooAlphaFunc_client_proc_t) (void * ctx, FooInt, FooFloat); +typedef FooBoolean (foo_APIENTRY *fooIsBuffer_client_proc_t) (void * ctx, void*); +typedef void (foo_APIENTRY *fooUnsupported_client_proc_t) (void * ctx, void*); +typedef void (foo_APIENTRY *fooDoEncoderFlush_client_proc_t) (void * ctx, FooInt); +typedef void (foo_APIENTRY *fooTakeConstVoidPtrConstPtr_client_proc_t) (void * ctx, const void* const*); + + +#endif diff --git a/external/android-emugl/host/tools/emugen/tests/t.001/expected/encoder/foo_enc.cpp b/external/android-emugl/host/tools/emugen/tests/t.001/expected/encoder/foo_enc.cpp new file mode 100644 index 0000000..94455dd --- /dev/null +++ b/external/android-emugl/host/tools/emugen/tests/t.001/expected/encoder/foo_enc.cpp @@ -0,0 +1,153 @@ +// Generated Code - DO NOT EDIT !! +// generated by 'emugen' + + +#include +#include +#include "foo_opcodes.h" + +#include "foo_enc.h" + + +#include + +namespace { + +void enc_unsupported() +{ + ALOGE("Function is unsupported\n"); +} + +void fooAlphaFunc_enc(void *self , FooInt func, FooFloat ref) +{ + + foo_encoder_context_t *ctx = (foo_encoder_context_t *)self; + IOStream *stream = ctx->m_stream; + ChecksumCalculator *checksumCalculator = ctx->m_checksumCalculator; + bool useChecksum = checksumCalculator->getVersion() > 0; + + unsigned char *ptr; + unsigned char *buf; + const size_t sizeWithoutChecksum = 8 + 4 + 4; + const size_t checksumSize = checksumCalculator->checksumByteSize(); + const size_t totalSize = sizeWithoutChecksum + checksumSize; + buf = stream->alloc(totalSize); + ptr = buf; + int tmp = OP_fooAlphaFunc;memcpy(ptr, &tmp, 4); ptr += 4; + memcpy(ptr, &totalSize, 4); ptr += 4; + + memcpy(ptr, &func, 4); ptr += 4; + memcpy(ptr, &ref, 4); ptr += 4; + + if (useChecksum) checksumCalculator->addBuffer(buf, ptr-buf); + if (useChecksum) checksumCalculator->writeChecksum(ptr, checksumSize); ptr += checksumSize; + +} + +FooBoolean fooIsBuffer_enc(void *self , void* stuff) +{ + + foo_encoder_context_t *ctx = (foo_encoder_context_t *)self; + IOStream *stream = ctx->m_stream; + ChecksumCalculator *checksumCalculator = ctx->m_checksumCalculator; + bool useChecksum = checksumCalculator->getVersion() > 0; + + const unsigned int __size_stuff = (4 * sizeof(float)); + unsigned char *ptr; + unsigned char *buf; + const size_t sizeWithoutChecksum = 8 + __size_stuff + 1*4; + const size_t checksumSize = checksumCalculator->checksumByteSize(); + const size_t totalSize = sizeWithoutChecksum + checksumSize; + buf = stream->alloc(totalSize); + ptr = buf; + int tmp = OP_fooIsBuffer;memcpy(ptr, &tmp, 4); ptr += 4; + memcpy(ptr, &totalSize, 4); ptr += 4; + + *(unsigned int *)(ptr) = __size_stuff; ptr += 4; + memcpy(ptr, stuff, __size_stuff);ptr += __size_stuff; + + if (useChecksum) checksumCalculator->addBuffer(buf, ptr-buf); + if (useChecksum) checksumCalculator->writeChecksum(ptr, checksumSize); ptr += checksumSize; + + + FooBoolean retval; + stream->readback(&retval, 1); + if (useChecksum) checksumCalculator->addBuffer(&retval, 1); + if (useChecksum) { + std::unique_ptr checksumBuf(new unsigned char[checksumSize]); + stream->readback(checksumBuf.get(), checksumSize); + if (!checksumCalculator->validate(checksumBuf.get(), checksumSize)) { + ALOGE("fooIsBuffer: GL communication error, please report this issue to b.android.com.\n"); + abort(); + } + } + return retval; +} + +void fooDoEncoderFlush_enc(void *self , FooInt param) +{ + + foo_encoder_context_t *ctx = (foo_encoder_context_t *)self; + IOStream *stream = ctx->m_stream; + ChecksumCalculator *checksumCalculator = ctx->m_checksumCalculator; + bool useChecksum = checksumCalculator->getVersion() > 0; + + unsigned char *ptr; + unsigned char *buf; + const size_t sizeWithoutChecksum = 8 + 4; + const size_t checksumSize = checksumCalculator->checksumByteSize(); + const size_t totalSize = sizeWithoutChecksum + checksumSize; + buf = stream->alloc(totalSize); + ptr = buf; + int tmp = OP_fooDoEncoderFlush;memcpy(ptr, &tmp, 4); ptr += 4; + memcpy(ptr, &totalSize, 4); ptr += 4; + + memcpy(ptr, ¶m, 4); ptr += 4; + + if (useChecksum) checksumCalculator->addBuffer(buf, ptr-buf); + if (useChecksum) checksumCalculator->writeChecksum(ptr, checksumSize); ptr += checksumSize; + + stream->flush(); +} + +void fooTakeConstVoidPtrConstPtr_enc(void *self , const void* const* param) +{ + + foo_encoder_context_t *ctx = (foo_encoder_context_t *)self; + IOStream *stream = ctx->m_stream; + ChecksumCalculator *checksumCalculator = ctx->m_checksumCalculator; + bool useChecksum = checksumCalculator->getVersion() > 0; + + const unsigned int __size_param = ; + unsigned char *ptr; + unsigned char *buf; + const size_t sizeWithoutChecksum = 8 + __size_param + 1*4; + const size_t checksumSize = checksumCalculator->checksumByteSize(); + const size_t totalSize = sizeWithoutChecksum + checksumSize; + buf = stream->alloc(totalSize); + ptr = buf; + int tmp = OP_fooTakeConstVoidPtrConstPtr;memcpy(ptr, &tmp, 4); ptr += 4; + memcpy(ptr, &totalSize, 4); ptr += 4; + + *(unsigned int *)(ptr) = __size_param; ptr += 4; + memcpy(ptr, param, __size_param);ptr += __size_param; + + if (useChecksum) checksumCalculator->addBuffer(buf, ptr-buf); + if (useChecksum) checksumCalculator->writeChecksum(ptr, checksumSize); ptr += checksumSize; + +} + +} // namespace + +foo_encoder_context_t::foo_encoder_context_t(IOStream *stream, ChecksumCalculator *checksumCalculator) +{ + m_stream = stream; + m_checksumCalculator = checksumCalculator; + + this->fooAlphaFunc = &fooAlphaFunc_enc; + this->fooIsBuffer = &fooIsBuffer_enc; + this->fooUnsupported = (fooUnsupported_client_proc_t) &enc_unsupported; + this->fooDoEncoderFlush = &fooDoEncoderFlush_enc; + this->fooTakeConstVoidPtrConstPtr = &fooTakeConstVoidPtrConstPtr_enc; +} + diff --git a/external/android-emugl/host/tools/emugen/tests/t.001/expected/encoder/foo_enc.h b/external/android-emugl/host/tools/emugen/tests/t.001/expected/encoder/foo_enc.h new file mode 100644 index 0000000..8325831 --- /dev/null +++ b/external/android-emugl/host/tools/emugen/tests/t.001/expected/encoder/foo_enc.h @@ -0,0 +1,23 @@ +// Generated Code - DO NOT EDIT !! +// generated by 'emugen' + +#ifndef GUARD_foo_encoder_context_t +#define GUARD_foo_encoder_context_t + +#include "IOStream.h" +#include "ChecksumCalculator.h" +#include "foo_client_context.h" + + +#include "fooUtils.h" +#include "fooBase.h" + +struct foo_encoder_context_t : public foo_client_context_t { + + IOStream *m_stream; + ChecksumCalculator *m_checksumCalculator; + + foo_encoder_context_t(IOStream *stream, ChecksumCalculator *checksumCalculator); +}; + +#endif // GUARD_foo_encoder_context_t \ No newline at end of file diff --git a/external/android-emugl/host/tools/emugen/tests/t.001/expected/encoder/foo_entry.cpp b/external/android-emugl/host/tools/emugen/tests/t.001/expected/encoder/foo_entry.cpp new file mode 100644 index 0000000..b91129c --- /dev/null +++ b/external/android-emugl/host/tools/emugen/tests/t.001/expected/encoder/foo_entry.cpp @@ -0,0 +1,53 @@ +// Generated Code - DO NOT EDIT !! +// generated by 'emugen' +#include +#include +#include "foo_client_context.h" + +#ifndef GL_TRUE +extern "C" { + void fooAlphaFunc(FooInt func, FooFloat ref); + FooBoolean fooIsBuffer(void* stuff); + void fooUnsupported(void* params); + void fooDoEncoderFlush(FooInt param); + void fooTakeConstVoidPtrConstPtr(const void* const* param); +}; + +#endif +#ifndef GET_CONTEXT +static foo_client_context_t::CONTEXT_ACCESSOR_TYPE *getCurrentContext = NULL; +void foo_client_context_t::setContextAccessor(CONTEXT_ACCESSOR_TYPE *f) { getCurrentContext = f; } +#define GET_CONTEXT foo_client_context_t * ctx = getCurrentContext() +#endif + +void fooAlphaFunc(FooInt func, FooFloat ref) +{ + GET_CONTEXT; + ctx->fooAlphaFunc(ctx, func, ref); +} + +FooBoolean fooIsBuffer(void* stuff) +{ + GET_CONTEXT; + if (n == NULL) { LOG(ERROR) << "NULL stuff"; return; } + return ctx->fooIsBuffer(ctx, stuff); +} + +void fooUnsupported(void* params) +{ + GET_CONTEXT; + ctx->fooUnsupported(ctx, params); +} + +void fooDoEncoderFlush(FooInt param) +{ + GET_CONTEXT; + ctx->fooDoEncoderFlush(ctx, param); +} + +void fooTakeConstVoidPtrConstPtr(const void* const* param) +{ + GET_CONTEXT; + ctx->fooTakeConstVoidPtrConstPtr(ctx, param); +} + diff --git a/external/android-emugl/host/tools/emugen/tests/t.001/expected/encoder/foo_ftable.h b/external/android-emugl/host/tools/emugen/tests/t.001/expected/encoder/foo_ftable.h new file mode 100644 index 0000000..e397e50 --- /dev/null +++ b/external/android-emugl/host/tools/emugen/tests/t.001/expected/encoder/foo_ftable.h @@ -0,0 +1,20 @@ +// Generated Code - DO NOT EDIT !! +// generated by 'emugen' +#ifndef __foo_client_ftable_t_h +#define __foo_client_ftable_t_h + + +static const struct _foo_funcs_by_name { + const char *name; + void *proc; +} foo_funcs_by_name[] = { + {"fooAlphaFunc", (void*)fooAlphaFunc}, + {"fooIsBuffer", (void*)fooIsBuffer}, + {"fooUnsupported", (void*)fooUnsupported}, + {"fooDoEncoderFlush", (void*)fooDoEncoderFlush}, + {"fooTakeConstVoidPtrConstPtr", (void*)fooTakeConstVoidPtrConstPtr}, +}; +static const int foo_num_funcs = sizeof(foo_funcs_by_name) / sizeof(struct _foo_funcs_by_name); + + +#endif diff --git a/external/android-emugl/host/tools/emugen/tests/t.001/expected/encoder/foo_opcodes.h b/external/android-emugl/host/tools/emugen/tests/t.001/expected/encoder/foo_opcodes.h new file mode 100644 index 0000000..7219caa --- /dev/null +++ b/external/android-emugl/host/tools/emugen/tests/t.001/expected/encoder/foo_opcodes.h @@ -0,0 +1,14 @@ +// Generated Code - DO NOT EDIT !! +// generated by 'emugen' +#ifndef __GUARD_foo_opcodes_h_ +#define __GUARD_foo_opcodes_h_ + +#define OP_fooAlphaFunc 200 +#define OP_fooIsBuffer 201 +#define OP_fooUnsupported 202 +#define OP_fooDoEncoderFlush 203 +#define OP_fooTakeConstVoidPtrConstPtr 204 +#define OP_last 205 + + +#endif diff --git a/external/android-emugl/host/tools/emugen/tests/t.001/expected/wrapper/foo_wrapper_context.cpp b/external/android-emugl/host/tools/emugen/tests/t.001/expected/wrapper/foo_wrapper_context.cpp new file mode 100644 index 0000000..6e132b1 --- /dev/null +++ b/external/android-emugl/host/tools/emugen/tests/t.001/expected/wrapper/foo_wrapper_context.cpp @@ -0,0 +1,20 @@ +// Generated Code - DO NOT EDIT !! +// generated by 'emugen' + + +#include +#include "foo_wrapper_context.h" + + +#include + +int foo_wrapper_context_t::initDispatchByName(void *(*getProc)(const char *, void *userData), void *userData) +{ + fooAlphaFunc = (fooAlphaFunc_wrapper_proc_t) getProc("fooAlphaFunc", userData); + fooIsBuffer = (fooIsBuffer_wrapper_proc_t) getProc("fooIsBuffer", userData); + fooUnsupported = (fooUnsupported_wrapper_proc_t) getProc("fooUnsupported", userData); + fooDoEncoderFlush = (fooDoEncoderFlush_wrapper_proc_t) getProc("fooDoEncoderFlush", userData); + fooTakeConstVoidPtrConstPtr = (fooTakeConstVoidPtrConstPtr_wrapper_proc_t) getProc("fooTakeConstVoidPtrConstPtr", userData); + return 0; +} + diff --git a/external/android-emugl/host/tools/emugen/tests/t.001/expected/wrapper/foo_wrapper_context.h b/external/android-emugl/host/tools/emugen/tests/t.001/expected/wrapper/foo_wrapper_context.h new file mode 100644 index 0000000..3315acc --- /dev/null +++ b/external/android-emugl/host/tools/emugen/tests/t.001/expected/wrapper/foo_wrapper_context.h @@ -0,0 +1,25 @@ +// Generated Code - DO NOT EDIT !! +// generated by 'emugen' +#ifndef __foo_wrapper_context_t_h +#define __foo_wrapper_context_t_h + +#include "foo_server_proc.h" + +#include "foo_types.h" + + +struct foo_wrapper_context_t { + + fooAlphaFunc_wrapper_proc_t fooAlphaFunc; + fooIsBuffer_wrapper_proc_t fooIsBuffer; + fooUnsupported_wrapper_proc_t fooUnsupported; + fooDoEncoderFlush_wrapper_proc_t fooDoEncoderFlush; + fooTakeConstVoidPtrConstPtr_wrapper_proc_t fooTakeConstVoidPtrConstPtr; + virtual ~foo_wrapper_context_t() {} + + typedef foo_wrapper_context_t *CONTEXT_ACCESSOR_TYPE(void); + static void setContextAccessor(CONTEXT_ACCESSOR_TYPE *f); + int initDispatchByName( void *(*getProc)(const char *name, void *userData), void *userData); +}; + +#endif diff --git a/external/android-emugl/host/tools/emugen/tests/t.001/expected/wrapper/foo_wrapper_entry.cpp b/external/android-emugl/host/tools/emugen/tests/t.001/expected/wrapper/foo_wrapper_entry.cpp new file mode 100644 index 0000000..7591393 --- /dev/null +++ b/external/android-emugl/host/tools/emugen/tests/t.001/expected/wrapper/foo_wrapper_entry.cpp @@ -0,0 +1,52 @@ +// Generated Code - DO NOT EDIT !! +// generated by 'emugen' +#include +#include +#include "foo_wrapper_context.h" + +#ifndef GL_TRUE +extern "C" { + void fooAlphaFunc(FooInt func, FooFloat ref); + FooBoolean fooIsBuffer(void* stuff); + void fooUnsupported(void* params); + void fooDoEncoderFlush(FooInt param); + void fooTakeConstVoidPtrConstPtr(const void* const* param); +}; + +#endif +#ifndef GET_CONTEXT +static foo_wrapper_context_t::CONTEXT_ACCESSOR_TYPE *getCurrentContext = NULL; +void foo_wrapper_context_t::setContextAccessor(CONTEXT_ACCESSOR_TYPE *f) { getCurrentContext = f; } +#define GET_CONTEXT foo_wrapper_context_t * ctx = getCurrentContext() +#endif + +void fooAlphaFunc(FooInt func, FooFloat ref) +{ + GET_CONTEXT; + ctx->fooAlphaFunc( func, ref); +} + +FooBoolean fooIsBuffer(void* stuff) +{ + GET_CONTEXT; + return ctx->fooIsBuffer( stuff); +} + +void fooUnsupported(void* params) +{ + GET_CONTEXT; + ctx->fooUnsupported( params); +} + +void fooDoEncoderFlush(FooInt param) +{ + GET_CONTEXT; + ctx->fooDoEncoderFlush( param); +} + +void fooTakeConstVoidPtrConstPtr(const void* const* param) +{ + GET_CONTEXT; + ctx->fooTakeConstVoidPtrConstPtr( param); +} + diff --git a/external/android-emugl/host/tools/emugen/tests/t.001/expected/wrapper/foo_wrapper_proc.h b/external/android-emugl/host/tools/emugen/tests/t.001/expected/wrapper/foo_wrapper_proc.h new file mode 100644 index 0000000..421c910 --- /dev/null +++ b/external/android-emugl/host/tools/emugen/tests/t.001/expected/wrapper/foo_wrapper_proc.h @@ -0,0 +1,21 @@ +// Generated Code - DO NOT EDIT !! +// generated by 'emugen' +#ifndef __foo_wrapper_proc_t_h +#define __foo_wrapper_proc_t_h + + + +#include "foo_types.h" + +#include "emugl/common/logging.h" +#ifndef foo_APIENTRY +#define foo_APIENTRY +#endif +typedef void (foo_APIENTRY *fooAlphaFunc_wrapper_proc_t) (FooInt, FooFloat); +typedef FooBoolean (foo_APIENTRY *fooIsBuffer_wrapper_proc_t) (void*); +typedef void (foo_APIENTRY *fooUnsupported_wrapper_proc_t) (void*); +typedef void (foo_APIENTRY *fooDoEncoderFlush_wrapper_proc_t) (FooInt); +typedef void (foo_APIENTRY *fooTakeConstVoidPtrConstPtr_wrapper_proc_t) (const void* const*); + + +#endif diff --git a/external/android-emugl/host/tools/emugen/tests/t.001/input/foo.attrib b/external/android-emugl/host/tools/emugen/tests/t.001/input/foo.attrib new file mode 100644 index 0000000..80644d8 --- /dev/null +++ b/external/android-emugl/host/tools/emugen/tests/t.001/input/foo.attrib @@ -0,0 +1,15 @@ +GLOBAL + base_opcode 200 + encoder_headers "fooUtils.h" "fooBase.h" + +fooIsBuffer + dir stuff in + len stuff (4 * sizeof(float)) + param_check stuff if (n == NULL) { LOG(ERROR) << "NULL stuff"; return; } + +fooUnsupported + dir params in + flag unsupported + +fooDoEncoderFlush + flag flushOnEncode diff --git a/external/android-emugl/host/tools/emugen/tests/t.001/input/foo.in b/external/android-emugl/host/tools/emugen/tests/t.001/input/foo.in new file mode 100644 index 0000000..4e98f88 --- /dev/null +++ b/external/android-emugl/host/tools/emugen/tests/t.001/input/foo.in @@ -0,0 +1,5 @@ +FOO_ENTRY(void, fooAlphaFunc, FooInt func, FooFloat ref) +FOO_ENTRY(FooBoolean, fooIsBuffer, void* stuff) +FOO_ENTRY(void, fooUnsupported, void* params) +FOO_ENTRY(void, fooDoEncoderFlush, FooInt param) +FOO_ENTRY(void, fooTakeConstVoidPtrConstPtr, const void* const* param) diff --git a/external/android-emugl/host/tools/emugen/tests/t.001/input/foo.types b/external/android-emugl/host/tools/emugen/tests/t.001/input/foo.types new file mode 100644 index 0000000..05d72fb --- /dev/null +++ b/external/android-emugl/host/tools/emugen/tests/t.001/input/foo.types @@ -0,0 +1,10 @@ +FooBoolean 8 %d +FooInt 32 %d +FooShort 16 %d +FooFloat 32 %f +FooEnum 32 %08x +FooVoid 0 %x +FooChar 8 %d +FooChar* 32 0x%08x +void* 32 0x%08x +void*const* 32 0x%08x diff --git a/external/android-emugl/scripts/gen-headers.sh b/external/android-emugl/scripts/gen-headers.sh new file mode 100755 index 0000000..c079fe7 --- /dev/null +++ b/external/android-emugl/scripts/gen-headers.sh @@ -0,0 +1,114 @@ +#!/bin/sh + +# Copyright (C) 2015 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Run this script to re-generate headers with the gen-entries.py script. + +set -e +export LANG=C +export LC_ALL=C + +PROGDIR=$(dirname "$0") + +panic () { + echo "ERROR: $@" + exit 1 +} + +QEMU_TOP_DIR=$(cd $PROGDIR/../../.. && pwd -P) +SCRIPT_DIR=android/scripts +if [ ! -d "$QEMU_TOP_DIR/$SCRIPT_DIR" ]; then + panic "Missing scripts directory: $QEMU_TOP_DIR/$SCRIPT_DIR" +fi + +cd $QEMU_TOP_DIR +GEN_ENTRIES=$SCRIPT_DIR/gen-entries.py +if [ ! -f "$GEN_ENTRIES" ]; then + panic "Missing script: $GEN_ENTRIES" +fi + +FAILURES= + +# $1: Source file +# $2: Target file +# $3: gen-entries script. +gen_functions_header () { + local SRC_FILE="$1" + local DST_FILE="$2" + local GEN_ENTRIES="$3" + if [ ! -f "$SRC_FILE" ]; then + echo "ERROR: Missing source file: $SRC_FILE" + FAILURES=true + else + echo "Generating $DST_FILE" + $GEN_ENTRIES --mode=functions $SRC_FILE --output=$DST_FILE + fi +} + +# $1: Source file +# $2: Target file +# $3: gen-entries script. +gen_funcargs_header () { + local SRC_FILE="$1" + local DST_FILE="$2" + local GEN_ENTRIES="$3" + if [ ! -f "$SRC_FILE" ]; then + echo "ERROR: Missing source file: $SRC_FILE" + FAILURES=true + else + echo "Generating $DST_FILE" + $GEN_ENTRIES --mode=funcargs $SRC_FILE --output=$DST_FILE + fi +} + + +## +## libOpenglRender headers. +## +LIBRENDER_DIR=distrib/android-emugl/host/libs/libOpenglRender +LIBRENDER_INCLUDE=distrib/android-emugl/host/include/OpenglRender +gen_funcargs_header \ + "$LIBRENDER_DIR"/render_api.entries \ + "$LIBRENDER_INCLUDE"/render_api_functions.h \ + "$GEN_ENTRIES" + +## +## libOpenGLESDispatch headers. +## +OPENGLES_DISPATCH_SRCDIR=distrib/android-emugl/host/libs/libOpenGLESDispatch +OPENGLES_DISPATCH_INCLUDE=distrib/android-emugl/host/include/OpenGLESDispatch + +gen_functions_header \ + "$OPENGLES_DISPATCH_SRCDIR"/render_egl.entries \ + "$OPENGLES_DISPATCH_INCLUDE"/RenderEGL_functions.h \ + "$GEN_ENTRIES" + +gen_functions_header \ + "$OPENGLES_DISPATCH_SRCDIR"/render_egl_extensions.entries \ + "$OPENGLES_DISPATCH_INCLUDE"/RenderEGL_extensions_functions.h \ + "$GEN_ENTRIES" + +GLES_ENTRIES="gles_common gles_extensions gles1_only gles1_extensions gles2_only \ +gles2_extensions gles3_only" + +for ENTRY in $GLES_ENTRIES; do + SRC_FILE=$OPENGLES_DISPATCH_SRCDIR/${ENTRY}.entries + DST_FILE=$OPENGLES_DISPATCH_INCLUDE/${ENTRY}_functions.h + gen_funcargs_header "$SRC_FILE" "$DST_FILE" "$GEN_ENTRIES" +done + +if [ "$FAILURES" ]; then + exit 1 +fi diff --git a/external/android-emugl/shared/CMakeLists.txt b/external/android-emugl/shared/CMakeLists.txt new file mode 100644 index 0000000..2fc0fcb --- /dev/null +++ b/external/android-emugl/shared/CMakeLists.txt @@ -0,0 +1,2 @@ +add_subdirectory(emugl) +add_subdirectory(OpenglCodecCommon) diff --git a/external/android-emugl/shared/OpenglCodecCommon/Android.mk b/external/android-emugl/shared/OpenglCodecCommon/Android.mk new file mode 100644 index 0000000..fff14ec --- /dev/null +++ b/external/android-emugl/shared/OpenglCodecCommon/Android.mk @@ -0,0 +1,24 @@ +# This build script corresponds to a library containing many definitions +# common to both the guest and the host. They relate to +# +LOCAL_PATH := $(call my-dir) + +commonSources := \ + glUtils.cpp \ + ChecksumCalculator.cpp \ + ChecksumCalculatorThreadInfo.cpp \ + +host_commonSources := $(commonSources) + +host_commonLdLibs := $(CXX_STD_LIB) + +### OpenglCodecCommon host ############################################## +$(call emugl-begin-host-static-library,libOpenglCodecCommon) + +LOCAL_SRC_FILES := $(host_commonSources) +$(call emugl-import, libemugl_common) +$(call emugl-export,C_INCLUDES,$(EMUGL_PATH)/host/include/libOpenglRender $(LOCAL_PATH)) +$(call emugl-export,LDLIBS,$(host_commonLdLibs)) +$(call emugl-end-module) + + diff --git a/external/android-emugl/shared/OpenglCodecCommon/CMakeLists.txt b/external/android-emugl/shared/OpenglCodecCommon/CMakeLists.txt new file mode 100644 index 0000000..1dda791 --- /dev/null +++ b/external/android-emugl/shared/OpenglCodecCommon/CMakeLists.txt @@ -0,0 +1,6 @@ +set(SOURCES + glUtils.cpp + ChecksumCalculator.cpp + ChecksumCalculatorThreadInfo.cpp) + +add_library(OpenglCodecCommon ${SOURCES}) diff --git a/external/android-emugl/shared/OpenglCodecCommon/ChecksumCalculator.cpp b/external/android-emugl/shared/OpenglCodecCommon/ChecksumCalculator.cpp new file mode 100644 index 0000000..60940b8 --- /dev/null +++ b/external/android-emugl/shared/OpenglCodecCommon/ChecksumCalculator.cpp @@ -0,0 +1,154 @@ +/* +* Copyright (C) 2016 The Android Open Source Project +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +#include "ChecksumCalculator.h" + +#include +#include +#include + +// Checklist when implementing new protocol: +// 1. update CHECKSUMHELPER_MAX_VERSION +// 2. update maxChecksumSize() +// 3. update checksumByteSize() +// 4. update addBuffer, writeChecksum, resetChecksum, validate + +// change CHECKSUMHELPER_MAX_VERSION when you want to update the protocol version +#define CHECKSUMHELPER_MAX_VERSION 1 + +// checksum buffer size +// Please add a new checksum buffer size when implementing a new protocol, +// as well as modifying the maxChecksumSize function. +static const size_t kV1ChecksumSize = 8; + +static constexpr size_t maxChecksumSize() { + return 0 > kV1ChecksumSize ? 0 : kV1ChecksumSize; +} + +static const size_t kMaxChecksumSize = maxChecksumSize(); + +// utility macros to create checksum string at compilation time +#define CHECKSUMHELPER_VERSION_STR_PREFIX "ANDROID_EMU_CHECKSUM_HELPER_v" +#define CHECKSUMHELPER_MACRO_TO_STR(x) #x +#define CHECKSUMHELPER_MACRO_VAL_TO_STR(x) CHECKSUMHELPER_MACRO_TO_STR(x) + +static const uint32_t kMaxVersion = CHECKSUMHELPER_MAX_VERSION; +static const char* kMaxVersionStrPrefix = CHECKSUMHELPER_VERSION_STR_PREFIX; +static const char* kMaxVersionStr = CHECKSUMHELPER_VERSION_STR_PREFIX CHECKSUMHELPER_MACRO_VAL_TO_STR(CHECKSUMHELPER_MAX_VERSION); + +#undef CHECKSUMHELPER_MAX_VERSION +#undef CHECKSUMHELPER_VERSION_STR_PREFIX +#undef CHECKSUMHELPER_MACRO_TO_STR +#undef CHECKSUMHELPER_MACRO_VAL_TO_STR + +uint32_t ChecksumCalculator::getMaxVersion() {return kMaxVersion;} +const char* ChecksumCalculator::getMaxVersionStr() {return kMaxVersionStr;} +const char* ChecksumCalculator::getMaxVersionStrPrefix() {return kMaxVersionStrPrefix;} + +bool ChecksumCalculator::setVersion(uint32_t version) { + if (version > kMaxVersion) { // unsupported version + LOG_CHECKSUMHELPER("%s: ChecksumCalculator Set Unsupported version Version %d\n", + __FUNCTION__, m_version); + return false; + } + if (m_isEncodingChecksum) { // setVersion is called in the middle of encoding checksums + LOG_CHECKSUMHELPER("%s: called between addBuffer and writeChecksum\n", + __FUNCTION__); + return false; + } + m_version = version; + LOG_CHECKSUMHELPER("%s: ChecksumCalculator Set Version %d\n", __FUNCTION__, + m_version); + return true; +} + +size_t ChecksumCalculator::checksumByteSize() const { + switch (m_version) { + case 0: + return 0; + case 1: + return sizeof(uint32_t) + sizeof(m_numWrite); + default: + return 0; + } +} + +void ChecksumCalculator::addBuffer(const void* buf, size_t packetLen) { + m_isEncodingChecksum = true; + switch (m_version) { + case 1: + m_v1BufferTotalLength += packetLen; + break; + } +} + +bool ChecksumCalculator::writeChecksum(void* outputChecksum, size_t outputChecksumLen) { + if (outputChecksumLen < checksumByteSize()) return false; + char *checksumPtr = (char *)outputChecksum; + switch (m_version) { + case 1: { // protocol v1 is to reverse the packetLen and write it at the end + uint32_t val = computeV1Checksum(); + memcpy(checksumPtr, &val, sizeof(val)); + memcpy(checksumPtr+sizeof(val), &m_numWrite, sizeof(m_numWrite)); + break; + } + } + resetChecksum(); + m_numWrite++; + return true; +} + +void ChecksumCalculator::resetChecksum() { + switch (m_version) { + case 1: + m_v1BufferTotalLength = 0; + break; + } + m_isEncodingChecksum = false; +} + +bool ChecksumCalculator::validate(const void* expectedChecksum, size_t expectedChecksumLen) { + size_t checksumSize = checksumByteSize(); + if (expectedChecksumLen != checksumSize) { + m_numRead++; + resetChecksum(); + return false; + } + // buffers for computing the checksum + unsigned char sChecksumBuffer[kMaxChecksumSize]; + switch (m_version) { + case 1: { + uint32_t val = computeV1Checksum(); + memcpy(sChecksumBuffer, &val, sizeof(val)); + memcpy(sChecksumBuffer+sizeof(val), &m_numRead, sizeof(m_numRead)); + break; + } + } + bool isValid = !memcmp(sChecksumBuffer, expectedChecksum, checksumSize); + m_numRead++; + resetChecksum(); + return isValid; +} + +uint32_t ChecksumCalculator::computeV1Checksum() { + uint32_t revLen = m_v1BufferTotalLength; + revLen = (revLen & 0xffff0000) >> 16 | (revLen & 0x0000ffff) << 16; + revLen = (revLen & 0xff00ff00) >> 8 | (revLen & 0x00ff00ff) << 8; + revLen = (revLen & 0xf0f0f0f0) >> 4 | (revLen & 0x0f0f0f0f) << 4; + revLen = (revLen & 0xcccccccc) >> 2 | (revLen & 0x33333333) << 2; + revLen = (revLen & 0xaaaaaaaa) >> 1 | (revLen & 0x55555555) << 1; + return revLen; +} diff --git a/external/android-emugl/shared/OpenglCodecCommon/ChecksumCalculator.h b/external/android-emugl/shared/OpenglCodecCommon/ChecksumCalculator.h new file mode 100644 index 0000000..4cc231a --- /dev/null +++ b/external/android-emugl/shared/OpenglCodecCommon/ChecksumCalculator.h @@ -0,0 +1,181 @@ +/* +* Copyright (C) 2016 The Android Open Source Project +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +#pragma once + +#include +#include + +// Set TRACE_CHECKSUMHELPER to 1 to debug creation/destruction of GLprotocol +// instances. +#define TRACE_CHECKSUMHELPER 0 + +#if TRACE_CHECKSUMHELPER +#define LOG_CHECKSUMHELPER(x...) fprintf(stderr, x) +#else +#define LOG_CHECKSUMHELPER(x...) +#endif + +// ChecksumCalculator adds checksum as an array of bytes to GL pipe communication, which +// size depends on the protocol version. Each pipe should use one ChecksumCalculator. +// It can: +// (1) take a list of buffers one by one and compute their checksum string, +// in this case the checksum should be as the data in those buffers are +// concatenated; +// (2) compute the checksum of the buffer list, then either write them into +// a buffer provided by user, or compare it against a checksum provided +// by user +// (3) support different checksum version in future. +// +// For backward compatibility, checksum version 0 behaves the same as there is +// no checksum (i.e., checksumByteSize returns 0, validate always returns true, +// addBuffer and writeCheckSum does nothing). +// +// Notice that to detect package lost, ChecksumCalculator also keeps track of how +// many times it generates/validates checksums, and might use it as part of the +// checksum. +// +// To evaluate checksums from a list of data buffers buf1, buf2... Please call +// addBuffer(buf1, buf1len), addBuffer(buf2, buf2len) ... in order. +// Then if the checksum needs to be encoded into a buffer, one needs to allocate +// a checksum buffer with size checksumByteSize(), and call +// writeChecksum(checksumBuffer) to write the checksum to the buffer. +// If the checksum needs to be validated against an existing one, one needs to +// call validate(existChecksum, existChecksumLen). +// +// The checksum generator and validator must be set to the same version, and +// the validator must check ALL checksums in the order they are generated, +// otherwise the validation function will return false. +// +// It is allowed to change the checksum version between calculating two +// checksums. This is designed for backward compatibility reason. +// +// Example 1, encoding and decoding: +// +// bool testChecksum(void* buf, size_t bufLen) { +// // encoding message +// ChecksumCalculator encoder; +// encoder.setVersion(1); +// encoder.addBuffer(buf, bufLen); +// std::vector message(bufLen + encoder.checksumByteSize()); +// memcpy(&message[0], buf, bufLen); +// encoder.writeChecksum(&message[0] + bufLen, encoder.checksumByteSize()); +// +// // decoding message +// ChecksumCalculator decoder; +// decoder.setVersion(1); +// decoder.addBuffer(&message[0], bufLen); +// return decoder.validate(&message[0] + bufLen, decoder.checksumByteSize()); +// } +// The return value is true. +// +// Example 2, decoding will fail if the order of messages is wrong: +// +// bool testChecksumOrder(void* buf1, size_t bufLen1, +// void* buf2, size_t bufLen2) { +// // encoding messages +// ChecksumCalculator encoder; +// encoder.setVersion(1); +// +// std::vector message1(bufLen1 + encoder.checksumByteSize()); +// std::vector message2(bufLen2 + encoder.checksumByteSize()); +// +// encoder.addBuffer(buf1, bufLen1); +// std::vector message1(bufLen1 + encoder.checksumByteSize()); +// memcpy(&message1[0], buf1, bufLen1); +// encoder.writeChecksum(&message1[0] + bufLen1, encoder.checksumByteSize()); +// +// encoder.addBuffer(buf2, bufLen2); +// std::vector message2(bufLen2 + encoder.checksumByteSize()); +// memcpy(&message2[0], buf2, bufLen2); +// encoder.writeChecksum(&message2[0] + bufLen2, encoder.checksumByteSize()); +// +// // decoding messages +// ChecksumCalculator decoder; +// decoder.setVersion(1); +// decoder.addBuffer(&message2[0], bufLen2); +// // returns false because the decoding order is not consistent with +// // encoding order +// if (!decoder.validate(&message2[0]+bufLen2, decoder.checksumByteSize())) { +// return false; +// } +// +// decoder.addBuffer(&message1[0], bufLen1); +// if (!decoder.validate(&message1[0]+bufLen1, decoder.checksumByteSize())) { +// return false; +// } +// +// return false; +// } + +class ChecksumCalculator { +public: + // Get and set current checksum version + uint32_t getVersion() const { return m_version; } + // Call setVersion to set a checksum version. It should be called before + // addBuffer(), writeChecksum() and validate(). And it should be called + // exact once per rendering thread if both host and guest support checksum. + // It won't be called if either host or guest does not support checksum. + bool setVersion(uint32_t version); + + // Maximum supported checksum version + static uint32_t getMaxVersion(); + // A version string that looks like "ANDROID_EMU_CHECKSUM_HELPER_v1" + // Used multiple times when the guest queries the maximum supported version + // from the host. + // The library owns the returned pointer. The returned pointer will be + // deconstructed when unloading library. + static const char* getMaxVersionStr(); + static const char* getMaxVersionStrPrefix(); + + // Size of checksum in the current version + size_t checksumByteSize() const; + + // Update the current checksum value from the data + // at |buf| of |bufLen| bytes. Once all buffers + // have been added, call writeChecksum() to store + // the final checksum value and reset its state. + void addBuffer(const void* buf, size_t bufLen); + // Write the checksum from the list of buffers to outputChecksum + // Will reset the list of buffers by calling resetChecksum. + // Return false if the buffer is not long enough + // Please query buffer size from checksumByteSize() + bool writeChecksum(void* outputChecksum, size_t outputChecksumLen); + // Restore the states for computing checksums. + // Automatically called at the end of writeChecksum and validate. + // Can also be used to abandon the current checksum being calculated. + // Notes: it doesn't update the internal read / write counter + void resetChecksum(); + + // Calculate the checksum from the list of buffers and + // compare it with the checksum encoded in expectedChecksum + // Will reset the list of buffers by calling resetChecksum. + bool validate(const void* expectedChecksum, size_t expectedChecksumLen); +protected: + uint32_t m_version = 0; + // A temporary state used to compute the total length of a list of buffers, + // if addBuffer is called. + uint32_t m_numRead = 0; + uint32_t m_numWrite = 0; + // m_isEncodingChecksum is true when between addBuffer and writeChecksum + bool m_isEncodingChecksum = false; +private: + // Compute a 32bit checksum + // Used in protocol v1 + uint32_t computeV1Checksum(); + // The buffer used in protocol version 1 to compute checksum. + uint32_t m_v1BufferTotalLength = 0; +}; diff --git a/external/android-emugl/shared/OpenglCodecCommon/ChecksumCalculatorThreadInfo.cpp b/external/android-emugl/shared/OpenglCodecCommon/ChecksumCalculatorThreadInfo.cpp new file mode 100644 index 0000000..6a35c92 --- /dev/null +++ b/external/android-emugl/shared/OpenglCodecCommon/ChecksumCalculatorThreadInfo.cpp @@ -0,0 +1,103 @@ +/* +* Copyright (C) 2016 The Android Open Source Project +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +#include "ChecksumCalculatorThreadInfo.h" + +#include "emugl/common/crash_reporter.h" +#include "emugl/common/lazy_instance.h" +#include "emugl/common/thread_store.h" + +#include +#include +#include + +namespace { + +class ChecksumCalculatorThreadStore : public ::emugl::ThreadStore { +public: + ChecksumCalculatorThreadStore() : ::emugl::ThreadStore(NULL) {} +}; + +#ifdef TRACE_CHECKSUMHELPER +std::atomic sNumInstances(0); +#endif // TRACE_CHECKSUMHELPER + +} + +static ::emugl::LazyInstance s_tls = + LAZY_INSTANCE_INIT; + +static ChecksumCalculatorThreadInfo* getChecksumCalculatorThreadInfo() { + return static_cast(s_tls->get()); +} + +ChecksumCalculatorThreadInfo::ChecksumCalculatorThreadInfo() { + LOG_CHECKSUMHELPER( + "%s: Checksum thread created (%u instances)\n", __FUNCTION__, + (size_t)sNumInstances); + s_tls->set(this); +} + +ChecksumCalculatorThreadInfo::~ChecksumCalculatorThreadInfo() { + LOG_CHECKSUMHELPER( + "%s: GLprotocol destroyed (%u instances)\n", __FUNCTION__, + (size_t)sNumInstances); + s_tls->set(NULL); +} + +uint32_t ChecksumCalculatorThreadInfo::getVersion() { + return getChecksumCalculatorThreadInfo()->m_protocol.getVersion(); +} + +bool ChecksumCalculatorThreadInfo::setVersion(uint32_t version) { + return getChecksumCalculatorThreadInfo()->m_protocol.setVersion(version); +} + +size_t ChecksumCalculatorThreadInfo::checksumByteSize() { + return getChecksumCalculatorThreadInfo()->m_protocol.checksumByteSize(); +} + +bool ChecksumCalculatorThreadInfo::writeChecksum(void* buf, + size_t bufLen, + void* outputChecksum, + size_t outputChecksumLen) { + ChecksumCalculator& protocol = + getChecksumCalculatorThreadInfo()->m_protocol; + protocol.addBuffer(buf, bufLen); + return protocol.writeChecksum(outputChecksum, outputChecksumLen); +} + +bool ChecksumCalculatorThreadInfo::validate(void* buf, + size_t bufLen, + void* checksum, + size_t checksumLen) { + ChecksumCalculator& protocol = + getChecksumCalculatorThreadInfo()->m_protocol; + protocol.addBuffer(buf, bufLen); + return protocol.validate(checksum, checksumLen); +} + +void ChecksumCalculatorThreadInfo::validOrDie(void* buf, + size_t bufLen, + void* checksum, + size_t checksumLen, + const char* message) { + // We should actually call crashhandler_die(message), but I don't think we + // can link to that library from here + if (!validate(buf, bufLen, checksum, checksumLen)) { + emugl_crash_reporter(message); + } +} diff --git a/external/android-emugl/shared/OpenglCodecCommon/ChecksumCalculatorThreadInfo.h b/external/android-emugl/shared/OpenglCodecCommon/ChecksumCalculatorThreadInfo.h new file mode 100644 index 0000000..993551f --- /dev/null +++ b/external/android-emugl/shared/OpenglCodecCommon/ChecksumCalculatorThreadInfo.h @@ -0,0 +1,56 @@ +/* +* Copyright (C) 2016 The Android Open Source Project +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +#pragma once + +#include "ChecksumCalculator.h" + +// ChecksumCalculatorThreadInfo is the class that makes ChecksumCalculator +// thread-safe. On the host, please only use ChecksumCalculator through this +// class. + +class ChecksumCalculatorThreadInfo { +public: + ChecksumCalculatorThreadInfo(); + ~ChecksumCalculatorThreadInfo(); + + static uint32_t getVersion(); + static bool setVersion(uint32_t version); + + static uint32_t getMaxVersion(); + static const char* getMaxVersionString() { + return ChecksumCalculator::getMaxVersionStr(); + } + + static size_t checksumByteSize(); + static bool writeChecksum(void* buf, + size_t bufLen, + void* outputChecksum, + size_t outputChecksumLen); + + static bool validate(void* buf, + size_t bufLen, + void* checksum, + size_t checksumLen); + static void validOrDie(void* buf, + size_t bufLen, + void* checksum, + size_t checksumLen, + const char* message); + +private: + ChecksumCalculator m_protocol; +}; diff --git a/external/android-emugl/shared/OpenglCodecCommon/ErrorLog.h b/external/android-emugl/shared/OpenglCodecCommon/ErrorLog.h new file mode 100644 index 0000000..bb450ee --- /dev/null +++ b/external/android-emugl/shared/OpenglCodecCommon/ErrorLog.h @@ -0,0 +1,28 @@ +/* +* Copyright (C) 2011 The Android Open Source Project +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ +#ifndef _ERROR_LOG_H_ +#define _ERROR_LOG_H_ + +#include +#define ERR(...) fprintf(stderr, __VA_ARGS__) +#define EMUGL_DEBUG +#ifdef EMUGL_DEBUG +# define DBG(...) fprintf(stderr, __VA_ARGS__) +#else +# define DBG(...) ((void)0) +#endif + +#endif // _ERROR_LOG_H_ diff --git a/external/android-emugl/shared/OpenglCodecCommon/GLDecoderContextData.h b/external/android-emugl/shared/OpenglCodecCommon/GLDecoderContextData.h new file mode 100644 index 0000000..3a6ee17 --- /dev/null +++ b/external/android-emugl/shared/OpenglCodecCommon/GLDecoderContextData.h @@ -0,0 +1,82 @@ +/* +* Copyright (C) 2011 The Android Open Source Project +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ +#pragma once + +#include +#include + +#include +#include + +// Convenient class used to hold the common context data shared +// by both the GLESv1 and GLESv2 decoders. This corresponds to +// vertex attribute buffers. +class GLDecoderContextData { +public: + // List of supported vertex attribute indices, as they appear in + // a glVertexAttribPointer() call. + typedef enum { + VERTEX_LOCATION = 0, + NORMAL_LOCATION = 1, + COLOR_LOCATION = 2, + POINTSIZE_LOCATION = 3, + TEXCOORD0_LOCATION = 4, + TEXCOORD1_LOCATION = 5, + TEXCOORD2_LOCATION = 6, + TEXCOORD3_LOCATION = 7, + TEXCOORD4_LOCATION = 8, + TEXCOORD5_LOCATION = 9, + TEXCOORD6_LOCATION = 10, + TEXCOORD7_LOCATION = 11, + MATRIXINDEX_LOCATION = 12, + WEIGHT_LOCATION = 13, + LAST_LOCATION = 14 + } PointerDataLocation; + + // Default constructor. + GLDecoderContextData(int numLocations = kMaxVertexAttributes) + : mPointerData(), + mNumLocations(static_cast(numLocations)) { + mPointerData.resize(mNumLocations); + } + + // Store |len| bytes from |data| into the buffer associated with + // vertex attribute index |loc|. + void storePointerData(unsigned int loc, void *data, size_t len) { + if (loc < mNumLocations) { + std::string& ptrData = mPointerData[loc]; + ptrData.assign(reinterpret_cast(data), len); + } else { + // User error, don't do anything here + } + } + + // Return pointer to data associated with vertex attribute index |loc| + void* pointerData(unsigned int loc) const { + if (loc < mNumLocations) { + return const_cast(mPointerData[loc].c_str()); + } else { + // User error. Return nullptr. + return nullptr; + } + } + +private: + static const int kMaxVertexAttributes = 64; + + std::vector mPointerData; + unsigned mNumLocations = 0; +}; diff --git a/external/android-emugl/shared/OpenglCodecCommon/Makefile b/external/android-emugl/shared/OpenglCodecCommon/Makefile new file mode 100644 index 0000000..e8bf431 --- /dev/null +++ b/external/android-emugl/shared/OpenglCodecCommon/Makefile @@ -0,0 +1,13 @@ + +ROOT=../.. + +include $(ROOT)/make/commondefs + +CXXFILES = TcpStream.cpp GLClientState.cpp glUtils.cpp +CXXINCS += -I$(ROOT)/libs/GLESv1 -I$(ROOT)/include + +LIBRARY_NAME = libcodecCommon.a + +include $(COMMONRULES) + + diff --git a/external/android-emugl/shared/OpenglCodecCommon/ProtocolUtils.h b/external/android-emugl/shared/OpenglCodecCommon/ProtocolUtils.h new file mode 100644 index 0000000..9b832dd --- /dev/null +++ b/external/android-emugl/shared/OpenglCodecCommon/ProtocolUtils.h @@ -0,0 +1,193 @@ +#ifndef EMUGL_PROTOCOL_UTILS_H +#define EMUGL_PROTOCOL_UTILS_H + +#include +#include +#include +#include + +namespace emugl { + +// Helper macro +#define COMPILE_ASSERT(cond) static char kAssert##__LINE__[1 - 2 * !(cond)] __attribute__((unused)) = { 0 } + +// Helper template: is_pointer. +// is_pointer::value is true iff |T| is a pointer type. +template struct is_pointer { + static const bool value = false; +}; + +template struct is_pointer { + static const bool value = true; +}; + +// A helper template to extract values form the wire protocol stream +// and convert them to appropriate host values. +// +// The wire protocol uses 32-bit exclusively when transferring +// GLintptr or GLsizei values, as well as opaque handles like GLeglImage, +// from the guest (even when the guest is 64-bit). +// +// The corresponding host definitions depend on the host bitness. For +// example, GLintptr is 64-bit on linux-x86_64. The following is a set +// of templates that can simplify the conversion of protocol values +// into host ones. +// +// The most important one is: +// +// unpack(const void* ptr) +// +// Which reads bytes from |ptr|, using |SIZE_TYPE| as the underlying +// sized-integer specifier (e.g. 'uint32_t'), and converting the result +// into a |HOST_TYPE| value. For example: +// +// unpack(ptr + 12); +// +// will read a 4-byte value from |ptr + 12| and convert it into +// an EGLImage, which is a host void*. The template detects host +// pointer types to perform proper type casting. +// +// TODO(digit): Add custom unpackers to handle generic opaque void* values. +// and map them to unique 32-bit values. + +template +struct UnpackerT {}; + +template +struct UnpackerT { + static inline T unpack(const void* ptr) { + COMPILE_ASSERT(sizeof(T) == sizeof(S)); + return (T)(*(S*)(ptr)); + } +}; + +template +struct UnpackerT { + static inline T unpack(const void* ptr) { + return (T)(uintptr_t)(*(S*)(ptr)); + } +}; + +template <> +struct UnpackerT { + static inline float unpack(const void* ptr) { + union { + float f; + uint32_t u; + } v; + v.u = *(uint32_t*)(ptr); + return v.f; + } +}; + +template <> +struct UnpackerT { + static inline double unpack(const void* ptr) { + union { + double d; + uint32_t u; + } v; + v.u = *(uint64_t*)(ptr); + return v.d; + } +}; + +template <> +struct UnpackerT { + static inline ssize_t unpack(const void* ptr) { + return (ssize_t)*(int32_t*)(ptr); + } +}; + +template +inline T Unpack(const void* ptr) { + return UnpackerT::value>::unpack(ptr); +} + +// Helper class used to ensure input buffers passed to EGL/GL functions +// are properly aligned (preventing crashes with some backends). +// Usage example: +// +// InputBuffer inputBuffer(ptr, size); +// glDoStuff(inputBuffer.get()); +// +// inputBuffer.get() will return the original value of |ptr| if it was +// aligned on an 8-byte boundary. Otherwise, it will return the address +// of an aligned heap-allocated copy of the original |size| bytes starting +// from |ptr|. The heap block is released at scope exit. +class InputBuffer { +public: + InputBuffer(const void* input, size_t size, size_t align = 8) : + mBuff(input), mIsCopy(false) { + if (((uintptr_t)input & (align - 1U)) != 0) { + void* newBuff = malloc(size); + memcpy(newBuff, input, size); + mBuff = newBuff; + mIsCopy = true; + } + } + + ~InputBuffer() { + if (mIsCopy) { + free((void*)mBuff); + } + } + + const void* get() const { + return mBuff; + } + +private: + const void* mBuff; + bool mIsCopy; +}; + +// Helper class used to ensure that output buffers passed to EGL/GL functions +// are aligned on 8-byte addresses. +// Usage example: +// +// ptr = stream->alloc(size); +// OutputBuffer outputBuffer(ptr, size); +// glGetStuff(outputBuffer.get()); +// outputBuffer.flush(); +// +// outputBuffer.get() returns the original value of |ptr| if it was already +// aligned on an 8=byte boundary. Otherwise, it returns the size of an heap +// allocated zeroed buffer of |size| bytes. +// +// outputBuffer.flush() copies the content of the heap allocated buffer back +// to |ptr| explictly, if needed. If a no-op if |ptr| was aligned. +class OutputBuffer { +public: + OutputBuffer(unsigned char* ptr, size_t size, size_t align = 8) : + mOrgBuff(ptr), mBuff(ptr), mSize(size) { + if (((uintptr_t)ptr & (align - 1U)) != 0) { + void* newBuff = calloc(1, size); + mBuff = newBuff; + } + } + + ~OutputBuffer() { + if (mBuff != mOrgBuff) { + free(mBuff); + } + } + + void* get() const { + return mBuff; + } + + void flush() { + if (mBuff != mOrgBuff) { + memcpy(mOrgBuff, mBuff, mSize); + } + } +private: + unsigned char* mOrgBuff; + void* mBuff; + size_t mSize; +}; + +} // namespace emugl + +#endif // EMUGL_PROTOCOL_UTILS_H diff --git a/external/android-emugl/shared/OpenglCodecCommon/glUtils.cpp b/external/android-emugl/shared/OpenglCodecCommon/glUtils.cpp new file mode 100644 index 0000000..e9e82b7 --- /dev/null +++ b/external/android-emugl/shared/OpenglCodecCommon/glUtils.cpp @@ -0,0 +1,416 @@ +/* +* Copyright (C) 2011 The Android Open Source Project +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ +#include "glUtils.h" + +#include "ErrorLog.h" + +#include + +size_t glSizeof(GLenum type) +{ + size_t retval = 0; + switch(type) { + case GL_BYTE: + case GL_UNSIGNED_BYTE: + retval = 1; + break; + case GL_SHORT: + case GL_UNSIGNED_SHORT: + case GL_HALF_FLOAT_OES: + retval = 2; + break; + case GL_INT: + case GL_FLOAT: + case GL_FIXED: + case GL_BOOL: + retval = 4; + break; +#ifdef GL_DOUBLE + case GL_DOUBLE: + retval = 8; + break; +#endif + case GL_FLOAT_VEC2: + case GL_INT_VEC2: + case GL_BOOL_VEC2: + retval = 8; + break; + case GL_INT_VEC3: + case GL_BOOL_VEC3: + case GL_FLOAT_VEC3: + retval = 12; + break; + case GL_FLOAT_VEC4: + case GL_BOOL_VEC4: + case GL_INT_VEC4: + case GL_FLOAT_MAT2: + retval = 16; + break; + case GL_FLOAT_MAT3: + retval = 36; + break; + case GL_FLOAT_MAT4: + retval = 64; + break; + case GL_SAMPLER_2D: + case GL_SAMPLER_CUBE: + retval = 4; + break; + default: + ERR("**** ERROR unknown type 0x%x (%s,%d)\n", type, __FUNCTION__,__LINE__); + } + return retval; + +} + +size_t glUtilsParamSize(GLenum param) +{ + size_t s = 0; + + switch(param) + { + case GL_DEPTH_TEST: + case GL_DEPTH_FUNC: + case GL_DEPTH_BITS: + case GL_MAX_CLIP_PLANES: + case GL_GREEN_BITS: + case GL_MAX_MODELVIEW_STACK_DEPTH: + case GL_MAX_PROJECTION_STACK_DEPTH: + case GL_MAX_TEXTURE_STACK_DEPTH: + case GL_IMPLEMENTATION_COLOR_READ_FORMAT_OES: + case GL_IMPLEMENTATION_COLOR_READ_TYPE_OES: + case GL_NUM_COMPRESSED_TEXTURE_FORMATS: + case GL_MAX_TEXTURE_SIZE: + case GL_TEXTURE_GEN_MODE_OES: + case GL_TEXTURE_ENV_MODE: + case GL_FOG_MODE: + case GL_FOG_DENSITY: + case GL_FOG_START: + case GL_FOG_END: + case GL_SPOT_EXPONENT: + case GL_CONSTANT_ATTENUATION: + case GL_LINEAR_ATTENUATION: + case GL_QUADRATIC_ATTENUATION: + case GL_SHININESS: + case GL_LIGHT_MODEL_TWO_SIDE: + case GL_POINT_SIZE: + case GL_POINT_SIZE_MIN: + case GL_POINT_SIZE_MAX: + case GL_POINT_FADE_THRESHOLD_SIZE: + case GL_CULL_FACE_MODE: + case GL_FRONT_FACE: + case GL_SHADE_MODEL: + case GL_DEPTH_WRITEMASK: + case GL_DEPTH_CLEAR_VALUE: + case GL_STENCIL_FAIL: + case GL_STENCIL_PASS_DEPTH_FAIL: + case GL_STENCIL_PASS_DEPTH_PASS: + case GL_STENCIL_REF: + case GL_STENCIL_WRITEMASK: + case GL_MATRIX_MODE: + case GL_MODELVIEW_STACK_DEPTH: + case GL_PROJECTION_STACK_DEPTH: + case GL_TEXTURE_STACK_DEPTH: + case GL_ALPHA_TEST_FUNC: + case GL_ALPHA_TEST_REF: + case GL_ALPHA_TEST: + case GL_BLEND_DST: + case GL_BLEND_SRC: + case GL_BLEND: + case GL_LOGIC_OP_MODE: + case GL_SCISSOR_TEST: + case GL_MAX_TEXTURE_UNITS: + case GL_ACTIVE_TEXTURE: + case GL_ALPHA_BITS: + case GL_ARRAY_BUFFER_BINDING: + case GL_BLUE_BITS: + case GL_CLIENT_ACTIVE_TEXTURE: + case GL_CLIP_PLANE0: + case GL_CLIP_PLANE1: + case GL_CLIP_PLANE2: + case GL_CLIP_PLANE3: + case GL_CLIP_PLANE4: + case GL_CLIP_PLANE5: + case GL_COLOR_ARRAY: + case GL_COLOR_ARRAY_BUFFER_BINDING: + case GL_COLOR_ARRAY_SIZE: + case GL_COLOR_ARRAY_STRIDE: + case GL_COLOR_ARRAY_TYPE: + case GL_COLOR_LOGIC_OP: + case GL_COLOR_MATERIAL: + case GL_PACK_ALIGNMENT: + case GL_PERSPECTIVE_CORRECTION_HINT: + case GL_POINT_SIZE_ARRAY_BUFFER_BINDING_OES: + case GL_POINT_SIZE_ARRAY_STRIDE_OES: + case GL_POINT_SIZE_ARRAY_TYPE_OES: + case GL_POINT_SMOOTH: + case GL_POINT_SMOOTH_HINT: + case GL_POINT_SPRITE_OES: + case GL_COORD_REPLACE_OES: + case GL_COMBINE_ALPHA: + case GL_SRC0_RGB: + case GL_SRC1_RGB: + case GL_SRC2_RGB: + case GL_OPERAND0_RGB: + case GL_OPERAND1_RGB: + case GL_OPERAND2_RGB: + case GL_SRC0_ALPHA: + case GL_SRC1_ALPHA: + case GL_SRC2_ALPHA: + case GL_OPERAND0_ALPHA: + case GL_OPERAND1_ALPHA: + case GL_OPERAND2_ALPHA: + case GL_RGB_SCALE: + case GL_ALPHA_SCALE: + case GL_COMBINE_RGB: + case GL_POLYGON_OFFSET_FACTOR: + case GL_POLYGON_OFFSET_FILL: + case GL_POLYGON_OFFSET_UNITS: + case GL_RED_BITS: + case GL_RESCALE_NORMAL: + case GL_SAMPLE_ALPHA_TO_COVERAGE: + case GL_SAMPLE_ALPHA_TO_ONE: + case GL_SAMPLE_BUFFERS: + case GL_SAMPLE_COVERAGE: + case GL_SAMPLE_COVERAGE_INVERT: + case GL_SAMPLE_COVERAGE_VALUE: + case GL_SAMPLES: + case GL_STENCIL_BITS: + case GL_STENCIL_CLEAR_VALUE: + case GL_STENCIL_FUNC: + case GL_STENCIL_TEST: + case GL_STENCIL_VALUE_MASK: + case GL_STENCIL_BACK_FUNC: + case GL_STENCIL_BACK_VALUE_MASK: + case GL_STENCIL_BACK_REF: + case GL_STENCIL_BACK_FAIL: + case GL_STENCIL_BACK_PASS_DEPTH_FAIL: + case GL_STENCIL_BACK_PASS_DEPTH_PASS: + case GL_STENCIL_BACK_WRITEMASK: + case GL_TEXTURE_2D: + case GL_TEXTURE_BINDING_2D: + case GL_TEXTURE_BINDING_CUBE_MAP: + case GL_TEXTURE_BINDING_EXTERNAL_OES: + case GL_TEXTURE_COORD_ARRAY: + case GL_TEXTURE_COORD_ARRAY_BUFFER_BINDING: + case GL_TEXTURE_COORD_ARRAY_SIZE: + case GL_TEXTURE_COORD_ARRAY_STRIDE: + case GL_TEXTURE_COORD_ARRAY_TYPE: + case GL_UNPACK_ALIGNMENT: + case GL_VERTEX_ARRAY: + case GL_VERTEX_ARRAY_BUFFER_BINDING: + case GL_VERTEX_ARRAY_SIZE: + case GL_VERTEX_ARRAY_STRIDE: + case GL_VERTEX_ARRAY_TYPE: + case GL_SPOT_CUTOFF: + case GL_TEXTURE_MIN_FILTER: + case GL_TEXTURE_MAG_FILTER: + case GL_TEXTURE_WRAP_S: + case GL_TEXTURE_WRAP_T: + case GL_GENERATE_MIPMAP: + case GL_GENERATE_MIPMAP_HINT: + case GL_RENDERBUFFER_WIDTH_OES: + case GL_RENDERBUFFER_HEIGHT_OES: + case GL_RENDERBUFFER_INTERNAL_FORMAT_OES: + case GL_RENDERBUFFER_RED_SIZE_OES: + case GL_RENDERBUFFER_GREEN_SIZE_OES: + case GL_RENDERBUFFER_BLUE_SIZE_OES: + case GL_RENDERBUFFER_ALPHA_SIZE_OES: + case GL_RENDERBUFFER_DEPTH_SIZE_OES: + case GL_RENDERBUFFER_STENCIL_SIZE_OES: + case GL_RENDERBUFFER_BINDING: + case GL_FRAMEBUFFER_BINDING: + case GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE_OES: + case GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME_OES: + case GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_LEVEL_OES: + case GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_CUBE_MAP_FACE_OES: + case GL_FENCE_STATUS_NV: + case GL_FENCE_CONDITION_NV: + case GL_TEXTURE_WIDTH_QCOM: + case GL_TEXTURE_HEIGHT_QCOM: + case GL_TEXTURE_DEPTH_QCOM: + case GL_TEXTURE_INTERNAL_FORMAT_QCOM: + case GL_TEXTURE_FORMAT_QCOM: + case GL_TEXTURE_TYPE_QCOM: + case GL_TEXTURE_IMAGE_VALID_QCOM: + case GL_TEXTURE_NUM_LEVELS_QCOM: + case GL_TEXTURE_TARGET_QCOM: + case GL_TEXTURE_OBJECT_VALID_QCOM: + case GL_BLEND_EQUATION_RGB_OES: + case GL_BLEND_EQUATION_ALPHA_OES: + case GL_BLEND_DST_RGB_OES: + case GL_BLEND_SRC_RGB_OES: + case GL_BLEND_DST_ALPHA_OES: + case GL_BLEND_SRC_ALPHA_OES: + case GL_MAX_LIGHTS: + case GL_SHADER_TYPE: + case GL_DELETE_STATUS: + case GL_COMPILE_STATUS: + case GL_INFO_LOG_LENGTH: + case GL_SHADER_SOURCE_LENGTH: + case GL_CURRENT_PROGRAM: + case GL_LINK_STATUS: + case GL_VALIDATE_STATUS: + case GL_ATTACHED_SHADERS: + case GL_ACTIVE_UNIFORMS: + case GL_ACTIVE_ATTRIBUTES: + case GL_SUBPIXEL_BITS: + case GL_MAX_CUBE_MAP_TEXTURE_SIZE: + case GL_NUM_SHADER_BINARY_FORMATS: + case GL_SHADER_COMPILER: + case GL_MAX_VERTEX_ATTRIBS: + case GL_MAX_VERTEX_UNIFORM_VECTORS: + case GL_MAX_VARYING_VECTORS: + case GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS: + case GL_MAX_VERTEX_TEXTURE_IMAGE_UNITS: + case GL_MAX_FRAGMENT_UNIFORM_VECTORS: + case GL_MAX_RENDERBUFFER_SIZE: + case GL_MAX_TEXTURE_IMAGE_UNITS: + case GL_REQUIRED_TEXTURE_IMAGE_UNITS_OES: + case GL_FRAGMENT_SHADER_DERIVATIVE_HINT_OES: + case GL_LINE_WIDTH: + s = 1; + break; + case GL_ALIASED_LINE_WIDTH_RANGE: + case GL_ALIASED_POINT_SIZE_RANGE: + case GL_DEPTH_RANGE: + case GL_MAX_VIEWPORT_DIMS: + case GL_SMOOTH_POINT_SIZE_RANGE: + case GL_SMOOTH_LINE_WIDTH_RANGE: + s= 2; + break; + case GL_SPOT_DIRECTION: + case GL_POINT_DISTANCE_ATTENUATION: + case GL_CURRENT_NORMAL: + s = 3; + break; + case GL_CURRENT_VERTEX_ATTRIB: + case GL_CURRENT_TEXTURE_COORDS: + case GL_CURRENT_COLOR: + case GL_FOG_COLOR: + case GL_AMBIENT: + case GL_DIFFUSE: + case GL_SPECULAR: + case GL_EMISSION: + case GL_POSITION: + case GL_LIGHT_MODEL_AMBIENT: + case GL_TEXTURE_ENV_COLOR: + case GL_SCISSOR_BOX: + case GL_VIEWPORT: + case GL_TEXTURE_CROP_RECT_OES: + case GL_COLOR_CLEAR_VALUE: + case GL_COLOR_WRITEMASK: + case GL_AMBIENT_AND_DIFFUSE: + case GL_BLEND_COLOR: + s = 4; + break; + case GL_MODELVIEW_MATRIX: + case GL_PROJECTION_MATRIX: + case GL_TEXTURE_MATRIX: + s = 16; + break; + default: + ERR("glUtilsParamSize: unknow param 0x%08x\n", param); + s = 1; // assume 1 + } + return s; +} + +void glUtilsPackPointerData(unsigned char *dst, unsigned char *src, + int size, GLenum type, unsigned int stride, + unsigned int datalen) +{ + unsigned int vsize = size * glSizeof(type); + if (stride == 0) stride = vsize; + + if (stride == vsize) { + memcpy(dst, src, datalen); + } else { + for (unsigned int i = 0; i < datalen; i += vsize) { + memcpy(dst, src, vsize); + dst += vsize; + src += stride; + } + } +} + +int glUtilsPixelBitSize(GLenum format, GLenum type) +{ + int components = 0; + int componentsize = 0; + int pixelsize = 0; + switch(type) { + case GL_BYTE: + case GL_UNSIGNED_BYTE: + componentsize = 8; + break; + case GL_SHORT: + case GL_UNSIGNED_SHORT: + case GL_UNSIGNED_SHORT_5_6_5: + case GL_UNSIGNED_SHORT_4_4_4_4: + case GL_UNSIGNED_SHORT_5_5_5_1: + case GL_RGB565_OES: + case GL_RGB5_A1_OES: + case GL_RGBA4_OES: + pixelsize = 16; + break; + case GL_INT: + case GL_UNSIGNED_INT: + case GL_FLOAT: + case GL_FIXED: + case GL_UNSIGNED_INT_24_8_OES: + pixelsize = 32; + break; + default: + ERR("glUtilsPixelBitSize: unknown pixel type - assuming pixel data 0\n"); + componentsize = 0; + } + + if (pixelsize == 0) { + switch(format) { +#if 0 + case GL_RED: + case GL_GREEN: + case GL_BLUE: +#endif + case GL_ALPHA: + case GL_LUMINANCE: + case GL_DEPTH_COMPONENT: + case GL_DEPTH_STENCIL_OES: + components = 1; + break; + case GL_LUMINANCE_ALPHA: + components = 2; + break; + case GL_RGB: +#if 0 + case GL_BGR: +#endif + components = 3; + break; + case GL_RGBA: + case GL_BGRA_EXT: + components = 4; + break; + default: + ERR("glUtilsPixelBitSize: unknown pixel format...\n"); + components = 0; + } + pixelsize = components * componentsize; + } + + return pixelsize; +} diff --git a/external/android-emugl/shared/OpenglCodecCommon/glUtils.h b/external/android-emugl/shared/OpenglCodecCommon/glUtils.h new file mode 100644 index 0000000..73c1912 --- /dev/null +++ b/external/android-emugl/shared/OpenglCodecCommon/glUtils.h @@ -0,0 +1,41 @@ +/* +* Copyright (C) 2011 The Android Open Source Project +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ +#pragma once + +#include +#include +#include +#include + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +size_t glSizeof(GLenum type); + +size_t glUtilsParamSize(GLenum param); + +void glUtilsPackPointerData(unsigned char *dst, unsigned char *str, + int size, GLenum type, unsigned int stride, + unsigned int datalen); + +int glUtilsPixelBitSize(GLenum format, GLenum type); + +#ifdef __cplusplus +}; +#endif diff --git a/external/android-emugl/shared/OpenglCodecCommon/gl_base_types.h b/external/android-emugl/shared/OpenglCodecCommon/gl_base_types.h new file mode 100644 index 0000000..38c594b --- /dev/null +++ b/external/android-emugl/shared/OpenglCodecCommon/gl_base_types.h @@ -0,0 +1,62 @@ +/* +* Copyright (C) 2011 The Android Open Source Project +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ +#ifndef __GL_BASE_TYPES__H +#define __GL_BASE_TYPES__H + +#include + +#ifndef gles1_APIENTRY +#define gles1_APIENTRY KHRONOS_APIENTRY +#endif + +#ifndef gles2_APIENTRY +#define gles2_APIENTRY KHRONOS_APIENTRY +#endif + +typedef void GLvoid; +typedef unsigned int GLenum; +typedef unsigned char GLboolean; +typedef unsigned int GLbitfield; +typedef char GLchar; +typedef khronos_int8_t GLbyte; +typedef short GLshort; +typedef int GLint; +typedef int GLsizei; +typedef khronos_uint8_t GLubyte; +typedef unsigned short GLushort; +typedef unsigned int GLuint; +typedef khronos_float_t GLfloat; +typedef khronos_float_t GLclampf; +typedef khronos_int32_t GLfixed; +typedef khronos_int32_t GLclampx; +typedef khronos_intptr_t GLintptr; +typedef khronos_ssize_t GLsizeiptr; +typedef char *GLstr; +/* JR XXX Treating this as an in handle - is this correct? */ +typedef void * GLeglImageOES; + +/* ErrorCode */ +#ifndef GL_INVALID_ENUM +#define GL_NO_ERROR 0 +#define GL_INVALID_ENUM 0x0500 +#define GL_INVALID_VALUE 0x0501 +#define GL_INVALID_OPERATION 0x0502 +#define GL_STACK_OVERFLOW 0x0503 +#define GL_STACK_UNDERFLOW 0x0504 +#define GL_OUT_OF_MEMORY 0x0505 +#endif + +#endif diff --git a/external/android-emugl/shared/emugl/CMakeLists.txt b/external/android-emugl/shared/emugl/CMakeLists.txt new file mode 100644 index 0000000..e4717b2 --- /dev/null +++ b/external/android-emugl/shared/emugl/CMakeLists.txt @@ -0,0 +1 @@ +add_subdirectory(common) diff --git a/external/android-emugl/shared/emugl/common/Android.mk b/external/android-emugl/shared/emugl/common/Android.mk new file mode 100644 index 0000000..a6c6a90 --- /dev/null +++ b/external/android-emugl/shared/emugl/common/Android.mk @@ -0,0 +1,65 @@ +# This build script corresponds to a library containing many definitions +# common to both the guest and the host. They relate to +# +LOCAL_PATH := $(call my-dir) + +### emugl_common host library ########################################### + +commonSources := \ + crash_reporter.cpp \ + id_to_object_map.cpp \ + lazy_instance.cpp \ + logging.cpp \ + message_channel.cpp \ + pod_vector.cpp \ + shared_library.cpp \ + smart_ptr.cpp \ + sockets.cpp \ + thread_store.cpp \ + +host_commonSources := $(commonSources) + +host_commonLdLibs := $(CXX_STD_LIB) + +ifneq (windows,$(BUILD_TARGET_OS)) + host_commonSources += \ + thread_pthread.cpp \ + + host_commonLdLibs += -ldl -lpthread +else + host_commonSources += \ + condition_variable_win32.cpp \ + thread_win32.cpp \ + +endif + +$(call emugl-begin-host-static-library,libemugl_common) +LOCAL_SRC_FILES := $(host_commonSources) +$(call emugl-export,C_INCLUDES,$(EMUGL_PATH)/shared) +$(call emugl-export,LDLIBS,$(host_commonLdLibs)) +$(call emugl-end-module) + +### emugl_common_unittests ############################################## + +host_commonSources := \ + condition_variable_unittest.cpp \ + id_to_object_map_unittest.cpp \ + lazy_instance_unittest.cpp \ + pod_vector_unittest.cpp \ + message_channel_unittest.cpp \ + mutex_unittest.cpp \ + shared_library_unittest.cpp \ + smart_ptr_unittest.cpp \ + thread_store_unittest.cpp \ + thread_unittest.cpp \ + unique_integer_map_unittest.cpp \ + +$(call emugl-begin-host-executable,emugl$(BUILD_TARGET_SUFFIX)_common_host_unittests) +LOCAL_SRC_FILES := $(host_commonSources) +$(call emugl-import,libemugl_common libemugl_gtest) +$(call emugl-end-module) + +$(call emugl-begin-host-shared-library,lib$(BUILD_TARGET_SUFFIX)emugl_test_shared_library) +LOCAL_SRC_FILES := testing/test_shared_library.cpp +LOCAL_CFLAGS := -fvisibility=default +$(call emugl-end-module) diff --git a/external/android-emugl/shared/emugl/common/CMakeLists.txt b/external/android-emugl/shared/emugl/common/CMakeLists.txt new file mode 100644 index 0000000..a479a9b --- /dev/null +++ b/external/android-emugl/shared/emugl/common/CMakeLists.txt @@ -0,0 +1,16 @@ +set(COMMON_SOURCES + crash_reporter.cpp + id_to_object_map.cpp + lazy_instance.cpp + logging.cpp + message_channel.cpp + pod_vector.cpp + shared_library.cpp + smart_ptr.cpp + sockets.cpp + thread_store.cpp + thread_pthread.cpp) + +add_library(emugl_common ${COMMON_SOURCES}) +target_link_libraries(emugl_common + dl pthread) diff --git a/external/android-emugl/shared/emugl/common/condition_variable.h b/external/android-emugl/shared/emugl/common/condition_variable.h new file mode 100644 index 0000000..66f9d02 --- /dev/null +++ b/external/android-emugl/shared/emugl/common/condition_variable.h @@ -0,0 +1,91 @@ +// Copyright (C) 2014 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef EMUGL_CONDITION_VARIABLE_H +#define EMUGL_CONDITION_VARIABLE_H + +#include "emugl/common/mutex.h" +#include "emugl/common/pod_vector.h" + +#ifdef _WIN32 +#include +#else +#include +#endif + +namespace emugl { + +// A class that implements a condition variable, which can be used in +// association with a Mutex to blocking-wait for specific conditions. +// Useful to implement various synchronization data structures. +// For an example, see ::emugl::MessageChannel. +class ConditionVariable { +public: +#ifdef _WIN32 + + // Default constructor. + ConditionVariable(); + + // Destructor. + ~ConditionVariable(); + + // Wait until the condition variable is signaled. Note that spurious + // wakeups are always a possibility, so always check the condition + // in a loop, i.e. do: + // + // while (!condition) { condVar.wait(&lock); } + // + // instead of: + // + // if (!condition) { condVar.wait(&lock); } + // + void wait(Mutex* userLock); + + // Signal that a condition was reached. This will wake at most one + // waiting thread that is blocked on wait(). + void signal(); + +private: + PodVector mWaiters; + Mutex mLock; + +#else // !_WIN32 + + // Note: on Posix systems, make it a naive wrapper around pthread_cond_t. + + ConditionVariable() { + pthread_cond_init(&mCond, NULL); + } + + ~ConditionVariable() { + pthread_cond_destroy(&mCond); + } + + void wait(Mutex* userLock) { + pthread_cond_wait(&mCond, &userLock->mLock); + } + + void signal() { + pthread_cond_signal(&mCond); + } + +private: + pthread_cond_t mCond; + +#endif // !_WIN32 +}; + +} // namespace emugl + +#endif // EMUGL_CONDITION_VARIABLE_H diff --git a/external/android-emugl/shared/emugl/common/condition_variable_unittest.cpp b/external/android-emugl/shared/emugl/common/condition_variable_unittest.cpp new file mode 100644 index 0000000..1ebaa63 --- /dev/null +++ b/external/android-emugl/shared/emugl/common/condition_variable_unittest.cpp @@ -0,0 +1,27 @@ +// Copyright (C) 2014 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "emugl/common/condition_variable.h" + +#include "emugl/common/mutex.h" + +#include + +namespace emugl { + +TEST(ConditionVariable, init) { + ConditionVariable cond; +} + +} // namespace emugl diff --git a/external/android-emugl/shared/emugl/common/condition_variable_win32.cpp b/external/android-emugl/shared/emugl/common/condition_variable_win32.cpp new file mode 100644 index 0000000..e0e51a4 --- /dev/null +++ b/external/android-emugl/shared/emugl/common/condition_variable_win32.cpp @@ -0,0 +1,114 @@ +// Copyright (C) 2014 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "emugl/common/condition_variable.h" + +#include "emugl/common/lazy_instance.h" +#include "emugl/common/mutex.h" +#include "emugl/common/pod_vector.h" + +// Technical note: this is loosely based on the Chromium implementation +// of ConditionVariable. This version works on Windows XP and above and +// doesn't try to use Vista's CONDITION_VARIABLE types. + +namespace emugl { + +namespace { + +// Helper class which implements a free list of event handles. +class WaitEventStorage { +public: + WaitEventStorage() : mFreeHandles(), mLock() {} + + ~WaitEventStorage() { + for (size_t n = 0; n < mFreeHandles.size(); ++n) { + CloseHandle(mFreeHandles[n]); + } + } + + HANDLE alloc() { + HANDLE handle; + mLock.lock(); + size_t size = mFreeHandles.size(); + if (size > 0) { + handle = mFreeHandles[size - 1U]; + mFreeHandles.remove(size - 1U); + } else { + handle = CreateEvent(NULL, TRUE, FALSE, NULL); + } + mLock.unlock(); + return handle; + } + + void free(HANDLE h) { + mLock.lock(); + ResetEvent(h); + mFreeHandles.push_back(h); + mLock.unlock(); + } + +private: + PodVector mFreeHandles; + Mutex mLock; +}; + +LazyInstance sWaitEvents = LAZY_INSTANCE_INIT; + +} // namespace + +ConditionVariable::ConditionVariable() : mWaiters(), mLock() {} + +ConditionVariable::~ConditionVariable() { + mLock.lock(); + for (size_t n = 0; n < mWaiters.size(); ++n) { + CloseHandle(mWaiters[n]); + } + mWaiters.resize(0U); + mLock.unlock(); +} + +void ConditionVariable::wait(Mutex* userLock) { + // Grab new waiter event handle. + mLock.lock(); + HANDLE handle = sWaitEvents->alloc(); + mWaiters.push_back(handle); + mLock.unlock(); + + // Unlock user lock then wait for event. + userLock->unlock(); + WaitForSingleObject(handle, INFINITE); + // NOTE: The handle has been removed from mWaiters here, + // see signal() below. Close/recycle the event. + sWaitEvents->free(handle); + userLock->lock(); +} + +void ConditionVariable::signal() { + mLock.lock(); + size_t size = mWaiters.size(); + if (size > 0U) { + // NOTE: This wakes up the thread that went to sleep most + // recently (LIFO) for performance reason. For better + // fairness, using (FIFO) would be appropriate. + HANDLE handle = mWaiters[size - 1U]; + mWaiters.remove(size - 1U); + SetEvent(handle); + // NOTE: The handle will be closed/recycled by the waiter. + } else { + // Nothing to signal. + } + mLock.unlock(); +} + +} // namespace emugl diff --git a/external/android-emugl/shared/emugl/common/crash_reporter.cpp b/external/android-emugl/shared/emugl/common/crash_reporter.cpp new file mode 100644 index 0000000..b4e0267 --- /dev/null +++ b/external/android-emugl/shared/emugl/common/crash_reporter.cpp @@ -0,0 +1,33 @@ +/* +* Copyright (C) 2016 The Android Open Source Project +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +#include "crash_reporter.h" + +#include + +void default_crash_reporter(const char* format, ...) { + abort(); +} + +crash_reporter_t emugl_crash_reporter = default_crash_reporter; + +void set_emugl_crash_reporter(crash_reporter_t crash_reporter) { + if (crash_reporter) { + emugl_crash_reporter = crash_reporter; + } else { + emugl_crash_reporter = default_crash_reporter; + } +} diff --git a/external/android-emugl/shared/emugl/common/crash_reporter.h b/external/android-emugl/shared/emugl/common/crash_reporter.h new file mode 100644 index 0000000..81bb21a --- /dev/null +++ b/external/android-emugl/shared/emugl/common/crash_reporter.h @@ -0,0 +1,22 @@ +/* +* Copyright (C) 2016 The Android Open Source Project +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +#pragma once + +typedef void (*crash_reporter_t)(const char* format, ...); + +extern crash_reporter_t emugl_crash_reporter; +void set_emugl_crash_reporter(crash_reporter_t crash_reporter); diff --git a/external/android-emugl/shared/emugl/common/id_to_object_map.cpp b/external/android-emugl/shared/emugl/common/id_to_object_map.cpp new file mode 100644 index 0000000..597c9eb --- /dev/null +++ b/external/android-emugl/shared/emugl/common/id_to_object_map.cpp @@ -0,0 +1,236 @@ +// Copyright (C) 2014 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "emugl/common/id_to_object_map.h" + +#include + +namespace emugl { + +namespace { + +typedef IdToObjectMapBase::KeyType KeyType; + +enum { + kMinShift = 3, + kMaxShift = 31, + kMinCapacity = (1 << kMinShift), + kLoadScale = 1024, + kMinLoad = kLoadScale/4, // 25% minimum load. + kMaxLoad = kLoadScale*3/4, // 75% maximum load. + + kInvalidKey = IdToObjectMapBase::kMaxId + 1U, + kTombstone = IdToObjectMapBase::kMaxId + 2U, +}; + +// Return a number that indicates if the current |capacity| is appropriate +// to hold |size| items in our map. +// -1 -> the capacity is too small and needs to be increased. +// 0 -> the capacity is ok. +// +1 -> the capacity is too large and needs to be decreased. +int capacityCompare(size_t shift, size_t size) { + size_t capacity = 1U << shift; + // Essentially, one can rewrite: + // load < minLoad + // as: + // size / capacity < minLoad + // capacity * minLoad > size + if (capacity * kMinLoad > size * kLoadScale) + return +1; + + // Similarly, one can rewrite: + // load > maxLoad + // as: + // size / capacity > maxLoad + // capacity * maxLoad < size + if (capacity * kMaxLoad < size * kLoadScale) + return -1; + + return 0; +} + +size_t probeKeys(const KeyType* keys, size_t shift, KeyType key) { + static const int kPrimes[] = { + 1, /* For 1 << 0 */ + 2, + 3, + 7, + 13, + 31, + 61, + 127, + 251, + 509, + 1021, + 2039, + 4093, + 8191, + 16381, + 32749, + 65521, /* For 1 << 16 */ + 131071, + 262139, + 524287, + 1048573, + 2097143, + 4194301, + 8388593, + 16777213, + 33554393, + 67108859, + 134217689, + 268435399, + 536870909, + 1073741789, + 2147483647 /* For 1 << 31 */ + }; + + size_t slot = key % kPrimes[shift]; + size_t step = 0; + for (;;) { + KeyType k = keys[slot]; + if (k == kInvalidKey || k == kTombstone || k == key) + return slot; + + step += 1; + slot = (slot + step) & (1U << shift); + } +} + +} // namespace + +IdToObjectMapBase::IdToObjectMapBase() : + mCount(0), mShift(kMinShift) { + size_t capacity = 1U << mShift; + mKeys = static_cast(::calloc(sizeof(mKeys[0]), capacity)); + mValues = static_cast(::calloc(sizeof(mValues[0]), capacity)); + for (size_t n = 0; n < capacity; ++n) { + mKeys[n] = kInvalidKey; + } +} + +IdToObjectMapBase::~IdToObjectMapBase() { + mShift = 0; + mCount = 0; + ::free(mKeys); + ::free(mValues); +} + +bool IdToObjectMapBase::contains(KeyType key) const { + size_t slot = probeKeys(mKeys, mShift, key); + switch (mKeys[slot]) { + case kInvalidKey: + case kTombstone: + return false; + default: + ; + } + return true; +} + +bool IdToObjectMapBase::find(KeyType key, void** value) const { + size_t slot = probeKeys(mKeys, mShift, key); + if (!isValidKey(mKeys[slot])) { + *value = NULL; + return false; + } + *value = mValues[slot]; + return true; +} + +void* IdToObjectMapBase::set(KeyType key, void* value) { + if (!value) + return remove(key); + + size_t slot = probeKeys(mKeys, mShift, key); + void* result; + if (isValidKey(mKeys[slot])) { + result = mValues[slot]; + mValues[slot] = value; + } else { + mKeys[slot] = key; + mValues[slot] = value; + result = NULL; + mCount++; + resize(mCount); + } + return result; +} + +void* IdToObjectMapBase::remove(KeyType key) { + size_t slot = probeKeys(mKeys, mShift, key); + if (!isValidKey(mKeys[slot])) + return NULL; + + void* result = mValues[slot]; + mValues[slot] = NULL; + mKeys[slot] = kTombstone; + mCount--; + return result; +} + +void IdToObjectMapBase::resize(size_t newSize) { + int ret = capacityCompare(mShift, newSize); + if (!ret) + return; + + size_t oldCapacity = 1U << mShift; + size_t newShift = mShift; + + if (ret < 0) { + // Capacity is too small and must be increased. + do { + if (newShift == kMaxShift) + break; + ++newShift; + } while (capacityCompare(newShift, newSize) < 0); + } else { + // Capacity is too large and must be decreased. + do { + if (newShift == kMinShift) + break; + newShift--; + } while (capacityCompare(newShift, newSize) > 0); + } + if (newShift == mShift) + return; + + // Allocate new arrays. + size_t newCapacity = 1U << newShift; + KeyType* newKeys = static_cast( + ::calloc(sizeof(newKeys[0]), newCapacity)); + void** newValues = static_cast( + ::calloc(sizeof(newValues[0]), newCapacity)); + for (size_t n = 0; n < newCapacity; ++n) + newKeys[n] = kInvalidKey; + + // Copy old entries into new arrays. + for (size_t n = 0; n < oldCapacity; ++n) { + KeyType key = mKeys[n]; + if (isValidKey(key)) { + size_t newSlot = probeKeys(newKeys, newShift, key); + newKeys[newSlot] = key; + newValues[newSlot] = mValues[n]; + } + } + + // Swap arrays, and get rid of old ones. + ::free(mKeys); + ::free(mValues); + mKeys = newKeys; + mValues = newValues; + mShift = newShift; +} + +} // namespace emugl diff --git a/external/android-emugl/shared/emugl/common/id_to_object_map.h b/external/android-emugl/shared/emugl/common/id_to_object_map.h new file mode 100644 index 0000000..e3d0a81 --- /dev/null +++ b/external/android-emugl/shared/emugl/common/id_to_object_map.h @@ -0,0 +1,176 @@ +// Copyright (C) 2014 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef EMUGL_COMMON_ID_TO_OBJECT_MAP_H +#define EMUGL_COMMON_ID_TO_OBJECT_MAP_H + +#include + +namespace emugl { + +// Base implementation class for IdToObjectMap template. +// Used to reduce template-instanciated code generation. +class IdToObjectMapBase { +public: + // The type of keys in this map. + typedef unsigned KeyType; + + // Values higher than kMaxId cannot be used as map keys. + enum { + kMaxId = 0xfffffffdU, + }; + + static inline bool isValidKey(KeyType key) { + return key <= kMaxId; + } + +protected: + IdToObjectMapBase(); + + ~IdToObjectMapBase(); + + void clear(); + + // Return size + inline size_t size() const { return mCount; } + + inline size_t capacity() const { return 1U << mShift; } + + // Return true iff the map contains a given key. + bool contains(KeyType key) const; + + // Find a value associated with a given |key| in the map. + // On success, return true and sets |*value| to the value/pointer, + // which is _still_ owned by the map. + // On failure, return false and sets |*value| to NULL. + bool find(KeyType key, void** value) const; + + // Associate a value with a given |key| in the map. + // Return the old value for the key, if any. Caller is responsible + // for freeing it. + void* set(KeyType key, void* value); + + // Remove the value associated with a given |key|. + // Return the old value, if any. Caller is responsible for + // freeing it. + void* remove(KeyType key); + + size_t mCount; + size_t mShift; + KeyType* mKeys; + void** mValues; + +private: + // Resize the map if needed to ensure it can hold at least |newSize| + // entries. + void resize(size_t newSize); +}; + +// A templated data container that acts as a dictionary mapping unsigned +// integer keys to heap-allocated objects of type T. The dictionary +// owns the objects associated with its keys, and automatically destroys +// them when it is destroyed, or during replacement or removal. +template +class IdToObjectMap : public IdToObjectMapBase { +public: + // Initialize an empty instance. + IdToObjectMap() : IdToObjectMapBase() {} + + // Destroy this instance. + ~IdToObjectMap() { + clear(); + } + + // Return the number of items in this map. + inline size_t size() const { return IdToObjectMapBase::size(); } + + // Return true iff the map is empty. + inline bool empty() const { return !IdToObjectMapBase::size(); } + + // Remove all items from the map. + void clear(); + + // Returns true iff the dictionary contains a value for |key|. + inline bool contains(KeyType key) const { + return IdToObjectMapBase::contains(key); + } + + // Find the value corresponding to |key| in this map. + // On success, return true, and sets |*value| to point to the + // value (still owned by the instance). On failure, return false. + inline bool find(KeyType key, T** value) const { + return IdToObjectMapBase::find(key, reinterpret_cast(value)); + } + + // Return the value associated with a given |key|, or NULL if it is + // not in the map. Result is still owned by the map. + inline T* get(KeyType key) const { + T* result = NULL; + this->find(key, &result); + return result; + } + + // Associate |value| with a given |key|. Returns true if a previous + // value was replaced, and false if this is the first time a value + // was associated with the given key. IMPORTANT: This transfers + // ownership of |value| to the map instance. In case of replacement, + // the old value is automatically destroyed. Using NULL as the value + // is equivalent to calling remove(). + bool set(KeyType key, T* value); + + // Remove any value associated with |key|. + // Return true iff a value was associated with the key and destroyed + // by this function, false if there was no value associated with the + // key (or if it was NULL). + bool remove(KeyType key); +}; + +template +void IdToObjectMap::clear() { + size_t n = capacity(); + while (n > 0) { + --n; + if (!isValidKey(mKeys[n])) + continue; + + delete static_cast(mValues[n]); + mValues[n] = NULL; + mKeys[n] = kMaxId + 1U; + } + mCount = 0; +} + +template +bool IdToObjectMap::set(KeyType key, T* value) { + T* oldValue = static_cast(IdToObjectMapBase::set(key, value)); + if (!oldValue) { + return false; + } + delete oldValue; + return true; +} + +template +bool IdToObjectMap::remove(KeyType key) { + T* oldValue = static_cast(IdToObjectMapBase::remove(key)); + if (!oldValue) + return false; + delete oldValue; + return true; +} + +} // namespace emugl + + +#endif // EMUGL_COMMON_ID_TO_OBJECT_MAP_H diff --git a/external/android-emugl/shared/emugl/common/id_to_object_map_unittest.cpp b/external/android-emugl/shared/emugl/common/id_to_object_map_unittest.cpp new file mode 100644 index 0000000..50740be --- /dev/null +++ b/external/android-emugl/shared/emugl/common/id_to_object_map_unittest.cpp @@ -0,0 +1,116 @@ +// Copyright (C) 2014 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "emugl/common/id_to_object_map.h" + +#include + +namespace emugl { + +namespace { + +typedef IdToObjectMapBase::KeyType KeyType; + +class Foo { +public: + Foo() : mVal(0) {} + Foo(int val) : mVal(val) {} + ~Foo() {} + int val() const { return mVal; } + void setVal(int val) { mVal = val; } +private: + int mVal; +}; + +} // namespace + +TEST(IdToObjectMap, Empty) { + IdToObjectMap map; + EXPECT_TRUE(map.empty()); + EXPECT_EQ(0U, map.size()); +} + +TEST(IdToObjectMap, SetIntegerRange) { + IdToObjectMap map; + KeyType kMax = 10000; + + // Add all items in the map. + for (KeyType n = 0; n < kMax; ++n) { + EXPECT_FALSE(map.set(n, new Foo(n))) << "For key " << n; + } + + // Check final size. + EXPECT_EQ(static_cast(kMax), map.size()); + + // Find all items in the map. + for (KeyType n = 0; n < kMax; ++n) { + EXPECT_TRUE(map.contains(n)) << "For key " << n; + Foo* foo = NULL; + EXPECT_TRUE(map.find(n, &foo)) << "For key " << n; + if (foo) { + EXPECT_EQ(static_cast(n), foo->val()) << "For key " << n; + } + } +} + +TEST(IdToObjectMap, RemoveAll) { + IdToObjectMap map; + KeyType kMax = 10000; + + // Add all items in the map. + for (KeyType n = 0; n < kMax; ++n) { + EXPECT_FALSE(map.set(n, new Foo(n))) << "For key " << n; + } + + EXPECT_EQ(static_cast(kMax), map.size()); + + for (KeyType n = 0; n < kMax; ++n) { + EXPECT_TRUE(map.remove(n)) << "For key " << n; + } + EXPECT_EQ(0U, map.size()); +} + +TEST(IdToObjectMap, RemoveOdd) { + IdToObjectMap map; + KeyType kMax = 10000; + + // Add all items in the map. + for (KeyType n = 0; n < kMax; ++n) { + EXPECT_FALSE(map.set(n, new Foo(n))) << "For key " << n; + } + + EXPECT_EQ(static_cast(kMax), map.size()); + + for (KeyType n = 0; n < kMax; ++n) { + if (n & 1) { + EXPECT_TRUE(map.remove(n)) << "For key " << n; + } + } + EXPECT_EQ(static_cast(kMax / 2), map.size()); + + for (KeyType n = 0; n < kMax; ++n) { + if (n & 1) { + EXPECT_FALSE(map.contains(n)) << "For key " << n; + } else { + EXPECT_TRUE(map.contains(n)) << "For key " << n; + Foo* foo = NULL; + EXPECT_TRUE(map.find(n, &foo)) << "For key " << n; + if (foo) { + EXPECT_EQ(static_cast(n), foo->val()); + } + } + } +} + +} // namespace emugl diff --git a/external/android-emugl/shared/emugl/common/lazy_instance.cpp b/external/android-emugl/shared/emugl/common/lazy_instance.cpp new file mode 100644 index 0000000..b1e6f1a --- /dev/null +++ b/external/android-emugl/shared/emugl/common/lazy_instance.cpp @@ -0,0 +1,101 @@ +// Copyright (C) 2014 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "emugl/common/lazy_instance.h" + +#ifdef _WIN32 +# define WIN32_LEAN_AND_MEAN 1 +# include +#else +# include +#endif + +namespace emugl { +namespace internal { + +typedef LazyInstanceState::AtomicType AtomicType; + +#if defined(__GNUC__) +static inline void compilerBarrier() { + __asm__ __volatile__ ("" : : : "memory"); +} +#else +#error "Your compiler is not supported" +#endif + +#if defined(__i386__) || defined(__x86_64__) || defined(__arm__) +# define acquireBarrier() compilerBarrier() +# define releaseBarrier() compilerBarrier() +#else +# error "Your CPU is not supported" +#endif + +static inline AtomicType loadAcquire(AtomicType volatile* ptr) { + AtomicType ret = *ptr; + acquireBarrier(); + return ret; +} + +static inline void storeRelease(AtomicType volatile* ptr, AtomicType value) { + releaseBarrier(); + *ptr = value; +} + +static int atomicCompareAndSwap(AtomicType volatile* ptr, + int expected, + int value) { +#ifdef _WIN32 + return InterlockedCompareExchange(ptr, value, expected); +#elif defined(__GNUC__) + return __sync_val_compare_and_swap(ptr, expected, value); +#else +#error "Your compiler is not supported" +#endif +} + +static void yieldThread() { +#ifdef _WIN32 + ::Sleep(0); +#else + sched_yield(); +#endif +} + +bool LazyInstanceState::inInitState() { + return loadAcquire(&mState) == STATE_INIT; +} + +bool LazyInstanceState::needConstruction() { + AtomicType state = loadAcquire(&mState); + if (mState == STATE_DONE) + return false; + + state = atomicCompareAndSwap(&mState, STATE_INIT, STATE_CONSTRUCTING); + if (state == STATE_INIT) + return true; + + do { + yieldThread(); + state = loadAcquire(&mState); + } while (state != STATE_DONE); + + return false; +} + +void LazyInstanceState::doneConstructing() { + storeRelease(&mState, STATE_DONE); +} + +} // namespace internal +} // namespace emugl diff --git a/external/android-emugl/shared/emugl/common/lazy_instance.h b/external/android-emugl/shared/emugl/common/lazy_instance.h new file mode 100644 index 0000000..6641c93 --- /dev/null +++ b/external/android-emugl/shared/emugl/common/lazy_instance.h @@ -0,0 +1,156 @@ +// Copyright (C) 2014 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef EMUGL_COMMON_LAZY_INSTANCE_H +#define EMUGL_COMMON_LAZY_INSTANCE_H + +#include + +#ifdef _WIN32 +# define WIN32_LEAN_AND_MEAN 1 +# include +#endif + +namespace emugl { +namespace internal { + +// A LazyInstance is a helper template that can be used to perform +// thread-safe lazy initialization of static C++ objects without forcing +// the generation of C++ static constructors in the final executable. +// +// In a nutshell, you can replace a statement like: +// +// static Foo gFoo; +// +// With: +// +// static LazyInstance gFoo = LAZY_INSTANCE_INIT; +// +// In the first case, a hidden static C++ constructor is embedded in the +// final executable, and executed at *load* *time* to call the Foo::Foo +// constructor on the gFoo object. +// +// On the second case, gFoo will only be initialized lazily, i.e. the first +// time any code actually tries to access the variable. +// +// Note that access is slightly different, i.e.: +// +// gFoo.get() returns a reference to the lazy-initialized object. +// gFoo.ptr() returns a pointer to it. +// gFoo->Something() is equivalent to doing gFoo.ptr()->Something(). +// +// 'gFoo' is stored in the .bss section and this doesn't use heap allocation. +// This class can only be used to perform lazy initialization through the +// class' default constructor. For more specialized cases, you will have +// to create a derived class, e.g.: +// +// class FoorWithDefaultParams : public Foo { +// public: +// FooWithDefaultParams() : Foo() {} +// }; +// +// LazyInstance gFoo = LAZY_INSTANCE_INIT; +// +// The implementation of LazyInstance relies on atomic operations and +// POD-struct class definitions, i.e. one that doesn't have any constructor, +// destructor, virtual members, or private ones, and that can be +// zero-initialized at link time. +// +// You can also use LazyInstance<> instances as static local variables, +// e.g.: +// +// Foo* getFooSingleton() { +// static LazyInstance sFoo = LAZY_INSTANCE_INIT; +// return sFoo.ptr(); +// } +// +// This is useful on Windows which doesn't support thread-safe lazy +// initialization of static C++ local variables, or when the code is +// compiled with -fno-threadsafe-statics. +// +// This class is heavily inspired by Chromium's implementation of the +// same-named class (see $CHROMIUM/src/base/lazy_instance.h). + +// Atomic state variable type. Used to ensure to synchronize concurrent +// initialization and access without incurring the full cost of a mutex +// lock/unlock. +struct LazyInstanceState { + enum { + STATE_INIT = 0, + STATE_CONSTRUCTING = 1, + STATE_DONE = 2, + }; + + bool inInitState(); + bool needConstruction(); + void doneConstructing(); + +#ifdef _WIN32 + typedef LONG volatile AtomicType; +#else + typedef int volatile AtomicType; +#endif + + volatile AtomicType mState; +}; + +#define LAZY_INSTANCE_STATE_INIT \ + { ::emugl::internal::LazyInstanceState::STATE_INIT } + +} // namespace internal + +// LazyInstance template definition, see comment above for usage +// instructions. It is crucial to make this a POD-struct compatible +// type [1]. +// +// [1] http://en.wikipedia.org/wiki/Plain_Old_Data_Structures +// +template +struct LazyInstance { + bool hasInstance() const { return !mState.inInitState(); } + + T& get() const { return *ptr(); } + + T* ptr() const; + + const T* operator->() const { return ptr(); } + + T* operator->() { return ptr(); } + + T& operator*() { return get(); } + + // Really private, do not use. + union { + mutable internal::LazyInstanceState mState; + double mPadding; + }; + mutable char mStorage[sizeof(T)]; +}; + +// Initialization value, must resolve to all-0 to ensure the object +// instance is actually placed in the .bss +#define LAZY_INSTANCE_INIT { { LAZY_INSTANCE_STATE_INIT }, { 0 } } + +template +T* LazyInstance::ptr() const { + if (mState.needConstruction()) { + new (mStorage) T(); + mState.doneConstructing(); + } + return reinterpret_cast(mStorage); +} + +} // namespace emugl + +#endif // EMUGL_COMMON_LAZY_INSTANCE_H diff --git a/external/android-emugl/shared/emugl/common/lazy_instance_unittest.cpp b/external/android-emugl/shared/emugl/common/lazy_instance_unittest.cpp new file mode 100644 index 0000000..824845f --- /dev/null +++ b/external/android-emugl/shared/emugl/common/lazy_instance_unittest.cpp @@ -0,0 +1,146 @@ +// Copyright (C) 2014 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "emugl/common/lazy_instance.h" + +#include "emugl/common/mutex.h" +#include "emugl/common/testing/test_thread.h" + +#include + +namespace emugl { + +namespace { + +class Foo { +public: + Foo() : mValue(42) {} + int get() const { return mValue; } + void set(int value) { mValue = value; } + ~Foo() { mValue = 13; } +private: + int mValue; +}; + +class StaticCounter { +public: + StaticCounter() { + Mutex::AutoLock lock(mMutex); + mCounter++; + } + + int getValue() const { + Mutex::AutoLock lock(mMutex); + return mCounter; + } + +private: + static Mutex mMutex; + static int mCounter; +}; + +// NOTE: This introduces a static C++ constructor for this object file, +// but that's ok because a LazyInstance should not be used to +// test the behaviour of LazyInstance :-) +Mutex StaticCounter::mMutex; +int StaticCounter::mCounter = 0; + +} // namespace + +TEST(LazyInstance, HasInstance) { + LazyInstance foo_instance = LAZY_INSTANCE_INIT; + EXPECT_FALSE(foo_instance.hasInstance()); + EXPECT_FALSE(foo_instance.hasInstance()); + foo_instance.ptr(); + EXPECT_TRUE(foo_instance.hasInstance()); +} + +TEST(LazyInstance, Simple) { + LazyInstance foo_instance = LAZY_INSTANCE_INIT; + Foo* foo1 = foo_instance.ptr(); + EXPECT_TRUE(foo1); + EXPECT_EQ(42, foo_instance->get()); + foo1->set(10); + EXPECT_EQ(10, foo_instance->get()); + EXPECT_EQ(foo1, foo_instance.ptr()); +} + +// For the following test, launch 1000 threads that each try to get +// the instance pointer of a lazy instance. Then verify that they're all +// the same value. +// +// The lazy instance has a special constructor that will increment a +// global counter. This allows us to ensure that it is only called once. +// + +namespace { + +// The following is the shared structure between all threads. +struct MultiState { + MultiState(LazyInstance* staticCounter) : + mMutex(), mStaticCounter(staticCounter), mCount(0) {} + + enum { + kMaxThreads = 1000, + }; + + Mutex mMutex; + LazyInstance* mStaticCounter; + size_t mCount; + void* mValues[kMaxThreads]; + TestThread* mThreads[kMaxThreads]; +}; + +// The thread function for the test below. +static void* threadFunc(void* param) { + MultiState* state = static_cast(param); + Mutex::AutoLock lock(state->mMutex); + if (state->mCount < MultiState::kMaxThreads) { + state->mValues[state->mCount++] = state->mStaticCounter->ptr(); + } + return NULL; +} + +} // namespace + +TEST(LazyInstance, MultipleThreads) { + LazyInstance counter_instance = LAZY_INSTANCE_INIT; + MultiState state(&counter_instance); + const size_t kNumThreads = MultiState::kMaxThreads; + + // Create all threads. + for (size_t n = 0; n < kNumThreads; ++n) { + state.mThreads[n] = new TestThread(threadFunc, &state); + } + + // Wait for their completion. + for (size_t n = 0; n < kNumThreads; ++n) { + state.mThreads[n]->join(); + } + + // Now check that the constructor was only called once. + EXPECT_EQ(1, counter_instance->getValue()); + + // Now compare all the store values, they should be the same. + StaticCounter* expectedValue = counter_instance.ptr(); + for (size_t n = 0; n < kNumThreads; ++n) { + EXPECT_EQ(expectedValue, state.mValues[n]) << "For thread " << n; + } + + for (size_t n = 0; n < kNumThreads; ++n) { + delete state.mThreads[n]; + } +} + +} // namespace emugl diff --git a/external/android-emugl/shared/emugl/common/logging.cpp b/external/android-emugl/shared/emugl/common/logging.cpp new file mode 100644 index 0000000..1f40ea8 --- /dev/null +++ b/external/android-emugl/shared/emugl/common/logging.cpp @@ -0,0 +1,38 @@ +/* +* Copyright (C) 2016 The Android Open Source Project +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +#include "emugl/common/logging.h" + +void default_logger(const char* fmt, ...) { } + +logger_t emugl_logger = default_logger; +logger_t emugl_cxt_logger = default_logger; + +void set_emugl_logger(logger_t f) { + if (!f) { + emugl_logger = default_logger; + } else { + emugl_logger = f; + } +} + +void set_emugl_cxt_logger(logger_t f) { + if (!f) { + emugl_cxt_logger = default_logger; + } else { + emugl_cxt_logger = f; + } +} diff --git a/external/android-emugl/shared/emugl/common/logging.h b/external/android-emugl/shared/emugl/common/logging.h new file mode 100644 index 0000000..8a6b413 --- /dev/null +++ b/external/android-emugl/shared/emugl/common/logging.h @@ -0,0 +1,37 @@ +/* +* Copyright (C) 2016 The Android Open Source Project +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +typedef void (*logger_t)(const char* fmt, ...); +extern logger_t emugl_logger; +extern logger_t emugl_cxt_logger; +void set_emugl_logger(logger_t f); +void set_emugl_cxt_logger(logger_t f); + +#define GL_LOGGING 1 + +#if GL_LOGGING + +#define GL_LOG(...) do { \ + emugl_logger(__VA_ARGS__); \ +} while (0) + +#define GL_CXT_LOG(...) do { \ + emugl_cxt_logger(__VA_ARGS__); \ +} while (0) + +#else +#define GL_LOG(...) 0 +#endif diff --git a/external/android-emugl/shared/emugl/common/message_channel.cpp b/external/android-emugl/shared/emugl/common/message_channel.cpp new file mode 100644 index 0000000..da4d711 --- /dev/null +++ b/external/android-emugl/shared/emugl/common/message_channel.cpp @@ -0,0 +1,64 @@ +// Copyright 2014 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "emugl/common/message_channel.h" + +namespace emugl { + +MessageChannelBase::MessageChannelBase(size_t capacity) : + mPos(0U), + mCount(0U), + mCapacity(capacity), + mLock(), + mCanRead(), + mCanWrite() {} + +MessageChannelBase::~MessageChannelBase() {} + +size_t MessageChannelBase::beforeWrite() { + mLock.lock(); + while (mCount >= mCapacity) { + mCanWrite.wait(&mLock); + } + size_t result = mPos + mCount; + if (result >= mCapacity) { + result -= mCapacity; + } + return result; +} + +void MessageChannelBase::afterWrite() { + mCount++; + mCanRead.signal(); + mLock.unlock(); +} + +size_t MessageChannelBase::beforeRead() { + mLock.lock(); + while (mCount == 0) { + mCanRead.wait(&mLock); + } + return mPos; +} + +void MessageChannelBase::afterRead() { + if (++mPos == mCapacity) { + mPos = 0U; + } + mCount--; + mCanWrite.signal(); + mLock.unlock(); +} + +} // namespace emugl diff --git a/external/android-emugl/shared/emugl/common/message_channel.h b/external/android-emugl/shared/emugl/common/message_channel.h new file mode 100644 index 0000000..3f463a9 --- /dev/null +++ b/external/android-emugl/shared/emugl/common/message_channel.h @@ -0,0 +1,98 @@ +// Copyright 2014 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef EMUGL_COMMON_MESSAGE_CHANNEL_H +#define EMUGL_COMMON_MESSAGE_CHANNEL_H + +#include "emugl/common/condition_variable.h" +#include "emugl/common/mutex.h" + +#include + +namespace emugl { + +// Base non-templated class used to reduce the amount of template +// specialization. +class MessageChannelBase { +public: + // Constructor. |capacity| is the buffer capacity in messages. + MessageChannelBase(size_t capacity); + + // Destructor. + ~MessageChannelBase(); + +protected: + // Call this method in the sender thread before writing a new message. + // This returns the position of the available slot in the message array + // where to copy the new fixed-size message. After the copy, call + // afterWrite(). + size_t beforeWrite(); + + // To be called after beforeWrite() and copying a new fixed-size message + // into the array. This signal the receiver thread that there is a new + // incoming message. + void afterWrite(); + + // Call this method in the receiver thread before reading a new message. + // This returns the position in the message array where the new message + // can be read. Caller must process the message, then call afterRead(). + size_t beforeRead(); + + // To be called in the receiver thread after beforeRead() and processing + // the corresponding message. + void afterRead(); + +private: + size_t mPos; + size_t mCount; + size_t mCapacity; + Mutex mLock; + ConditionVariable mCanRead; + ConditionVariable mCanWrite; +}; + +// Helper class used to implement an uni-directional IPC channel between +// two threads. The channel can be used to send fixed-size messages of type +// |T|, with an internal buffer size of |CAPACITY| items. All calls are +// blocking. +// +// Usage is pretty straightforward: +// +// - From the sender thread, call send(msg); +// - From the receiver thread, call receive(&msg); +// +template +class MessageChannel : public MessageChannelBase { +public: + MessageChannel() : MessageChannelBase(CAPACITY) {} + + void send(const T& msg) { + size_t pos = beforeWrite(); + mItems[pos] = msg; + afterWrite(); + } + + void receive(T* msg) { + size_t pos = beforeRead(); + *msg = mItems[pos]; + afterRead(); + } + +private: + T mItems[CAPACITY]; +}; + +} // namespace emugl + +#endif // EMUGL_COMMON_MESSAGE_CHANNEL_H diff --git a/external/android-emugl/shared/emugl/common/message_channel_unittest.cpp b/external/android-emugl/shared/emugl/common/message_channel_unittest.cpp new file mode 100644 index 0000000..2e9d55e --- /dev/null +++ b/external/android-emugl/shared/emugl/common/message_channel_unittest.cpp @@ -0,0 +1,101 @@ +// Copyright 2014 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "emugl/common/message_channel.h" + +#include "emugl/common/testing/test_thread.h" + +#include + +#include + +namespace emugl { + +namespace { + +struct PingPongState { + MessageChannel in; + MessageChannel out; +}; + +void* pingPongFunction(void* param) { + for (;;) { + PingPongState* s = static_cast(param); + std::string str; + s->in.receive(&str); + s->out.send(str); + if (str == "quit") { + break; + } + } + return 0; +} + +} // namespace + +TEST(MessageChannel, SingleThreadWithInt) { + MessageChannel channel; + channel.send(1); + channel.send(2); + channel.send(3); + + int ret; + channel.receive(&ret); + EXPECT_EQ(1, ret); + channel.receive(&ret); + EXPECT_EQ(2, ret); + channel.receive(&ret); + EXPECT_EQ(3, ret); +} + +TEST(MessageChannel, SingleThreadWithStdString) { + MessageChannel channel; + channel.send(std::string("foo")); + channel.send(std::string("bar")); + channel.send(std::string("zoo")); + + std::string str; + channel.receive(&str); + EXPECT_STREQ("foo", str.c_str()); + channel.receive(&str); + EXPECT_STREQ("bar", str.c_str()); + channel.receive(&str); + EXPECT_STREQ("zoo", str.c_str()); +} + +TEST(MessageChannel, TwoThreadsPingPong) { + PingPongState state; + TestThread* thread = new TestThread(pingPongFunction, &state); + + std::string str; + const size_t kCount = 100; + for (size_t n = 0; n < kCount; ++n) { + state.in.send(std::string("foo")); + state.in.send(std::string("bar")); + state.in.send(std::string("zoo")); + state.out.receive(&str); + EXPECT_STREQ("foo", str.c_str()); + state.out.receive(&str); + EXPECT_STREQ("bar", str.c_str()); + state.out.receive(&str); + EXPECT_STREQ("zoo", str.c_str()); + } + state.in.send(std::string("quit")); + state.out.receive(&str); + EXPECT_STREQ("quit", str.c_str()); + + thread->join(); +} + +} // namespace emugl diff --git a/external/android-emugl/shared/emugl/common/mutex.h b/external/android-emugl/shared/emugl/common/mutex.h new file mode 100644 index 0000000..6a161ae --- /dev/null +++ b/external/android-emugl/shared/emugl/common/mutex.h @@ -0,0 +1,95 @@ +// Copyright (C) 2014 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef EMUGL_MUTEX_H +#define EMUGL_MUTEX_H + +#ifdef _WIN32 +# define WIN32_LEAN_AND_MEAN 1 +# include +#else +# include +#endif + +namespace emugl { + +class ConditionVariable; + +// Simple wrapper class for mutexes. +class Mutex { +public: + // Constructor. + Mutex() { +#ifdef _WIN32 + ::InitializeCriticalSection(&mLock); +#else + ::pthread_mutex_init(&mLock, NULL); +#endif + } + + // Destructor. + ~Mutex() { +#ifdef _WIN32 + ::DeleteCriticalSection(&mLock); +#else + ::pthread_mutex_destroy(&mLock); +#endif + } + + // Acquire the mutex. + void lock() { +#ifdef _WIN32 + ::EnterCriticalSection(&mLock); +#else + ::pthread_mutex_lock(&mLock); +#endif + } + + // Release the mutex. + void unlock() { +#ifdef _WIN32 + ::LeaveCriticalSection(&mLock); +#else + ::pthread_mutex_unlock(&mLock); +#endif + } + + // Helper class to lock / unlock a mutex automatically on scope + // entry and exit. + class AutoLock { + public: + AutoLock(Mutex& mutex) : mMutex(&mutex) { + mMutex->lock(); + } + + ~AutoLock() { + mMutex->unlock(); + } + private: + Mutex* mMutex; + }; + +private: +#ifdef _WIN32 + CRITICAL_SECTION mLock; +#else + friend class ConditionVariable; + pthread_mutex_t mLock; +#endif + +}; + +} // namespace emugl + +#endif // EMUGL_MUTEX_H diff --git a/external/android-emugl/shared/emugl/common/mutex_unittest.cpp b/external/android-emugl/shared/emugl/common/mutex_unittest.cpp new file mode 100644 index 0000000..e952d75 --- /dev/null +++ b/external/android-emugl/shared/emugl/common/mutex_unittest.cpp @@ -0,0 +1,108 @@ +// Copyright (C) 2014 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "emugl/common/mutex.h" + +#include "emugl/common/testing/test_thread.h" + +#include + +namespace emugl { + +// Check that construction and destruction doesn't crash. +TEST(Mutex, ConstructionDestruction) { + Mutex lock; +} + +// Check that a simple lock + unlock works. +TEST(Mutex, LockUnlock) { + Mutex lock; + lock.lock(); + lock.unlock(); +} + +// Check that AutoLock compiles and doesn't crash. +TEST(Mutex, AutoLock) { + Mutex mutex; + Mutex::AutoLock lock(mutex); +} + +// Wrapper class for threads. Does not use emugl::Thread intentionally. +// Common data type used by the following tests below. +struct ThreadParams { + ThreadParams() : mutex(), counter(0) {} + + Mutex mutex; + int counter; +}; + +// This thread function uses Mutex::lock/unlock to synchronize a counter +// increment operation. +static void* threadFunction(void* param) { + ThreadParams* p = static_cast(param); + + p->mutex.lock(); + p->counter++; + p->mutex.unlock(); + return NULL; +} + +TEST(Mutex, Synchronization) { + const size_t kNumThreads = 2000; + TestThread* threads[kNumThreads]; + ThreadParams p; + + // Create and launch all threads. + for (size_t n = 0; n < kNumThreads; ++n) { + threads[n] = new TestThread(threadFunction, &p); + } + + // Wait until their completion. + for (size_t n = 0; n < kNumThreads; ++n) { + threads[n]->join(); + delete threads[n]; + } + + EXPECT_EQ(static_cast(kNumThreads), p.counter); +} + +// This thread function uses a Mutex::AutoLock to protect the counter. +static void* threadAutoLockFunction(void* param) { + ThreadParams* p = static_cast(param); + + Mutex::AutoLock lock(p->mutex); + p->counter++; + return NULL; +} + +TEST(Mutex, AutoLockSynchronization) { + const size_t kNumThreads = 2000; + TestThread* threads[kNumThreads]; + ThreadParams p; + + // Create and launch all threads. + for (size_t n = 0; n < kNumThreads; ++n) { + threads[n] = new TestThread(threadAutoLockFunction, &p); + } + + // Wait until their completion. + for (size_t n = 0; n < kNumThreads; ++n) { + threads[n]->join(); + delete threads[n]; + } + + EXPECT_EQ(static_cast(kNumThreads), p.counter); +} + +} // namespace emugl diff --git a/external/android-emugl/shared/emugl/common/pod_vector.cpp b/external/android-emugl/shared/emugl/common/pod_vector.cpp new file mode 100644 index 0000000..c8ec3a6 --- /dev/null +++ b/external/android-emugl/shared/emugl/common/pod_vector.cpp @@ -0,0 +1,150 @@ +// Copyright (C) 2014 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "emugl/common/pod_vector.h" + +#include +#include + +#define USE_MALLOC_USABLE_SIZE 0 + +namespace emugl { + +static inline void swapPointers(char** p1, char** p2) { + char* tmp = *p1; + *p1 = *p2; + *p2 = tmp; +} + +PodVectorBase::PodVectorBase(const PodVectorBase& other) { + initFrom(other.begin(), other.byteSize()); +} + +PodVectorBase& PodVectorBase::operator=(const PodVectorBase& other) { + initFrom(other.begin(), other.byteSize()); + return *this; +} + +PodVectorBase::~PodVectorBase() { + if (mBegin) { + // Sanity. + ::memset(mBegin, 0xee, byteSize()); + ::free(mBegin); + mBegin = NULL; + mEnd = NULL; + mLimit = NULL; + } +} + +void PodVectorBase::initFrom(const void* from, size_t fromLen) { + if (!fromLen || !from) { + mBegin = NULL; + mEnd = NULL; + mLimit = NULL; + } else { + mBegin = static_cast(::malloc(fromLen)); + mEnd = mLimit = mBegin + fromLen; + ::memcpy(mBegin, from, fromLen); + } +} + +void PodVectorBase::assignFrom(const PodVectorBase& other) { + resize(other.byteSize(), 1U); + ::memmove(begin(), other.begin(), byteSize()); +} + +void PodVectorBase::resize(size_t newSize, size_t itemSize) { + const size_t kMaxSize = maxItemCapacity(itemSize); + size_t oldCapacity = itemCapacity(itemSize); + const size_t kMinCapacity = 256 / itemSize; + + if (newSize < oldCapacity) { + // Only shrink if the new size is really small. + if (newSize < oldCapacity / 2 && oldCapacity > kMinCapacity) { + reserve(newSize, itemSize); + } + } else if (newSize > oldCapacity) { + size_t newCapacity = oldCapacity; + while (newCapacity < newSize) { + size_t newCapacity2 = newCapacity + (newCapacity >> 2) + 8; + if (newCapacity2 < newCapacity || newCapacity > kMaxSize) { + newCapacity = kMaxSize; + } else { + newCapacity = newCapacity2; + } + } + reserve(newCapacity, itemSize); + } + mEnd = mBegin + newSize * itemSize; +} + +void PodVectorBase::reserve(size_t newSize, size_t itemSize) { + if (newSize == 0) { + ::free(mBegin); + mBegin = NULL; + mEnd = NULL; + mLimit = NULL; + return; + } + + size_t oldByteSize = byteSize(); + size_t newByteCapacity = newSize * itemSize; + char* newBegin = static_cast(::realloc(mBegin, newByteCapacity)); + mBegin = newBegin; + mEnd = newBegin + oldByteSize; +#if USE_MALLOC_USABLE_SIZE + size_t usableSize = malloc_usable_size(mBegin); + if (usableSize > newByteCapacity) { + newByteCapacity = usableSize - (usableSize % itemSize); + } +#endif + mLimit = newBegin + newByteCapacity; + // Sanity. + if (newByteCapacity > oldByteSize) { + ::memset(mBegin + oldByteSize, 0, newByteCapacity - oldByteSize); + } +} + +void PodVectorBase::removeAt(size_t itemPos, size_t itemSize) { + size_t count = itemCount(itemSize); + if (itemPos < count) { + size_t pos = itemPos * itemSize; + ::memmove(mBegin + pos, + mBegin + pos + itemSize, + byteSize() - pos - itemSize); + resize(count - 1U, itemSize); + } +} + +void* PodVectorBase::insertAt(size_t itemPos, size_t itemSize) { + size_t count = this->itemCount(itemSize); + resize(count + 1, itemSize); + size_t pos = itemPos * itemSize; + if (itemPos < count) { + ::memmove(mBegin + pos + itemSize, + mBegin + pos, + count * itemSize - pos); + // Sanity to avoid copying pointers and other bad stuff. + ::memset(mBegin + pos, 0, itemSize); + } + return mBegin + pos; +} + +void PodVectorBase::swapAll(PodVectorBase* other) { + swapPointers(&mBegin, &other->mBegin); + swapPointers(&mEnd, &other->mEnd); + swapPointers(&mLimit, &other->mLimit); +} + +} // namespace emugl diff --git a/external/android-emugl/shared/emugl/common/pod_vector.h b/external/android-emugl/shared/emugl/common/pod_vector.h new file mode 100644 index 0000000..c4e184b --- /dev/null +++ b/external/android-emugl/shared/emugl/common/pod_vector.h @@ -0,0 +1,265 @@ +// Copyright (C) 2014 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef EMUGL_COMMON_POD_VECTOR_H +#define EMUGL_COMMON_POD_VECTOR_H + + +#include + +#ifndef __STDC_LIMIT_MACROS +#define __STDC_LIMIT_MACROS 1 +#endif +#ifndef __STDC_FORMAT_MACROS +#define __STDC_FORMAT_MACROS 1 +#endif +#include + +#ifndef SIZE_MAX +#error "You must define __STDC_LIMIT_MACROS before including " +#endif + +namespace emugl { + +// A PodVector is a templated vector-like type that is used to store +// POD-struct compatible items only. This allows the implementation to +// use ::memmove() to move items, and also malloc_usable_size() to +// determine the best capacity. +// +// std::vector<> is capable of doing this in theory, using horrible +// templating tricks that make error messages very difficult to +// understand. +// +// Note that a PodVector can be used to store items that contain pointers, +// as long as these do not point to items in the same container. +// +// The PodVector provides methods that also follow the std::vector<> +// conventions, i.e. push_back() is an alias for append(). +// + +// NOTE: This is a re-implementation of +// external/qemu/android/base/containers/PodVector.h for emugl. + +// PodVectorBase is a base, non-templated, implementation class that all +// PodVector instances derive from. This is used to reduce template +// specialization. Do not use directly, i..e it's an implementation detail. +class PodVectorBase { +protected: + PodVectorBase() : mBegin(NULL), mEnd(NULL), mLimit(NULL) {} + explicit PodVectorBase(const PodVectorBase& other); + PodVectorBase& operator=(const PodVectorBase& other); + ~PodVectorBase(); + + bool empty() const { return mEnd == mBegin; } + + size_t byteSize() const { return mEnd - mBegin; } + + size_t byteCapacity() const { return mLimit - mBegin; } + + void* begin() { return mBegin; } + const void* begin() const { return mBegin; } + void* end() { return mEnd; } + const void* end() const { return mEnd; } + + void* itemAt(size_t pos, size_t itemSize) { + return mBegin + pos * itemSize; + } + + const void* itemAt(size_t pos, size_t itemSize) const { + return mBegin + pos * itemSize; + } + + void assignFrom(const PodVectorBase& other); + + inline size_t itemCount(size_t itemSize) const { + return byteSize() / itemSize; + } + + inline size_t itemCapacity(size_t itemSize) const { + return byteCapacity() / itemSize; + } + + inline size_t maxItemCapacity(size_t itemSize) const { + return SIZE_MAX / itemSize; + } + + void resize(size_t newSize, size_t itemSize); + void reserve(size_t newSize, size_t itemSize); + + void removeAt(size_t index, size_t itemSize); + void* insertAt(size_t index, size_t itemSize); + void swapAll(PodVectorBase* other); + + char* mBegin; + char* mEnd; + char* mLimit; + +private: + void initFrom(const void* from, size_t fromLen); +}; + + +// A PodVector holds a vector (dynamically resizable array) or items +// that must be POD-struct compatible (i.e. they cannot have constructors, +// destructors, or virtual members). This allows the implementation to be +// small, fast and efficient, memory-wise. +// +// If you want to implement a vector of C++ objects, consider using +// std::vector<> instead, but keep in mind that this implies a non-trivial +// cost when appending, inserting, removing items in the collection. +// +template +class PodVector : public PodVectorBase { +public: + // Default constructor for an empty PodVector + PodVector() : PodVectorBase() {} + + // Copy constructor. This copies all items from |other| into + // the new instance with ::memmove(). + PodVector(const PodVector& other) : PodVectorBase(other) {} + + // Assignment operator. + PodVector& operator=(const PodVector& other) { + this->assignFrom(other); + return *this; + } + + // Destructor, this simply releases the internal storage block that + // holds all the items, but doesn't touch them otherwise. + ~PodVector() {} + + // Return true iff the PodVector instance is empty, i.e. does not + // have any items. + bool empty() const { return PodVectorBase::empty(); } + + // Return the number of items in the current PodVector instance. + size_t size() const { return PodVectorBase::itemCount(sizeof(T)); } + + // Return the current capacity in the current PodVector instance. + // Do not use directly, except if you know what you're doing. Try to + // use resize() or reserve() instead. + size_t capacity() const { + return PodVectorBase::itemCapacity(sizeof(T)); + } + + // Return the maximum capacity of any PodVector instance. + static inline size_t maxCapacity() { return SIZE_MAX / sizeof(T); } + + // Resize the vector to ensure it can hold |newSize| + // items. This may or may not call reserve() under the hood. + // It's a fatal error to try to resize above maxCapacity(). + void resize(size_t newSize) { + PodVectorBase::resize(newSize, sizeof(T)); + } + + // Resize the vector's storage array to ensure that it can hold at + // least |newSize| items. It's a fatal error to try to resize above + // maxCapacity(). + void reserve(size_t newSize) { + PodVectorBase::reserve(newSize, sizeof(T)); + } + + // Return a pointer to the first item in the vector. This is only + // valid until the next call to any function that changes the size + // or capacity of the vector. Can be NULL if the vector is empty. + T* begin() { + return reinterpret_cast(PodVectorBase::begin()); + } + + // Return a constant pointer to the first item in the vector. This is + // only valid until the next call to any function that changes the + // size of capacity of the vector. + const T* begin() const { + return reinterpret_cast(PodVectorBase::begin()); + } + + // Return a pointer past the last item in the vector. I.e. if the + // result is not NULL, then |result - 1| points to the last item. + // Can be NULL if the vector is empty. + T* end() { + return reinterpret_cast(PodVectorBase::end()); + } + + // Return a constant pointer past the last item in the vector. I.e. if + // the result is not NULL, then |result - 1| points to the last item. + // Can be NULL if the vector is empty. + const T* end() const { + return reinterpret_cast(PodVectorBase::end()); + } + + // Returns a reference to the item a position |index| in the vector. + // It may be a fatal error to access an out-of-bounds position. + T& operator[](size_t index) { + return *reinterpret_cast( + PodVectorBase::itemAt(index, sizeof(T))); + } + + const T& operator[](size_t index) const { + return *reinterpret_cast( + PodVectorBase::itemAt(index, sizeof(T))); + } + + // Increase the vector's size by 1, then move all items past a given + // position to the right. Return the position of the insertion point + // to let the caller copy the content it desires there. It's preferrable + // to use insert() directly, which will do the item copy for you. + T* emplace(size_t index) { + return reinterpret_cast( + PodVectorBase::insertAt(index, sizeof(T))); + } + + // Insert an item at a given position. |index| is the insertion position + // which must be between 0 and size() included, or a fatal error may + // occur. |item| is a reference to an item to copy into the array, + // byte-by-byte. + void insert(size_t index, const T& item) { + *(this->emplace(index)) = item; + } + + // Prepend an item at the start of a vector. This moves all vector items + // to the right, and is thus costly. |item| is a reference to an item + // to copy to the start of the vector. + void prepend(const T& item) { + *(this->emplace(0U)) = item; + } + + // Append an item at the end of a vector. Specialized version of insert() + // which always uses size() as the insertion position. + void append(const T& item) { + *(this->emplace(this->size())) = item; + } + + // Remove the item at a given position. |index| is the position of the + // item to delete. It must be between 0 and size(), included, or + // a fatal error may occur. Deleting the item at position size() + // doesn't do anything. + void remove(size_t index) { + PodVectorBase::removeAt(index, sizeof(T)); + } + + void swap(PodVector* other) { + PodVectorBase::swapAll(other); + } + + // Compatibility methods for std::vector<> + void push_back(const T& item) { append(item); } + void pop() { remove(0U); } + + typedef T* iterator; + typedef const T* const_iterator; +}; + +} // namespace emugl + +#endif // EMUGL_COMMON_POD_VECTOR_H diff --git a/external/android-emugl/shared/emugl/common/pod_vector_unittest.cpp b/external/android-emugl/shared/emugl/common/pod_vector_unittest.cpp new file mode 100644 index 0000000..9ce509a --- /dev/null +++ b/external/android-emugl/shared/emugl/common/pod_vector_unittest.cpp @@ -0,0 +1,127 @@ +// Copyright (C) 2014 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "emugl/common/pod_vector.h" + +#include + +namespace emugl { + +static int hashIndex(size_t n) { + return static_cast(((n >> 14) * 13773) + (n * 51)); +} + +TEST(PodVector, Empty) { + PodVector v; + EXPECT_TRUE(v.empty()); + EXPECT_EQ(0U, v.size()); +} + +TEST(PodVector, AppendOneItem) { + PodVector v; + v.append(10234); + EXPECT_FALSE(v.empty()); + EXPECT_EQ(1U, v.size()); + EXPECT_EQ(10234, v[0]); +} + +TEST(PodVector, AppendLotsOfItems) { + PodVector v; + const size_t kMaxCount = 10000; + for (size_t n = 0; n < kMaxCount; ++n) { + v.append(hashIndex(n)); + } + EXPECT_EQ(kMaxCount, v.size()); + for (size_t n = 0; n < kMaxCount; ++n) { + EXPECT_EQ(hashIndex(n), v[n]) << "At index " << n; + } +} + +TEST(PodVector, RemoveFrontItems) { + PodVector v; + const size_t kMaxCount = 100; + for (size_t n = 0; n < kMaxCount; ++n) { + v.append(hashIndex(n)); + } + EXPECT_EQ(kMaxCount, v.size()); + for (size_t n = 0; n < kMaxCount; ++n) { + EXPECT_EQ(hashIndex(n), v[0]) << "At index " << n; + v.remove(0U); + EXPECT_EQ(kMaxCount - n - 1U, v.size()) << "At index " << n; + } +} + +TEST(PodVector, PrependItems) { + PodVector v; + const size_t kMaxCount = 100; + for (size_t n = 0; n < kMaxCount; ++n) { + v.prepend(hashIndex(n)); + } + EXPECT_EQ(kMaxCount, v.size()); + for (size_t n = 0; n < kMaxCount; ++n) { + EXPECT_EQ(hashIndex(kMaxCount - n - 1), v[n]) << "At index " << n; + } +} + +TEST(PodVector, ResizeExpands) { + PodVector v; + const size_t kMaxCount = 100; + const size_t kMaxCount2 = 10000; + for (size_t n = 0; n < kMaxCount; ++n) { + v.append(hashIndex(n)); + } + EXPECT_EQ(kMaxCount, v.size()); + v.resize(kMaxCount2); + EXPECT_EQ(kMaxCount2, v.size()); + for (size_t n = 0; n < kMaxCount; ++n) { + EXPECT_EQ(hashIndex(n), v[n]) << "At index " << n; + } +} + +TEST(PodVector, ResizeTruncates) { + PodVector v; + const size_t kMaxCount = 10000; + const size_t kMaxCount2 = 10; + for (size_t n = 0; n < kMaxCount; ++n) { + v.append(hashIndex(n)); + } + EXPECT_EQ(kMaxCount, v.size()); + v.resize(kMaxCount2); + EXPECT_EQ(kMaxCount2, v.size()); + for (size_t n = 0; n < kMaxCount2; ++n) { + EXPECT_EQ(hashIndex(n), v[n]) << "At index " << n; + } +} + + +TEST(PodVector, AssignmentOperator) { + PodVector v1; + const size_t kMaxCount = 10000; + for (size_t n = 0; n < kMaxCount; ++n) { + v1.append(hashIndex(n)); + } + EXPECT_EQ(kMaxCount, v1.size()); + + PodVector v2; + v2 = v1; + + v1.reserve(0); + + EXPECT_EQ(kMaxCount, v2.size()); + for (size_t n = 0; n < kMaxCount; ++n) { + EXPECT_EQ(hashIndex(n), v2[n]) << "At index " << n; + } +} + +} // namespace emugl diff --git a/external/android-emugl/shared/emugl/common/scoped_pointer_vector.h b/external/android-emugl/shared/emugl/common/scoped_pointer_vector.h new file mode 100644 index 0000000..3d263e9 --- /dev/null +++ b/external/android-emugl/shared/emugl/common/scoped_pointer_vector.h @@ -0,0 +1,27 @@ +// Copyright (C) 2014 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef EMUGL_COMMON_SCOPED_POINTER_VECTOR_H +#define EMUGL_COMMON_SCOPED_POINTER_VECTOR_H + +namespace emugl { + +template +class ScopedPointerVector { + ScopedPointerVector +}; + +} // namespace emugl + +#endif // EMUGL_COMMON_SCOPED_POINTER_VECTOR_H diff --git a/external/android-emugl/shared/emugl/common/shared_library.cpp b/external/android-emugl/shared/emugl/common/shared_library.cpp new file mode 100644 index 0000000..86622f1 --- /dev/null +++ b/external/android-emugl/shared/emugl/common/shared_library.cpp @@ -0,0 +1,169 @@ +// Copyright (C) 2014 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "emugl/common/shared_library.h" + +#include +#include +#include + +#ifndef _WIN32 +#include +#include +#endif + +namespace emugl { + +// static +SharedLibrary* SharedLibrary::open(const char* libraryName) { + char error[1]; + return open(libraryName, error, sizeof(error)); +} + +#ifdef _WIN32 + +// static +SharedLibrary* SharedLibrary::open(const char* libraryName, + char* error, + size_t errorSize) { + HMODULE lib = LoadLibrary(libraryName); + if (lib) { + return new SharedLibrary(lib); + } + + if (errorSize == 0) { + return NULL; + } + + // Convert error into human-readable message. + DWORD errorCode = ::GetLastError(); + LPSTR message = NULL; + size_t messageLen = FormatMessageA( + FORMAT_MESSAGE_ALLOCATE_BUFFER | + FORMAT_MESSAGE_FROM_SYSTEM | + FORMAT_MESSAGE_IGNORE_INSERTS, + NULL, + errorCode, + MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), + (LPSTR) &message, + 0, + NULL); + + int ret = snprintf(error, errorSize, "%.*s", (int)messageLen, message); + if (ret < 0 || ret == static_cast(errorSize)) { + // snprintf() on Windows doesn't behave as expected by C99, + // this path is to ensure that the result is always properly + // zero-terminated. + ret = static_cast(errorSize - 1); + error[ret] = '\0'; + } + // Remove any trailing \r\n added by FormatMessage + if (ret > 0 && error[ret - 1] == '\n') { + error[--ret] = '\0'; + } + if (ret > 0 && error[ret - 1] == '\r') { + error[--ret] = '\0'; + } + return NULL; +} + +SharedLibrary::SharedLibrary(HandleType lib) : mLib(lib) {} + +SharedLibrary::~SharedLibrary() { + if (mLib) { + FreeLibrary(mLib); + } +} + +SharedLibrary::FunctionPtr SharedLibrary::findSymbol( + const char* symbolName) const { + if (!mLib || !symbolName) { + return NULL; + } + return reinterpret_cast( + GetProcAddress(mLib, symbolName)); +} + +#else // !_WIN32 + +// static +SharedLibrary* SharedLibrary::open(const char* libraryName, + char* error, + size_t errorSize) { + const char* libPath = libraryName; + char* path = NULL; + + const char* libBaseName = strrchr(libraryName, '/'); + if (!libBaseName) { + libBaseName = libraryName; + } + + if (!strchr(libBaseName, '.')) { + // There is no extension in this library name, so append one. +#ifdef __APPLE__ + static const char kDllExtension[] = ".dylib"; +#else + static const char kDllExtension[] = ".so"; +#endif + size_t pathLen = strlen(libraryName) + sizeof(kDllExtension); + path = static_cast(malloc(pathLen)); + snprintf(path, pathLen, "%s%s", libraryName, kDllExtension); + libPath = path; + } + + dlerror(); // clear error. + +#ifdef __APPLE__ + // On OSX, some libraries don't include an extension (notably OpenGL) + // On OSX we try to open |libraryName| first. If that doesn't exist, + // we try |libraryName|.dylib + void* lib = dlopen(libraryName, RTLD_NOW); + if (lib == NULL) { + lib = dlopen(libPath, RTLD_NOW); + } +#else + void* lib = dlopen(libPath, RTLD_NOW); +#endif + + if (path) { + free(path); + } + + if (lib) { + return new SharedLibrary(lib); + } + + snprintf(error, errorSize, "%s", dlerror()); + return NULL; +} + +SharedLibrary::SharedLibrary(HandleType lib) : mLib(lib) {} + +SharedLibrary::~SharedLibrary() { + if (mLib) { + dlclose(mLib); + } +} + +SharedLibrary::FunctionPtr SharedLibrary::findSymbol( + const char* symbolName) const { + if (!mLib || !symbolName) { + return NULL; + } + return reinterpret_cast(dlsym(mLib, symbolName)); +} + +#endif // !_WIN32 + +} // namespace emugl diff --git a/external/android-emugl/shared/emugl/common/shared_library.h b/external/android-emugl/shared/emugl/common/shared_library.h new file mode 100644 index 0000000..62d1f0b --- /dev/null +++ b/external/android-emugl/shared/emugl/common/shared_library.h @@ -0,0 +1,106 @@ +// Copyright (C) 2014 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef EMUGL_COMMON_SHARED_LIBRARY_H +#define EMUGL_COMMON_SHARED_LIBRARY_H + +#include + +#ifdef _WIN32 +#include +#endif + +namespace emugl { + +// A class used to open a platform-specific shared library, and probe +// it for symbols. Usage is the following: +// +// // Open the library. +// SharedLibrary* library = SharedLibrary::open("libFoo"); +// if (!library) { +// ... could not find / open library! +// } +// +// //Probe for function symbol. +// FunctionPtr my_func = library->findSymbol("my_func"); +// +// // Closes library/ +// delete library; +// +class SharedLibrary { +public: + // Open a given library. If |libraryName| has no extension, a + // platform-appropriate extension is added and that path is opened. + // If the |libraryName| has an extension, that form is opened. + // + // On OSX, some libraries don't include an extension (notably OpenGL) + // On OSX we try to open |libraryName| first. If that doesn't exist, + // we try |libraryName|.dylib + // + // On success, returns a new SharedLibrary instance that must be + // deleted by the caller. + static SharedLibrary* open(const char* libraryName); + + // A variant of open() that can report a human-readable error if loading + // the library fails. |error| is a caller-provided buffer of |errorSize| + // bytes that will be filled with snprintf() and always zero terminated. + // + // On success, return a new SharedLibrary instance, and do not touch + // the content of |error|. On failure, return NULL, and sets the content + // of |error|. + static SharedLibrary* open(const char* libraryName, + char* error, + size_t errorSize); + + // Closes an existing SharedLibrary instance. + ~SharedLibrary(); + + // Generic function pointer type, for values returned by the + // findSymbol() method. + typedef void (*FunctionPtr)(void); + + // Probe a given SharedLibrary instance to find a symbol named + // |symbolName| in it. Return its address as a FunctionPtr, or + // NULL if the symbol is not found. + FunctionPtr findSymbol(const char* symbolName) const; + +private: +#ifdef _WIN32 + typedef HMODULE HandleType; +#else + typedef void* HandleType; +#endif + + // Constructor intentionally hidden. + SharedLibrary(HandleType); + + HandleType mLib; +}; + +// Macro to compose emugl shared library name under various OS and bitness +// eg. +// on x86_64, EMUGL_LIBNAME("foo") --> "lib64foo" + +#if defined(__x86_64__) +# define EMUGL_LIBNAME(name) "lib64" name +#elif defined(__i386__) +# define EMUGL_LIBNAME(name) "lib" name +#else +/* This header is included by target w/o using EMUGL_LIBNAME(). Don't #error, leave it undefined */ +# define EMUGL_LIBNAME(name) "lib" name +#endif + +} // namespace emugl + +#endif // EMUGL_COMMON_SHARED_LIBRARY_H diff --git a/external/android-emugl/shared/emugl/common/shared_library_unittest.cpp b/external/android-emugl/shared/emugl/common/shared_library_unittest.cpp new file mode 100644 index 0000000..c946cca --- /dev/null +++ b/external/android-emugl/shared/emugl/common/shared_library_unittest.cpp @@ -0,0 +1,177 @@ +// Copyright (C) 2014 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "emugl/common/shared_library.h" + +#include + +#include + +#include +#include + +// Hack to get the current executable's full path. +namespace testing { +namespace internal { + +extern std::string g_executable_path; + +} // namespace internal +} // namespace testing + +namespace emugl { + +namespace { + +// Return the name/path of the test shared library to load. +// Note that this doesn't include a platform-specific extension. +// This assumes that the test shared library is under the lib/ sub-directory +// of the current executable's path! +std::string GetTestLibraryName() { +#ifdef __x86_64__ + static const char kLibraryPrefix[] = "lib64"; + static const char kSubDir[] = "lib64/"; +#else + static const char kLibraryPrefix[] = "lib"; + static const char kSubDir[] = "lib/"; +#endif + static const char kTestLibrarySuffix[] = "emugl_test_shared_library"; + + const char* exec_path = testing::internal::g_executable_path.c_str(); + +#ifdef _WIN32 + const char* p = strrchr(exec_path, '/'); + const char* p2 = strrchr(exec_path, '\\'); + if (p2) { + if (!p || p2 > p) { + p = p2; + } + } +#else + const char* p = strrchr(exec_path, '/'); +#endif + + std::string path; + + if (!p) { + path = "./"; + } else { + path = std::string(exec_path, p - exec_path + 1U); + } + path += kSubDir; + path += kLibraryPrefix; + path += kTestLibrarySuffix; + printf("Library path: %s\n", path.c_str()); + return path; +} + +class SharedLibraryTest : public testing::Test { +public: + SharedLibraryTest() { + // Locate the shared library + mLibraryPath = GetTestLibraryName(); + } + + ~SharedLibraryTest() {} + + const char* library_path() const { return mLibraryPath.c_str(); } + +private: + std::string mLibraryPath; +}; + +class ScopedSharedLibrary { +public: + explicit ScopedSharedLibrary(const SharedLibrary* lib) : mLib(lib) {} + ~ScopedSharedLibrary() { + delete mLib; + } + const SharedLibrary* get() const { return mLib; } + + const SharedLibrary* operator->() { return mLib; } + + void release() { + delete mLib; + mLib = NULL; + } + +private: + const SharedLibrary* mLib; +}; + +} // namespace + +TEST_F(SharedLibraryTest, Open) { + ScopedSharedLibrary lib(SharedLibrary::open(library_path())); + EXPECT_TRUE(lib.get()); +} + +TEST_F(SharedLibraryTest, OpenFailureWithError) { + char error[256]; + error[0] = '\0'; + SharedLibrary* lib = SharedLibrary::open("/tmp/does/not/exists", + error, + sizeof(error)); + EXPECT_FALSE(lib); + EXPECT_TRUE(error[0]) + << "Could not get error string when failing to load library"; + printf("Expected library load failure: [%s]\n", error); +} + +TEST_F(SharedLibraryTest, OpenLibraryWithExtension) { + std::string path = library_path(); + + // test extension append + ScopedSharedLibrary libNoExtension(SharedLibrary::open(path.c_str())); + EXPECT_TRUE(libNoExtension.get()); + libNoExtension.release(); + +#ifdef _WIN32 + path += ".dll"; +#elif defined(__APPLE__) + // try to open the library without an extension + + path += ".dylib"; +#else + path += ".so"; +#endif + + // test open with prepended extension + ScopedSharedLibrary lib(SharedLibrary::open(path.c_str())); + EXPECT_TRUE(lib.get()); +} + +#ifdef __APPLE__ +TEST_F(SharedLibraryTest, OpenLibraryWithoutExtension) { + const char* library = "/System/Library/Frameworks/OpenGL.framework/OpenGL"; + ScopedSharedLibrary lib(SharedLibrary::open(library)); + EXPECT_TRUE(lib.get()); +} +#endif + +TEST_F(SharedLibraryTest, FindSymbol) { + ScopedSharedLibrary lib(SharedLibrary::open(library_path())); + EXPECT_TRUE(lib.get()); + + if (lib.get()) { + typedef int (*FooFunction)(void); + + FooFunction foo_func = reinterpret_cast( + lib->findSymbol("foo_function")); + EXPECT_TRUE(foo_func); + EXPECT_EQ(42, (*foo_func)()); + } +} + +} // namespace emugl diff --git a/external/android-emugl/shared/emugl/common/smart_ptr.cpp b/external/android-emugl/shared/emugl/common/smart_ptr.cpp new file mode 100644 index 0000000..703487d --- /dev/null +++ b/external/android-emugl/shared/emugl/common/smart_ptr.cpp @@ -0,0 +1,113 @@ +// Copyright (C) 2014 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "emugl/common/smart_ptr.h" + +#ifdef _WIN32 +#define WIN32_LEAN_AND_MEAN +#include +#endif + +namespace emugl { + +// Thread-safe atomic reference-counting type. +class RefCount { +public: + RefCount() : mCount(1) {} + ~RefCount() {} + + // Technically not thread-safe, use only for testing. + int count() const { return (int)mCount; } + + void increment() { +#ifdef _WIN32 + InterlockedIncrement(&mCount); +#else + __sync_fetch_and_add(&mCount, 1); +#endif + } + + bool decrement() { +#ifdef _WIN32 + return InterlockedDecrement(&mCount) == 0; +#else + return __sync_add_and_fetch(&mCount, -1) == 0; +#endif + } + +private: +#ifdef _WIN32 + LONG mCount; +#else + int mCount; +#endif +}; + + +// SmartPtrBase implementation. + +SmartPtrBase::SmartPtrBase(void* ptr) : mPtr(ptr), mRefCount(NULL) { + if (mPtr) + mRefCount = new RefCount(); +} + + +SmartPtrBase::SmartPtrBase(const SmartPtrBase& other) + : mPtr(other.mPtr), mRefCount(other.mRefCount) { + if (mRefCount) + mRefCount->increment(); +} + + +int SmartPtrBase::getRefCount() const { + return mRefCount ? mRefCount->count() : 0; +} + + +void SmartPtrBase::addRef() { + if (mRefCount) + mRefCount->increment(); +} + + +void* SmartPtrBase::copyFrom(const SmartPtrBase& other) { + void* old_ptr = release(); + + mPtr = other.mPtr; + mRefCount = other.mRefCount; + if (mRefCount) + mRefCount->increment(); + + return old_ptr; +} + + +void* SmartPtrBase::release() { + void* old_ptr = mPtr; + RefCount* old_refcount = mRefCount; + + if (old_refcount) { + mPtr = NULL; + mRefCount = NULL; + + if (old_refcount->decrement()) { + delete old_refcount; + return old_ptr; + } + } + + return NULL; +} + +} // namespace emugl diff --git a/external/android-emugl/shared/emugl/common/smart_ptr.h b/external/android-emugl/shared/emugl/common/smart_ptr.h new file mode 100644 index 0000000..73efdd6 --- /dev/null +++ b/external/android-emugl/shared/emugl/common/smart_ptr.h @@ -0,0 +1,150 @@ +// Copyright (C) 2014 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +#ifndef EMUGL_SMART_PTR_H +#define EMUGL_SMART_PTR_H + +#include + +namespace emugl { + +// Hidden atomic ref-counting implementation. +class RefCount; + +// Base class for all templated SmartPtr<> instances. Reduces +// template expansion and code. Consider this to be an implementation +// detail of SmartPtr<>, so don't rely on anything here. +class SmartPtrBase { +public: + // Defrault constructor. + SmartPtrBase() : mPtr(NULL), mRefCount(NULL) {} + + // Normal constructor. This takes ownership of |ptr|, though only + // template instances are capable of destroying the object. + explicit SmartPtrBase(void* ptr); + + // Copy-constructor, this increments the reference count. + SmartPtrBase(const SmartPtrBase& other); + + // Assignment operator, also increments the reference count. + SmartPtrBase& operator=(const SmartPtrBase& other); + + // Nothing happens in this destructor, the real work must be performed + // in subclasses. + ~SmartPtrBase() {} + + + // Used to enable 'if (smart_ptr) { ... }' properly. + operator void*() const { + return mPtr; + } + + // Return internal reference count value, only use for unit testing. + int getRefCount() const; + +protected: + // Used internally to increment the reference count. + void addRef(); + + // Copy the |other| into this instance, returns the old pointer value + // if it needs to be destroyed by the caller, or NULL otherwise. + void* copyFrom(const SmartPtrBase& other); + + // Used internally to decrement the reference count, if it reaches 0, + // returns the pointer to be destroyed, NULL otherwise. + void* release(); + + void* mPtr; + RefCount* mRefCount; +}; + + +// The real template class to be used for smart pointers. +// Typical uses: +// +// SmartPtr ptr(new Foo()); // takes ownership. +// SmartPtr ptr2; // empty pointer. +// ptr2 = ptr; // copies pointer + increment reference count. +// Foo* obj = ptr.Ptr(); // access pointed object. +// ptr->DoStuff(); // operate directly on pointed object. +// (*ptr)->DoStuff(); // same here. +// +// On scope exit, the internal reference count is decremented and the +// object is deleted automatically when it reaches 0, indicating that +// there are no more owners. +// +// IMPORTANT: You need to be sure that only one 'chain' of smart pointers +// own a given object. I.e. the following is incorrect: +// +// Foo* foo = new Foo(); // create new instance. +// SmartPtr ptr(foo); // |ptr| takes ownership of |foo|. +// SmartPtr ptr2(foo); // |ptr2| takes also ownership of |foo|. +// +// The problem is that |ptr| and |ptr2| don't know anything about each +// other, and will not share the same reference count. Once a smart pointer +// owns an object, only use other smart pointers that are copy-constructed +// or assigned with the initial one to keep everything consistent. +template +class SmartPtr : public emugl::SmartPtrBase { +public: + // Default constructor. The instance holds a NULL pointer. + SmartPtr() : SmartPtrBase() {} + + // Regular constructor, takes ownership of |ptr|. + explicit SmartPtr(T* ptr) : SmartPtrBase(ptr) {} + + // Copy-constructor, |this| and |other| will share the same internal + // reference count, which is incremented by 1. + SmartPtr(const SmartPtr& other) + : SmartPtrBase(reinterpret_cast(other)) {} + + // Assignment operator, same semantics as copy-constructor. + SmartPtr& operator=(const SmartPtr& other) { + void* old_ptr = copyFrom(static_cast(other)); + if (old_ptr) + delete reinterpret_cast(old_ptr); + return *this; + } + + // Destructor, decrements reference count and destroys the object + // if it reaches 0 (indicating this was the last owning smart pointer). + ~SmartPtr() { + void* ptr = release(); + if (ptr) + delete reinterpret_cast(ptr); + } + + // Return owned object instance, or NULL. + T* Ptr() const { + return reinterpret_cast(mPtr); + } + + // Return owned object instance, or NULL + const T* constPtr() const { + return reinterpret_cast(mPtr); + } + + // Operate directly on owned object. + T* operator->() const { + return Ptr(); + } + + // Return reference to owned object. + T& operator*() const { + return *Ptr(); + } +}; + +} // namespace emugl + +#endif // EMUGL_SMART_PTR_H diff --git a/external/android-emugl/shared/emugl/common/smart_ptr_unittest.cpp b/external/android-emugl/shared/emugl/common/smart_ptr_unittest.cpp new file mode 100644 index 0000000..db9e5f2 --- /dev/null +++ b/external/android-emugl/shared/emugl/common/smart_ptr_unittest.cpp @@ -0,0 +1,140 @@ +// Copyright (C) 2014 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "emugl/common/smart_ptr.h" + +#include + +namespace emugl { + +// This Test sub-class is used to track allocations and deallocations of +// the MyTestClass instances that are created through newInstance(). +// See below for typical usage. +class SmartPtrTest : public testing::Test { +public: + SmartPtrTest() : mNewCount(0), mDeleteCount(0), mDoCount(0) {} + + ~SmartPtrTest() { + mNewCount = 0; + mDoCount = 0; + mDeleteCount = 0; + } + + class MyClass; + + MyClass* newInstance() { + return new MyClass(this); + } + + class MyClass { + public: + MyClass(SmartPtrTest* test) : mTest(test) { + mTest->mNewCount++; + } + + void doStuff() { + mTest->mDoCount++; + } + + ~MyClass() { + mTest->mDeleteCount++; + } + private: + SmartPtrTest* mTest; + }; + + int mNewCount; + int mDeleteCount; + int mDoCount; +}; + + +TEST_F(SmartPtrTest, Empty) { + SmartPtr ptr; + EXPECT_FALSE(ptr.Ptr()); + + EXPECT_EQ(0, mNewCount); + EXPECT_EQ(0, mDeleteCount); + EXPECT_EQ(0, mDoCount); +} + + +TEST_F(SmartPtrTest, SingleRef) { + MyClass* obj = newInstance(); + EXPECT_EQ(1, mNewCount); + + { + SmartPtr ptr(obj); + EXPECT_EQ(obj, ptr.Ptr()); + + EXPECT_EQ(1, mNewCount); + EXPECT_EQ(0, mDeleteCount); + EXPECT_EQ(0, mDoCount); + } + // Check that the object was deleted. + EXPECT_EQ(1, mDeleteCount); +} + + +TEST_F(SmartPtrTest, CopyConstructor) { + MyClass* obj = newInstance(); + EXPECT_EQ(1, mNewCount); + + { + SmartPtr ptr1(obj); + { + SmartPtr ptr2(ptr1); + EXPECT_EQ(2, ptr1.getRefCount()); + EXPECT_EQ(2, ptr2.getRefCount()); + EXPECT_EQ(1, mNewCount); + EXPECT_EQ(0, mDeleteCount); + EXPECT_EQ(0, mDoCount); + } + EXPECT_EQ(1, mNewCount); + EXPECT_EQ(0, mDeleteCount); + EXPECT_EQ(0, mDoCount); + } + EXPECT_EQ(1, mNewCount); + EXPECT_EQ(1, mDeleteCount); + EXPECT_EQ(0, mDoCount); +} + + +TEST_F(SmartPtrTest, AssignmentOperator) { + SmartPtr ptr1(newInstance()); + SmartPtr ptr2(newInstance()); + EXPECT_EQ(2, mNewCount); + EXPECT_EQ(0, mDeleteCount); + EXPECT_EQ(0, mDoCount); + + ptr2 = ptr1; + EXPECT_EQ(2, mNewCount); + EXPECT_EQ(1, mDeleteCount); + + EXPECT_EQ(ptr1.Ptr(), ptr2.Ptr()); + EXPECT_EQ(2, ptr1.getRefCount()); + EXPECT_EQ(2, ptr2.getRefCount()); +} + + +TEST_F(SmartPtrTest, ArrowOperator) { + SmartPtr ptr(newInstance()); + ptr->doStuff(); + EXPECT_EQ(1, mDoCount); + + (*ptr).doStuff(); + EXPECT_EQ(2, mDoCount); +} + +} // namespace emugl diff --git a/external/android-emugl/shared/emugl/common/sockets.cpp b/external/android-emugl/shared/emugl/common/sockets.cpp new file mode 100644 index 0000000..b38a4be --- /dev/null +++ b/external/android-emugl/shared/emugl/common/sockets.cpp @@ -0,0 +1,247 @@ +// Copyright (C) 2014 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "emugl/common/sockets.h" + +#include + +#ifdef _WIN32 +#include +#include +#else +#include +#include +#include +#include +#include +#endif + +#include +#include +#include +#include + +namespace emugl { + +namespace { + +static void socketSetDontLinger(int s) { +#ifdef _WIN32 + // TODO: Verify default behavior on WINDOWS +#else + // Ungraceful shutdown, no reason to linger at all + struct linger so_linger; + so_linger.l_onoff = 1; + so_linger.l_linger = 0; + if(setsockopt(s, SOL_SOCKET, SO_LINGER, &so_linger, sizeof so_linger) < 0) + perror("Setting socket option SO_LINGER={on, 0} failed"); +#endif +} + +static void socketSetReuseAddress(int s) { +#ifdef _WIN32 + // The default behaviour on Windows is equivalent to SO_REUSEADDR + // so we don't need to set this option. Moreover, one should never + // set this option with WinSock because it's badly implemented and + // generates a huge security issue. See: + // http://msdn.microsoft.com/en-us/library/windows/desktop/ms740621(v=vs.85).aspx +#else + int val = 1; + if(setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &val, sizeof(val)) < 0) + perror("Setting socket option SO_REUSEADDR failed"); +#endif +} + +// Helper union to store a socket address. +struct SockAddr { + socklen_t len; + union { + sockaddr generic; + sockaddr_in inet; +#ifndef _WIN32 + sockaddr_un local; +#endif + }; + + int getFamily() const { return generic.sa_family; } + + void initEmpty() { + ::memset(this, 0, sizeof(*this)); + this->len = static_cast(sizeof(*this)); + } + + int initFromInet(uint32_t ip_address, int port) { + if (port < 0 || port >= 65536) + return -EINVAL; + + ::memset(this, 0, sizeof(*this)); + this->inet.sin_family = AF_INET; + this->inet.sin_port = htons(port); + this->inet.sin_addr.s_addr = htonl(ip_address); + this->len = sizeof(this->inet); + return 0; + } + + int initFromLocalhost(int port) { + return initFromInet(0x7f000001, port); + } + +#ifndef _WIN32 + // Initialize the SockAddr from a Unix path. Returns 0 on success, + // or -errno code on failure. + int initFromUnixPath(const char* path) { + if (!path || !path[0]) + return -EINVAL; + + size_t pathLen = ::strlen(path); + if (pathLen >= sizeof(local.sun_path)) + return -E2BIG; + + ::memset(this, 0, sizeof(*this)); + this->local.sun_family = AF_LOCAL; + ::memcpy(this->local.sun_path, path, pathLen + 1U); + this->len = pathLen + offsetof(sockaddr_un, sun_path); + return 0; + } +#endif +}; + +int socketBindInternal(const SockAddr* addr, int socketType) { + int s = ::socket(addr->getFamily(), socketType, 0); + if (s < 0) { + perror("Could not create socket to bind"); + return -errno; + } + + socketSetDontLinger(s); + socketSetReuseAddress(s); + + // Bind to the socket. + if (::bind(s, &addr->generic, addr->len) < 0 || + ::listen(s, 5) < 0) { + int ret = -errno; + perror("Could not bind or listen to socket"); + ::close(s); + return ret; + } + + return s; +} + +int socketConnectInternal(const SockAddr* addr, int socketType) { + int s = ::socket(addr->getFamily(), socketType, 0); + if (s < 0) { + perror("Could not create socket to connect"); + return -errno; + } + + socketSetDontLinger(s); + socketSetReuseAddress(s); + + int ret; + do { + ret = ::connect(s, &addr->generic, addr->len); + } while (ret < 0 && errno == EINTR); + + if (ret < 0) { + ret = -errno; + ::close(s); + return ret; + } + + return s; +} + +} // namespace + +void socketTcpDisableNagle(int s) { + // disable Nagle algorithm to improve bandwidth of small + // packets which are quite common in our implementation. +#ifdef _WIN32 + DWORD flag; +#else + int flag; +#endif + flag = 1; + setsockopt(s, IPPROTO_TCP, TCP_NODELAY, + (const char*)&flag, sizeof(flag)); +} + +int socketGetPort(int s) { + SockAddr addr; + addr.initEmpty(); + if (getsockname(s, &addr.generic, &addr.len) < 0) { + return -errno; + } + switch (addr.generic.sa_family) { + case AF_INET: + return ntohs(addr.inet.sin_port); + default: + ; + } + return -EINVAL; +} + +#ifndef _WIN32 +int socketLocalServer(const char* path, int socketType) { + SockAddr addr; + int ret = addr.initFromUnixPath(path); + if (ret < 0) { + return ret; + } + return socketBindInternal(&addr, socketType); +} + +int socketLocalClient(const char* path, int socketType) { + SockAddr addr; + int ret = addr.initFromUnixPath(path); + if (ret < 0) { + return ret; + } + return socketConnectInternal(&addr, socketType); +} +#endif // !_WIN32 + +int socketTcpLoopbackServer(int port, int socketType) { + SockAddr addr; + int ret = addr.initFromLocalhost(port); + if (ret < 0) { + return ret; + } + return socketBindInternal(&addr, socketType); +} + +int socketTcpLoopbackClient(int port, int socketType) { + SockAddr addr; + int ret = addr.initFromLocalhost(port); + if (ret < 0) { + return ret; + } + return socketConnectInternal(&addr, socketType); +} + +int socketTcpClient(const char* hostname, int port, int socketType) { + // TODO(digit): Implement this. + return -ENOSYS; +} + +int socketAccept(int serverSocket) { + int ret; + do { + ret = ::accept(serverSocket, NULL, NULL); + } while (ret < 0 && errno == EINTR); + return ret; +} + +} // namespace emugl diff --git a/external/android-emugl/shared/emugl/common/sockets.h b/external/android-emugl/shared/emugl/common/sockets.h new file mode 100644 index 0000000..11e7ac7 --- /dev/null +++ b/external/android-emugl/shared/emugl/common/sockets.h @@ -0,0 +1,57 @@ +// Copyright (C) 2014 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef EMUGL_COMMON_SOCKETS_H +#define EMUGL_COMMON_SOCKETS_H + +// A set of helper functions used to deal with sockets in a portable way. + +namespace emugl { + +// Disable Nagle's algorithm for socket descriptor |s|. This assumes +// that |s| is a TCP socket descriptor. +void socketTcpDisableNagle(int s); + +// Return the port associated with a given IP or IPv6 socket, +// or -errno code on failure. +int socketGetPort(int s); + +// Bind to a local/Unix path, and return its socket descriptor on success, +// or -errno code on failure. +int socketLocalServer(const char* path, int socketType); + +// Connect to a Unix local path, and return a new socket descriptor +// on success, or -errno code on failure. +int socketLocalClient(const char* path, int socketType); + +// Bind to a localhost TCP socket, and return its socket descriptor on +// success, or -errno code on failure. +int socketTcpLoopbackServer(int port, int socketType); + +// Connect to a localhost TCP port, and return a new socket descriptor on +// success, or -errno code on failure. +int socketTcpLoopbackClient(int port, int socketType); + +// Connect to a TCP host, and return a new socket descriptor on +// success, or -errno code on failure. +int socketTcpClient(const char* hostname, int port, int socketType); + +// Accept a new connection. |serverSocket| must be a bound server socket +// descriptor. Returns new socket descriptor on success, or -errno code +// on failure. +int socketAccept(int serverSocket); + +} // namespace emugl + +#endif // EMUGL_COMMON_SOCKETS_H diff --git a/external/android-emugl/shared/emugl/common/testing/test_shared_library.cpp b/external/android-emugl/shared/emugl/common/testing/test_shared_library.cpp new file mode 100644 index 0000000..598a963 --- /dev/null +++ b/external/android-emugl/shared/emugl/common/testing/test_shared_library.cpp @@ -0,0 +1,22 @@ +// Copyright (C) 2014 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// This source file must be compiled into a simple shared library which +// will be used by shared_library_unittest.cpp to verify that the +// emugl::SharedLibrary class works properly. + + +extern "C" int foo_function(void) { + return 42; +} diff --git a/external/android-emugl/shared/emugl/common/testing/test_thread.h b/external/android-emugl/shared/emugl/common/testing/test_thread.h new file mode 100644 index 0000000..adc8e5f --- /dev/null +++ b/external/android-emugl/shared/emugl/common/testing/test_thread.h @@ -0,0 +1,78 @@ +// Copyright (C) 2014 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef EMUGL_COMMON_TESTING_TEST_THREAD_H +#define EMUGL_COMMON_TESTING_TEST_THREAD_H + +#ifdef _WIN32 +# define WIN32_LEAN_AND_MEAN 1 +# include +#else +# include +#endif + +namespace emugl { + +// Very basic platform thread wrapper that only uses a tiny stack. +// This shall only be used during unit testing, and allows test code +// to not depend on the implementation of emugl::Thread. +class TestThread { +public: + // Main thread function type. + typedef void* (ThreadFunction)(void* param); + + // Constructor actually launches a new platform thread. + TestThread(ThreadFunction* func, void* funcParam) { +#ifdef _WIN32 + mThread = CreateThread(NULL, + 16384, + (DWORD WINAPI (*)(void*))func, + funcParam, + 0, + NULL); +#else + pthread_attr_t attr; + pthread_attr_init(&attr); + pthread_attr_setstacksize(&attr, 16384); + pthread_create(&mThread, &attr, func, funcParam); + pthread_attr_destroy(&attr); +#endif + } + + ~TestThread() { +#ifdef _WIN32 + CloseHandle(mThread); +#endif + } + + void join() { +#ifdef _WIN32 + WaitForSingleObject(mThread, INFINITE); +#else + void* ret = NULL; + pthread_join(mThread, &ret); +#endif + } + +private: +#ifdef _WIN32 + HANDLE mThread; +#else + pthread_t mThread; +#endif +}; + +} // namespace emugl + +#endif // EMUGL_COMMON_TESTING_TEST_THREAD_H diff --git a/external/android-emugl/shared/emugl/common/thread.h b/external/android-emugl/shared/emugl/common/thread.h new file mode 100644 index 0000000..f524512 --- /dev/null +++ b/external/android-emugl/shared/emugl/common/thread.h @@ -0,0 +1,103 @@ +// Copyright (C) 2014 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef EMUGL_COMMON_THREAD_H +#define EMUGL_COMMON_THREAD_H + +#ifdef _WIN32 +#include +#else +#include +#endif + +#include + +namespace emugl { + +// Wrapper class for platform-specific threads. +// To create your own thread, define a sub-class of emugl::Thread +// and override its main() method. +// +// For example: +// +// class MyThread : public emugl::Thread { +// public: +// MyThread() : Thread() {} +// +// virtual intptr_t main() { +// ... main thread loop implementation +// return 0; +// } +// }; +// +// ... +// +// // Create new instance, but does not start it. +// MyThread* thread = new MyThread(); +// +// // Start the thread. +// thread->start(); +// +// // Wait for thread completion, and gets result into |exitStatus|. +// int exitStatus; +// thread->wait(&exitStatus); +// +class Thread { +public: + // Public constructor. + Thread(); + + // Virtual destructor. + virtual ~Thread(); + + // Override this method in your own thread sub-classes. This will + // be called when start() is invoked on the Thread instance. + virtual intptr_t main() = 0; + + // Start a thread instance. Return true on success, false otherwise + // (e.g. if the thread was already started or terminated). + bool start(); + + // Wait for thread termination and retrieve exist status into + // |*exitStatus|. Return true on success, false otherwise. + // NOTE: |exitStatus| can be NULL. + bool wait(intptr_t *exitStatus); + + // Check whether a thread has terminated. On success, return true + // and sets |*exitStatus|. On failure, return false. + // NOTE: |exitStatus| can be NULL. + bool tryWait(intptr_t *exitStatus); + +private: +#ifdef _WIN32 + static DWORD WINAPI thread_main(void* arg); + + HANDLE mThread; + DWORD mThreadId; + CRITICAL_SECTION mLock; +#else // !WIN32 + static void* thread_main(void* arg); + + pthread_t mThread; + pthread_mutex_t mLock; + bool mJoined; +#endif + intptr_t mExitStatus; + bool mIsRunning; +}; + +} // namespace emugl + +#endif // EMUGL_COMMON_THREAD_H + diff --git a/external/android-emugl/shared/emugl/common/thread_pthread.cpp b/external/android-emugl/shared/emugl/common/thread_pthread.cpp new file mode 100644 index 0000000..edd1f6a --- /dev/null +++ b/external/android-emugl/shared/emugl/common/thread_pthread.cpp @@ -0,0 +1,136 @@ +// Copyright (C) 2014 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "emugl/common/thread.h" + +#include "emugl/common/thread_store.h" + +#include +#include + +namespace emugl { + +namespace { + +class ScopedLocker { +public: + ScopedLocker(pthread_mutex_t* mutex) : mMutex(mutex) { + pthread_mutex_lock(mMutex); + } + + ~ScopedLocker() { + pthread_mutex_unlock(mMutex); + } +private: + pthread_mutex_t* mMutex; +}; + +} // namespace + +Thread::Thread() : + mThread((pthread_t)NULL), + mLock(), + mJoined(false), + mExitStatus(0), + mIsRunning(false) { + pthread_mutex_init(&mLock, NULL); +} + +Thread::~Thread() { + assert(!mIsRunning); + assert(mJoined); + pthread_mutex_destroy(&mLock); +} + +bool Thread::start() { + bool ret = true; + pthread_mutex_lock(&mLock); + mIsRunning = true; + if (pthread_create(&mThread, NULL, thread_main, this)) { + ret = false; + mIsRunning = false; + } + pthread_mutex_unlock(&mLock); + return ret; +} + +bool Thread::wait(intptr_t *exitStatus) { + { + ScopedLocker locker(&mLock); + if (!mIsRunning) { + // Thread already stopped. + if (exitStatus) { + *exitStatus = mExitStatus; + } + if (!mJoined) { + // reclaim thread stack + pthread_join(mThread, NULL); + mJoined = true; + } + return true; + } + } + + // NOTE: Do not hold the lock when waiting for the thread to ensure + // it can update mIsRunning and mExitStatus properly in thread_main + // without blocking. + void *retval; + if (pthread_join(mThread, &retval)) { + return false; + } + if (exitStatus) { + *exitStatus = (intptr_t)retval; + } + // Note: Updating mJoined must be performed inside the lock to avoid + // race conditions between two threads waiting for the same thread + // that just completed its execution. + { + ScopedLocker locker(&mLock); + mJoined = true; + } + return true; +} + +bool Thread::tryWait(intptr_t *exitStatus) { + ScopedLocker locker(&mLock); + if (mIsRunning) { + return false; + } + if (!mJoined) { + // Reclaim stack. + pthread_join(mThread, NULL); + mJoined = true; + } + if (exitStatus) { + *exitStatus = mExitStatus; + } + return true; +} + +// static +void* Thread::thread_main(void *arg) { + Thread* self = reinterpret_cast(arg); + intptr_t ret = self->main(); + + pthread_mutex_lock(&self->mLock); + self->mIsRunning = false; + self->mExitStatus = ret; + pthread_mutex_unlock(&self->mLock); + + ::emugl::ThreadStore::OnThreadExit(); + + return (void*)ret; +} + +} // namespace emugl diff --git a/external/android-emugl/shared/emugl/common/thread_store.cpp b/external/android-emugl/shared/emugl/common/thread_store.cpp new file mode 100644 index 0000000..ea64c6f --- /dev/null +++ b/external/android-emugl/shared/emugl/common/thread_store.cpp @@ -0,0 +1,242 @@ +// Copyright (C) 2014 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "emugl/common/thread_store.h" + +#ifdef _WIN32 +#include "emugl/common/lazy_instance.h" +#endif + +#include +#include +#include +#include + +// Set to 1 to print debug messages. +#define DEBUG_THREAD_STORE 0 + +#if DEBUG_THREAD_STORE +# define D(...) do { printf("%s:%d: ", __FUNCTION__, __LINE__); printf(__VA_ARGS__); fflush(stdout); } while (0) +#else +# define D(...) ((void)0) +#endif + +namespace emugl { + +#ifdef _WIN32 + +namespace { + +// The ThreadStore implementation on Windows is very tricky, because +// TlsAlloc() doesn't allow one to provide a destructor function. As +// such threads are expected to destroy all TLS values explicitely. +// +// To solve this issue, this source file provides a static method called +// ThreadStore::OnThreadExit() that must be called when a thread exits, +// which will cleanup all values for the current thread. +// +// But this forces us to track thread-specific values ourselves. + +// Maximum amount of thread-specific slots supported by this implementation. +enum { + kMaxTlsSlots = 64 +}; + +// TlsSlotArray is a thread-specific array of values. Instances will +// be stored in a Win32 TLS value controlled by a single master TLS +// key. +// +typedef void* TlsSlotArray[kMaxTlsSlots]; + +// Global state shared by all threads +class GlobalState { +public: + GlobalState() { + D("Entering\n"); + mMasterTls = TlsAlloc(); + D("Master TLS = %d\n", (int)mMasterTls); + InitializeCriticalSection(&mSection); + mLastIndex = 0; + ::memset(mDestructors, 0, sizeof(mDestructors)); + D("Exiting\n"); + } + + // Register a new TLS key, or return -1 on error (too many keys). + // |destroy| is the destructor function for the key. + int registerKey(ThreadStore::Destructor* destroy) { + D("Entering destroy=%p\n", destroy); + int ret = -1; + EnterCriticalSection(&mSection); + if (mLastIndex < kMaxTlsSlots) { + ret = mLastIndex++; + mDestructors[ret] = destroy; + } + LeaveCriticalSection(&mSection); + D("Exiting newKey=%d\n", ret); + return ret; + } + + void unregisterKey(int key) { + D("key=%d\n", key); + if (key < 0 || key >= kMaxTlsSlots) { + D("Invalid key\n"); + return; + } + + // Note: keys are not reusable, but remove the destructor to avoid + // crashes in leaveCurrentThread() when it points to a function that + // is going to be unloaded from the process' address space. + EnterCriticalSection(&mSection); + mDestructors[key] = NULL; + LeaveCriticalSection(&mSection); + D("Exiting\n"); + } + + // Get the current thread-local value for a given |key|. + void* getValue(int key) const { + D("Entering key=%d\n", key); + if (key < 0 || key >= kMaxTlsSlots) { + D("Invalid key, result=NULL\n"); + return NULL; + } + + TlsSlotArray* array = getArray(); + void* ret = (*array)[key]; + D("Exiting keyValue=%p\n", ret); + return ret; + } + + // Set the current thread-local |value| for a given |key|. + void setValue(int key, void* value) { + D("Entering key=%d\n",key); + if (key < 0 || key >= kMaxTlsSlots) { + D("Invalid key, returning\n"); + return; + } + + TlsSlotArray* array = getArray(); + (*array)[key] = value; + D("Exiting\n"); + } + + // Call this when a thread exits to destroy all its thread-local values. + void leaveCurrentThread() { + D("Entering\n"); + TlsSlotArray* array = + reinterpret_cast(TlsGetValue(mMasterTls)); + if (!array) { + D("Exiting, no thread-local data in this thread\n"); + return; + } + + for (size_t n = 0; n < kMaxTlsSlots; ++n) { + void* value = array[n]; + if (value) { + (*array)[n] = NULL; + // NOTE: In theory, a destructor could reset the slot to + // a new value, and we would have to loop in this function + // in interesting ways. In practice, ignore the issue. + EnterCriticalSection(&mSection); + ThreadStore::Destructor* destroy = mDestructors[n]; + LeaveCriticalSection(&mSection); + if (destroy) { + D("Calling destructor %p for key=%d, with value=%p\n", + destroy, (int)n, value); + (*destroy)(value); + } + } + } + TlsSetValue(mMasterTls, NULL); + ::free(array); + D("Exiting\n"); + } + +private: + // Return the thread-local array of TLS slots for the current thread. + // Cannot return NULL. + TlsSlotArray* getArray() const { + D("Entering\n"); + TlsSlotArray* array = + reinterpret_cast(TlsGetValue(mMasterTls)); + if (!array) { + array = reinterpret_cast( + ::calloc(sizeof(*array), 1)); + TlsSetValue(mMasterTls, array); + D("Allocated new array at %p\n", array); + } else { + D("Retrieved array at %p\n", array); + } + return array; + } + + DWORD mMasterTls; + CRITICAL_SECTION mSection; + int mLastIndex; + ThreadStore::Destructor* mDestructors[kMaxTlsSlots]; +}; + +LazyInstance gGlobalState = LAZY_INSTANCE_INIT; + +} // namespace + +ThreadStore::ThreadStore(Destructor* destroy) { + D("Entering this=%p destroy=%p\n", this, destroy); + mKey = gGlobalState->registerKey(destroy); + D("Exiting this=%p key=%d\n", this, mKey); +} + +ThreadStore::~ThreadStore() { + D("Entering this=%p\n", this); + GlobalState* state = gGlobalState.ptr(); + state->unregisterKey(mKey); + D("Exiting this=%p\n", this); +} + +void* ThreadStore::get() const { + D("Entering this=%p\n", this); + void* ret = gGlobalState->getValue(mKey); + D("Exiting this=%p value=%p\n", this, ret); + return ret; +} + +void ThreadStore::set(void* value) { + D("Entering this=%p value=%p\n", this, value); + gGlobalState->setValue(mKey, value); + D("Exiting this=%p\n", this); +} + +// static +void ThreadStore::OnThreadExit() { + gGlobalState->leaveCurrentThread(); +} + +#else // !_WIN32 + +ThreadStore::ThreadStore(Destructor* destroy) { + int ret = pthread_key_create(&mKey, destroy); + if (ret != 0) { + fprintf(stderr, + "Could not create thread store key: %s\n", + strerror(ret)); + exit(1); + } +} + +ThreadStore::~ThreadStore() { + pthread_key_delete(mKey); +} + +#endif // !_WIN32 + +} // namespace emugl diff --git a/external/android-emugl/shared/emugl/common/thread_store.h b/external/android-emugl/shared/emugl/common/thread_store.h new file mode 100644 index 0000000..5fd08bd --- /dev/null +++ b/external/android-emugl/shared/emugl/common/thread_store.h @@ -0,0 +1,110 @@ +// Copyright (C) 2014 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef EMUGL_COMMON_THREAD_STORE_H +#define EMUGL_COMMON_THREAD_STORE_H + +#ifdef _WIN32 +# define WIN32_LEAN_AND_MEAN 1 +# include +#else +# include +#endif + +namespace emugl { + +// A class to model storage of thread-specific values, that can be +// destroyed on thread exit. +// +// Note that on Windows, a thread must call OnThreadExit() explicitly +// here to ensure that the values are probably discarded. This is an +// unfortunate requirement of the Win32 API, which doesn't support +// destructors at all. +// +// There are various hacks on the web to try to achieve this automatically +// (e.g. [1]) but they rely on using the Microsoft build tools, +// which doesn't work for us. +// +// Note another important issue with ThreadStore instances: if you create +// one instance in a shared library, you need to make sure that it is +// always destroyed before the library is unloaded. Otherwise, future +// thread exit will likely crash, due to calling a destructor function +// that is no longer in the process' address space. +// +// Finally, destroying an instance does _not_ free the corresponding values, +// because doing so properly requires coordinating all participating threads, +// which is impossible to achieve in the most general case. Thus, consider +// that thread-local values are always leaked on library unload, or on +// program exit. +// +// [1] http://stackoverflow.com/questions/14538159/about-tls-callback-in-windows + +class ThreadStore { +public: + // Type of a function used to destroy a thread-specific value that + // was previously assigned by calling set(). + typedef void (Destructor)(void* value); + + // Initialize instance so that is hold keys that must be destroyed + // on thread exit by calling |destroy|. + explicit ThreadStore(Destructor* destroy); + + // NOTE: Destructor don't free the thread-local values, but are required + // to avoid crashes (see note above). + ~ThreadStore(); + + // Retrieve current thread-specific value from store. +#ifdef _WIN32 + void* get() const; +#else + inline void* get() const { + return pthread_getspecific(mKey); + } +#endif + + // Set the new thread-specific value. +#ifdef _WIN32 + void set(void* value); +#else + inline void set(void* value) { + pthread_setspecific(mKey, value); + } +#endif + +#ifdef _WIN32 + // Each thread should call this function on exit to ensure that + // all corresponding TLS values are properly freed. + static void OnThreadExit(); +#else + // Nothing to do on Posix. + static inline void OnThreadExit() {} +#endif + +private: + // Ensure you can't create an empty ThreadStore instance, or simply + // copy it in any way. + ThreadStore(); + ThreadStore(const ThreadStore&); + ThreadStore& operator=(const ThreadStore&); + +#ifdef _WIN32 + int mKey; +#else + pthread_key_t mKey; +#endif +}; + +} // namespace emugl + +#endif // EMUGL_COMMON_THREAD_STORE_H diff --git a/external/android-emugl/shared/emugl/common/thread_store_unittest.cpp b/external/android-emugl/shared/emugl/common/thread_store_unittest.cpp new file mode 100644 index 0000000..6b5dddb --- /dev/null +++ b/external/android-emugl/shared/emugl/common/thread_store_unittest.cpp @@ -0,0 +1,146 @@ +// Copyright (C) 2014 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "emugl/common/thread_store.h" + +#include "emugl/common/mutex.h" +#include "emugl/common/testing/test_thread.h" + +#include + +namespace emugl { + +namespace { + +// Helper class used to count instance creation and destruction. +class StaticCounter { +public: + enum { + kMaxInstances = 1000, + }; + + StaticCounter() { + Mutex::AutoLock lock(mMutex); + if (mCreationCount < kMaxInstances) + mInstances[mCreationCount] = this; + mCreationCount++; + } + + ~StaticCounter() { + Mutex::AutoLock lock(mMutex); + mDestructionCount++; + } + + static void reset() { + Mutex::AutoLock lock(mMutex); + mCreationCount = 0; + mDestructionCount = 0; + } + + static size_t getCreationCount() { + Mutex::AutoLock lock(mMutex); + return mCreationCount; + } + + static size_t getDestructionCount() { + Mutex::AutoLock lock(mMutex); + return mDestructionCount; + } + + static void freeAll() { + for (size_t n = 0; n < kMaxInstances; ++n) + delete mInstances[n]; + } + +private: + static Mutex mMutex; + static size_t mCreationCount; + static size_t mDestructionCount; + static StaticCounter* mInstances[kMaxInstances]; +}; + +Mutex StaticCounter::mMutex; +size_t StaticCounter::mCreationCount = 0; +size_t StaticCounter::mDestructionCount = 0; +StaticCounter* StaticCounter::mInstances[kMaxInstances]; + +} // namespace + +// Just check that we can create a new ThreadStore with an empty +// destructor, and use it in the current thread. +TEST(ThreadStore, MainThreadWithoutDestructor) { + ThreadStore store(NULL); + static int x = 42; + store.set(&x); + EXPECT_EQ(&x, store.get()); +} + +// The following test checks that exiting a thread correctly deletes +// any thread-local value stored in it. +static void simplyDestroy(void* value) { + delete (StaticCounter*) value; +} + +static void* simpleThreadFunc(void* param) { + ThreadStore* store = static_cast(param); + store->set(new StaticCounter()); + ThreadStore::OnThreadExit(); + return NULL; +} + +TEST(ThreadStore, ThreadsWithDestructor) { + ThreadStore store(simplyDestroy); + const size_t kNumThreads = 1000; + TestThread* threads[kNumThreads]; + StaticCounter::reset(); + + for (size_t n = 0; n < kNumThreads; ++n) { + threads[n] = new TestThread(&simpleThreadFunc, &store); + } + for (size_t n = 0; n < kNumThreads; ++n) { + threads[n]->join(); + } + + EXPECT_EQ(kNumThreads, StaticCounter::getCreationCount()); + EXPECT_EQ(kNumThreads, StaticCounter::getDestructionCount()); + + for (size_t n = 0; n < kNumThreads; ++n) { + delete threads[n]; + } +} + +TEST(ThreadStore, ThreadsWithoutDestructor) { + ThreadStore store(NULL); + const size_t kNumThreads = 1000; + TestThread* threads[kNumThreads]; + StaticCounter::reset(); + + for (size_t n = 0; n < kNumThreads; ++n) { + threads[n] = new TestThread(&simpleThreadFunc, &store); + } + for (size_t n = 0; n < kNumThreads; ++n) { + threads[n]->join(); + } + + EXPECT_EQ(kNumThreads, StaticCounter::getCreationCount()); + EXPECT_EQ(0U, StaticCounter::getDestructionCount()); + + StaticCounter::freeAll(); + + for (size_t n = 0; n < kNumThreads; ++n) { + delete threads[n]; + } +} + +} // namespace emugl diff --git a/external/android-emugl/shared/emugl/common/thread_unittest.cpp b/external/android-emugl/shared/emugl/common/thread_unittest.cpp new file mode 100644 index 0000000..efc2c6e --- /dev/null +++ b/external/android-emugl/shared/emugl/common/thread_unittest.cpp @@ -0,0 +1,164 @@ +// Copyright (C) 2014 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "emugl/common/thread.h" + +#include "emugl/common/mutex.h" + +#include + +namespace emugl { + +namespace { + +// A simple thread instance that does nothing at all and exits immediately. +class EmptyThread : public ::emugl::Thread { +public: + intptr_t main() { return 42; } +}; + +class CountingThread : public ::emugl::Thread { +public: + class State { + public: + State() : mLock(), mCount(0) {} + ~State() {} + + void increment() { + mLock.lock(); + mCount++; + mLock.unlock(); + } + + int count() const { + int ret; + mLock.lock(); + ret = mCount; + mLock.unlock(); + return ret; + } + + private: + mutable Mutex mLock; + int mCount; + }; + + CountingThread(State* state) : mState(state) {} + + intptr_t main() { + mState->increment(); + return 0; + } + +private: + State* mState; +}; + +class WaitingThread : public ::emugl::Thread { +public: + WaitingThread(Mutex* lock) : mLock(lock) {} + + intptr_t main() { + // Try to acquire lock. + mLock->lock(); + + // Then try to release it. + mLock->unlock(); + return 0; + } +private: + Mutex* mLock; +}; + +} // namespace + +TEST(ThreadTest, WaitForSimpleThread) { + Thread* thread = new EmptyThread(); + EXPECT_TRUE(thread); + EXPECT_TRUE(thread->start()); + intptr_t status; + EXPECT_TRUE(thread->wait(&status)); + EXPECT_EQ(42, status); +} + +TEST(ThreadTest, WaitForMultipleThreads) { + CountingThread::State state; + const size_t kMaxThreads = 100; + Thread* threads[kMaxThreads]; + + // Create all threads. + for (size_t n = 0; n < kMaxThreads; ++n) { + threads[n] = new CountingThread(&state); + EXPECT_TRUE(threads[n]) << "thread " << n; + } + + // Start them all. + for (size_t n = 0; n < kMaxThreads; ++n) { + EXPECT_TRUE(threads[n]->start()) << "thread " << n; + } + + // Wait for them all. + for (size_t n = 0; n < kMaxThreads; ++n) { + EXPECT_TRUE(threads[n]->wait(NULL)) << "thread " << n; + } + + // Check state. + EXPECT_EQ((int)kMaxThreads, state.count()); + + // Delete them all. + for (size_t n = 0; n < kMaxThreads; ++n) { + delete threads[n]; + } +} + +TEST(ThreadTest, TryWaitForMultipleThreads) { + Mutex lock; + const size_t kMaxThreads = 100; + Thread* threads[kMaxThreads]; + + // Create all threads. + for (size_t n = 0; n < kMaxThreads; ++n) { + threads[n] = new WaitingThread(&lock); + EXPECT_TRUE(threads[n]) << "thread " << n; + } + + // Acquire the lock, this will block all threads. + lock.lock(); + + // Start them all. + for (size_t n = 0; n < kMaxThreads; ++n) { + EXPECT_TRUE(threads[n]->start()) << "thread " << n; + } + + // Check that tryWait() fails for all threads. + for (size_t n = 0; n < kMaxThreads; ++n) { + EXPECT_FALSE(threads[n]->tryWait(NULL)) << "thread" << n; + } + + // Release the lock, this will unblock all threads. + lock.unlock(); + + // Wait for them all. + for (size_t n = 0; n < kMaxThreads; ++n) { + EXPECT_TRUE(threads[n]->wait(NULL)) << "thread " << n; + EXPECT_TRUE(threads[n]->tryWait(NULL)) << "thread " << n; + } + + // Delete them all. + for (size_t n = 0; n < kMaxThreads; ++n) { + delete threads[n]; + } +} + +} // namespace emugl diff --git a/external/android-emugl/shared/emugl/common/thread_win32.cpp b/external/android-emugl/shared/emugl/common/thread_win32.cpp new file mode 100644 index 0000000..bd8b37f --- /dev/null +++ b/external/android-emugl/shared/emugl/common/thread_win32.cpp @@ -0,0 +1,121 @@ +// Copyright (C) 2014 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "emugl/common/thread.h" + +#include "emugl/common/thread_store.h" + +namespace emugl { + +namespace { + +class ScopedLocker { +public: + ScopedLocker(CRITICAL_SECTION* section) : mSection(section) { + EnterCriticalSection(mSection); + } + + ~ScopedLocker() { + LeaveCriticalSection(mSection); + } +private: + CRITICAL_SECTION* mSection; +}; + +} // namespace + +Thread::Thread() : + mThread(INVALID_HANDLE_VALUE), + mThreadId(0), + mExitStatus(0), + mIsRunning(false) { + InitializeCriticalSection(&mLock); +} + +Thread::~Thread() { + if(mThread != INVALID_HANDLE_VALUE) { + CloseHandle(mThread); + } + DeleteCriticalSection(&mLock); +} + +bool Thread::start() { + ScopedLocker locker(&mLock); + + bool ret = true; + mIsRunning = true; + mThread = CreateThread(NULL, 0, &Thread::thread_main, this, 0, &mThreadId); + if (!mThread) { + ret = false; + mIsRunning = false; + } + return ret; +} + +bool Thread::wait(intptr_t* exitStatus) { + { + ScopedLocker locker(&mLock); + if (!mIsRunning) { + // Thread already stopped. + if (exitStatus) { + *exitStatus = mExitStatus; + } + return true; + } + } + + // NOTE: Do not hold lock during wait to aloow thread_main to + // properly update mIsRunning and mExitStatus on thread exit. + if (WaitForSingleObject(mThread, INFINITE) == WAIT_FAILED) { + return false; + } + + if (exitStatus) { + ScopedLocker locker(&mLock); + *exitStatus = mExitStatus; + } + return true; +} + +bool Thread::tryWait(intptr_t* exitStatus) { + ScopedLocker locker(&mLock); + + if (mIsRunning && WaitForSingleObject(mThread, 0) != WAIT_OBJECT_0) { + return false; + } + + if (exitStatus) { + *exitStatus = mExitStatus; + } + return true; +} + +// static +DWORD WINAPI Thread::thread_main(void *arg) +{ + Thread* self = reinterpret_cast(arg); + intptr_t ret = self->main(); + + EnterCriticalSection(&self->mLock); + self->mIsRunning = false; + self->mExitStatus = ret; + LeaveCriticalSection(&self->mLock); + + // Ensure all thread-local values are released for this thread. + ::emugl::ThreadStore::OnThreadExit(); + + return static_cast(ret); +} + +} // namespace emugl diff --git a/external/android-emugl/shared/emugl/common/unique_integer_map.h b/external/android-emugl/shared/emugl/common/unique_integer_map.h new file mode 100644 index 0000000..720aceb --- /dev/null +++ b/external/android-emugl/shared/emugl/common/unique_integer_map.h @@ -0,0 +1,225 @@ +// Copyright (C) 2014 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef EMUGL_COMMON_UNIQUE_INTEGER_MAP_H +#define EMUGL_COMMON_UNIQUE_INTEGER_MAP_H + +#include "emugl/common/pod_vector.h" + +#include + +namespace emugl { + +// Helper template class that implements a bi-directional mapping between +// two integer types |A| and |B|. More specifically: +// +// - The map allocates values of type |B| when a key of type |A| is entered +// in the map. +// +// - keys and values cannot be 0, which is reserved (i.e. means 'invalid'). +// +// This is used in EmuGL to map liberal 'void*' values (e.g. EGLimages ones) +// to unique 32-bit IDs that can be written to / read from the wire protocol. +template +class UniqueIntegerMap { +public: + UniqueIntegerMap() : mForwardPairs(), mBackwardPairs() {} + ~UniqueIntegerMap() {} + + // Return true iff the map is empty. + const bool empty() const { return mForwardPairs.empty(); } + + // Return the number of (key,value) pairs in the map. + size_t size() const { return mForwardPairs.size(); } + + // Find the value associated with |key| in the map. + // Returns 0 in case of failure, or if |key| is 0. + B find(const A key) const; + + // Find the key associated with a given |value| in the map. + // Returns 0 if |value| is 0, or in case of failure. + A findKeyFor(const B value) const; + + // Add |key| to the map and return an automatically-allocated + // unique value for it. Return 0 if |key| is 0. + B add(const A key); + + // Delete the entry associated with a given |key|. The + // corresponding value may be recycled by future calls to add(). + void del(const A key); + +private: + typedef struct { + A first; + B second; + } ForwardPair; + + typedef struct { + B first; + A second; + } BackwardPair; + + size_t findKeyIndexPlusOne(const A key) const; + size_t findValueIndexPlusOne(const B value) const; + + B allocValue(); + void freeValue(B value); + + PodVector mForwardPairs; + PodVector mBackwardPairs; + + B mLastValue; + PodVector mFreeValues; +}; + +template +B UniqueIntegerMap::find(const A key) const { + size_t keyIndex = findKeyIndexPlusOne(key); + if (!keyIndex) { + return 0; + } + return mForwardPairs[keyIndex - 1U].second; +} + +template +A UniqueIntegerMap::findKeyFor(const B value) const { + size_t valueIndex = findValueIndexPlusOne(value); + if (!valueIndex) { + return 0; + } + return mBackwardPairs[valueIndex - 1U].second; +} + +template +B UniqueIntegerMap::add(const A key) { + // Binary search to find the proper insertion point for the key. + // Also checks that the key isn't already in the set. + size_t min = 0; + size_t max = mForwardPairs.size(); + while (min < max) { + size_t mid = min + ((max - min) >> 1); + A midKey = mForwardPairs[mid].first; + if (midKey < key) { + min = mid + 1U; + } else if (midKey > key) { + max = mid; + } else { + // Already in the set. + return 0; + } + } + + // Generate new unique value + B value = allocValue(); + + ForwardPair* pair = mForwardPairs.emplace(min); + pair->first = key; + pair->second = value; + + // Binary search to find proper insertion point for the value. + min = 0; + max = mBackwardPairs.size(); + while (min < max) { + size_t mid = min + ((max - min) >> 1); + B midValue = mBackwardPairs[mid].first; + if (midValue < value) { + min = mid + 1U; + } else { + max = mid; + } + } + + BackwardPair* backPair = mBackwardPairs.emplace(min); + backPair->first = value; + backPair->second = key; + + return value; +} + +template +void UniqueIntegerMap::del(const A key) { + size_t keyIndex = findKeyIndexPlusOne(key); + if (!keyIndex) { + return; + } + B value = mForwardPairs[keyIndex - 1U].second; + size_t valueIndex = findValueIndexPlusOne(value); + mForwardPairs.remove(keyIndex - 1U); + mBackwardPairs.remove(valueIndex - 1U); + freeValue(value); +} + +template +size_t UniqueIntegerMap::findKeyIndexPlusOne(const A key) const { + // Binary search in forward pair array. + size_t min = 0; + size_t max = mForwardPairs.size(); + while (min < max) { + size_t mid = min + ((max - min) >> 1); + A midKey = mForwardPairs[mid].first; + if (midKey < key) { + min = mid + 1U; + } else if (midKey > key) { + max = mid; + } else { + return mid + 1U; + } + } + return 0U; +} + +template +size_t UniqueIntegerMap::findValueIndexPlusOne(const B value) const { + // Binary search in revere pair array. + size_t min = 0; + size_t max = mBackwardPairs.size(); + while (min < max) { + size_t mid = min + ((max - min) >> 1); + B midValue = mBackwardPairs[mid].first; + if (midValue < value) { + min = mid + 1U; + } else if (midValue > value) { + max = mid; + } else { + return mid + 1U; + } + } + return 0U; +} + +template +B UniqueIntegerMap::allocValue() { + if (!mFreeValues.empty()) { + B result = mFreeValues[0]; + mFreeValues.pop(); + return result; + } + return ++mLastValue; +} + +template +void UniqueIntegerMap::freeValue(B value) { + if (!value) { + return; + } + if (value == mLastValue) { + mLastValue--; + return; + } + mFreeValues.append(value); +} + +} // namespace emugl + +#endif // EMUGL_COMMON_INTEGER_MAP_H diff --git a/external/android-emugl/shared/emugl/common/unique_integer_map_unittest.cpp b/external/android-emugl/shared/emugl/common/unique_integer_map_unittest.cpp new file mode 100644 index 0000000..8aee013 --- /dev/null +++ b/external/android-emugl/shared/emugl/common/unique_integer_map_unittest.cpp @@ -0,0 +1,103 @@ +// Copyright (C) 2014 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "emugl/common/unique_integer_map.h" + +#include + +#include + +namespace emugl { + +typedef UniqueIntegerMap MyMap; + +TEST(UniqueIntegerMap, Empty) { + MyMap map; + + EXPECT_TRUE(map.empty()); + EXPECT_EQ(0U, map.size()); + EXPECT_EQ(0U, map.find(0U)); + EXPECT_EQ(0U, map.find(1U)); + EXPECT_EQ(0U, map.find(2U)); + EXPECT_EQ(0U, map.find(4U)); +} + +TEST(UniqueIntegerMap, AddOne) { + MyMap map; + uintptr_t key1 = 1U; + uint32_t val1 = map.add(key1); + + EXPECT_NE(0U, val1); + EXPECT_EQ(val1, map.find(key1)); + EXPECT_EQ(key1, map.findKeyFor(val1)); + + EXPECT_FALSE(map.empty()); + EXPECT_EQ(1U, map.size()); + + EXPECT_EQ(0U, map.find(0)); + EXPECT_EQ(0U, map.findKeyFor(0)); + + EXPECT_EQ(0U, map.find(key1 + 1)); + EXPECT_EQ(0U, map.findKeyFor(val1 + 1)); +} + +TEST(UniqueIntegerMap, AddMultiple) { + MyMap map; + const size_t kCount = 100; + const size_t kKeyMultiplier = 3U; // must be >= 2. + uint32_t values[kCount]; + + for (size_t n = 0; n < kCount; ++n) { + uintptr_t key = 1U + n * kKeyMultiplier; + values[n] = map.add(key); + EXPECT_NE(0U, values[n]) << "key #" << n; + } + + EXPECT_EQ(kCount, map.size()); + + for (size_t n = 0; n < kCount; ++n) { + uintptr_t key = 1U + n * kKeyMultiplier; + EXPECT_EQ(values[n], map.find(key)) << "key #" << n; + EXPECT_EQ(0U, map.find(key + 1U)) << "key #" << n; + } + + for (size_t n = 0; n < kCount; ++n) { + uintptr_t key = 1U + n * kKeyMultiplier; + EXPECT_EQ(key, map.findKeyFor(values[n])); + } +} + +TEST(UniqueIntegerMap, Del) { + MyMap map; + const size_t kCount = 100; + const size_t kKeyMultiplier = 3U; // must be >= 2. + uint32_t values[kCount]; + + for (size_t n = 0; n < kCount; ++n) { + uintptr_t key = 1U + n * kKeyMultiplier; + values[n] = map.add(key); + } + + for (size_t n = 0; n < kCount; ++n) { + uintptr_t key = 1U + n * kKeyMultiplier; + map.del(key); + EXPECT_EQ(kCount - 1U - n, map.size()); + EXPECT_EQ(0U, map.find(key)); + EXPECT_EQ(0U, map.findKeyFor(values[n])); + } + + EXPECT_TRUE(map.empty()); +} + +} // namespace emugl diff --git a/external/bubblewrap/.dir-locals.el b/external/bubblewrap/.dir-locals.el new file mode 100644 index 0000000..3514040 --- /dev/null +++ b/external/bubblewrap/.dir-locals.el @@ -0,0 +1 @@ +((c-mode . ((indent-tabs-mode . nil) (c-file-style . "gnu")))) diff --git a/external/bubblewrap/.editorconfig b/external/bubblewrap/.editorconfig new file mode 100644 index 0000000..d062d2c --- /dev/null +++ b/external/bubblewrap/.editorconfig @@ -0,0 +1,6 @@ +[*.[ch]] +indent_style = space +indent_size = 2 +trim_trailing_whitespace = true +indent_brace_style = gnu + diff --git a/external/bubblewrap/.travis.yml b/external/bubblewrap/.travis.yml new file mode 100644 index 0000000..dbba64b --- /dev/null +++ b/external/bubblewrap/.travis.yml @@ -0,0 +1,25 @@ +language: c +dist: trusty +addons: + apt: + packages: + - automake + - autotools-dev + - libcap-dev +script: + - env NOCONFIGURE=1 ./autogen.sh + - mkdir build + - cd build && ../configure + - make -j 2 + - make check + +notifications: + # This is Colin's instance of Homu, in the future + # we'll move this to a production cluster. + webhooks: http://escher.verbum.org:54856/travis + email: false + +branches: + only: + - auto + diff --git a/external/bubblewrap/CMakeLists.txt b/external/bubblewrap/CMakeLists.txt new file mode 100644 index 0000000..e7677a2 --- /dev/null +++ b/external/bubblewrap/CMakeLists.txt @@ -0,0 +1,14 @@ +# Don't treat any warnings as error as we take the source directly from +# upstream and just compile it. +set(CMAKE_C_FLAGS "-Wall") + +set(SOURCES + bind-mount.c + bubblewrap.c + network.c + utils.c) + +set(HEADERS + config.h) + +add_library(bwrap ${SOURCES} ${HEADERS}) diff --git a/external/bubblewrap/COPYING b/external/bubblewrap/COPYING new file mode 100644 index 0000000..5bc8fb2 --- /dev/null +++ b/external/bubblewrap/COPYING @@ -0,0 +1,481 @@ + GNU LIBRARY GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1991 Free Software Foundation, Inc. + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + +[This is the first released version of the library GPL. It is + numbered 2 because it goes with version 2 of the ordinary GPL.] + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +Licenses are intended to guarantee your freedom to share and change +free software--to make sure the software is free for all its users. + + This license, the Library General Public License, applies to some +specially designated Free Software Foundation software, and to any +other libraries whose authors decide to use it. You can use it for +your libraries, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if +you distribute copies of the library, or if you modify it. + + For example, if you distribute copies of the library, whether gratis +or for a fee, you must give the recipients all the rights that we gave +you. You must make sure that they, too, receive or can get the source +code. If you link a program with the library, you must provide +complete object files to the recipients so that they can relink them +with the library, after making changes to the library and recompiling +it. And you must show them these terms so they know their rights. + + Our method of protecting your rights has two steps: (1) copyright +the library, and (2) offer you this license which gives you legal +permission to copy, distribute and/or modify the library. + + Also, for each distributor's protection, we want to make certain +that everyone understands that there is no warranty for this free +library. If the library is modified by someone else and passed on, we +want its recipients to know that what they have is not the original +version, so that any problems introduced by others will not reflect on +the original authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that companies distributing free +software will individually obtain patent licenses, thus in effect +transforming the program into proprietary software. To prevent this, +we have made it clear that any patent must be licensed for everyone's +free use or not licensed at all. + + Most GNU software, including some libraries, is covered by the ordinary +GNU General Public License, which was designed for utility programs. This +license, the GNU Library General Public License, applies to certain +designated libraries. This license is quite different from the ordinary +one; be sure to read it in full, and don't assume that anything in it is +the same as in the ordinary license. + + The reason we have a separate public license for some libraries is that +they blur the distinction we usually make between modifying or adding to a +program and simply using it. Linking a program with a library, without +changing the library, is in some sense simply using the library, and is +analogous to running a utility program or application program. However, in +a textual and legal sense, the linked executable is a combined work, a +derivative of the original library, and the ordinary General Public License +treats it as such. + + Because of this blurred distinction, using the ordinary General +Public License for libraries did not effectively promote software +sharing, because most developers did not use the libraries. We +concluded that weaker conditions might promote sharing better. + + However, unrestricted linking of non-free programs would deprive the +users of those programs of all benefit from the free status of the +libraries themselves. This Library General Public License is intended to +permit developers of non-free programs to use free libraries, while +preserving your freedom as a user of such programs to change the free +libraries that are incorporated in them. (We have not seen how to achieve +this as regards changes in header files, but we have achieved it as regards +changes in the actual functions of the Library.) The hope is that this +will lead to faster development of free libraries. + + The precise terms and conditions for copying, distribution and +modification follow. Pay close attention to the difference between a +"work based on the library" and a "work that uses the library". The +former contains code derived from the library, while the latter only +works together with the library. + + Note that it is possible for a library to be covered by the ordinary +General Public License rather than by this special one. + + GNU LIBRARY GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License Agreement applies to any software library which +contains a notice placed by the copyright holder or other authorized +party saying it may be distributed under the terms of this Library +General Public License (also called "this License"). Each licensee is +addressed as "you". + + A "library" means a collection of software functions and/or data +prepared so as to be conveniently linked with application programs +(which use some of those functions and data) to form executables. + + The "Library", below, refers to any such software library or work +which has been distributed under these terms. A "work based on the +Library" means either the Library or any derivative work under +copyright law: that is to say, a work containing the Library or a +portion of it, either verbatim or with modifications and/or translated +straightforwardly into another language. (Hereinafter, translation is +included without limitation in the term "modification".) + + "Source code" for a work means the preferred form of the work for +making modifications to it. For a library, complete source code means +all the source code for all modules it contains, plus any associated +interface definition files, plus the scripts used to control compilation +and installation of the library. + + Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running a program using the Library is not restricted, and output from +such a program is covered only if its contents constitute a work based +on the Library (independent of the use of the Library in a tool for +writing it). Whether that is true depends on what the Library does +and what the program that uses the Library does. + + 1. You may copy and distribute verbatim copies of the Library's +complete source code as you receive it, in any medium, provided that +you conspicuously and appropriately publish on each copy an +appropriate copyright notice and disclaimer of warranty; keep intact +all the notices that refer to this License and to the absence of any +warranty; and distribute a copy of this License along with the +Library. + + You may charge a fee for the physical act of transferring a copy, +and you may at your option offer warranty protection in exchange for a +fee. + + 2. You may modify your copy or copies of the Library or any portion +of it, thus forming a work based on the Library, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) The modified work must itself be a software library. + + b) You must cause the files modified to carry prominent notices + stating that you changed the files and the date of any change. + + c) You must cause the whole of the work to be licensed at no + charge to all third parties under the terms of this License. + + d) If a facility in the modified Library refers to a function or a + table of data to be supplied by an application program that uses + the facility, other than as an argument passed when the facility + is invoked, then you must make a good faith effort to ensure that, + in the event an application does not supply such function or + table, the facility still operates, and performs whatever part of + its purpose remains meaningful. + + (For example, a function in a library to compute square roots has + a purpose that is entirely well-defined independent of the + application. Therefore, Subsection 2d requires that any + application-supplied function or table used by this function must + be optional: if the application does not supply it, the square + root function must still compute square roots.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Library, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Library, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote +it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Library. + +In addition, mere aggregation of another work not based on the Library +with the Library (or with a work based on the Library) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may opt to apply the terms of the ordinary GNU General Public +License instead of this License to a given copy of the Library. To do +this, you must alter all the notices that refer to this License, so +that they refer to the ordinary GNU General Public License, version 2, +instead of to this License. (If a newer version than version 2 of the +ordinary GNU General Public License has appeared, then you can specify +that version instead if you wish.) Do not make any other change in +these notices. + + Once this change is made in a given copy, it is irreversible for +that copy, so the ordinary GNU General Public License applies to all +subsequent copies and derivative works made from that copy. + + This option is useful when you wish to copy part of the code of +the Library into a program that is not a library. + + 4. You may copy and distribute the Library (or a portion or +derivative of it, under Section 2) in object code or executable form +under the terms of Sections 1 and 2 above provided that you accompany +it with the complete corresponding machine-readable source code, which +must be distributed under the terms of Sections 1 and 2 above on a +medium customarily used for software interchange. + + If distribution of object code is made by offering access to copy +from a designated place, then offering equivalent access to copy the +source code from the same place satisfies the requirement to +distribute the source code, even though third parties are not +compelled to copy the source along with the object code. + + 5. A program that contains no derivative of any portion of the +Library, but is designed to work with the Library by being compiled or +linked with it, is called a "work that uses the Library". Such a +work, in isolation, is not a derivative work of the Library, and +therefore falls outside the scope of this License. + + However, linking a "work that uses the Library" with the Library +creates an executable that is a derivative of the Library (because it +contains portions of the Library), rather than a "work that uses the +library". The executable is therefore covered by this License. +Section 6 states terms for distribution of such executables. + + When a "work that uses the Library" uses material from a header file +that is part of the Library, the object code for the work may be a +derivative work of the Library even though the source code is not. +Whether this is true is especially significant if the work can be +linked without the Library, or if the work is itself a library. The +threshold for this to be true is not precisely defined by law. + + If such an object file uses only numerical parameters, data +structure layouts and accessors, and small macros and small inline +functions (ten lines or less in length), then the use of the object +file is unrestricted, regardless of whether it is legally a derivative +work. (Executables containing this object code plus portions of the +Library will still fall under Section 6.) + + Otherwise, if the work is a derivative of the Library, you may +distribute the object code for the work under the terms of Section 6. +Any executables containing that work also fall under Section 6, +whether or not they are linked directly with the Library itself. + + 6. As an exception to the Sections above, you may also compile or +link a "work that uses the Library" with the Library to produce a +work containing portions of the Library, and distribute that work +under terms of your choice, provided that the terms permit +modification of the work for the customer's own use and reverse +engineering for debugging such modifications. + + You must give prominent notice with each copy of the work that the +Library is used in it and that the Library and its use are covered by +this License. You must supply a copy of this License. If the work +during execution displays copyright notices, you must include the +copyright notice for the Library among them, as well as a reference +directing the user to the copy of this License. Also, you must do one +of these things: + + a) Accompany the work with the complete corresponding + machine-readable source code for the Library including whatever + changes were used in the work (which must be distributed under + Sections 1 and 2 above); and, if the work is an executable linked + with the Library, with the complete machine-readable "work that + uses the Library", as object code and/or source code, so that the + user can modify the Library and then relink to produce a modified + executable containing the modified Library. (It is understood + that the user who changes the contents of definitions files in the + Library will not necessarily be able to recompile the application + to use the modified definitions.) + + b) Accompany the work with a written offer, valid for at + least three years, to give the same user the materials + specified in Subsection 6a, above, for a charge no more + than the cost of performing this distribution. + + c) If distribution of the work is made by offering access to copy + from a designated place, offer equivalent access to copy the above + specified materials from the same place. + + d) Verify that the user has already received a copy of these + materials or that you have already sent this user a copy. + + For an executable, the required form of the "work that uses the +Library" must include any data and utility programs needed for +reproducing the executable from it. However, as a special exception, +the source code distributed need not include anything that is normally +distributed (in either source or binary form) with the major +components (compiler, kernel, and so on) of the operating system on +which the executable runs, unless that component itself accompanies +the executable. + + It may happen that this requirement contradicts the license +restrictions of other proprietary libraries that do not normally +accompany the operating system. Such a contradiction means you cannot +use both them and the Library together in an executable that you +distribute. + + 7. You may place library facilities that are a work based on the +Library side-by-side in a single library together with other library +facilities not covered by this License, and distribute such a combined +library, provided that the separate distribution of the work based on +the Library and of the other library facilities is otherwise +permitted, and provided that you do these two things: + + a) Accompany the combined library with a copy of the same work + based on the Library, uncombined with any other library + facilities. This must be distributed under the terms of the + Sections above. + + b) Give prominent notice with the combined library of the fact + that part of it is a work based on the Library, and explaining + where to find the accompanying uncombined form of the same work. + + 8. You may not copy, modify, sublicense, link with, or distribute +the Library except as expressly provided under this License. Any +attempt otherwise to copy, modify, sublicense, link with, or +distribute the Library is void, and will automatically terminate your +rights under this License. However, parties who have received copies, +or rights, from you under this License will not have their licenses +terminated so long as such parties remain in full compliance. + + 9. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Library or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Library (or any work based on the +Library), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Library or works based on it. + + 10. Each time you redistribute the Library (or any work based on the +Library), the recipient automatically receives a license from the +original licensor to copy, distribute, link with or modify the Library +subject to these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 11. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Library at all. For example, if a patent +license would not permit royalty-free redistribution of the Library by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Library. + +If any portion of this section is held invalid or unenforceable under any +particular circumstance, the balance of the section is intended to apply, +and the section as a whole is intended to apply in other circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 12. If the distribution and/or use of the Library is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Library under this License may add +an explicit geographical distribution limitation excluding those countries, +so that distribution is permitted only in or among countries not thus +excluded. In such case, this License incorporates the limitation as if +written in the body of this License. + + 13. The Free Software Foundation may publish revised and/or new +versions of the Library General Public License from time to time. +Such new versions will be similar in spirit to the present version, +but may differ in detail to address new problems or concerns. + +Each version is given a distinguishing version number. If the Library +specifies a version number of this License which applies to it and +"any later version", you have the option of following the terms and +conditions either of that version or of any later version published by +the Free Software Foundation. If the Library does not specify a +license version number, you may choose any version ever published by +the Free Software Foundation. + + 14. If you wish to incorporate parts of the Library into other free +programs whose distribution conditions are incompatible with these, +write to the author to ask for permission. For software which is +copyrighted by the Free Software Foundation, write to the Free +Software Foundation; we sometimes make exceptions for this. Our +decision will be guided by the two goals of preserving the free status +of all derivatives of our free software and of promoting the sharing +and reuse of software generally. + + NO WARRANTY + + 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO +WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. +EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR +OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY +KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE +LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME +THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN +WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY +AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU +FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR +CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE +LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING +RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A +FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF +SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Libraries + + If you develop a new library, and you want it to be of the greatest +possible use to the public, we recommend making it free software that +everyone can redistribute and change. You can do so by permitting +redistribution under these terms (or, alternatively, under the terms of the +ordinary General Public License). + + To apply these terms, attach the following notices to the library. It is +safest to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least the +"copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +Also add information on how to contact you by electronic and paper mail. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the library, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the + library `Frob' (a library for tweaking knobs) written by James Random Hacker. + + , 1 April 1990 + Ty Coon, President of Vice + +That's all there is to it! diff --git a/external/bubblewrap/LICENSE b/external/bubblewrap/LICENSE new file mode 120000 index 0000000..d24842f --- /dev/null +++ b/external/bubblewrap/LICENSE @@ -0,0 +1 @@ +COPYING \ No newline at end of file diff --git a/external/bubblewrap/Makefile-bwrap.am b/external/bubblewrap/Makefile-bwrap.am new file mode 100644 index 0000000..f4c2a35 --- /dev/null +++ b/external/bubblewrap/Makefile-bwrap.am @@ -0,0 +1,13 @@ + +bwrap_SOURCES = \ + $(bwrap_srcpath)/bubblewrap.c \ + $(bwrap_srcpath)/bind-mount.h \ + $(bwrap_srcpath)/bind-mount.c \ + $(bwrap_srcpath)/network.h \ + $(bwrap_srcpath)/network.c \ + $(bwrap_srcpath)/utils.h \ + $(bwrap_srcpath)/utils.c \ + $(NULL) + +bwrap_CFLAGS = $(AM_CFLAGS) +bwrap_LDFLAGS = $(SELINUX_LIBS) diff --git a/external/bubblewrap/Makefile-docs.am b/external/bubblewrap/Makefile-docs.am new file mode 100644 index 0000000..c70fa62 --- /dev/null +++ b/external/bubblewrap/Makefile-docs.am @@ -0,0 +1,17 @@ +XSLTPROC = xsltproc + +XSLTPROC_FLAGS = \ + --nonet \ + --stringparam man.output.quietly 1 \ + --stringparam funcsynopsis.style ansi \ + --stringparam man.th.extra1.suppress 1 \ + --stringparam man.authors.section.enabled 0 \ + --stringparam man.copyright.section.enabled 0 + +.xml.1: + $(XSLTPROC) $(XSLTPROC_FLAGS) http://docbook.sourceforge.net/release/xsl/current/manpages/docbook.xsl $< + +if ENABLE_MAN +man_MANS = bwrap.1 +CLEANFILES += $(man_MANS) +endif diff --git a/external/bubblewrap/Makefile.am b/external/bubblewrap/Makefile.am new file mode 100644 index 0000000..6e47b5f --- /dev/null +++ b/external/bubblewrap/Makefile.am @@ -0,0 +1,31 @@ +AM_CFLAGS = $(WARN_CFLAGS) +CLEANFILES = + +GITIGNOREFILES = build-aux/ gtk-doc.make config.h.in aclocal.m4 + +bin_PROGRAMS = bwrap + +bwrap_srcpath := $(srcdir) +include Makefile-bwrap.am + +install-exec-hook: +if PRIV_MODE_SETUID + $(SUDO_BIN) chown root $(DESTDIR)$(bindir)/bwrap + $(SUDO_BIN) chmod u+s $(DESTDIR)$(bindir)/bwrap +else +if PRIV_MODE_FILECAPS + $(SUDO_BIN) setcap cap_sys_admin,cap_net_admin,cap_sys_chroot,cap_setuid,cap_setgid+ep $(DESTDIR)$(bindir)/bwrap +endif +endif + +include Makefile-docs.am + +TESTS = tests/test-basic.sh +TESTS_ENVIRONMENT = PATH=$$(cd $(top_builddir) && pwd):$${PATH} + +if ENABLE_BASH_COMPLETION +bashcompletiondir = $(BASH_COMPLETION_DIR) +dist_bashcompletion_DATA = completions/bash/bwrap +endif + +-include $(top_srcdir)/git.mk diff --git a/external/bubblewrap/README.md b/external/bubblewrap/README.md new file mode 100644 index 0000000..2dd65bc --- /dev/null +++ b/external/bubblewrap/README.md @@ -0,0 +1,191 @@ +Bubblewrap +========== + +Many container runtime tools like `systemd-nspawn`, `docker`, +etc. focus on providing infrastructure for system administrators and +orchestration tools (e.g. Kubernetes) to run containers. + +These tools are not suitable to give to unprivileged users, because it +is trivial to turn such access into to a fully privileged root shell +on the host. + +User namespaces +--------------- + +There is an effort in the Linux kernel called +[user namespaces](https://www.google.com/search?q=user+namespaces+site%3Ahttps%3A%2F%2Flwn.net) +which attempts to allow unprivileged users to use container features. +While significant progress has been made, there are +[still concerns](https://lwn.net/Articles/673597/) about it, and +it is not available to unprivileged users in several production distributions +such as CentOS/Red Hat Enterprise Linux 7, Debian Jessie, etc. + +See for example +[CVE-2016-3135](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2016-3135) +which is a local root vulnerability introduced by userns. +[This March 2016 post](https://lkml.org/lkml/2016/3/9/555) has some +more discussion. + +Bubblewrap could be viewed as setuid implementation of a *subset* of +user namespaces. Emphasis on subset - specifically relevant to the +above CVE, bubblewrap does not allow control over iptables. + +The original bubblewrap code existed before user namespaces - it inherits code from +[xdg-app helper](https://cgit.freedesktop.org/xdg-app/xdg-app/tree/common/xdg-app-helper.c) +which in turn distantly derives from +[linux-user-chroot](https://git.gnome.org/browse/linux-user-chroot). + +Security +-------- + +The maintainers of this tool believe that it does not, even when used +in combination with typical software installed on that distribution, +allow privilege escalation. It may increase the ability of a logged +in user to perform denial of service attacks, however. + +In particular, bubblewrap uses `PR_SET_NO_NEW_PRIVS` to turn off +setuid binaries, which is the [traditional way](https://en.wikipedia.org/wiki/Chroot#Limitations) to get out of things +like chroots. + +Users +----- + +This program can be shared by all container tools which perform +non-root operation, such as: + + - [xdg-app](https://cgit.freedesktop.org/xdg-app/xdg-app) + - [rpm-ostree unprivileged](https://github.com/projectatomic/rpm-ostree/pull/209) + +We would also like to see this be available in Kubernetes/OpenShift +clusters. Having the ability for unprivileged users to use container +features would make it significantly easier to do interactive +debugging scenarios and the like. + +Usage +----- + +bubblewrap works by creating a new, completely empty, mount +namespace where the root is on a tmpfs that is invisible from the +host, and will be automatically cleaned up when the last process +exists. You can then use commandline options to construct the root +filesystem and process environment and command to run in the +namespace. + +A simple example is +``` +bwrap --ro-bind / / bash +``` +This will create a read-only bind mount of the host root at the +sandbox root, and then start a bash. + +Another simple example would be a read-write chroot operation: +``` +bwrap --bind /some/chroot/dir / bash +``` + +A more complex example is to run a with a custom (readonly) /usr, +but your own (tmpfs) data, running in a PID and network namespace: + +``` +bwrap --ro-bind /usr /usr \ + --tmpfs /tmp \ + --proc /proc \ + --dev /dev \ + --ro-bind /etc/resolv.conf /etc/resolv.conf \ + --symlink usr/lib /lib \ + --symlink usr/lib64 /lib64 \ + --symlink usr/bin /bin \ + --symlink usr/sbin /sbin \ + --chdir / \ + --unshare-pid \ + --unshare-net \ + --dir /run/user/$(id -u) \ + --setenv XDG_RUNTIME_DIR "/run/user/`id -u`" \ + /bin/sh +``` + +Sandboxing +---------- + +The goal of bubblewrap is to run an application in a sandbox, where it +has restricted access to parts of the operating system or user data +such as the home directory. + +bubblewrap always creates a new mount namespace, and the user can specify +exactly what parts of the filesystem should be visible in the sandbox. +Any such directories you specify mounted `nodev` by default, and can be made readonly. + +Additionally you can use these kernel features: + +User namespaces ([CLONE_NEWUSER](http://linux.die.net/man/2/clone)): This hides all but the current uid and gid from the +sandbox. You can also change what the value of uid/gid should be in the sandbox. + +IPC namespaces ([CLONE_NEWIPC](http://linux.die.net/man/2/clone)): The sandbox will get its own copy of all the +different forms of IPCs, like SysV shared memory and semaphores. + +PID namespaces ([CLONE_NEWPID](http://linux.die.net/man/2/clone)): The sandbox will not see any processes outside the sandbox. Additionally, bubblewrap will run a trivial pid1 inside your container to handle the requirements of reaping children in the sandbox. .This avoids what is known now as the [Docker pid 1 problem](https://blog.phusion.nl/2015/01/20/docker-and-the-pid-1-zombie-reaping-problem/). + + +Network namespaces ([CLONE_NEWNET](http://linux.die.net/man/2/clone)): The sandbox will not see the network. Instead it will have its own network namespace with only a loopback device. + +UTS namespace ([CLONE_NEWUTS](http://linux.die.net/man/2/clone)): The sandbox will have its own hostname. + +Seccomp filters: You can pass in seccomp filters that limit which syscalls can be done in the sandbox. For more information, see [Seccomp](https://en.wikipedia.org/wiki/Seccomp). + +Related project comparison: Firejail +------------------------------------ + +[Firejail](https://github.com/netblue30/firejail/tree/master/src/firejail) is +similar to xdg-app before bubblewrap was split out in that it combines +a setuid tool with a lot of desktop-specific sandboxing features. For +example, Firejail knows about Pulseaudio, whereas bubblewrap does not. + +The bubblewrap authors believe it's much easier to audit a small +setuid program, and keep features such as Pulseaudio filtering as an +unprivileged process, as now occurs in xdg-app. + +Also, @cgwalters thinks trying to +[whitelist file paths](https://github.com/netblue30/firejail/blob/37a5a3545ef6d8d03dad8bbd888f53e13274c9e5/src/firejail/fs_whitelist.c#L176) +is a bad idea given the myriad ways users have to manipulate paths, +and the myriad ways in which system administrators may configure a +system. The bubblewrap approach is to only retain a few specific +Linux capabilities such as `CAP_SYS_ADMIN`, but to always access the +filesystem as the invoking uid. This entirely closes +[TOCTOCU attacks](https://cwe.mitre.org/data/definitions/367.html) and +such. + +Related project comparison: Sandstorm.io +---------------------------------------- + +[Sandstorm.io](https://sandstorm.io/) also has a setuid helper +process. @cgwalters believes their setuid code is fairly good, but it +could still make sense to unify on bubblewrap as a setuid core. That +hasn't been ruled out, but neither is it being actively pursued today. + +Related project comparison: runc/binctr +---------------------------------------- + +[runc](https://github.com/opencontainers/runc) is similar to +[systemd nspawn](https://www.freedesktop.org/software/systemd/man/systemd-nspawn.html) +in that it is tooling intended to be invoked by root. There is an +effort to have runc optionally use +[user namespaces](https://github.com/opencontainers/runc/issues/38), +but no plans for any setuid support. + +The bubblewrap authors believe that runc and systemd-nspawn are not +designed to be made setuid and are distant from supporting such a +mode. + +[binctr](https://github.com/jfrazelle/binctr) is just a wrapper for +runc, so inherits all of its design tradeoffs. + +Whats with the name ?! +---------------------- + +The name bubblewrap was chosen to convey that this +tool runs as the parent of the application (so wraps it in some sense) and creates +a protective layer (the sandbox) around it. + +![](bubblewrap.jpg) + +(Bubblewrap cat by [dancing_stupidity](https://www.flickr.com/photos/27549668@N03/)) diff --git a/external/bubblewrap/autogen.sh b/external/bubblewrap/autogen.sh new file mode 100755 index 0000000..4674ca8 --- /dev/null +++ b/external/bubblewrap/autogen.sh @@ -0,0 +1,19 @@ +#!/bin/sh + +test -n "$srcdir" || srcdir=`dirname "$0"` +test -n "$srcdir" || srcdir=. + +olddir=`pwd` +cd $srcdir + +if ! (autoreconf --version >/dev/null 2>&1); then + echo "*** No autoreconf found, please install it ***" + exit 1 +fi + +mkdir -p m4 + +autoreconf --force --install --verbose + +cd $olddir +test -n "$NOCONFIGURE" || "$srcdir/configure" "$@" diff --git a/external/bubblewrap/bind-mount.c b/external/bubblewrap/bind-mount.c new file mode 100644 index 0000000..72fd3c5 --- /dev/null +++ b/external/bubblewrap/bind-mount.c @@ -0,0 +1,285 @@ +/* bubblewrap + * Copyright (C) 2016 Alexander Larsson + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see . + * + */ + +#include "config.h" + +#include + +#include "utils.h" +#include "bind-mount.h" + +static char * +skip_line (char *line) +{ + while (*line != 0 && *line != '\n') + line++; + + if (*line == '\n') + line++; + + return line; +} + +static char * +skip_token (char *line, bool eat_whitespace) +{ + while (*line != ' ' && *line != '\n') + line++; + + if (eat_whitespace && *line == ' ') + line++; + + return line; +} + +static char * +unescape_mountpoint (const char *escaped, ssize_t len) +{ + char *unescaped, *res; + const char *end; + + if (len < 0) + len = strlen (escaped); + end = escaped + len; + + unescaped = res = xmalloc (len + 1); + while (escaped < end) + { + if (*escaped == '\\') + { + *unescaped++ = + ((escaped[1] - '0') << 6) | + ((escaped[2] - '0') << 3) | + ((escaped[3] - '0') << 0); + escaped += 4; + } + else + { + *unescaped++ = *escaped++; + } + } + *unescaped = 0; + return res; +} + +static char * +get_mountinfo (int proc_fd, + const char *mountpoint) +{ + char *line_mountpoint, *line_mountpoint_end; + cleanup_free char *mountinfo = NULL; + cleanup_free char *free_me = NULL; + char *line, *line_start; + char *res = NULL; + int i; + + if (mountpoint[0] != '/') + { + cleanup_free char *cwd = getcwd (NULL, 0); + if (cwd == NULL) + die_oom (); + + mountpoint = free_me = strconcat3 (cwd, "/", mountpoint); + } + + mountinfo = load_file_at (proc_fd, "self/mountinfo"); + if (mountinfo == NULL) + return NULL; + + line = mountinfo; + + while (*line != 0) + { + cleanup_free char *unescaped = NULL; + + line_start = line; + for (i = 0; i < 4; i++) + line = skip_token (line, TRUE); + line_mountpoint = line; + line = skip_token (line, FALSE); + line_mountpoint_end = line; + line = skip_line (line); + + unescaped = unescape_mountpoint (line_mountpoint, line_mountpoint_end - line_mountpoint); + if (strcmp (mountpoint, unescaped) == 0) + { + res = line_start; + line[-1] = 0; + /* Keep going, because we want to return the *last* match */ + } + } + + if (res) + return xstrdup (res); + return NULL; +} + +static unsigned long +get_mountflags (int proc_fd, + const char *mountpoint) +{ + cleanup_free char *line = NULL; + char *token, *end_token; + int i; + unsigned long flags = 0; + static const struct { int flag; + char *name; + } flags_data[] = { + { 0, "rw" }, + { MS_RDONLY, "ro" }, + { MS_NOSUID, "nosuid" }, + { MS_NODEV, "nodev" }, + { MS_NOEXEC, "noexec" }, + { MS_NOATIME, "noatime" }, + { MS_NODIRATIME, "nodiratime" }, + { MS_RELATIME, "relatime" }, + { 0, NULL } + }; + + line = get_mountinfo (proc_fd, mountpoint); + if (line == NULL) + return 0; + + token = line; + for (i = 0; i < 5; i++) + token = skip_token (token, TRUE); + + end_token = skip_token (token, FALSE); + *end_token = 0; + + do + { + end_token = strchr (token, ','); + if (end_token != NULL) + *end_token = 0; + + for (i = 0; flags_data[i].name != NULL; i++) + if (strcmp (token, flags_data[i].name) == 0) + flags |= flags_data[i].flag; + + if (end_token) + token = end_token + 1; + else + token = NULL; + } + while (token != NULL); + + return flags; +} + + +static char ** +get_submounts (int proc_fd, + const char *parent_mount) +{ + char *mountpoint, *mountpoint_end; + char **submounts; + int i, n_submounts, submounts_size; + cleanup_free char *mountinfo = NULL; + char *line; + + mountinfo = load_file_at (proc_fd, "self/mountinfo"); + if (mountinfo == NULL) + return NULL; + + submounts_size = 8; + n_submounts = 0; + submounts = xmalloc (sizeof (char *) * submounts_size); + + line = mountinfo; + + while (*line != 0) + { + cleanup_free char *unescaped = NULL; + for (i = 0; i < 4; i++) + line = skip_token (line, TRUE); + mountpoint = line; + line = skip_token (line, FALSE); + mountpoint_end = line; + line = skip_line (line); + *mountpoint_end = 0; + + unescaped = unescape_mountpoint (mountpoint, -1); + + if (has_path_prefix (unescaped, parent_mount)) + { + if (n_submounts + 1 >= submounts_size) + { + submounts_size *= 2; + submounts = xrealloc (submounts, sizeof (char *) * submounts_size); + } + submounts[n_submounts++] = xstrdup (unescaped); + } + } + + submounts[n_submounts] = NULL; + + return submounts; +} + +int +bind_mount (int proc_fd, + const char *src, + const char *dest, + bind_option_t options) +{ + bool readonly = (options & BIND_READONLY) != 0; + bool devices = (options & BIND_DEVICES) != 0; + bool recursive = (options & BIND_RECURSIVE) != 0; + unsigned long current_flags, new_flags; + int i; + + if (mount (src, dest, NULL, MS_MGC_VAL | MS_BIND | (recursive ? MS_REC : 0), NULL) != 0) + return 1; + + current_flags = get_mountflags (proc_fd, dest); + + new_flags = current_flags | (devices ? 0 : MS_NODEV) | MS_NOSUID | (readonly ? MS_RDONLY : 0); + if (new_flags != current_flags && + mount ("none", dest, + NULL, MS_MGC_VAL | MS_BIND | MS_REMOUNT | new_flags, NULL) != 0) + return 3; + + /* We need to work around the fact that a bind mount does not apply the flags, so we need to manually + * apply the flags to all submounts in the recursive case. + * Note: This does not apply the flags to mounts which are later propagated into this namespace. + */ + if (recursive) + { + cleanup_strv char **submounts = get_submounts (proc_fd, dest); + if (submounts == NULL) + return 4; + + for (i = 0; submounts[i] != NULL; i++) + { + current_flags = get_mountflags (proc_fd, submounts[i]); + new_flags = current_flags | (devices ? 0 : MS_NODEV) | MS_NOSUID | (readonly ? MS_RDONLY : 0); + if (new_flags != current_flags && + mount ("none", submounts[i], + NULL, MS_MGC_VAL | MS_BIND | MS_REMOUNT | new_flags, NULL) != 0) + { + /* If we can't read the mountpoint we can't remount it, but that should + be safe to ignore because its not something the user can access. */ + if (errno != EACCES) + return 5; + } + } + } + + return 0; +} diff --git a/external/bubblewrap/bind-mount.h b/external/bubblewrap/bind-mount.h new file mode 100644 index 0000000..c763763 --- /dev/null +++ b/external/bubblewrap/bind-mount.h @@ -0,0 +1,30 @@ +/* bubblewrap + * Copyright (C) 2016 Alexander Larsson + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see . + * + */ + +#pragma once + +typedef enum { + BIND_READONLY = (1 << 0), + BIND_DEVICES = (1 << 2), + BIND_RECURSIVE = (1 << 3), +} bind_option_t; + +int bind_mount (int proc_fd, + const char *src, + const char *dest, + bind_option_t options); diff --git a/external/bubblewrap/bubblewrap.c b/external/bubblewrap/bubblewrap.c new file mode 100644 index 0000000..da737f7 --- /dev/null +++ b/external/bubblewrap/bubblewrap.c @@ -0,0 +1,1727 @@ +/* bubblewrap + * Copyright (C) 2016 Alexander Larsson + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see . + * + */ + +#include "config.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "utils.h" +#include "network.h" +#include "bind-mount.h" + +#ifndef CLONE_NEWCGROUP +#define CLONE_NEWCGROUP 0x02000000 /* New cgroup namespace */ +#endif + +/* Globals to avoid having to use getuid(), since the uid/gid changes during runtime */ +static uid_t uid; +static gid_t gid; +static bool is_privileged; +static const char *argv0; +static const char *host_tty_dev; +static int proc_fd = -1; +static char *opt_exec_label = NULL; +static char *opt_file_label = NULL; + +typedef enum { + SETUP_BIND_MOUNT, + SETUP_RO_BIND_MOUNT, + SETUP_DEV_BIND_MOUNT, + SETUP_MOUNT_PROC, + SETUP_MOUNT_DEV, + SETUP_MOUNT_TMPFS, + SETUP_MOUNT_MQUEUE, + SETUP_MAKE_DIR, + SETUP_MAKE_FILE, + SETUP_MAKE_BIND_FILE, + SETUP_MAKE_SYMLINK, +} SetupOpType; + +typedef struct _SetupOp SetupOp; + +struct _SetupOp +{ + SetupOpType type; + const char *source; + const char *dest; + int fd; + SetupOp *next; +}; + +typedef struct _LockFile LockFile; + +struct _LockFile +{ + const char *path; + LockFile *next; +}; + +static SetupOp *ops = NULL; +static SetupOp *last_op = NULL; +static LockFile *lock_files = NULL; +static LockFile *last_lock_file = NULL; + +enum { + PRIV_SEP_OP_DONE, + PRIV_SEP_OP_BIND_MOUNT, + PRIV_SEP_OP_PROC_MOUNT, + PRIV_SEP_OP_TMPFS_MOUNT, + PRIV_SEP_OP_DEVPTS_MOUNT, + PRIV_SEP_OP_MQUEUE_MOUNT, +}; + +typedef struct +{ + uint32_t op; + uint32_t flags; + uint32_t arg1_offset; + uint32_t arg2_offset; +} PrivSepOp; + +static SetupOp * +setup_op_new (SetupOpType type) +{ + SetupOp *op = xcalloc (sizeof (SetupOp)); + + op->type = type; + op->fd = -1; + if (last_op != NULL) + last_op->next = op; + else + ops = op; + + last_op = op; + return op; +} + +static LockFile * +lock_file_new (const char *path) +{ + LockFile *lock = xcalloc (sizeof (LockFile)); + + lock->path = path; + if (last_lock_file != NULL) + last_lock_file->next = lock; + else + lock_files = lock; + + last_lock_file = lock; + return lock; +} + + +static void +usage (int ecode, FILE *out) +{ + fprintf (out, "usage: %s [OPTIONS...] COMMAND [ARGS...]\n\n", argv0); + + fprintf (out, + " --help Print this help\n" + " --version Print version\n" + " --args FD Parse nul-separated args from FD\n" + " --unshare-user Create new user namespace (may be automatically implied if not setuid)\n" + " --unshare-ipc Create new ipc namespace\n" + " --unshare-pid Create new pid namespace\n" + " --unshare-net Create new network namespace\n" + " --unshare-uts Create new uts namespace\n" + " --unshare-cgroup Create new cgroup namespace\n" + " --unshare-cgroup-try Create new cgroup namespace if possible else continue by skipping it\n" + " --uid UID Custom uid in the sandbox (requires --unshare-user)\n" + " --gid GID Custon gid in the sandbox (requires --unshare-user)\n" + " --chdir DIR Change directory to DIR\n" + " --setenv VAR VALUE Set an environment variable\n" + " --unsetenv VAR Unset an environment variable\n" + " --lock-file DEST Take a lock on DEST while sandbox is running\n" + " --sync-fd FD Keep this fd open while sandbox is running\n" + " --bind SRC DEST Bind mount the host path SRC on DEST\n" + " --dev-bind SRC DEST Bind mount the host path SRC on DEST, allowing device access\n" + " --ro-bind SRC DEST Bind mount the host path SRC readonly on DEST\n" + " --exec-label LABEL Exec Label for the sandbox\n" + " --file-label LABEL File label for temporary sandbox content\n" + " --proc DEST Mount procfs on DEST\n" + " --dev DEST Mount new dev on DEST\n" + " --tmpfs DEST Mount new tmpfs on DEST\n" + " --mqueue DEST Mount new mqueue on DEST\n" + " --dir DEST Create dir at DEST\n" + " --file FD DEST Copy from FD to dest DEST\n" + " --bind-data FD DEST Copy from FD to file which is bind-mounted on DEST\n" + " --symlink SRC DEST Create symlink at DEST with target SRC\n" + " --seccomp FD Load and use seccomp rules from FD\n" + " --pid-file DEST Store container execute process PID in file DEST\n" + ); + exit (ecode); +} + +static void +block_sigchild (void) +{ + sigset_t mask; + + sigemptyset (&mask); + sigaddset (&mask, SIGCHLD); + + if (sigprocmask (SIG_BLOCK, &mask, NULL) == -1) + die_with_error ("sigprocmask"); +} + +static void +unblock_sigchild (void) +{ + sigset_t mask; + + sigemptyset (&mask); + sigaddset (&mask, SIGCHLD); + + if (sigprocmask (SIG_UNBLOCK, &mask, NULL) == -1) + die_with_error ("sigprocmask"); +} + +/* Closes all fd:s except 0,1,2 and the passed in array of extra fds */ +static int +close_extra_fds (void *data, int fd) +{ + int *extra_fds = (int *) data; + int i; + + for (i = 0; extra_fds[i] != -1; i++) + if (fd == extra_fds[i]) + return 0; + + if (fd <= 2) + return 0; + + close (fd); + return 0; +} + +/* This stays around for as long as the initial process in the app does + * and when that exits it exits, propagating the exit status. We do this + * by having pid 1 in the sandbox detect this exit and tell the monitor + * the exit status via a eventfd. We also track the exit of the sandbox + * pid 1 via a signalfd for SIGCHLD, and exit with an error in this case. + * This is to catch e.g. problems during setup. */ +static void +monitor_child (int event_fd) +{ + int res; + uint64_t val; + ssize_t s; + int signal_fd; + sigset_t mask; + struct pollfd fds[2]; + int num_fds; + struct signalfd_siginfo fdsi; + int dont_close[] = { event_fd, -1 }; + + /* Close all extra fds in the monitoring process. + Any passed in fds have been passed on to the child anyway. */ + fdwalk (proc_fd, close_extra_fds, dont_close); + + sigemptyset (&mask); + sigaddset (&mask, SIGCHLD); + + signal_fd = signalfd (-1, &mask, SFD_CLOEXEC | SFD_NONBLOCK); + if (signal_fd == -1) + die_with_error ("Can't create signalfd"); + + num_fds = 1; + fds[0].fd = signal_fd; + fds[0].events = POLLIN; + if (event_fd != -1) + { + fds[1].fd = event_fd; + fds[1].events = POLLIN; + num_fds++; + } + + while (1) + { + fds[0].revents = fds[1].revents = 0; + res = poll (fds, num_fds, -1); + if (res == -1 && errno != EINTR) + die_with_error ("poll"); + + /* Always read from the eventfd first, if pid 2 died then pid 1 often + * dies too, and we could race, reporting that first and we'd lose + * the real exit status. */ + if (event_fd != -1) + { + s = read (event_fd, &val, 8); + if (s == -1 && errno != EINTR && errno != EAGAIN) + die_with_error ("read eventfd"); + else if (s == 8) + exit ((int) val - 1); + } + + s = read (signal_fd, &fdsi, sizeof (struct signalfd_siginfo)); + if (s == -1 && errno != EINTR && errno != EAGAIN) + { + die_with_error ("read signalfd"); + } + else if (s == sizeof (struct signalfd_siginfo)) + { + if (fdsi.ssi_signo != SIGCHLD) + die ("Read unexpected signal\n"); + exit (fdsi.ssi_status); + } + } +} + +/* This is pid 1 in the app sandbox. It is needed because we're using + * pid namespaces, and someone has to reap zombies in it. We also detect + * when the initial process (pid 2) dies and report its exit status to + * the monitor so that it can return it to the original spawner. + * + * When there are no other processes in the sandbox the wait will return + * ECHILD, and we then exit pid 1 to clean up the sandbox. */ +static int +do_init (int event_fd, pid_t initial_pid) +{ + int initial_exit_status = 1; + LockFile *lock; + + for (lock = lock_files; lock != NULL; lock = lock->next) + { + int fd = open (lock->path, O_RDONLY | O_CLOEXEC); + if (fd == -1) + die_with_error ("Unable to open lock file %s", lock->path); + + struct flock l = { + .l_type = F_RDLCK, + .l_whence = SEEK_SET, + .l_start = 0, + .l_len = 0 + }; + + if (fcntl (fd, F_SETLK, &l) < 0) + die_with_error ("Unable to lock file %s", lock->path); + + /* Keep fd open to hang on to lock */ + } + + while (TRUE) + { + pid_t child; + int status; + + child = wait (&status); + if (child == initial_pid && event_fd != -1) + { + uint64_t val; + int res UNUSED; + + if (WIFEXITED (status)) + initial_exit_status = WEXITSTATUS (status); + + val = initial_exit_status + 1; + res = write (event_fd, &val, 8); + /* Ignore res, if e.g. the parent died and closed event_fd + we don't want to error out here */ + } + + if (child == -1 && errno != EINTR) + { + if (errno != ECHILD) + die_with_error ("init wait()"); + break; + } + } + + return initial_exit_status; +} + +/* low 32bit caps needed */ +#define REQUIRED_CAPS_0 (CAP_TO_MASK (CAP_SYS_ADMIN) | CAP_TO_MASK (CAP_SYS_CHROOT) | CAP_TO_MASK (CAP_NET_ADMIN) | CAP_TO_MASK (CAP_SETUID) | CAP_TO_MASK (CAP_SETGID)) +/* high 32bit caps needed */ +#define REQUIRED_CAPS_1 0 + +static void +acquire_caps (void) +{ + struct __user_cap_header_struct hdr = { _LINUX_CAPABILITY_VERSION_3, 0 }; + struct __user_cap_data_struct data[2] = { { 0 } }; + + if (capget (&hdr, data) < 0) + die_with_error ("capget failed"); + + if (((data[0].effective & REQUIRED_CAPS_0) == REQUIRED_CAPS_0) && + ((data[0].permitted & REQUIRED_CAPS_0) == REQUIRED_CAPS_0) && + ((data[1].effective & REQUIRED_CAPS_1) == REQUIRED_CAPS_1) && + ((data[1].permitted & REQUIRED_CAPS_1) == REQUIRED_CAPS_1)) + is_privileged = TRUE; + + if (getuid () != geteuid ()) + { + /* Tell kernel not clear capabilities when dropping root */ + if (prctl (PR_SET_KEEPCAPS, 1, 0, 0, 0) < 0) + die_with_error ("prctl(PR_SET_KEEPCAPS) failed"); + + /* Drop root uid, but retain the required permitted caps */ + if (setuid (getuid ()) < 0) + die_with_error ("unable to drop privs"); + } + + if (is_privileged) + { + /* Drop all non-require capabilities */ + data[0].effective = REQUIRED_CAPS_0; + data[0].permitted = REQUIRED_CAPS_0; + data[0].inheritable = 0; + data[1].effective = REQUIRED_CAPS_1; + data[1].permitted = REQUIRED_CAPS_1; + data[1].inheritable = 0; + if (capset (&hdr, data) < 0) + die_with_error ("capset failed"); + } + /* Else, we try unprivileged user namespaces */ + + /* We need the process to be dumpable, or we can't access /proc/self/uid_map */ + if (prctl (PR_SET_DUMPABLE, 1, 0, 0, 0) < 0) + die_with_error ("prctl(PR_SET_DUMPABLE) failed"); +} + +static void +drop_caps (void) +{ + struct __user_cap_header_struct hdr = { _LINUX_CAPABILITY_VERSION_3, 0 }; + struct __user_cap_data_struct data[2] = { { 0 } }; + + if (!is_privileged) + return; + + if (capset (&hdr, data) < 0) + die_with_error ("capset failed"); +} + +static char * +get_newroot_path (const char *path) +{ + while (*path == '/') + path++; + return strconcat ("/newroot/", path); +} + +static char * +get_oldroot_path (const char *path) +{ + while (*path == '/') + path++; + return strconcat ("/oldroot/", path); +} + +static void +write_uid_gid_map (uid_t sandbox_uid, + uid_t parent_uid, + uid_t sandbox_gid, + uid_t parent_gid, + pid_t pid, + bool deny_groups, + bool map_root) +{ + cleanup_free char *uid_map = NULL; + cleanup_free char *gid_map = NULL; + cleanup_free char *dir = NULL; + cleanup_fd int dir_fd = -1; + + if (pid == -1) + dir = xstrdup ("self"); + else + dir = xasprintf ("%d", pid); + + dir_fd = openat (proc_fd, dir, O_RDONLY | O_PATH); + if (dir_fd < 0) + die_with_error ("open /proc/%s failed", dir); + + if (map_root && parent_uid != 0 && sandbox_uid != 0) + uid_map = xasprintf ("0 0 1\n" + "%d %d 1\n", sandbox_uid, parent_uid); + else + uid_map = xasprintf ("%d %d 1\n", sandbox_uid, parent_uid); + + if (write_file_at (dir_fd, "uid_map", uid_map) != 0) + die_with_error ("setting up uid map"); + + if (deny_groups && + write_file_at (dir_fd, "setgroups", "deny\n") != 0) + die_with_error ("error writing to setgroups"); + + if (map_root && parent_gid != 0 && sandbox_gid != 0) + gid_map = xasprintf ("0 0 1\n" + "%d %d 1\n", sandbox_gid, parent_gid); + else + gid_map = xasprintf ("%d %d 1\n", sandbox_gid, parent_gid); + + if (write_file_at (dir_fd, "gid_map", gid_map) != 0) + die_with_error ("setting up gid map"); +} + +static void +privileged_op (int privileged_op_socket, + uint32_t op, + uint32_t flags, + const char *arg1, + const char *arg2) +{ + if (privileged_op_socket != -1) + { + uint32_t buffer[2048]; /* 8k, but is int32 to guarantee nice alignment */ + PrivSepOp *op_buffer = (PrivSepOp *) buffer; + size_t buffer_size = sizeof (PrivSepOp); + uint32_t arg1_offset = 0, arg2_offset = 0; + if (arg1 != NULL) + { + arg1_offset = buffer_size; + buffer_size += strlen (arg1) + 1; + } + if (arg2 != NULL) + { + arg2_offset = buffer_size; + buffer_size += strlen (arg2) + 1; + } + + if (buffer_size >= sizeof (buffer)) + die ("privilege separation operation to large"); + + op_buffer->op = op; + op_buffer->flags = flags; + op_buffer->arg1_offset = arg1_offset; + op_buffer->arg2_offset = arg2_offset; + if (arg1 != NULL) + strcpy ((char *) buffer + arg1_offset, arg1); + if (arg2 != NULL) + strcpy ((char *) buffer + arg2_offset, arg2); + + if (write (privileged_op_socket, buffer, buffer_size) != buffer_size) + die ("Can't write to privileged_op_socket"); + + if (read (privileged_op_socket, buffer, 1) != 1) + die ("Can't read from privileged_op_socket"); + + return; + } + + switch (op) + { + case PRIV_SEP_OP_DONE: + break; + + case PRIV_SEP_OP_BIND_MOUNT: + /* We always bind directories recursively, otherwise this would let us + access files that are otherwise covered on the host */ + if (bind_mount (proc_fd, arg1, arg2, BIND_RECURSIVE | flags) != 0) + die_with_error ("Can't bind mount %s on %s", arg1, arg2); + break; + + case PRIV_SEP_OP_PROC_MOUNT: + if (mount ("proc", arg1, "proc", MS_MGC_VAL | MS_NOSUID | MS_NOEXEC | MS_NODEV, NULL) != 0) + die_with_error ("Can't mount proc on %s", arg1); + break; + + case PRIV_SEP_OP_TMPFS_MOUNT: + { + cleanup_free char *opt = label_mount ("mode=0755", opt_file_label); + if (mount ("tmpfs", arg1, "tmpfs", MS_MGC_VAL | MS_NOSUID | MS_NODEV, opt) != 0) + die_with_error ("Can't mount tmpfs on %s", arg1); + break; + } + + case PRIV_SEP_OP_DEVPTS_MOUNT: + if (mount ("devpts", arg1, "devpts", MS_MGC_VAL | MS_NOSUID | MS_NOEXEC, + "newinstance,ptmxmode=0666,mode=620") != 0) + die_with_error ("Can't mount devpts on %s", arg1); + break; + + case PRIV_SEP_OP_MQUEUE_MOUNT: + if (mount ("mqueue", arg1, "mqueue", 0, NULL) != 0) + die_with_error ("Can't mount mqueue on %s", arg1); + break; + + default: + die ("Unexpected privileged op %d", op); + } +} + +static void +setup_newroot (bool unshare_pid, + int privileged_op_socket) +{ + SetupOp *op; + + for (op = ops; op != NULL; op = op->next) + { + cleanup_free char *source = NULL; + cleanup_free char *dest = NULL; + int source_mode = 0; + int i; + + if (op->source && + op->type != SETUP_MAKE_SYMLINK) + { + source = get_oldroot_path (op->source); + source_mode = get_file_mode (source); + if (source_mode < 0) + die_with_error ("Can't get type of source %s", op->source); + } + + if (op->dest) + { + dest = get_newroot_path (op->dest); + if (mkdir_with_parents (dest, 0755, FALSE) != 0) + die_with_error ("Can't mkdir parents for %s", op->dest); + } + + switch (op->type) + { + case SETUP_RO_BIND_MOUNT: + case SETUP_DEV_BIND_MOUNT: + case SETUP_BIND_MOUNT: + if (source_mode == S_IFDIR) + { + if (mkdir (dest, 0755) != 0 && errno != EEXIST) + die_with_error ("Can't mkdir %s", op->dest); + } + else if (ensure_file (dest, 0666) != 0) + die_with_error ("Can't create file at %s", op->dest); + + privileged_op (privileged_op_socket, + PRIV_SEP_OP_BIND_MOUNT, + (op->type == SETUP_RO_BIND_MOUNT ? BIND_READONLY : 0) | + (op->type == SETUP_DEV_BIND_MOUNT ? BIND_DEVICES : 0), + source, dest); + break; + + case SETUP_MOUNT_PROC: + if (mkdir (dest, 0755) != 0 && errno != EEXIST) + die_with_error ("Can't mkdir %s", op->dest); + + if (unshare_pid) + { + /* Our own procfs */ + privileged_op (privileged_op_socket, + PRIV_SEP_OP_PROC_MOUNT, 0, + dest, NULL); + } + else + { + /* Use system procfs, as we share pid namespace anyway */ + privileged_op (privileged_op_socket, + PRIV_SEP_OP_BIND_MOUNT, 0, + "oldroot/proc", dest); + } + + /* There are a bunch of weird old subdirs of /proc that could potentially be + problematic (for instance /proc/sysrq-trigger lets you shut down the machine + if you have write access). We should not have access to these as a non-privileged + user, but lets cover them anyway just to make sure */ + const char *cover_proc_dirs[] = { "sys", "sysrq-trigger", "irq", "bus" }; + for (i = 0; i < N_ELEMENTS (cover_proc_dirs); i++) + { + cleanup_free char *subdir = strconcat3 (dest, "/", cover_proc_dirs[i]); + privileged_op (privileged_op_socket, + PRIV_SEP_OP_BIND_MOUNT, BIND_READONLY, + subdir, subdir); + } + + break; + + case SETUP_MOUNT_DEV: + if (mkdir (dest, 0755) != 0 && errno != EEXIST) + die_with_error ("Can't mkdir %s", op->dest); + + privileged_op (privileged_op_socket, + PRIV_SEP_OP_TMPFS_MOUNT, 0, + dest, NULL); + + static const char *const devnodes[] = { "null", "zero", "full", "random", "urandom", "tty" }; + for (i = 0; i < N_ELEMENTS (devnodes); i++) + { + cleanup_free char *node_dest = strconcat3 (dest, "/", devnodes[i]); + cleanup_free char *node_src = strconcat ("/oldroot/dev/", devnodes[i]); + if (create_file (node_dest, 0666, NULL) != 0) + die_with_error ("Can't create file %s/%s", op->dest, devnodes[i]); + privileged_op (privileged_op_socket, + PRIV_SEP_OP_BIND_MOUNT, BIND_DEVICES, + node_src, node_dest); + } + + static const char *const stdionodes[] = { "stdin", "stdout", "stderr" }; + for (i = 0; i < N_ELEMENTS (stdionodes); i++) + { + cleanup_free char *target = xasprintf ("/proc/self/fd/%d", i); + cleanup_free char *node_dest = strconcat3 (dest, "/", stdionodes[i]); + if (symlink (target, node_dest) < 0) + die_with_error ("Can't create symlink %s/%s", op->dest, stdionodes[i]); + } + + { + cleanup_free char *pts = strconcat (dest, "/pts"); + cleanup_free char *ptmx = strconcat (dest, "/ptmx"); + cleanup_free char *shm = strconcat (dest, "/shm"); + + if (mkdir (shm, 0755) == -1) + die_with_error ("Can't create %s/shm", op->dest); + + if (mkdir (pts, 0755) == -1) + die_with_error ("Can't create %s/devpts", op->dest); + privileged_op (privileged_op_socket, + PRIV_SEP_OP_DEVPTS_MOUNT, BIND_DEVICES, + pts, NULL); + + if (symlink ("pts/ptmx", ptmx) != 0) + die_with_error ("Can't make symlink at %s/ptmx", op->dest); + } + + /* If stdout is a tty, that means the sandbox can write to the + outside-sandbox tty. In that case we also create a /dev/console + that points to this tty device. This should not cause any more + access than we already have, and it makes ttyname() work in the + sandbox. */ + if (host_tty_dev != NULL && *host_tty_dev != 0) + { + cleanup_free char *src_tty_dev = strconcat ("/oldroot", host_tty_dev); + cleanup_free char *dest_console = strconcat (dest, "/console"); + + if (create_file (dest_console, 0666, NULL) != 0) + die_with_error ("creating %s/console", op->dest); + + privileged_op (privileged_op_socket, + PRIV_SEP_OP_BIND_MOUNT, BIND_DEVICES, + src_tty_dev, dest_console); + } + + break; + + case SETUP_MOUNT_TMPFS: + if (mkdir (dest, 0755) != 0 && errno != EEXIST) + die_with_error ("Can't mkdir %s", op->dest); + + privileged_op (privileged_op_socket, + PRIV_SEP_OP_TMPFS_MOUNT, 0, + dest, NULL); + break; + + case SETUP_MOUNT_MQUEUE: + if (mkdir (dest, 0755) != 0 && errno != EEXIST) + die_with_error ("Can't mkdir %s", op->dest); + + privileged_op (privileged_op_socket, + PRIV_SEP_OP_MQUEUE_MOUNT, 0, + dest, NULL); + break; + + case SETUP_MAKE_DIR: + if (mkdir (dest, 0755) != 0 && errno != EEXIST) + die_with_error ("Can't mkdir %s", op->dest); + + break; + + case SETUP_MAKE_FILE: + { + cleanup_fd int dest_fd = -1; + + dest_fd = creat (dest, 0666); + if (dest_fd == -1) + die_with_error ("Can't create file %s", op->dest); + + if (copy_file_data (op->fd, dest_fd) != 0) + die_with_error ("Can't write data to file %s", op->dest); + + close (op->fd); + } + break; + + case SETUP_MAKE_BIND_FILE: + { + cleanup_fd int dest_fd = -1; + char tempfile[] = "/bindfileXXXXXX"; + + dest_fd = mkstemp (tempfile); + if (dest_fd == -1) + die_with_error ("Can't create tmpfile for %s", op->dest); + + if (copy_file_data (op->fd, dest_fd) != 0) + die_with_error ("Can't write data to file %s", op->dest); + + close (op->fd); + + if (ensure_file (dest, 0666) != 0) + die_with_error ("Can't create file at %s", op->dest); + + privileged_op (privileged_op_socket, + PRIV_SEP_OP_BIND_MOUNT, + 0, tempfile, dest); + } + break; + + case SETUP_MAKE_SYMLINK: + if (symlink (op->source, dest) != 0) + die_with_error ("Can't make symlink at %s", op->dest); + break; + + default: + die ("Unexpected type %d", op->type); + } + } + privileged_op (privileged_op_socket, + PRIV_SEP_OP_DONE, 0, NULL, NULL); +} + +static const char * +resolve_string_offset (void *buffer, + size_t buffer_size, + uint32_t offset) +{ + if (offset == 0) + return NULL; + + if (offset > buffer_size) + die ("Invalid string offset %d (buffer size %zd)", offset, buffer_size); + + return (const char *) buffer + offset; +} + +static uint32_t +read_priv_sec_op (int read_socket, + void *buffer, + size_t buffer_size, + uint32_t *flags, + const char **arg1, + const char **arg2) +{ + const PrivSepOp *op = (const PrivSepOp *) buffer; + ssize_t rec_len; + + do + rec_len = read (read_socket, buffer, buffer_size - 1); + while (rec_len == -1 && errno == EINTR); + + if (rec_len < 0) + die_with_error ("Can't read from unprivileged helper"); + + if (rec_len < sizeof (PrivSepOp)) + die ("Invalid size %zd from unprivileged helper", rec_len); + + /* Guarantee zero termination of any strings */ + ((char *) buffer)[rec_len] = 0; + + *flags = op->flags; + *arg1 = resolve_string_offset (buffer, rec_len, op->arg1_offset); + *arg2 = resolve_string_offset (buffer, rec_len, op->arg2_offset); + + return op->op; +} + +char *opt_chdir_path = NULL; +bool opt_unshare_user = FALSE; +bool opt_unshare_pid = FALSE; +bool opt_unshare_ipc = FALSE; +bool opt_unshare_net = FALSE; +bool opt_unshare_uts = FALSE; +bool opt_unshare_cgroup = FALSE; +bool opt_unshare_cgroup_try = FALSE; +bool opt_needs_devpts = FALSE; +uid_t opt_sandbox_uid = -1; +gid_t opt_sandbox_gid = -1; +int opt_sync_fd = -1; +int opt_seccomp_fd = -1; +char *opt_pid_file = NULL; + + +static void +parse_args_recurse (int *argcp, + char ***argvp, + bool in_file, + int *total_parsed_argc_p) +{ + SetupOp *op; + int argc = *argcp; + char **argv = *argvp; + /* I can't imagine a case where someone wants more than this. + * If you do...you should be able to pass multiple files + * via a single tmpfs and linking them there, etc. + * + * We're adding this hardening due to precedent from + * http://googleprojectzero.blogspot.com/2014/08/the-poisoned-nul-byte-2014-edition.html + * + * I picked 9000 because the Internet told me to and it was hard to + * resist. + */ + static const uint32_t MAX_ARGS = 9000; + + if (*total_parsed_argc_p > MAX_ARGS) + die ("Exceeded maximum number of arguments %u", MAX_ARGS); + + while (argc > 0) + { + const char *arg = argv[0]; + + if (strcmp (arg, "--help") == 0) + { + usage (EXIT_SUCCESS, stdout); + } + else if (strcmp (arg, "--version") == 0) + { + printf ("%s\n", PACKAGE_STRING); + exit (0); + } + else if (strcmp (arg, "--args") == 0) + { + int the_fd; + char *endptr; + char *data, *p; + char *data_end; + size_t data_len; + cleanup_free char **data_argv = NULL; + char **data_argv_copy; + int data_argc; + int i; + + if (in_file) + die ("--args not supported in arguments file"); + + if (argc < 2) + die ("--args takes an argument"); + + the_fd = strtol (argv[1], &endptr, 10); + if (argv[1][0] == 0 || endptr[0] != 0 || the_fd < 0) + die ("Invalid fd: %s", argv[1]); + + data = load_file_data (the_fd, &data_len); + if (data == NULL) + die_with_error ("Can't read --args data"); + + data_end = data + data_len; + data_argc = 0; + + p = data; + while (p != NULL && p < data_end) + { + data_argc++; + (*total_parsed_argc_p)++; + if (*total_parsed_argc_p > MAX_ARGS) + die ("Exceeded maximum number of arguments %u", MAX_ARGS); + p = memchr (p, 0, data_end - p); + if (p != NULL) + p++; + } + + data_argv = xcalloc (sizeof (char *) * (data_argc + 1)); + + i = 0; + p = data; + while (p != NULL && p < data_end) + { + /* Note: load_file_data always adds a nul terminator, so this is safe + * even for the last string. */ + data_argv[i++] = p; + p = memchr (p, 0, data_end - p); + if (p != NULL) + p++; + } + + data_argv_copy = data_argv; /* Don't change data_argv, we need to free it */ + parse_args_recurse (&data_argc, &data_argv_copy, TRUE, total_parsed_argc_p); + + argv += 1; + argc -= 1; + } + else if (strcmp (arg, "--unshare-user") == 0) + { + opt_unshare_user = TRUE; + } + else if (strcmp (arg, "--unshare-ipc") == 0) + { + opt_unshare_ipc = TRUE; + } + else if (strcmp (arg, "--unshare-pid") == 0) + { + opt_unshare_pid = TRUE; + } + else if (strcmp (arg, "--unshare-net") == 0) + { + opt_unshare_net = TRUE; + } + else if (strcmp (arg, "--unshare-uts") == 0) + { + opt_unshare_uts = TRUE; + } + else if (strcmp (arg, "--unshare-cgroup") == 0) + { + opt_unshare_cgroup = TRUE; + } + else if (strcmp (arg, "--unshare-cgroup-try") == 0) + { + opt_unshare_cgroup_try = TRUE; + } + else if (strcmp (arg, "--chdir") == 0) + { + if (argc < 2) + die ("--chdir takes one argument"); + + opt_chdir_path = argv[1]; + argv++; + argc--; + } + else if (strcmp (arg, "--bind") == 0) + { + if (argc < 3) + die ("--bind takes two arguments"); + + op = setup_op_new (SETUP_BIND_MOUNT); + op->source = canonicalize_file_name (argv[1]); + if (op->source == NULL) + die_with_error ("Can't find source path %s", argv[1]); + op->dest = argv[2]; + + argv += 2; + argc -= 2; + } + else if (strcmp (arg, "--ro-bind") == 0) + { + if (argc < 3) + die ("--ro-bind takes two arguments"); + + op = setup_op_new (SETUP_RO_BIND_MOUNT); + op->source = canonicalize_file_name (argv[1]); + if (op->source == NULL) + die_with_error ("Can't find source path %s", argv[1]); + op->dest = argv[2]; + + argv += 2; + argc -= 2; + } + else if (strcmp (arg, "--dev-bind") == 0) + { + if (argc < 3) + die ("--dev-bind takes two arguments"); + + op = setup_op_new (SETUP_DEV_BIND_MOUNT); + op->source = canonicalize_file_name (argv[1]); + if (op->source == NULL) + die_with_error ("Can't find source path %s", argv[1]); + op->dest = argv[2]; + + argv += 2; + argc -= 2; + } + else if (strcmp (arg, "--proc") == 0) + { + if (argc < 2) + die ("--proc takes an argument"); + + op = setup_op_new (SETUP_MOUNT_PROC); + op->dest = argv[1]; + + argv += 1; + argc -= 1; + } + else if (strcmp (arg, "--exec-label") == 0) + { + if (argc < 2) + die ("--exec-label takes an argument"); + opt_exec_label = argv[1]; + die_unless_label_valid (opt_exec_label); + + argv += 1; + argc -= 1; + } + else if (strcmp (arg, "--file-label") == 0) + { + if (argc < 2) + die ("--file-label takes an argument"); + opt_file_label = argv[1]; + die_unless_label_valid (opt_file_label); + if (label_create_file (opt_file_label)) + die_with_error ("--file-label setup failed"); + + argv += 1; + argc -= 1; + } + else if (strcmp (arg, "--dev") == 0) + { + if (argc < 2) + die ("--dev takes an argument"); + + op = setup_op_new (SETUP_MOUNT_DEV); + op->dest = argv[1]; + opt_needs_devpts = TRUE; + + argv += 1; + argc -= 1; + } + else if (strcmp (arg, "--tmpfs") == 0) + { + if (argc < 2) + die ("--tmpfs takes an argument"); + + op = setup_op_new (SETUP_MOUNT_TMPFS); + op->dest = argv[1]; + + argv += 1; + argc -= 1; + } + else if (strcmp (arg, "--mqueue") == 0) + { + if (argc < 2) + die ("--mqueue takes an argument"); + + op = setup_op_new (SETUP_MOUNT_MQUEUE); + op->dest = argv[1]; + + argv += 1; + argc -= 1; + } + else if (strcmp (arg, "--dir") == 0) + { + if (argc < 2) + die ("--dir takes an argument"); + + op = setup_op_new (SETUP_MAKE_DIR); + op->dest = argv[1]; + + argv += 1; + argc -= 1; + } + else if (strcmp (arg, "--file") == 0) + { + int file_fd; + char *endptr; + + if (argc < 3) + die ("--file takes two arguments"); + + file_fd = strtol (argv[1], &endptr, 10); + if (argv[1][0] == 0 || endptr[0] != 0 || file_fd < 0) + die ("Invalid fd: %s", argv[1]); + + op = setup_op_new (SETUP_MAKE_FILE); + op->fd = file_fd; + op->dest = argv[2]; + + argv += 2; + argc -= 2; + } + else if (strcmp (arg, "--bind-data") == 0) + { + int file_fd; + char *endptr; + + if (argc < 3) + die ("--bind-data takes two arguments"); + + file_fd = strtol (argv[1], &endptr, 10); + if (argv[1][0] == 0 || endptr[0] != 0 || file_fd < 0) + die ("Invalid fd: %s", argv[1]); + + op = setup_op_new (SETUP_MAKE_BIND_FILE); + op->fd = file_fd; + op->dest = argv[2]; + + argv += 2; + argc -= 2; + } + else if (strcmp (arg, "--symlink") == 0) + { + if (argc < 3) + die ("--symlink takes two arguments"); + + op = setup_op_new (SETUP_MAKE_SYMLINK); + op->source = argv[1]; + op->dest = argv[2]; + + argv += 2; + argc -= 2; + } + else if (strcmp (arg, "--lock-file") == 0) + { + if (argc < 2) + die ("--lock-file takes an argument"); + + (void) lock_file_new (argv[1]); + + argv += 1; + argc -= 1; + } + else if (strcmp (arg, "--sync-fd") == 0) + { + int the_fd; + char *endptr; + + if (argc < 2) + die ("--sync-fd takes an argument"); + + the_fd = strtol (argv[1], &endptr, 10); + if (argv[1][0] == 0 || endptr[0] != 0 || the_fd < 0) + die ("Invalid fd: %s", argv[1]); + + opt_sync_fd = the_fd; + + argv += 1; + argc -= 1; + } + else if (strcmp (arg, "--seccomp") == 0) + { + int the_fd; + char *endptr; + + if (argc < 2) + die ("--seccomp takes an argument"); + + the_fd = strtol (argv[1], &endptr, 10); + if (argv[1][0] == 0 || endptr[0] != 0 || the_fd < 0) + die ("Invalid fd: %s", argv[1]); + + opt_seccomp_fd = the_fd; + + argv += 1; + argc -= 1; + } + else if (strcmp (arg, "--setenv") == 0) + { + if (argc < 3) + die ("--setenv takes two arguments"); + + xsetenv (argv[1], argv[2], 1); + + argv += 2; + argc -= 2; + } + else if (strcmp (arg, "--unsetenv") == 0) + { + if (argc < 2) + die ("--unsetenv takes an argument"); + + xunsetenv (argv[1]); + + argv += 1; + argc -= 1; + } + else if (strcmp (arg, "--uid") == 0) + { + int the_uid; + char *endptr; + + if (argc < 2) + die ("--uid takes an argument"); + + the_uid = strtol (argv[1], &endptr, 10); + if (argv[1][0] == 0 || endptr[0] != 0 || the_uid < 0) + die ("Invalid uid: %s", argv[1]); + + opt_sandbox_uid = the_uid; + + argv += 1; + argc -= 1; + } + else if (strcmp (arg, "--gid") == 0) + { + int the_gid; + char *endptr; + + if (argc < 2) + die ("--gid takes an argument"); + + the_gid = strtol (argv[1], &endptr, 10); + if (argv[1][0] == 0 || endptr[0] != 0 || the_gid < 0) + die ("Invalid gid: %s", argv[1]); + + opt_sandbox_gid = the_gid; + + argv += 1; + argc -= 1; + } + else if (strcmp (arg, "--pid-file") == 0) + { + if (argc < 2) + die ("--pid-file takes an argument"); + + opt_pid_file = argv[1]; + + argv += 1; + argc -= 1; + } + else if (*arg == '-') + { + die ("Unknown option %s", arg); + } + else + { + break; + } + + argv++; + argc--; + } + + *argcp = argc; + *argvp = argv; +} + +static void +parse_args (int *argcp, + char ***argvp) +{ + int total_parsed_argc = *argcp; + + parse_args_recurse (argcp, argvp, FALSE, &total_parsed_argc); +} + +int +bwrap_main (int argc, + char **argv) +{ + mode_t old_umask; + cleanup_free char *base_path = NULL; + int clone_flags; + char *old_cwd = NULL; + pid_t pid; + int event_fd = -1; + int child_wait_fd = -1; + const char *new_cwd; + uid_t ns_uid; + gid_t ns_gid; + struct stat sbuf; + uint64_t val; + int res UNUSED; + + /* Get the (optional) capabilities we need, drop root */ + acquire_caps (); + + /* Never gain any more privs during exec */ + if (prctl (PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0) < 0) + die_with_error ("prctl(PR_SET_NO_NEW_CAPS) failed"); + + /* The initial code is run with high permissions + (i.e. CAP_SYS_ADMIN), so take lots of care. */ + + argv0 = argv[0]; + + if (isatty (1)) + host_tty_dev = ttyname (1); + + argv++; + argc--; + + if (argc == 0) + usage (EXIT_FAILURE, stderr); + + parse_args (&argc, &argv); + + /* We have to do this if we weren't installed setuid, so let's just DWIM */ + if (!is_privileged) + opt_unshare_user = TRUE; + + if (argc == 0) + usage (EXIT_FAILURE, stderr); + + __debug__ (("Creating root mount point\n")); + + uid = getuid (); + if (opt_sandbox_uid == -1) + opt_sandbox_uid = uid; + gid = getgid (); + if (opt_sandbox_gid == -1) + opt_sandbox_gid = gid; + + if (!opt_unshare_user && opt_sandbox_uid != uid) + die ("Specifying --uid requires --unshare-user"); + + if (!opt_unshare_user && opt_sandbox_gid != gid) + die ("Specifying --gid requires --unshare-user"); + + /* We need to read stuff from proc during the pivot_root dance, etc. + Lets keep a fd to it open */ + proc_fd = open ("/proc", O_RDONLY | O_PATH); + if (proc_fd == -1) + die_with_error ("Can't open /proc"); + + /* We need *some* mountpoint where we can mount the root tmpfs. + We first try in /run, and if that fails, try in /tmp. */ + base_path = xasprintf ("/run/user/%d/.bubblewrap", uid); + if (mkdir (base_path, 0755) && errno != EEXIST) + { + free (base_path); + base_path = xasprintf ("/tmp/.bubblewrap-%d", uid); + if (mkdir (base_path, 0755) && errno != EEXIST) + die_with_error ("Creating root mountpoint failed"); + } + + __debug__ (("creating new namespace\n")); + + if (opt_unshare_pid) + { + event_fd = eventfd (0, EFD_CLOEXEC | EFD_NONBLOCK); + if (event_fd == -1) + die_with_error ("eventfd()"); + } + + /* We block sigchild here so that we can use signalfd in the monitor. */ + block_sigchild (); + + clone_flags = SIGCHLD | CLONE_NEWNS; + if (opt_unshare_user) + clone_flags |= CLONE_NEWUSER; + if (opt_unshare_pid) + clone_flags |= CLONE_NEWPID; + if (opt_unshare_net) + clone_flags |= CLONE_NEWNET; + if (opt_unshare_ipc) + clone_flags |= CLONE_NEWIPC; + if (opt_unshare_uts) + clone_flags |= CLONE_NEWUTS; + if (opt_unshare_cgroup) + { + if (stat ("/proc/self/ns/cgroup", &sbuf)) + { + if (errno == ENOENT) + die ("Cannot create new cgroup namespace because the kernel does not support it"); + else + die_with_error ("stat on /proc/self/ns/cgroup failed"); + } + clone_flags |= CLONE_NEWCGROUP; + } + if (opt_unshare_cgroup_try) + if (!stat ("/proc/self/ns/cgroup", &sbuf)) + clone_flags |= CLONE_NEWCGROUP; + + child_wait_fd = eventfd (0, EFD_CLOEXEC); + if (child_wait_fd == -1) + die_with_error ("eventfd()"); + + pid = raw_clone (clone_flags, NULL); + if (pid == -1) + { + if (opt_unshare_user) + { + if (errno == EINVAL) + die ("Creating new namespace failed, likely because the kernel does not support user namespaces. bwrap must be installed setuid on such systems."); + else if (errno == EPERM && !is_privileged) + die ("No permissions to creating new namespace, likely because the kernel does not allow non-privileged user namespaces. On e.g. debian this can be enabled with 'sysctl kernel.unprivileged_userns_clone=1'."); + } + + die_with_error ("Creating new namespace failed"); + } + + ns_uid = opt_sandbox_uid; + ns_gid = opt_sandbox_gid; + + if (pid != 0) + { + if (is_privileged && opt_unshare_user) + { + /* Map the uid/gid 0 if opt_needs_devpts, as otherwise + * mounting it will fail. + * Due to this non-direct mapping we need to have set[ug]id + * caps in the parent namespaces, and thus we need to write + * the map in the parent namespace, not the child. */ + write_uid_gid_map (ns_uid, uid, + ns_gid, gid, + pid, TRUE, opt_needs_devpts); + } + + /* Initial launched process, wait for exec:ed command to exit */ + + if (opt_pid_file) { + /* drop old left over pid file */ + remove(opt_pid_file); + + int pid_file_fd = open(opt_pid_file, O_CREAT | O_WRONLY, 0644); + char pid_str[20]; + snprintf(pid_str, 20, "%i", pid); + write(pid_file_fd, pid_str, strlen(pid_str)); + close(pid_file_fd); + } + + /* We don't need any caps in the launcher, drop them immediately. */ + drop_caps (); + + /* Let child run */ + val = 1; + res = write (child_wait_fd, &val, 8); + /* Ignore res, if e.g. the child died and closed child_wait_fd we don't want to error out here */ + close (child_wait_fd); + + monitor_child (event_fd); + exit (0); /* Should not be reached, but better safe... */ + } + + /* Wait for the parent to init uid/gid maps and drop caps */ + res = read (child_wait_fd, &val, 8); + close (child_wait_fd); + + if (opt_unshare_net && loopback_setup () != 0) + die ("Can't create loopback device"); + + ns_uid = opt_sandbox_uid; + ns_gid = opt_sandbox_gid; + if (!is_privileged && opt_unshare_user) + { + /* In the unprivileged case we have to write the uid/gid maps in + * the child, because we have no caps in the parent */ + + if (opt_needs_devpts) + { + /* This is a bit hacky, but we need to first map the real uid/gid to + 0, otherwise we can't mount the devpts filesystem because root is + not mapped. Later we will create another child user namespace and + map back to the real uid */ + ns_uid = 0; + ns_gid = 0; + } + + write_uid_gid_map (ns_uid, uid, + ns_gid, gid, + -1, TRUE, FALSE); + } + + old_umask = umask (0); + + /* Mark everything as slave, so that we still + * receive mounts from the real root, but don't + * propagate mounts to the real root. */ + if (mount (NULL, "/", NULL, MS_SLAVE | MS_REC, NULL) < 0) + die_with_error ("Failed to make / slave"); + + /* Create a tmpfs which we will use as / in the namespace */ + if (mount ("", base_path, "tmpfs", MS_NODEV | MS_NOSUID, NULL) != 0) + die_with_error ("Failed to mount tmpfs"); + + old_cwd = get_current_dir_name (); + + /* Chdir to the new root tmpfs mount. This will be the CWD during + the entire setup. Access old or new root via "oldroot" and "newroot". */ + if (chdir (base_path) != 0) + die_with_error ("chdir base_path"); + + /* We create a subdir "$base_path/newroot" for the new root, that + * way we can pivot_root to base_path, and put the old root at + * "$base_path/oldroot". This avoids problems accessing the oldroot + * dir if the user requested to bind mount something over / */ + + if (mkdir ("newroot", 0755)) + die_with_error ("Creating newroot failed"); + + if (mkdir ("oldroot", 0755)) + die_with_error ("Creating oldroot failed"); + + if (pivot_root (base_path, "oldroot")) + die_with_error ("pivot_root"); + + if (chdir ("/") != 0) + die_with_error ("chdir / (base path)"); + + if (is_privileged) + { + pid_t child; + int privsep_sockets[2]; + + if (socketpair (AF_UNIX, SOCK_SEQPACKET | SOCK_CLOEXEC, 0, privsep_sockets) != 0) + die_with_error ("Can't create privsep socket"); + + child = fork (); + if (child == -1) + die_with_error ("Can't fork unprivileged helper"); + + if (child == 0) + { + /* Unprivileged setup process */ + drop_caps (); + close (privsep_sockets[0]); + setup_newroot (opt_unshare_pid, privsep_sockets[1]); + exit (0); + } + else + { + uint32_t buffer[2048]; /* 8k, but is int32 to guarantee nice alignment */ + uint32_t op, flags; + const char *arg1, *arg2; + cleanup_fd int unpriv_socket = -1; + + unpriv_socket = privsep_sockets[0]; + close (privsep_sockets[1]); + + do + { + op = read_priv_sec_op (unpriv_socket, buffer, sizeof (buffer), + &flags, &arg1, &arg2); + privileged_op (-1, op, flags, arg1, arg2); + if (write (unpriv_socket, buffer, 1) != 1) + die ("Can't write to op_socket"); + } + while (op != PRIV_SEP_OP_DONE); + + /* Continue post setup */ + } + } + else + { + setup_newroot (opt_unshare_pid, -1); + } + + /* The old root better be rprivate or we will send unmount events to the parent namespace */ + if (mount ("oldroot", "oldroot", NULL, MS_REC | MS_PRIVATE, NULL) != 0) + die_with_error ("Failed to make old root rprivate"); + + if (umount2 ("oldroot", MNT_DETACH)) + die_with_error ("unmount old root"); + + if (opt_unshare_user && + (ns_uid != opt_sandbox_uid || ns_gid != opt_sandbox_gid)) + { + /* Now that devpts is mounted and we've no need for mount + permissions we can create a new userspace and map our uid + 1:1 */ + + if (unshare (CLONE_NEWUSER)) + die_with_error ("unshare user ns"); + + write_uid_gid_map (opt_sandbox_uid, ns_uid, + opt_sandbox_gid, ns_gid, + -1, FALSE, FALSE); + } + + /* Now make /newroot the real root */ + if (chdir ("/newroot") != 0) + die_with_error ("chdir newroot"); + if (chroot ("/newroot") != 0) + die_with_error ("chroot /newroot"); + if (chdir ("/") != 0) + die_with_error ("chdir /"); + + /* Now we have everything we need CAP_SYS_ADMIN for, so drop it */ + drop_caps (); + + if (opt_seccomp_fd != -1) + { + cleanup_free char *seccomp_data = NULL; + size_t seccomp_len; + struct sock_fprog prog; + + seccomp_data = load_file_data (opt_seccomp_fd, &seccomp_len); + if (seccomp_data == NULL) + die_with_error ("Can't read seccomp data"); + + if (seccomp_len % 8 != 0) + die ("Invalide seccomp data, must be multiple of 8"); + + prog.len = seccomp_len / 8; + prog.filter = (struct sock_filter *) seccomp_data; + + close (opt_seccomp_fd); + + if (prctl (PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &prog) != 0) + die_with_error ("prctl(PR_SET_SECCOMP)"); + } + + umask (old_umask); + + new_cwd = "/"; + if (opt_chdir_path) + { + if (chdir (opt_chdir_path)) + die_with_error ("Can't chdir to %s", opt_chdir_path); + new_cwd = opt_chdir_path; + } + else if (chdir (old_cwd) == 0) + { + /* If the old cwd is mapped in the sandbox, go there */ + new_cwd = old_cwd; + } + else + { + /* If the old cwd is not mapped, go to home */ + const char *home = getenv ("HOME"); + if (home != NULL && + chdir (home) == 0) + new_cwd = home; + } + xsetenv ("PWD", new_cwd, 1); + free (old_cwd); + + __debug__ (("forking for child\n")); + + if (opt_unshare_pid || lock_files != NULL || opt_sync_fd != -1) + { + /* We have to have a pid 1 in the pid namespace, because + * otherwise we'll get a bunch of zombies as nothing reaps + * them. Alternatively if we're using sync_fd or lock_files we + * need some process to own these. + */ + + pid = fork (); + if (pid == -1) + die_with_error ("Can't fork for pid 1"); + + if (pid != 0) + { + /* Close fds in pid 1, except stdio and optionally event_fd + (for syncing pid 2 lifetime with monitor_child) and + opt_sync_fd (for syncing sandbox lifetime with outside + process). + Any other fds will been passed on to the child though. */ + { + int dont_close[3]; + int j = 0; + if (event_fd != -1) + dont_close[j++] = event_fd; + if (opt_sync_fd != -1) + dont_close[j++] = opt_sync_fd; + dont_close[j++] = -1; + fdwalk (proc_fd, close_extra_fds, dont_close); + } + + return do_init (event_fd, pid); + } + } + + __debug__ (("launch executable %s\n", argv[0])); + + if (proc_fd != -1) + close (proc_fd); + + if (opt_sync_fd != -1) + close (opt_sync_fd); + + /* We want sigchild in the child */ + unblock_sigchild (); + + if (label_exec (opt_exec_label) == -1) + die_with_error ("label_exec %s", argv[0]); + + if (execvp (argv[0], argv) == -1) + die_with_error ("execvp %s", argv[0]); + + return 0; +} diff --git a/external/bubblewrap/bubblewrap.jpg b/external/bubblewrap/bubblewrap.jpg new file mode 100644 index 0000000..d3c243d Binary files /dev/null and b/external/bubblewrap/bubblewrap.jpg differ diff --git a/external/bubblewrap/bwrap.xml b/external/bubblewrap/bwrap.xml new file mode 100644 index 0000000..0d5e236 --- /dev/null +++ b/external/bubblewrap/bwrap.xml @@ -0,0 +1,266 @@ + + + + + + bwrap + Project Atomic + + + Developer + Alexander + Larsson + + + Developer + Colin + Walters + + + + + + bwrap + 1 + User Commands + + + + bwrap + container setup utility + + + + +bwrap +OPTION +COMMAND + + + +Description + + bwrap is a privileged helper for container setup. You + are unlikely to use it directly from the commandline, although that is possible. + + + It works by creating a new, completely empty, filesystem namespace where the root + is on a tmpfs that is invisible from the host, and which will be automatically + cleaned up when the last process exists. You can then use commandline options to + construct the root filesystem and process environment for the command to run in + the namespace. + + + By default, bwrap creates a new user namespace for the sandbox. + Optionally it also sets up new ipc, pid, network and uts namespaces. The application + in the sandbox can be made to run with a different UID and GID. + + + If needed (e.g. when using a PID namespace) bwrap + is running a minimal pid 1 process in the sandbox that is + responsible for reaping zombies. It also detects when the initial + application process (pid 2) dies and reports its exit status back to + the original spawner. The pid 1 process exits to clean up the + sandbox when there are no other processes in the sandbox left. + + + +Options + + When options are used multiple times, the last option wins, unless otherwise + specified. + + General options: + + + + Print help and exit + + + + Print version + + + + + Parse nul-separated arguments from the given file descriptor. + This option can be used multiple times to parse options from + multiple sources. + + + + Options related to kernel namespaces: + + + + Don't create a new user namespace + + + + Create a new ipc namespace + + + + Create a new pid namespace + + + + Create a new network namespace + + + + Create a new uts namespace + + + + Create a new cgroup namespace + + + + Create a new cgroup namespace if possible else skip it + + + + Use a custom user id in the sandbox (incompatible with ) + + + + Use a custom group id in the sandbox (incompatible with ) + + + Options about environment setup: + + + + Change directory to DIR + + + + Set an environment variable + + + + Unset an environment variable + + + Options for monitoring the sandbox from the outside: + + + + + Take a lock on DEST while the sandbox is running. + This option can be used multiple times to take locks on multiple files. + + + + + Keep this file descriptor open while the sandbox is running + + + + Filesystem related options. These are all operations that modify the filesystem directly, or + mounts stuff in the filesystem. These are applied in the order they are given as arguments. + Any missing parent directories that are required to create a specified destination are + automatically created as needed. + + + + + Bind mount the host path SRC on DEST + + + + Bind mount the host path SRC on DEST, allowing device access + + + + Bind mount the host path SRC readonly on DEST + + + + Mount procfs on DEST + + + + Mount new devtmpfs on DEST + + + + Mount new tmpfs on DEST + + + + Mount new mqueue on DEST + + + + Create a directory at DEST + + + + Copy from the file descriptor FD to DEST + + + + Copy from the file descriptor FD to a file which is bind-mounted on DEST + + + + Create a symlink at DEST with target SRC + + + Lockdown options: + + + + + Load and use seccomp rules from FD. + The rules need to be in the form of a compiled eBPF program, + as generated by seccomp_export_bpf. + + + + + + Exec Label from the sandbox. On an SELinux system you can specify the SELinux + context for the sandbox process(s). + + + + + + File label for temporary sandbox content. On an SELinux system you can specify + the SELinux context for the sandbox content. + + + + + + + Environment + + + + HOME + + Used as the cwd in the sandbox if has not been + explicitly specified and the current cwd is not present inside the sandbox. + The option can be used to override the value + that is used here. + + + + + + + Exit status + + + The bwrap command returns the exit status of the + initial application process (pid 2 in the sandbox). + + + + diff --git a/external/bubblewrap/completions/bash/bwrap b/external/bubblewrap/completions/bash/bwrap new file mode 100644 index 0000000..ba312fd --- /dev/null +++ b/external/bubblewrap/completions/bash/bwrap @@ -0,0 +1,52 @@ +#!/bin/bash +# +# bash completion file for bubblewrap commands +# + +_bwrap() { + local cur prev words cword + _init_completion || return + + local boolean_options=" + --help + --share-user + --unshare-ipc + --unshare-net + --unshare-pid + --unshare-uts + --version + " + + local options_with_args=" + $boolean_optons + --args + --bind + --bind-data + --chdir + --dev + --dev-bind + --dir + --exec-label + --file + --file-label + --gid + --lock-file + --proc + --ro-bind + --seccomp + --setenv + --symlink + --sync-fd + --uid + --unsetenv + --seccomp + --symlink + " + + if [[ "$cur" == -* ]]; then + COMPREPLY=( $( compgen -W "$boolean_options $options_with_args" -- "$cur" ) ) + fi + + return 0 +} +complete -F _bwrap bwrap diff --git a/external/bubblewrap/config.h b/external/bubblewrap/config.h new file mode 100644 index 0000000..383e36f --- /dev/null +++ b/external/bubblewrap/config.h @@ -0,0 +1,5 @@ +#ifndef _GNU_SOURCE +# define _GNU_SOURCE 1 +#endif + +#define PACKAGE_STRING "bubblewrap 0.1" diff --git a/external/bubblewrap/configure.ac b/external/bubblewrap/configure.ac new file mode 100644 index 0000000..d22bd31 --- /dev/null +++ b/external/bubblewrap/configure.ac @@ -0,0 +1,103 @@ +AC_PREREQ([2.63]) +AC_INIT([bubblewrap], [0.1], [alexl@redhat.com]) +AC_CONFIG_HEADER([config.h]) +AC_CONFIG_MACRO_DIR([m4]) +AC_CONFIG_AUX_DIR([build-aux]) + +AC_USE_SYSTEM_EXTENSIONS + +AM_INIT_AUTOMAKE([1.11 -Wno-portability foreign no-define tar-ustar no-dist-gzip dist-xz]) +AM_MAINTAINER_MODE([enable]) +AM_SILENT_RULES([yes]) + +AC_SYS_LARGEFILE + +AC_PROG_CC +AM_PROG_CC_C_O + +AC_CHECK_HEADERS([sys/capability.h], [], [AC_MSG_ERROR([*** POSIX caps headers not found])]) + +AC_ARG_ENABLE(man, + [AS_HELP_STRING([--enable-man], + [generate man pages [default=auto]])],, + enable_man=maybe) + +AS_IF([test "$enable_man" != no], [ + AC_PATH_PROG([XSLTPROC], [xsltproc], []) + AS_IF([test -z "$XSLTPROC"], [ + AS_IF([test "$enable_man" = yes], [ + AC_MSG_ERROR([xsltproc is required for --enable-man]) + ]) + enable_man=no + ], [ + enable_man=yes + ]) +]) +AM_CONDITIONAL(ENABLE_MAN, test "$enable_man" != no) + +AC_ARG_WITH([bash-completion-dir], + AS_HELP_STRING([--with-bash-completion-dir[=PATH]], + [Install the bash auto-completion script in this directory. @<:@default=yes@:>@]), + [], + [with_bash_completion_dir=yes]) + +if test "x$with_bash_completion_dir" = "xyes"; then + PKG_CHECK_MODULES([BASH_COMPLETION], [bash-completion >= 2.0], + [BASH_COMPLETION_DIR="`pkg-config --variable=completionsdir bash-completion`"], + [BASH_COMPLETION_DIR="$datadir/bash-completion/completions"]) +else + BASH_COMPLETION_DIR="$with_bash_completion_dir" +fi + +AC_SUBST([BASH_COMPLETION_DIR]) +AM_CONDITIONAL([ENABLE_BASH_COMPLETION],[test "x$with_bash_completion_dir" != "xno"]) +# ------------------------------------------------------------------------------ +have_selinux=no +AC_ARG_ENABLE(selinux, AS_HELP_STRING([--disable-selinux], [Disable optional SELINUX support])) +if test "x$enable_selinux" != "xno"; then + PKG_CHECK_MODULES([SELINUX], [libselinux >= 2.1.9], + [AC_DEFINE(HAVE_SELINUX, 1, [Define if SELinux is available]) + have_selinux=yes + M4_DEFINES="$M4_DEFINES -DHAVE_SELINUX"], + [have_selinux=no]) + if test "x$have_selinux" = xno -a "x$enable_selinux" = xyes; then + AC_MSG_ERROR([*** SELinux support requested but libraries not found]) + fi +fi +AM_CONDITIONAL(HAVE_SELINUX, [test "$have_selinux" = "yes"]) + +changequote(,)dnl +if test "x$GCC" = "xyes"; then + WARN_CFLAGS="-Wall -Werror=missing-prototypes" +fi +changequote([,])dnl +AC_SUBST(WARN_CFLAGS) + +AC_ARG_WITH(priv-mode, + AS_HELP_STRING([--with-priv-mode=setuid/caps/none], + [How to set privilege-raising during make install)]), + [], + [with_priv_mode="none"]) + +AM_CONDITIONAL(PRIV_MODE_FILECAPS, test "x$with_priv_mode" = "xcaps") +AM_CONDITIONAL(PRIV_MODE_SETUID, test "x$with_priv_mode" = "xsetuid") + +AC_ARG_ENABLE(sudo, + AS_HELP_STRING([--enable-sudo],[Use sudo to set privileged mode on binaries during install (only needed if --with-priv-mode used)]), + [SUDO_BIN="sudo"], [SUDO_BIN=""]) +AC_SUBST([SUDO_BIN]) + +AC_CONFIG_FILES([ +Makefile +]) +AC_OUTPUT + +echo " + bubblewrap $VERSION + =================== + + man pages (xsltproc): $enable_man + SELinux: $have_selinux + setuid mode on make install: $with_priv_mode + mysteriously satisfying to pop: yes" +echo "" diff --git a/external/bubblewrap/demos/bubblewrap-shell.sh b/external/bubblewrap/demos/bubblewrap-shell.sh new file mode 100755 index 0000000..da06d49 --- /dev/null +++ b/external/bubblewrap/demos/bubblewrap-shell.sh @@ -0,0 +1,21 @@ +#!/usr/bin/env bash +# Use bubblewrap to run /bin/sh in the host's rootfs. +set -euo pipefail +(exec bwrap --ro-bind /usr /usr \ + --dir /tmp \ + --proc /proc \ + --dev /dev \ + --ro-bind /etc/resolv.conf /etc/resolv.conf \ + --symlink usr/lib /lib \ + --symlink usr/lib64 /lib64 \ + --symlink usr/bin /bin \ + --symlink usr/sbin /sbin \ + --chdir / \ + --unshare-pid \ + --dir /run/user/$(id -u) \ + --setenv XDG_RUNTIME_DIR "/run/user/`id -u`" \ + --file 11 /etc/passwd \ + --file 12 /etc/group \ + /bin/sh) \ + 11< <(getent passwd $UID 65534) \ + 12< <(getent group $(id -g) 65534) diff --git a/external/bubblewrap/demos/xdg-app-run.sh b/external/bubblewrap/demos/xdg-app-run.sh new file mode 100755 index 0000000..02295cd --- /dev/null +++ b/external/bubblewrap/demos/xdg-app-run.sh @@ -0,0 +1,65 @@ +#!/bin/bash +# For this to work you first have to run these commands: +# curl -O http://sdk.gnome.org/nightly/keys/nightly.gpg +# xdg-app --user remote-add --gpg-key=nightly.gpg gnome-nightly http://sdk.gnome.org/nightly/repo/ +# xdg-app --user install gnome-nightly org.gnome.Platform +# xdg-app --user install gnome-nightly org.gnome.Weather + +mkdir -p ~/.var/app/org.gnome.Weather/cache ~/.var/app/org.gnome.Weather/config ~/.var/app/org.gnome.Weather/data + +( + exec bwrap \ + --ro-bind ~/.local/share/xdg-app/runtime/org.gnome.Platform/x86_64/master/active/files /usr \ + --lock-file /usr/.ref \ + --ro-bind ~/.local/share/xdg-app/app/org.gnome.Weather/x86_64/master/active/files/ /app \ + --lock-file /app/.ref \ + --dev /dev \ + --proc /proc \ + --dir /tmp \ + --symlink /tmp /var/tmp \ + --symlink /run /var/run \ + --symlink usr/lib /lib \ + --symlink usr/lib64 /lib64 \ + --symlink usr/bin /bin \ + --symlink usr/sbin /sbin \ + --symlink usr/etc /etc \ + --dir /run/user/`id -u` \ + --ro-bind /etc/machine-id /usr/etc/machine-id \ + --ro-bind /etc/resolv.conf /run/host/monitor/resolv.conf \ + --ro-bind /sys/block /sys/block \ + --ro-bind /sys/bus /sys/bus \ + --ro-bind /sys/class /sys/class \ + --ro-bind /sys/dev /sys/dev \ + --ro-bind /sys/devices /sys/devices \ + --dev-bind /dev/dri /dev/dri \ + --bind /tmp/.X11-unix/X0 /tmp/.X11-unix/X99 \ + --bind ~/.var/app/org.gnome.Weather ~/.var/app/org.gnome.Weather \ + --bind ~/.config/dconf ~/.config/dconf \ + --bind /run/user/`id -u`/dconf /run/user/`id -u`/dconf \ + --unshare-pid \ + --setenv XDG_RUNTIME_DIR "/run/user/`id -u`" \ + --setenv DISPLAY :99 \ + --setenv GI_TYPELIB_PATH /app/lib/girepository-1.0 \ + --setenv GST_PLUGIN_PATH /app/lib/gstreamer-1.0 \ + --setenv LD_LIBRARY_PATH /app/lib:/usr/lib/GL \ + --setenv DCONF_USER_CONFIG_DIR .config/dconf \ + --setenv PATH /app/bin:/usr/bin \ + --setenv XDG_CONFIG_DIRS /app/etc/xdg:/etc/xdg \ + --setenv XDG_DATA_DIRS /app/share:/usr/share \ + --setenv SHELL /bin/sh \ + --setenv XDG_CACHE_HOME ~/.var/app/org.gnome.Weather/cache \ + --setenv XDG_CONFIG_HOME ~/.var/app/org.gnome.Weather/config \ + --setenv XDG_DATA_HOME ~/.var/app/org.gnome.Weather/data \ + --file 10 /run/user/`id -u`/xdg-app-info \ + --bind-data 11 /usr/etc/passwd \ + --bind-data 12 /usr/etc/group \ + --seccomp 13 \ + /bin/sh) \ + 11< <(getent passwd $UID 65534 ) \ + 12< <(getent group $(id -g) 65534) \ + 13< `dirname $0`/xdg-app.bpf \ + 10</dev/null; then \ + echo "$$x already includes git.mk"; \ + else \ + failed=; \ + echo "Updating $$x"; \ + { cat $$x; \ + echo ''; \ + echo '-include $$(top_srcdir)/git.mk'; \ + } > $$x.tmp || failed=1; \ + if test x$$failed = x; then \ + mv $$x.tmp $$x || failed=1; \ + fi; \ + if test x$$failed = x; then : else \ + echo "Failed updating $$x"; >&2 \ + any_failed=1; \ + fi; \ + fi; done; test -z "$$any_failed" + +git-mk-update: + wget $(GIT_MK_URL) -O $(top_srcdir)/git.mk + +.PHONY: git-all git-mk-install git-mk-update + + + +############################################################################### +# Actual .gitignore generation: +############################################################################### + +$(srcdir)/.gitignore: Makefile.am $(top_srcdir)/git.mk + @echo "git.mk: Generating $@" + @{ \ + if test "x$(DOC_MODULE)" = x -o "x$(DOC_MAIN_SGML_FILE)" = x; then :; else \ + for x in \ + $(DOC_MODULE)-decl-list.txt \ + $(DOC_MODULE)-decl.txt \ + tmpl/$(DOC_MODULE)-unused.sgml \ + "tmpl/*.bak" \ + $(REPORT_FILES) \ + $(DOC_MODULE).pdf \ + xml html \ + ; do echo "/$$x"; done; \ + FLAVOR=$$(cd $(top_srcdir); $(AUTOCONF) --trace 'GTK_DOC_CHECK:$$2' ./configure.ac); \ + case $$FLAVOR in *no-tmpl*) echo /tmpl;; esac; \ + if echo "$(SCAN_OPTIONS)" | grep -q "\-\-rebuild-types"; then \ + echo "/$(DOC_MODULE).types"; \ + fi; \ + if echo "$(SCAN_OPTIONS)" | grep -q "\-\-rebuild-sections"; then \ + echo "/$(DOC_MODULE)-sections.txt"; \ + fi; \ + if test "$(abs_srcdir)" != "$(abs_builddir)" ; then \ + for x in \ + $(SETUP_FILES) \ + $(DOC_MODULE).types \ + ; do echo "/$$x"; done; \ + fi; \ + fi; \ + if test "x$(DOC_MODULE)$(DOC_ID)" = x -o "x$(DOC_LINGUAS)" = x; then :; else \ + for lc in $(DOC_LINGUAS); do \ + for x in \ + $(if $(DOC_MODULE),$(DOC_MODULE).xml) \ + $(DOC_PAGES) \ + $(DOC_INCLUDES) \ + ; do echo "/$$lc/$$x"; done; \ + done; \ + for x in \ + $(_DOC_OMF_ALL) \ + $(_DOC_DSK_ALL) \ + $(_DOC_HTML_ALL) \ + $(_DOC_MOFILES) \ + $(DOC_H_FILE) \ + "*/.xml2po.mo" \ + "*/*.omf.out" \ + ; do echo /$$x; done; \ + fi; \ + if test "x$(HELP_ID)" = x -o "x$(HELP_LINGUAS)" = x; then :; else \ + for lc in $(HELP_LINGUAS); do \ + for x in \ + $(HELP_FILES) \ + "$$lc.stamp" \ + "$$lc.mo" \ + ; do echo "/$$lc/$$x"; done; \ + done; \ + fi; \ + if test "x$(gsettings_SCHEMAS)" = x; then :; else \ + for x in \ + $(gsettings_SCHEMAS:.xml=.valid) \ + $(gsettings__enum_file) \ + ; do echo "/$$x"; done; \ + fi; \ + if test "x$(appdata_XML)" = x; then :; else \ + for x in \ + $(appdata_XML:.xml=.valid) \ + ; do echo "/$$x"; done; \ + fi; \ + if test "x$(appstream_XML)" = x; then :; else \ + for x in \ + $(appstream_XML:.xml=.valid) \ + ; do echo "/$$x"; done; \ + fi; \ + if test -f $(srcdir)/po/Makefile.in.in; then \ + for x in \ + po/Makefile.in.in \ + po/Makefile.in.in~ \ + po/Makefile.in \ + po/Makefile \ + po/Makevars.template \ + po/POTFILES \ + po/Rules-quot \ + po/stamp-it \ + po/stamp-po \ + po/.intltool-merge-cache \ + "po/*.gmo" \ + "po/*.header" \ + "po/*.mo" \ + "po/*.sed" \ + "po/*.sin" \ + po/$(GETTEXT_PACKAGE).pot \ + intltool-extract.in \ + intltool-merge.in \ + intltool-update.in \ + ; do echo "/$$x"; done; \ + fi; \ + if test -f $(srcdir)/configure; then \ + for x in \ + autom4te.cache \ + configure \ + config.h \ + stamp-h1 \ + libtool \ + config.lt \ + ; do echo "/$$x"; done; \ + fi; \ + if test "x$(DEJATOOL)" = x; then :; else \ + for x in \ + $(DEJATOOL) \ + ; do echo "/$$x.sum"; echo "/$$x.log"; done; \ + echo /site.exp; \ + fi; \ + if test "x$(am__dirstamp)" = x; then :; else \ + echo "$(am__dirstamp)"; \ + fi; \ + if test "x$(LTCOMPILE)" = x -a "x$(LTCXXCOMPILE)" = x -a "x$(GTKDOC_RUN)" = x; then :; else \ + for x in \ + "*.lo" \ + ".libs" "_libs" \ + ; do echo "$$x"; done; \ + fi; \ + for x in \ + .gitignore \ + $(GITIGNOREFILES) \ + $(CLEANFILES) \ + $(PROGRAMS) $(check_PROGRAMS) $(EXTRA_PROGRAMS) \ + $(LIBRARIES) $(check_LIBRARIES) $(EXTRA_LIBRARIES) \ + $(LTLIBRARIES) $(check_LTLIBRARIES) $(EXTRA_LTLIBRARIES) \ + so_locations \ + $(MOSTLYCLEANFILES) \ + $(TEST_LOGS) \ + $(TEST_LOGS:.log=.trs) \ + $(TEST_SUITE_LOG) \ + $(TESTS:=.test) \ + "*.gcda" \ + "*.gcno" \ + $(DISTCLEANFILES) \ + $(am__CONFIG_DISTCLEAN_FILES) \ + $(CONFIG_CLEAN_FILES) \ + TAGS ID GTAGS GRTAGS GSYMS GPATH tags \ + "*.tab.c" \ + $(MAINTAINERCLEANFILES) \ + $(BUILT_SOURCES) \ + $(patsubst %.vala,%.c,$(filter %.vala,$(SOURCES))) \ + $(filter %_vala.stamp,$(DIST_COMMON)) \ + $(filter %.vapi,$(DIST_COMMON)) \ + $(filter $(addprefix %,$(notdir $(patsubst %.vapi,%.h,$(filter %.vapi,$(DIST_COMMON))))),$(DIST_COMMON)) \ + Makefile \ + Makefile.in \ + "*.orig" \ + "*.rej" \ + "*.bak" \ + "*~" \ + ".*.sw[nop]" \ + ".dirstamp" \ + ; do echo "/$$x"; done; \ + for x in \ + "*.$(OBJEXT)" \ + $(DEPDIR) \ + ; do echo "$$x"; done; \ + } | \ + sed "s@^/`echo "$(srcdir)" | sed 's/\(.\)/[\1]/g'`/@/@" | \ + sed 's@/[.]/@/@g' | \ + LC_ALL=C sort | uniq > $@.tmp && \ + mv $@.tmp $@; + +all: $(srcdir)/.gitignore gitignore-recurse-maybe +gitignore: $(srcdir)/.gitignore gitignore-recurse + +gitignore-recurse-maybe: + @for subdir in $(DIST_SUBDIRS); do \ + case " $(SUBDIRS) " in \ + *" $$subdir "*) :;; \ + *) test "$$subdir" = . -o -e "$$subdir/.git" || (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) gitignore || echo "Skipping $$subdir");; \ + esac; \ + done +gitignore-recurse: + @for subdir in $(DIST_SUBDIRS); do \ + test "$$subdir" = . -o -e "$$subdir/.git" || (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) gitignore || echo "Skipping $$subdir"); \ + done + +maintainer-clean: gitignore-clean +gitignore-clean: + -rm -f $(srcdir)/.gitignore + +.PHONY: gitignore-clean gitignore gitignore-recurse gitignore-recurse-maybe diff --git a/external/bubblewrap/network.c b/external/bubblewrap/network.c new file mode 100644 index 0000000..f5d131d --- /dev/null +++ b/external/bubblewrap/network.c @@ -0,0 +1,200 @@ +/* bubblewrap + * Copyright (C) 2016 Alexander Larsson + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see . + * + */ + +#include "config.h" + +#include +#include +#include +#include +#include +#include + +#include "utils.h" +#include "network.h" + +static void * +add_rta (struct nlmsghdr *header, + int type, + size_t size) +{ + struct rtattr *rta; + size_t rta_size = RTA_LENGTH (size); + + rta = (struct rtattr *) ((char *) header + NLMSG_ALIGN (header->nlmsg_len)); + rta->rta_type = type; + rta->rta_len = rta_size; + + header->nlmsg_len = NLMSG_ALIGN (header->nlmsg_len) + rta_size; + + return RTA_DATA (rta); +} + +static int +rtnl_send_request (int rtnl_fd, + struct nlmsghdr *header) +{ + struct sockaddr_nl dst_addr = { AF_NETLINK, 0 }; + ssize_t sent; + + sent = sendto (rtnl_fd, (void *) header, header->nlmsg_len, 0, + (struct sockaddr *) &dst_addr, sizeof (dst_addr)); + if (sent < 0) + return -1; + + return 0; +} + +static int +rtnl_read_reply (int rtnl_fd, + int seq_nr) +{ + char buffer[1024]; + ssize_t received; + struct nlmsghdr *rheader; + + while (1) + { + received = recv (rtnl_fd, buffer, sizeof (buffer), 0); + if (received < 0) + return -1; + + rheader = (struct nlmsghdr *) buffer; + while (received >= NLMSG_HDRLEN) + { + if (rheader->nlmsg_seq != seq_nr) + return -1; + if (rheader->nlmsg_pid != getpid ()) + return -1; + if (rheader->nlmsg_type == NLMSG_ERROR) + { + uint32_t *err = NLMSG_DATA (rheader); + if (*err == 0) + return 0; + + return -1; + } + if (rheader->nlmsg_type == NLMSG_DONE) + return 0; + + rheader = NLMSG_NEXT (rheader, received); + } + } +} + +static int +rtnl_do_request (int rtnl_fd, + struct nlmsghdr *header) +{ + if (rtnl_send_request (rtnl_fd, header) != 0) + return -1; + + if (rtnl_read_reply (rtnl_fd, header->nlmsg_seq) != 0) + return -1; + + return 0; +} + +static struct nlmsghdr * +rtnl_setup_request (char *buffer, + int type, + int flags, + size_t size) +{ + struct nlmsghdr *header; + size_t len = NLMSG_LENGTH (size); + static uint32_t counter = 0; + + memset (buffer, 0, len); + + header = (struct nlmsghdr *) buffer; + header->nlmsg_len = len; + header->nlmsg_type = type; + header->nlmsg_flags = flags | NLM_F_REQUEST; + header->nlmsg_seq = counter++; + header->nlmsg_pid = getpid (); + + return (struct nlmsghdr *) header; +} + +int +loopback_setup (void) +{ + int r, if_loopback; + cleanup_fd int rtnl_fd = -1; + char buffer[1024]; + struct sockaddr_nl src_addr = { AF_NETLINK, 0 }; + struct nlmsghdr *header; + struct ifaddrmsg *addmsg; + struct ifinfomsg *infomsg; + struct in_addr *ip_addr; + + src_addr.nl_pid = getpid (); + + if_loopback = (int) if_nametoindex ("lo"); + if (if_loopback <= 0) + return -1; + + rtnl_fd = socket (PF_NETLINK, SOCK_RAW | SOCK_CLOEXEC, NETLINK_ROUTE); + if (rtnl_fd < 0) + return -1; + + r = bind (rtnl_fd, (struct sockaddr *) &src_addr, sizeof (src_addr)); + if (r < 0) + return -1; + + header = rtnl_setup_request (buffer, RTM_NEWADDR, + NLM_F_CREATE | NLM_F_EXCL | NLM_F_ACK, + sizeof (struct ifaddrmsg)); + addmsg = NLMSG_DATA (header); + + addmsg->ifa_family = AF_INET; + addmsg->ifa_prefixlen = 8; + addmsg->ifa_flags = IFA_F_PERMANENT; + addmsg->ifa_scope = RT_SCOPE_HOST; + addmsg->ifa_index = if_loopback; + + ip_addr = add_rta (header, IFA_LOCAL, sizeof (*ip_addr)); + ip_addr->s_addr = htonl (INADDR_LOOPBACK); + + ip_addr = add_rta (header, IFA_ADDRESS, sizeof (*ip_addr)); + ip_addr->s_addr = htonl (INADDR_LOOPBACK); + + assert (header->nlmsg_len < sizeof (buffer)); + + if (rtnl_do_request (rtnl_fd, header) != 0) + return -1; + + header = rtnl_setup_request (buffer, RTM_NEWLINK, + NLM_F_ACK, + sizeof (struct ifinfomsg)); + infomsg = NLMSG_DATA (header); + + infomsg->ifi_family = AF_UNSPEC; + infomsg->ifi_type = 0; + infomsg->ifi_index = if_loopback; + infomsg->ifi_flags = IFF_UP; + infomsg->ifi_change = IFF_UP; + + assert (header->nlmsg_len < sizeof (buffer)); + + if (rtnl_do_request (rtnl_fd, header) != 0) + return -1; + + return 0; +} diff --git a/external/bubblewrap/network.h b/external/bubblewrap/network.h new file mode 100644 index 0000000..d712d91 --- /dev/null +++ b/external/bubblewrap/network.h @@ -0,0 +1,21 @@ +/* bubblewrap + * Copyright (C) 2016 Alexander Larsson + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see . + * + */ + +#pragma once + +int loopback_setup (void); diff --git a/external/bubblewrap/packaging/bubblewrap.spec b/external/bubblewrap/packaging/bubblewrap.spec new file mode 100644 index 0000000..1c3bb4a --- /dev/null +++ b/external/bubblewrap/packaging/bubblewrap.spec @@ -0,0 +1,49 @@ +%global commit0 66d12bb23b04e201c5846e325f0b10930ed802f8 +%global shortcommit0 %(c=%{commit0}; echo ${c:0:7}) + +Summary: Core execution tool for unprivileged containers +Name: bubblewrap +Version: 0 +Release: 1%{?dist} +#VCS: git:https://github.com/projectatomic/bubblewrap +Source0: https://github.com/projectatomic/%{name}/archive/%{commit0}.tar.gz#/%{name}-%{shortcommit0}.tar.gz +License: LGPLv2+ +URL: https://github.com/projectatomic/bubblewrap + +BuildRequires: git +# We always run autogen.sh +BuildRequires: autoconf automake libtool +BuildRequires: libcap-devel +BuildRequires: pkgconfig(libselinux) +BuildRequires: libxslt +BuildRequires: docbook-style-xsl + +%description +Bubblewrap (/usr/bin/bwrap) is a core execution engine for unprivileged +containers that works as a setuid binary on kernels without +user namespaces. + +%prep +%autosetup -Sgit -n %{name}-%{version} + +%build +env NOCONFIGURE=1 ./autogen.sh +%configure --disable-silent-rules --with-priv-mode=none + +make %{?_smp_mflags} + +%install +make install DESTDIR=$RPM_BUILD_ROOT INSTALL="install -p -c" +find $RPM_BUILD_ROOT -name '*.la' -delete + +%files +%license COPYING +%doc README.md +%{_datadir}/bash-completion/completions/bwrap +%if (0%{?rhel} != 0 && 0%{?rhel} <= 7) +%attr(0755,root,root) %caps(cap_sys_admin,cap_net_admin,cap_sys_chroot=ep) %{_bindir}/bwrap +%else +%{_bindir}/bwrap +%endif +%{_mandir}/man1/* + diff --git a/external/bubblewrap/tests/test-basic.sh b/external/bubblewrap/tests/test-basic.sh new file mode 100755 index 0000000..7513933 --- /dev/null +++ b/external/bubblewrap/tests/test-basic.sh @@ -0,0 +1,35 @@ +#!/bin/bash + +set -xeuo pipefail + +srcd=$(cd $(dirname $0) && pwd) +bn=$(basename $0) +tempdir=$(mktemp -d /var/tmp/tap-test.XXXXXX) +touch ${tempdir}/.testtmp +function cleanup () { + if test -n "${TEST_SKIP_CLEANUP:-}"; then + echo "Skipping cleanup of ${test_tmpdir}" + else if test -f ${tempdir}/.test; then + rm "${tempdir}" -rf + fi + fi +} +trap cleanup EXIT +cd ${tempdir} + +assert_not_reached () { + echo $@ 1>&2; exit 1 +} + +assert_file_has_content () { + if ! grep -q -e "$2" "$1"; then + echo 1>&2 "File '$1' doesn't match regexp '$2'"; exit 1 + fi +} + +# At the moment we're testing in Travis' container infrastructure +# which also uses PR_SET_NO_NEW_PRIVS...but let's at least +# verify --help works! +bwrap --help >out.txt 2>&1 +assert_file_has_content out.txt "--lock-file" + diff --git a/external/bubblewrap/uncrustify.cfg b/external/bubblewrap/uncrustify.cfg new file mode 100644 index 0000000..6d3ad56 --- /dev/null +++ b/external/bubblewrap/uncrustify.cfg @@ -0,0 +1,136 @@ +newlines lf + +input_tab_size 8 +output_tab_size 8 + +string_escape_char 92 +string_escape_char2 0 + +# indenting +indent_columns 2 +indent_with_tabs 0 +indent_align_string True +indent_brace 2 +indent_braces false +indent_braces_no_func True +indent_func_call_param false +indent_func_def_param false +indent_func_proto_param false +indent_switch_case 0 +indent_case_brace 2 +indent_paren_close 1 + +# spacing +sp_arith Add +sp_assign Add +sp_enum_assign Add +sp_bool Add +sp_compare Add +sp_inside_paren Remove +sp_inside_fparens Remove +sp_func_def_paren Force +sp_func_proto_paren Force +sp_paren_paren Remove +sp_balance_nested_parens False +sp_paren_brace Remove +sp_before_square Remove +sp_before_squares Remove +sp_inside_square Remove +sp_before_ptr_star Add +sp_between_ptr_star Remove +sp_after_comma Add +sp_before_comma Remove +sp_after_cast Add +sp_sizeof_paren Add +sp_not Remove +sp_inv Remove +sp_addr Remove +sp_member Remove +sp_deref Remove +sp_sign Remove +sp_incdec Remove +sp_attribute_paren remove +sp_macro Force +sp_func_call_paren Force +sp_func_call_user_paren Remove +set func_call_user _ N_ C_ g_autoptr g_auto +sp_brace_typedef add +sp_cond_colon add +sp_cond_question add +sp_defined_paren remove + +# alignment +align_keep_tabs False +align_with_tabs False +align_on_tabstop False +align_number_left True +align_func_params True +align_var_def_span 0 +align_var_def_amp_style 1 +align_var_def_colon true +align_enum_equ_span 0 +align_var_struct_span 2 +align_var_def_star_style 2 +align_var_def_amp_style 2 +align_typedef_span 2 +align_typedef_func 0 +align_typedef_star_style 2 +align_typedef_amp_style 2 + +# newlines +nl_assign_leave_one_liners True +nl_enum_leave_one_liners False +nl_func_leave_one_liners False +nl_if_leave_one_liners False +nl_end_of_file Add +nl_assign_brace Remove +nl_func_var_def_blk 1 +nl_fcall_brace Add +nl_enum_brace Remove +nl_struct_brace Force +nl_union_brace Force +nl_if_brace Force +nl_brace_else Force +nl_elseif_brace Force +nl_else_brace Add +nl_for_brace Force +nl_while_brace Force +nl_do_brace Force +nl_brace_while Force +nl_switch_brace Force +nl_before_case True +nl_after_case False +nl_func_type_name Force +nl_func_proto_type_name Remove +nl_func_paren Remove +nl_func_decl_start Remove +nl_func_decl_args Force +nl_func_decl_end Remove +nl_fdef_brace Force +nl_after_return False +nl_define_macro False +nl_create_if_one_liner False +nl_create_for_one_liner False +nl_create_while_one_liner False +nl_after_semicolon True +nl_multi_line_cond true + +# mod +# I'd like these to be remove, but that removes brackets in if { if { foo } }, which i dislike +# Not clear what to do about that... +mod_full_brace_for Remove +mod_full_brace_if Remove +mod_full_brace_if_chain True +mod_full_brace_while Remove +mod_full_brace_do Remove +mod_full_brace_nl 3 +mod_paren_on_return Remove + +# line splitting +#code_width = 78 +ls_for_split_full True +ls_func_split_full True + +# positioning +pos_bool Trail +pos_conditional Trail diff --git a/external/bubblewrap/uncrustify.sh b/external/bubblewrap/uncrustify.sh new file mode 100755 index 0000000..7c9080b --- /dev/null +++ b/external/bubblewrap/uncrustify.sh @@ -0,0 +1,2 @@ +#!/bin/sh +uncrustify -c uncrustify.cfg --no-backup `git ls-tree --name-only -r HEAD | grep \\\.[ch]$` diff --git a/external/bubblewrap/utils.c b/external/bubblewrap/utils.c new file mode 100644 index 0000000..acedfe8 --- /dev/null +++ b/external/bubblewrap/utils.c @@ -0,0 +1,676 @@ +/* bubblewrap + * Copyright (C) 2016 Alexander Larsson + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see . + * + */ +#include "config.h" + +#include "utils.h" +#include +#ifdef HAVE_SELINUX +#include +#endif + +void +die_with_error (const char *format, ...) +{ + va_list args; + int errsv; + + errsv = errno; + + va_start (args, format); + vfprintf (stderr, format, args); + va_end (args); + + fprintf (stderr, ": %s\n", strerror (errsv)); + + exit (1); +} + +void +die (const char *format, ...) +{ + va_list args; + + va_start (args, format); + vfprintf (stderr, format, args); + va_end (args); + + fprintf (stderr, "\n"); + + exit (1); +} + +void +die_unless_label_valid (const char *label) +{ +#ifdef HAVE_SELINUX + if (is_selinux_enabled () == 1) + { + if (security_check_context ((security_context_t) label) < 0) + die_with_error ("invalid label %s", label); + return; + } +#endif + die ("labeling not supported on this system"); +} + +void +die_oom (void) +{ + puts ("Out of memory"); + exit (1); +} + +void * +xmalloc (size_t size) +{ + void *res = malloc (size); + + if (res == NULL) + die_oom (); + return res; +} + +void * +xcalloc (size_t size) +{ + void *res = calloc (1, size); + + if (res == NULL) + die_oom (); + return res; +} + +void * +xrealloc (void *ptr, size_t size) +{ + void *res = realloc (ptr, size); + + if (size != 0 && res == NULL) + die_oom (); + return res; +} + +char * +xstrdup (const char *str) +{ + char *res; + + assert (str != NULL); + + res = strdup (str); + if (res == NULL) + die_oom (); + + return res; +} + +void +strfreev (char **str_array) +{ + if (str_array) + { + int i; + + for (i = 0; str_array[i] != NULL; i++) + free (str_array[i]); + + free (str_array); + } +} + +/* Compares if str has a specific path prefix. This differs + from a regular prefix in two ways. First of all there may + be multiple slashes separating the path elements, and + secondly, if a prefix is matched that has to be en entire + path element. For instance /a/prefix matches /a/prefix/foo/bar, + but not /a/prefixfoo/bar. */ +bool +has_path_prefix (const char *str, + const char *prefix) +{ + while (TRUE) + { + /* Skip consecutive slashes to reach next path + element */ + while (*str == '/') + str++; + while (*prefix == '/') + prefix++; + + /* No more prefix path elements? Done! */ + if (*prefix == 0) + return TRUE; + + /* Compare path element */ + while (*prefix != 0 && *prefix != '/') + { + if (*str != *prefix) + return FALSE; + str++; + prefix++; + } + + /* Matched prefix path element, + must be entire str path element */ + if (*str != '/' && *str != 0) + return FALSE; + } +} + +bool +has_prefix (const char *str, + const char *prefix) +{ + return strncmp (str, prefix, strlen (prefix)) == 0; +} + +void +xsetenv (const char *name, const char *value, int overwrite) +{ + if (setenv (name, value, overwrite)) + die ("setenv failed"); +} + +void +xunsetenv (const char *name) +{ + if (unsetenv (name)) + die ("unsetenv failed"); +} + +char * +strconcat (const char *s1, + const char *s2) +{ + size_t len = 0; + char *res; + + if (s1) + len += strlen (s1); + if (s2) + len += strlen (s2); + + res = xmalloc (len + 1); + *res = 0; + if (s1) + strcat (res, s1); + if (s2) + strcat (res, s2); + + return res; +} + +char * +strconcat3 (const char *s1, + const char *s2, + const char *s3) +{ + size_t len = 0; + char *res; + + if (s1) + len += strlen (s1); + if (s2) + len += strlen (s2); + if (s3) + len += strlen (s3); + + res = xmalloc (len + 1); + *res = 0; + if (s1) + strcat (res, s1); + if (s2) + strcat (res, s2); + if (s3) + strcat (res, s3); + + return res; +} + +char * +xasprintf (const char *format, + ...) +{ + char *buffer = NULL; + va_list args; + + va_start (args, format); + if (vasprintf (&buffer, format, args) == -1) + die_oom (); + va_end (args); + + return buffer; +} + +int +fdwalk (int proc_fd, int (*cb)(void *data, + int fd), void *data) +{ + int open_max; + int fd; + int dfd; + int res = 0; + DIR *d; + + dfd = openat (proc_fd, "self/fd", O_DIRECTORY | O_RDONLY | O_NONBLOCK | O_CLOEXEC | O_NOCTTY); + if (dfd == -1) + return res; + + if ((d = fdopendir (dfd))) + { + struct dirent *de; + + while ((de = readdir (d))) + { + long l; + char *e = NULL; + + if (de->d_name[0] == '.') + continue; + + errno = 0; + l = strtol (de->d_name, &e, 10); + if (errno != 0 || !e || *e) + continue; + + fd = (int) l; + + if ((long) fd != l) + continue; + + if (fd == dirfd (d)) + continue; + + if ((res = cb (data, fd)) != 0) + break; + } + + closedir (d); + return res; + } + + open_max = sysconf (_SC_OPEN_MAX); + + for (fd = 0; fd < open_max; fd++) + if ((res = cb (data, fd)) != 0) + break; + + return res; +} + +/* Sets errno on error (!= 0), ENOSPC on short write */ +int +write_to_fd (int fd, + const char *content, + ssize_t len) +{ + ssize_t res; + + while (len > 0) + { + res = write (fd, content, len); + if (res < 0 && errno == EINTR) + continue; + if (res <= 0) + { + if (res == 0) /* Unexpected short write, should not happen when writing to a file */ + errno = ENOSPC; + return -1; + } + len -= res; + content += res; + } + + return 0; +} + +/* Sets errno on error (!= 0), ENOSPC on short write */ +int +write_file_at (int dirfd, + const char *path, + const char *content) +{ + int fd; + bool res; + int errsv; + + fd = openat (dirfd, path, O_RDWR | O_CLOEXEC, 0); + if (fd == -1) + return -1; + + res = 0; + if (content) + res = write_to_fd (fd, content, strlen (content)); + + errsv = errno; + close (fd); + errno = errsv; + + return res; +} + +/* Sets errno on error (!= 0), ENOSPC on short write */ +int +create_file (const char *path, + mode_t mode, + const char *content) +{ + int fd; + int res; + int errsv; + + fd = creat (path, mode); + if (fd == -1) + return -1; + + res = 0; + if (content) + res = write_to_fd (fd, content, strlen (content)); + + errsv = errno; + close (fd); + errno = errsv; + + return res; +} + +int +ensure_file (const char *path, + mode_t mode) +{ + struct stat buf; + + /* We check this ahead of time, otherwise + the create file will fail in the read-only + case with EROFD instead of EEXIST */ + if (stat (path, &buf) == 0 && + S_ISREG (buf.st_mode)) + return 0; + + if (create_file (path, mode, NULL) != 0 && errno != EEXIST) + return -1; + + return 0; +} + + +#define BUFSIZE 8192 +/* Sets errno on error (!= 0), ENOSPC on short write */ +int +copy_file_data (int sfd, + int dfd) +{ + char buffer[BUFSIZE]; + ssize_t bytes_read; + + while (TRUE) + { + bytes_read = read (sfd, buffer, BUFSIZE); + if (bytes_read == -1) + { + if (errno == EINTR) + continue; + + return -1; + } + + if (bytes_read == 0) + break; + + if (write_to_fd (dfd, buffer, bytes_read) != 0) + return -1; + } + + return 0; +} + +/* Sets errno on error (!= 0), ENOSPC on short write */ +int +copy_file (const char *src_path, + const char *dst_path, + mode_t mode) +{ + int sfd; + int dfd; + int res; + int errsv; + + sfd = open (src_path, O_CLOEXEC | O_RDONLY); + if (sfd == -1) + return -1; + + dfd = creat (dst_path, mode); + if (dfd == -1) + { + errsv = errno; + close (sfd); + errno = errsv; + return -1; + } + + res = copy_file_data (sfd, dfd); + + errsv = errno; + close (sfd); + close (dfd); + errno = errsv; + + return res; +} + +/* Sets errno on error (== NULL), + * Always ensures terminating zero */ +char * +load_file_data (int fd, + size_t *size) +{ + cleanup_free char *data = NULL; + ssize_t data_read; + ssize_t data_len; + ssize_t res; + int errsv; + + data_read = 0; + data_len = 4080; + data = xmalloc (data_len); + + do + { + if (data_len == data_read + 1) + { + data_len *= 2; + data = xrealloc (data, data_len); + } + + do + res = read (fd, data + data_read, data_len - data_read - 1); + while (res < 0 && errno == EINTR); + + if (res < 0) + { + errsv = errno; + close (fd); + errno = errsv; + return NULL; + } + + data_read += res; + } + while (res > 0); + + data[data_read] = 0; + + if (size) + *size = (size_t) data_read; + + return steal_pointer (&data); +} + +/* Sets errno on error (== NULL), + * Always ensures terminating zero */ +char * +load_file_at (int dirfd, + const char *path) +{ + int fd; + char *data; + int errsv; + + fd = openat (dirfd, path, O_CLOEXEC | O_RDONLY); + if (fd == -1) + return NULL; + + data = load_file_data (fd, NULL); + + errsv = errno; + close (fd); + errno = errsv; + + return data; +} + +/* Sets errno on error (< 0) */ +int +get_file_mode (const char *pathname) +{ + struct stat buf; + + if (stat (pathname, &buf) != 0) + return -1; + + return buf.st_mode & S_IFMT; +} + +/* Sets errno on error (!= 0) */ +int +mkdir_with_parents (const char *pathname, + int mode, + bool create_last) +{ + cleanup_free char *fn = NULL; + char *p; + struct stat buf; + + if (pathname == NULL || *pathname == '\0') + { + errno = EINVAL; + return -1; + } + + fn = xstrdup (pathname); + + p = fn; + while (*p == '/') + p++; + + do + { + while (*p && *p != '/') + p++; + + if (!*p) + p = NULL; + else + *p = '\0'; + + if (!create_last && p == NULL) + break; + + if (stat (fn, &buf) != 0) + { + if (mkdir (fn, mode) == -1 && errno != EEXIST) + return -1; + } + else if (!S_ISDIR (buf.st_mode)) + { + errno = ENOTDIR; + return -1; + } + + if (p) + { + *p++ = '/'; + while (*p && *p == '/') + p++; + } + } + while (p); + + return 0; +} + +int +raw_clone (unsigned long flags, + void *child_stack) +{ +#if defined(__s390__) || defined(__CRIS__) + /* On s390 and cris the order of the first and second arguments + * of the raw clone() system call is reversed. */ + return (int) syscall (__NR_clone, child_stack, flags); +#else + return (int) syscall (__NR_clone, flags, child_stack); +#endif +} + +int +pivot_root (const char * new_root, const char * put_old) +{ +#ifdef __NR_pivot_root + return syscall (__NR_pivot_root, new_root, put_old); +#else + errno = ENOSYS; + return -1; +#endif +} + +char * +label_mount (const char *opt, const char *mount_label) +{ +#ifdef HAVE_SELINUX + if (mount_label) + { + if (opt) + return xasprintf ("%s,context=\"%s\"", opt, mount_label); + else + return xasprintf ("context=\"%s\"", mount_label); + } +#endif + if (opt) + return xstrdup (opt); + return NULL; +} + +int +label_create_file (const char *file_label) +{ +#ifdef HAVE_SELINUX + if (is_selinux_enabled () > 0 && file_label) + return setfscreatecon ((security_context_t) file_label); +#endif + return 0; +} + +int +label_exec (const char *exec_label) +{ +#ifdef HAVE_SELINUX + if (is_selinux_enabled () > 0 && exec_label) + return setexeccon ((security_context_t) exec_label); +#endif + return 0; +} diff --git a/external/bubblewrap/utils.h b/external/bubblewrap/utils.h new file mode 100644 index 0000000..8ec86bd --- /dev/null +++ b/external/bubblewrap/utils.h @@ -0,0 +1,164 @@ +/* bubblewrap + * Copyright (C) 2016 Alexander Larsson + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see . + * + */ + +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#if 1 +#define __debug__(x) printf x +#else +#define __debug__(x) +#endif + +#define UNUSED __attribute__((__unused__)) + +#define N_ELEMENTS(arr) (sizeof (arr) / sizeof ((arr)[0])) + +#define TRUE 1 +#define FALSE 0 +typedef int bool; + +#define PIPE_READ_END 0 +#define PIPE_WRITE_END 1 + +void die_with_error (const char *format, + ...) __attribute__((__noreturn__)) __attribute__((format (printf, 1, 2))); +void die (const char *format, + ...) __attribute__((__noreturn__)); +void die_oom (void) __attribute__((__noreturn__)); +void die_unless_label_valid (const char *label); + +void *xmalloc (size_t size); +void *xcalloc (size_t size); +void *xrealloc (void *ptr, + size_t size); +char *xstrdup (const char *str); +void strfreev (char **str_array); +void xsetenv (const char *name, + const char *value, + int overwrite); +void xunsetenv (const char *name); +char *strconcat (const char *s1, + const char *s2); +char *strconcat3 (const char *s1, + const char *s2, + const char *s3); +char * xasprintf (const char *format, + ...) __attribute__((format (printf, 1, 2))); +bool has_prefix (const char *str, + const char *prefix); +bool has_path_prefix (const char *str, + const char *prefix); +int fdwalk (int proc_fd, + int (*cb)(void *data, + int fd), + void *data); +char *load_file_data (int fd, + size_t *size); +char *load_file_at (int dirfd, + const char *path); +int write_file_at (int dirfd, + const char *path, + const char *content); +int write_to_fd (int fd, + const char *content, + ssize_t len); +int copy_file_data (int sfd, + int dfd); +int copy_file (const char *src_path, + const char *dst_path, + mode_t mode); +int create_file (const char *path, + mode_t mode, + const char *content); +int ensure_file (const char *path, + mode_t mode); +int get_file_mode (const char *pathname); +int mkdir_with_parents (const char *pathname, + int mode, + bool create_last); + +/* syscall wrappers */ +int raw_clone (unsigned long flags, + void *child_stack); +int pivot_root (const char *new_root, + const char *put_old); +char *label_mount (const char *opt, + const char *mount_label); +int label_exec (const char *exec_label); +int label_create_file (const char *file_label); + +static inline void +cleanup_freep (void *p) +{ + void **pp = (void **) p; + + if (*pp) + free (*pp); +} + +static inline void +cleanup_strvp (void *p) +{ + void **pp = (void **) p; + + strfreev (*pp); +} + +static inline void +cleanup_fdp (int *fdp) +{ + int fd; + + assert (fdp); + + fd = *fdp; + if (fd != -1) + (void) close (fd); +} + +#define cleanup_free __attribute__((cleanup (cleanup_freep))) +#define cleanup_fd __attribute__((cleanup (cleanup_fdp))) +#define cleanup_strv __attribute__((cleanup (cleanup_strvp))) + +static inline void * +steal_pointer (void *pp) +{ + void **ptr = (void **) pp; + void *ref; + + ref = *ptr; + *ptr = NULL; + + return ref; +} + +/* type safety */ +#define steal_pointer(pp) \ + (0 ? (*(pp)) : (steal_pointer) (pp)) diff --git a/external/process-cpp-minimal/CMakeLists.txt b/external/process-cpp-minimal/CMakeLists.txt new file mode 100644 index 0000000..da19732 --- /dev/null +++ b/external/process-cpp-minimal/CMakeLists.txt @@ -0,0 +1,39 @@ +# Copyright © 2013 Canonical Ltd. +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License version 3 as +# published by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +# +# Authored by: Thomas Voss + +cmake_minimum_required(VERSION 2.8) + +project(process-cpp) + +find_package(Boost COMPONENTS iostreams system REQUIRED) +find_package(Threads REQUIRED) + +include(GNUInstallDirs) + +set(PROCESS_CPP_VERSION_MAJOR 2) +set(PROCESS_CPP_VERSION_MINOR 0) +set(PROCESS_CPP_VERSION_PATCH 0) + +include(CTest) + +include_directories( + include/ + src/ + external/process-cpp-minimal + ${Boost_INCLUDE_DIRS} +) + +add_subdirectory(src) diff --git a/external/process-cpp-minimal/COPYING b/external/process-cpp-minimal/COPYING new file mode 100644 index 0000000..65c5ca8 --- /dev/null +++ b/external/process-cpp-minimal/COPYING @@ -0,0 +1,165 @@ + GNU LESSER GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + + This version of the GNU Lesser General Public License incorporates +the terms and conditions of version 3 of the GNU General Public +License, supplemented by the additional permissions listed below. + + 0. Additional Definitions. + + As used herein, "this License" refers to version 3 of the GNU Lesser +General Public License, and the "GNU GPL" refers to version 3 of the GNU +General Public License. + + "The Library" refers to a covered work governed by this License, +other than an Application or a Combined Work as defined below. + + An "Application" is any work that makes use of an interface provided +by the Library, but which is not otherwise based on the Library. +Defining a subclass of a class defined by the Library is deemed a mode +of using an interface provided by the Library. + + A "Combined Work" is a work produced by combining or linking an +Application with the Library. The particular version of the Library +with which the Combined Work was made is also called the "Linked +Version". + + The "Minimal Corresponding Source" for a Combined Work means the +Corresponding Source for the Combined Work, excluding any source code +for portions of the Combined Work that, considered in isolation, are +based on the Application, and not on the Linked Version. + + The "Corresponding Application Code" for a Combined Work means the +object code and/or source code for the Application, including any data +and utility programs needed for reproducing the Combined Work from the +Application, but excluding the System Libraries of the Combined Work. + + 1. Exception to Section 3 of the GNU GPL. + + You may convey a covered work under sections 3 and 4 of this License +without being bound by section 3 of the GNU GPL. + + 2. Conveying Modified Versions. + + If you modify a copy of the Library, and, in your modifications, a +facility refers to a function or data to be supplied by an Application +that uses the facility (other than as an argument passed when the +facility is invoked), then you may convey a copy of the modified +version: + + a) under this License, provided that you make a good faith effort to + ensure that, in the event an Application does not supply the + function or data, the facility still operates, and performs + whatever part of its purpose remains meaningful, or + + b) under the GNU GPL, with none of the additional permissions of + this License applicable to that copy. + + 3. Object Code Incorporating Material from Library Header Files. + + The object code form of an Application may incorporate material from +a header file that is part of the Library. You may convey such object +code under terms of your choice, provided that, if the incorporated +material is not limited to numerical parameters, data structure +layouts and accessors, or small macros, inline functions and templates +(ten or fewer lines in length), you do both of the following: + + a) Give prominent notice with each copy of the object code that the + Library is used in it and that the Library and its use are + covered by this License. + + b) Accompany the object code with a copy of the GNU GPL and this license + document. + + 4. Combined Works. + + You may convey a Combined Work under terms of your choice that, +taken together, effectively do not restrict modification of the +portions of the Library contained in the Combined Work and reverse +engineering for debugging such modifications, if you also do each of +the following: + + a) Give prominent notice with each copy of the Combined Work that + the Library is used in it and that the Library and its use are + covered by this License. + + b) Accompany the Combined Work with a copy of the GNU GPL and this license + document. + + c) For a Combined Work that displays copyright notices during + execution, include the copyright notice for the Library among + these notices, as well as a reference directing the user to the + copies of the GNU GPL and this license document. + + d) Do one of the following: + + 0) Convey the Minimal Corresponding Source under the terms of this + License, and the Corresponding Application Code in a form + suitable for, and under terms that permit, the user to + recombine or relink the Application with a modified version of + the Linked Version to produce a modified Combined Work, in the + manner specified by section 6 of the GNU GPL for conveying + Corresponding Source. + + 1) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (a) uses at run time + a copy of the Library already present on the user's computer + system, and (b) will operate properly with a modified version + of the Library that is interface-compatible with the Linked + Version. + + e) Provide Installation Information, but only if you would otherwise + be required to provide such information under section 6 of the + GNU GPL, and only to the extent that such information is + necessary to install and execute a modified version of the + Combined Work produced by recombining or relinking the + Application with a modified version of the Linked Version. (If + you use option 4d0, the Installation Information must accompany + the Minimal Corresponding Source and Corresponding Application + Code. If you use option 4d1, you must provide the Installation + Information in the manner specified by section 6 of the GNU GPL + for conveying Corresponding Source.) + + 5. Combined Libraries. + + You may place library facilities that are a work based on the +Library side by side in a single library together with other library +facilities that are not Applications and are not covered by this +License, and convey such a combined library under terms of your +choice, if you do both of the following: + + a) Accompany the combined library with a copy of the same work based + on the Library, uncombined with any other library facilities, + conveyed under the terms of this License. + + b) Give prominent notice with the combined library that part of it + is a work based on the Library, and explaining where to find the + accompanying uncombined form of the same work. + + 6. Revised Versions of the GNU Lesser General Public License. + + The Free Software Foundation may publish revised and/or new versions +of the GNU Lesser General Public License from time to time. Such new +versions will be similar in spirit to the present version, but may +differ in detail to address new problems or concerns. + + Each version is given a distinguishing version number. If the +Library as you received it specifies that a certain numbered version +of the GNU Lesser General Public License "or any later version" +applies to it, you have the option of following the terms and +conditions either of that published version or of any later version +published by the Free Software Foundation. If the Library as you +received it does not specify a version number of the GNU Lesser +General Public License, you may choose any version of the GNU Lesser +General Public License ever published by the Free Software Foundation. + + If the Library as you received it specifies that a proxy can decide +whether future versions of the GNU Lesser General Public License shall +apply, that proxy's public statement of acceptance of any version is +permanent authorization for you to choose that version for the +Library. diff --git a/external/process-cpp-minimal/README.md b/external/process-cpp-minimal/README.md new file mode 100644 index 0000000..41e6e90 --- /dev/null +++ b/external/process-cpp-minimal/README.md @@ -0,0 +1,86 @@ +process-cpp {#mainpage} +=========== + +process-cpp is a simple and straightforward wrapper around process creation and +control targeted towards linux. It helps both with handling child processes and with +interacting with the current process. Some of its features include: + + - Thread-safe get/set/unset operation on the current process's environment. + - Throwing and non-throwing overloads of functions when system calls are involved. + - Seamless redirection of input, output and error streams of child processes. + - Type-safe interaction with the virtual proc filesystem, both for reading & writing. + +The library's main purpose is to assist in testing and when a software component +needs to carry out process creation/control tasks, e.g., a graphical shell. To this end, +the library is extensively tested and tries to ensure fail-safe operation as much as possible. + +A simple echo +------------- + +~~~~~~~~~~~~~{.cpp} +// Fork and run a simple echo: +posix::ChildProcess child = posix::fork( + []() + { + std::string line; + while(true) + { + std::cin >> line; + std::cout << line << std::endl; + } + return EXIT_FAILURE; + }, + posix::StandardStreamFlags() + .set(posix::StandardStream::stdin) + .set(posix::StandardStream::stdout)); + +// Check that the resulting process has a valid pid. +EXPECT_TRUE(child.pid() > 0); + +// Check on echo functionality. +const std::string echo_value{"42"}; +child.cin() << echo_value << std::endl; +std::string line; child.cout() >> line; +EXPECT_EQ(echo_value, line); + +// Stop the process and synchronize with the process changing state. +EXPECT_NO_THROW(child.send_signal(posix::Signal::sig_stop)); +auto result = child.wait_for(posix::wait::Flag::untraced); +EXPECT_EQ(posix::wait::Result::Status::stopped, + result.status); +EXPECT_EQ(posix::Signal::sig_stop, + result.detail.if_stopped.signal); + +// Kill the stopped process and synchronize to its state change. +EXPECT_NO_THROW(child.send_signal(posix::Signal::sig_kill)); +result = child.wait_for(posix::wait::Flag::untraced); +EXPECT_EQ(posix::wait::Result::Status::signaled, + result.status); +EXPECT_EQ(posix::Signal::sig_kill, + result.detail.if_signaled.signal); +~~~~~~~~~~~~~ + +Adjusting OOM Score Values +-------------------------- + +~~~~~~~~~~~~~{.cpp} +// Setup the manipulator with a well-known value. +posix::linux::proc::process::OomScoreAdj oom_score_adj +{ + posix::linux::proc::process::OomScoreAdj::max_value() +}; +// Apply the manipulator to the current process +EXPECT_NO_THROW(posix::this_process::instance() << oom_score_adj); +// Read back the manipulators value for the current process +EXPECT_NO_THROW(posix::this_process::instance() >> oom_score_adj); +// And check that applying the manipulator was successful. +EXPECT_EQ(posix::linux::proc::process::OomScoreAdj::max_value(), + oom_score_adj.value); +// Instantiate the observer... +posix::linux::proc::process::OomScore oom_score; +// ... and fill in its value for the current process. +EXPECT_NO_THROW(posix::this_process::instance() >> oom_score); +// Check that applying the manipulator before results in adjustments to the +// OOM score. +EXPECT_TRUE(is_approximately_equal(oom_score.value, posix::linux::proc::process::OomScoreAdj::max_value())); +~~~~~~~~~~~~~ diff --git a/external/process-cpp-minimal/include/core/connection.h b/external/process-cpp-minimal/include/core/connection.h new file mode 100644 index 0000000..03dbaf8 --- /dev/null +++ b/external/process-cpp-minimal/include/core/connection.h @@ -0,0 +1,190 @@ +/* + * Copyright © 2013 Canonical Ltd. + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License version 3, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + * + * Authored by: Thomas Voß + */ +#ifndef COM_UBUNTU_CONNECTION_H_ +#define COM_UBUNTU_CONNECTION_H_ + +#include +#include +#include + +namespace core +{ +class ScopedConnection; +/** + * @brief The Connection class models a signal-slot connection. + */ +class Connection +{ +public: + typedef std::function&)> Dispatcher; + + /** + * @brief Checks if this instance corresponds to an active signal-slot connection. + * @return true iff the instance corresponds to an active signal-slot connection. + */ + inline bool is_connected() const + { + if (!d) + return false; + + return (d->disconnector ? true : false); + } + + /** + * @brief End a signal-slot connection. + */ + inline void disconnect() + { + if (d) + d->disconnect(); + } + + /** + * @brief Installs a dispatcher for this signal-slot connection. + * @param dispatcher The dispatcher to be used for signal emissions. + */ + inline void dispatch_via(const Dispatcher& dispatcher) + { + if (d && d->dispatcher_installer) + d->dispatcher_installer(dispatcher); + } + +private: + friend class ScopedConnection; + + typedef std::function Disconnector; + typedef std::function DispatcherInstaller; + + template friend class Signal; + + inline Connection(const Disconnector& disconnector, + const DispatcherInstaller& installer) + : d(std::make_shared(disconnector, installer)) + { + } + + inline bool operator<(const Connection& rhs) const + { + return d < rhs.d; + } + + inline void reset() + { + if (d) + d->reset(); + } + + struct Private + { + Private(const Connection::Disconnector& disconnector, + const Connection::DispatcherInstaller& dispatcher_installer) + : disconnector(disconnector), + dispatcher_installer(dispatcher_installer) + { + } + + inline void reset() + { + std::lock_guard lg(guard); + reset_locked(); + } + + inline void reset_locked() + { + static const Connection::Disconnector empty_disconnector{}; + static const Connection::DispatcherInstaller empty_dispatcher_installer{}; + + disconnector = empty_disconnector; + dispatcher_installer = empty_dispatcher_installer; + } + + inline void disconnect() + { + static const Connection::Disconnector empty_disconnector{}; + + std::lock_guard lg(guard); + + if (disconnector) + disconnector(); + + reset_locked(); + } + + std::mutex guard; + Connection::Disconnector disconnector; + Connection::DispatcherInstaller dispatcher_installer; + }; + + // The whole class is implicitly shared and we thus forward our complete + // shared state to a private structure that is lifetime-managed by a shared_ptr. + std::shared_ptr d; +}; + +/** + * @brief Scoped helper class to map signal-slot connection mgmt. to RAII. + */ +class ScopedConnection +{ +public: + /** + * @brief Constructs an instance for an existing signal-slot connection. + * @param c The existing signal-slot connection. + */ + inline ScopedConnection(const Connection& c) : connection(c) + { + } + + inline ScopedConnection(ScopedConnection&& rhs) : connection(std::move(rhs.connection)) + { + } + + ScopedConnection(const ScopedConnection&) = delete; + + /** + * @brief Disconnects the signal-slot connection. + */ + inline ~ScopedConnection() noexcept(true) + { + try + { + connection.disconnect(); + } catch(...) + { + } + } + + inline ScopedConnection& operator=(ScopedConnection&& rhs) + { + connection = std::move(rhs.connection); + return *this; + } + + ScopedConnection& operator=(const ScopedConnection&) = delete; + bool operator==(const ScopedConnection&) = delete; + + inline bool operator<(const ScopedConnection& rhs) const + { + return connection < rhs.connection; + } + +private: + Connection connection; +}; +} + +#endif // COM_UBUNTU_CONNECTION_H_ diff --git a/external/process-cpp-minimal/include/core/posix/child_process.h b/external/process-cpp-minimal/include/core/posix/child_process.h new file mode 100644 index 0000000..edbd6d3 --- /dev/null +++ b/external/process-cpp-minimal/include/core/posix/child_process.h @@ -0,0 +1,169 @@ +/* + * Copyright © 2013 Canonical Ltd. + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License version 3, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + * + * Authored by: Thomas Voß + */ + +#ifndef CORE_POSIX_CHILD_PROCESS_H_ +#define CORE_POSIX_CHILD_PROCESS_H_ + +#include +#include +#include + +#include + +#include +#include + +namespace core +{ +namespace posix +{ +/** + * @brief The Process class models a child process of this process. + * + * In addition to the functionality offered by the Process class, an instance + * of ChildProcess offers functionality to wait for status changes of the child + * process and to access the child process's standard streams if they have been + * redirected when forking or exec'ing. + */ +class CORE_POSIX_DLL_PUBLIC ChildProcess : public Process +{ +public: + /** + * @brief The DeathObserver class observes child process' states and emits a signal when a monitored child has died. + * + * Please note that the name of this class is morbid for a reason: Listening + * for SIGCHLD is not enough to catch all dying children. Whenever a SIGCHLD is + * received, we have to wait for all the children of this process and reap all + * monitored ones. We are thus changing state and potentially race with other + * wait operations on children. + * + */ + class DeathObserver + { + public: + /** + * @brief Creates the unique instance of class DeathObserver. + * @throw std::logic_error if the given SignalTrap instance does not trap Signal::sig_chld. + * @throw std::runtime_error if there already is an instance of the death observer. + */ + static std::unique_ptr create_once_with_signal_trap( + std::shared_ptr trap); + + DeathObserver(const DeathObserver&) = delete; + virtual ~DeathObserver() = default; + + DeathObserver& operator=(const DeathObserver&) = delete; + bool operator==(const DeathObserver&) const = delete; + + /** + * @brief add adds the specified child to the list of observed child processes. + * @param child The child to be observed. + * @return true iff the child has been added to the list of observed child processes. + */ + virtual bool add(const ChildProcess& child) = 0; + + /** + * @brief has checks whether the specified child is observed. + * @param child The child to check for. + * @return true iff the specified child is observed for state changes. + */ + virtual bool has(const ChildProcess& child) const = 0; + + /** + * @brief child_died is emitted whenever an observed child ceases to exist. + */ + virtual const core::Signal& child_died() const = 0; + + /** + * @brief Checks and reaps all child processes registered with the observer instance. + */ + virtual void on_sig_child() = 0; + + protected: + DeathObserver() = default; + }; + + /** + * @brief Creates an invalid ChildProcess. + * @return An invalid ChildProcess instance. + */ + static ChildProcess invalid(); + + ~ChildProcess(); + + /** + * @brief Wait for the child process to change state. + * @param [in] flags Alters the behavior of the wait operation. + * @return Result of the wait operation, as well as information about the + * reasons for a child process's state change. + */ + wait::Result wait_for(const wait::Flags& flags); + + /** + * @brief Access this process's stderr. + */ + std::istream& cerr(); + + /** + * @brief Access this process's stdin. + */ + std::ostream& cin(); + + /** + * @brief Access this process's stdout. + */ + std::istream& cout(); + +private: + friend ChildProcess fork(const std::function&, const StandardStream&); + friend ChildProcess vfork(const std::function&, const StandardStream&); + + class CORE_POSIX_DLL_LOCAL Pipe + { + public: + static Pipe invalid(); + + Pipe(); + Pipe(const Pipe& rhs); + ~Pipe(); + + Pipe& operator=(const Pipe& rhs); + + int read_fd() const; + void close_read_fd(); + + int write_fd() const; + void close_write_fd(); + + private: + Pipe(int fds[2]); + int fds[2]; + }; + + CORE_POSIX_DLL_LOCAL ChildProcess(pid_t pid, + const Pipe& stdin, + const Pipe& stdout, + const Pipe& stderr); + + struct CORE_POSIX_DLL_LOCAL Private; + std::shared_ptr d; +}; +} +} + +#endif // CORE_POSIX_CHILD_PROCESS_H_ diff --git a/external/process-cpp-minimal/include/core/posix/exec.h b/external/process-cpp-minimal/include/core/posix/exec.h new file mode 100644 index 0000000..147e69f --- /dev/null +++ b/external/process-cpp-minimal/include/core/posix/exec.h @@ -0,0 +1,68 @@ +/* + * Copyright © 2013 Canonical Ltd. + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License version 3, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + * + * Authored by: Thomas Voß + */ + +#ifndef CORE_POSIX_EXEC_H_ +#define CORE_POSIX_EXEC_H_ + +#include +#include + +#include +#include +#include +#include + +namespace core +{ +namespace posix +{ +enum class RedirectFlags; + +/** + * @brief exec execve's the executable with the provided arguments and environment. + * @throws std::system_error in case of errors. + * @param fn The executable to run. + * @param argv Vector of command line arguments + * @param env Environment that the new process should run under + * @param flags Specifies which standard streams should be redirected. + * @return An instance of ChildProcess corresponding to the newly exec'd process. + */ +CORE_POSIX_DLL_PUBLIC ChildProcess exec(const std::string& fn, + const std::vector& argv, + const std::map& env, + const StandardStream& flags); + +/** + * @brief exec execve's the executable with the provided arguments and environment. + * @throws std::system_error in case of errors. + * @param fn The executable to run. + * @param argv Vector of command line arguments + * @param env Environment that the new process should run under + * @param flags Specifies which standard streams should be redirected. + * @param child_setup Function to run in the child just before exec(). + * @return An instance of ChildProcess corresponding to the newly exec'd process. + */ +CORE_POSIX_DLL_PUBLIC ChildProcess exec(const std::string& fn, + const std::vector& argv, + const std::map& env, + const StandardStream& flags, + const std::function& child_setup); +} +} + +#endif // CORE_POSIX_EXEC_H_ diff --git a/external/process-cpp-minimal/include/core/posix/exit.h b/external/process-cpp-minimal/include/core/posix/exit.h new file mode 100644 index 0000000..183f5c7 --- /dev/null +++ b/external/process-cpp-minimal/include/core/posix/exit.h @@ -0,0 +1,42 @@ +/* + * Copyright © 2013 Canonical Ltd. + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License version 3, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + * + * Authored by: Thomas Voß + */ + +#ifndef CORE_POSIX_EXIT_H_ +#define CORE_POSIX_EXIT_H_ + +#include + +namespace core +{ +namespace posix +{ +namespace exit +{ + /** + * @brief The Status enum wrap's the posix exit status. + */ + enum class Status + { + success = EXIT_SUCCESS, + failure = EXIT_FAILURE + }; +} +} +} + +#endif // CORE_POSIX_EXIT_H_ diff --git a/external/process-cpp-minimal/include/core/posix/fork.h b/external/process-cpp-minimal/include/core/posix/fork.h new file mode 100644 index 0000000..d41f967 --- /dev/null +++ b/external/process-cpp-minimal/include/core/posix/fork.h @@ -0,0 +1,54 @@ +/* + * Copyright © 2013 Canonical Ltd. + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License version 3, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + * + * Authored by: Thomas Voß + */ + +#ifndef CORE_POSIX_FORK_H_ +#define CORE_POSIX_FORK_H_ + +#include +#include +#include + +#include + +namespace core +{ +namespace posix +{ +/** + * @brief fork forks a new process and executes the provided main function in the newly forked process. + * @throws std::system_error in case of errors. + * @param [in] main The main function of the newly forked process. + * @param [in] flags Specify which standard streams should be redirected to the parent process. + * @return An instance of ChildProcess in case of success. + */ +CORE_POSIX_DLL_PUBLIC ChildProcess fork(const std::function& main, + const StandardStream& flags); + +/** + * @brief fork vforks a new process and executes the provided main function in the newly forked process. + * @throws std::system_error in case of errors. + * @param [in] main The main function of the newly forked process. + * @param [in] flags Specify which standard streams should be redirected to the parent process. + * @return An instance of ChildProcess in case of success. + */ +CORE_POSIX_DLL_PUBLIC ChildProcess vfork(const std::function& main, + const StandardStream& flags); +} +} + +#endif // CORE_POSIX_FORK_H_ diff --git a/external/process-cpp-minimal/include/core/posix/linux/proc/process/oom_adj.h b/external/process-cpp-minimal/include/core/posix/linux/proc/process/oom_adj.h new file mode 100644 index 0000000..3cab652 --- /dev/null +++ b/external/process-cpp-minimal/include/core/posix/linux/proc/process/oom_adj.h @@ -0,0 +1,106 @@ +/* + * Copyright © 2012-2013 Canonical Ltd. + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License version 3, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + * + * Authored by: Thomas Voß + */ +#ifndef CORE_POSIX_LINUX_PROC_PROCESS_OOM_ADJ_H_ +#define CORE_POSIX_LINUX_PROC_PROCESS_OOM_ADJ_H_ + +#include + +namespace core +{ +namespace posix +{ +class Process; +namespace linux +{ +namespace proc +{ +namespace process +{ +/** + * This file can be used to adjust the score used to select which process + * should be killed in an out-of-memory (OOM) situation. The kernel uses this + * value for a bit-shift operation of the process's oom_score value: valid + * values are in the range -16 to +15, plus the special value -17, which disables + * OOM-killing altogether for this process. A positive score increases the + * likelihood of this process being killed by the OOM-killer; a negative score + * decreases the likelihood. + * + * The default value for this file is 0; a new process inherits its parent's + * oom_adj setting. A process must be privileged (CAP_SYS_RESOURCE) to update + * this file. + * + * Since Linux 2.6.36, use of this file is deprecated in favor of + * /proc/[pid]/oom_score_adj. + */ +struct CORE_POSIX_DLL_PUBLIC OomAdj +{ + /** + * @brief Returns the value that makes a process "invisible" to the oom killer. + * @return Returns the value that makes a process "invisible" to the oom killer. + */ + static int disable_value(); + + /** + * @brief Returns the minimum valid value. + * @return The minimum valid value that the OomAdj can be set to. + */ + static int min_value(); + + /** + * @brief Returns the maximum valid value. + * @return The maximum valid value that the OomAdj can be set to. + */ + static int max_value(); + + /** + * @brief is_valid checks whether the contained value is within the predefined bounds. + * @return true iff min_value() <= value <= max_value(). + */ + inline bool is_valid() const + { + return (disable_value() <= value) && (value <= max_value()); + } + + /** + * @brief Current value. + */ + int value; +}; + +/** + * \brief Read the OomAdj value for a process instance. + * \throws std::runtime_error in case of errors. + * \param [in] process The process to read the score for. + * \param [out] adj The destination to store the value in. + */ +CORE_POSIX_DLL_PUBLIC const posix::Process& operator>>(const posix::Process& process, OomAdj& adj); + +/** + * \brief Write the OomAdj value for a process instance. + * \throw std::runtime_error in case of errors and std::logic_error if score_adj.is_valid() returns false. + * \param [in] process The process to write the score for. + * \param [in] adj The new value to store. + */ +CORE_POSIX_DLL_PUBLIC const posix::Process& operator<<(const posix::Process& process, + const OomAdj& adj); +} +} +} +} +} +#endif // CORE_POSIX_LINUX_PROC_PROCESS_OOM_ADJ_H_ diff --git a/external/process-cpp-minimal/include/core/posix/linux/proc/process/oom_score.h b/external/process-cpp-minimal/include/core/posix/linux/proc/process/oom_score.h new file mode 100644 index 0000000..8e7b9a4 --- /dev/null +++ b/external/process-cpp-minimal/include/core/posix/linux/proc/process/oom_score.h @@ -0,0 +1,67 @@ +/* + * Copyright © 2012-2013 Canonical Ltd. + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License version 3, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + * + * Authored by: Thomas Voß + */ +#ifndef CORE_POSIX_LINUX_PROC_PROCESS_OOM_SCORE_H_ +#define CORE_POSIX_LINUX_PROC_PROCESS_OOM_SCORE_H_ + +#include + +namespace core +{ +namespace posix +{ +class Process; +namespace linux +{ +namespace proc +{ +namespace process +{ +/** + * This file displays the current score that the kernel gives to this process + * for the purpose of selecting a process for the OOM-killer. A higher score + * means that the process is more likely to be selected by the OOM-killer. The + * basis for this score is the amount of memory used by the process, with + * increases (+) or decreases (-) for factors including: + * + * - whether the process creates a lot of children using fork(2) (+); + * - whether the process has been running a long time, or has used a lot of CPU time (-); + * - whether the process has a low nice value (i.e., > 0) (+); + * - whether the process is privileged (-); and + * - whether the process is making direct hardware access (-). + * + * The oom_score also reflects the adjustment specified by the oom_score_adj or + * oom_adj setting for the process. + */ +struct CORE_POSIX_DLL_PUBLIC OomScore +{ + int value = 0; ///< Current OomScore as calculated by the kernel. +}; + +/** + * \brief Read the OomScore for a process instance. + * \throws std::runtime_error in case of errors. + * \param [in] process The process to read the score for. + * \param [out] score The destination to store the value in. + */ +CORE_POSIX_DLL_PUBLIC const posix::Process& operator>>(const posix::Process& process, OomScore& score); +} +} +} +} +} +#endif // CORE_POSIX_LINUX_PROC_PROCESS_OOM_SCORE_H_ diff --git a/external/process-cpp-minimal/include/core/posix/linux/proc/process/oom_score_adj.h b/external/process-cpp-minimal/include/core/posix/linux/proc/process/oom_score_adj.h new file mode 100644 index 0000000..eb52b9a --- /dev/null +++ b/external/process-cpp-minimal/include/core/posix/linux/proc/process/oom_score_adj.h @@ -0,0 +1,131 @@ +/* + * Copyright © 2012-2013 Canonical Ltd. + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License version 3, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + * + * Authored by: Thomas Voß + */ +#ifndef CORE_POSIX_LINUX_PROC_PROCESS_OOM_SCORE_ADJ_H_ +#define CORE_POSIX_LINUX_PROC_PROCESS_OOM_SCORE_ADJ_H_ + +#include + +namespace core +{ +namespace posix +{ +class Process; +namespace linux +{ +namespace proc +{ +namespace process +{ +/** + * This file can be used to adjust the badness heuristic used to select which + * process gets killed in out-of-memory conditions. + * + * The badness heuristic assigns a value to each candidate task ranging from 0 + * (never kill) to 1000 (always kill) to determine which process is targeted. + * The units are roughly a proportion along that range of allowed memory the + * process may allocate from, based on an estimation of its current memory and + * swap use. For example, if a task is using all allowed memory, its badness + * score will be 1000. If it is using half of its allowed memory, its score + * will be 500. + * + * There is an additional factor included in the badness score: root processes are + * given 3% extra memory over other tasks. + * + * The amount of "allowed" memory depends on the context in which the + * OOM-killer was called. If it is due to the memory assigned to the allocating + * task's cpuset being exhausted, the allowed memory represents the set of mems + * assigned to that cpuset (see cpuset(7)). If it is due to a mempolicy's node(s) + * being exhausted, the allowed memory represents the set of mempolicy nodes. If + * it is due to a memory limit (or swap limit) being reached, the allowed memory + * is that configured limit. Finally, if it is due to the entire system being out + * of memory, the allowed memory represents all allocatable resources. + * + * The value of oom_score_adj is added to the badness score before it is used + * to determine which task to kill. Acceptable values range from -1000 + * (OOM_SCORE_ADJ_MIN) to +1000 (OOM_SCORE_ADJ_MAX). This allows user space to + * control the preference for OOM-killing, ranging from always preferring a + * certain task or completely disabling it from OOM- killing. The lowest possible + * value, -1000, is equivalent to disabling OOM-killing entirely for that task, + * since it will always report a badness score of 0. + * + * Consequently, it is very simple for user space to define the amount of + * memory to consider for each task. Setting a oom_score_adj value of +500, for + * example, is roughly equivalent to allowing the remainder of tasks sharing + * the same system, cpuset, mempolicy, or memory controller resources to use at + * least 50% more memory. A value of -500, on the other hand, would be roughly + * equivalent to discounting 50% of the task's allowed memory from being + * considered as scoring against the task. + * + * For backward compatibility with previous kernels, /proc/[pid]/oom_adj can + * still be used to tune the badness score. Its value is scaled linearly with + * oom_score_adj. + * + * Writing to /proc/[pid]/oom_score_adj or /proc/[pid]/oom_adj will change the + * other with its scaled value. + */ +struct CORE_POSIX_DLL_PUBLIC OomScoreAdj +{ + /** + * @brief Returns the minimum valid value. + * @return The minimum valid value that the Oom Score Adj can be set to. + */ + static int min_value(); + + /** + * @brief Returns the maximum valid value. + * @return The maximum valid value that the Oom Score Adj can be set to. + */ + static int max_value(); + + /** + * @brief is_valid checks whether the contained value is within the predefined bounds. + * @return true iff min_value() <= value <= max_value(). + */ + inline bool is_valid() const + { + return (min_value() <= value) && (value <= max_value()); + } + + /** + * @brief Current value. + */ + int value; +}; + +/** + * @brief Read the OomScoreAdj value for a process instance. + * @throw std::runtime_error in case of errors. + * @param [in] process The process to read the score for. + * @param [out] score_adj The destination to store the value in. + */ +CORE_POSIX_DLL_PUBLIC const posix::Process& operator>>(const posix::Process& process, OomScoreAdj& score_adj); + +/** + * @brief Write the OomScoreAdj value for a process instance. + * @throw std::runtime_error in case of errors and std::logic_error if score_adj.is_valid() returns false. + * @param [in] process The process to write the score for. + * @param [in] score_adj The new value to store. + */ +CORE_POSIX_DLL_PUBLIC const posix::Process& operator<<(const posix::Process& process, + const OomScoreAdj& score_adj); +} +} +} +} +} +#endif // CORE_POSIX_LINUX_PROC_PROCESS_OOM_SCORE_ADJ_H_ diff --git a/external/process-cpp-minimal/include/core/posix/linux/proc/process/stat.h b/external/process-cpp-minimal/include/core/posix/linux/proc/process/stat.h new file mode 100644 index 0000000..bfbe440 --- /dev/null +++ b/external/process-cpp-minimal/include/core/posix/linux/proc/process/stat.h @@ -0,0 +1,120 @@ +/* + * Copyright © 2012-2013 Canonical Ltd. + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License version 3, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + * + * Authored by: Thomas Voß + */ +#ifndef CORE_POSIX_LINUX_PROC_PROCESS_STAT_H_ +#define CORE_POSIX_LINUX_PROC_PROCESS_STAT_H_ + +#include + +#include + +#include + +namespace core +{ +namespace posix +{ +class Process; +namespace linux +{ +namespace proc +{ +namespace process +{ +/** + * @brief The Stat struct encapsulates status information about a process. + */ +struct CORE_POSIX_DLL_PUBLIC Stat +{ + pid_t pid = 1; ///< The process ID + std::string executable; ///< The filename of the executable, in parentheses. + State state = State::undefined; ///< State of the process. + pid_t parent = -1; ///< The PID of the parent. + pid_t process_group = -1; ///< The process group ID of the process. + int session_id = -1; ///< The session ID of the process. + int tty_nr = -1; ///< The controlling terminal of the process. + int controlling_process_group = -1; ///< The ID of the foreground process group of the controlling terminal of the process. + unsigned int kernel_flags = 0; ///< The kernel flags word of the process. + long unsigned int minor_faults_count = 0; ///< The number of minor faults the process has made which have not required loading a memory page from disk. + long unsigned int minor_faults_count_by_children = 0; ///< The number of minor faults that the process's waited-for children have made. + long unsigned int major_faults_count = 0; ///< The number of major faults the process has made which have required loading a memory page from disk. + long unsigned int major_faults_count_by_children = 0; ///< The number of major faults that the process's waited-for children have made. + struct + { + long unsigned int user = 0; ///< Amount of time that this process has been scheduled in user mode, [clock ticks]. + long unsigned int system = 0; ///< Amount of time that this process has been scheduled in kernel mode, [clock ticks]. + long unsigned int user_for_children = 0; ///< Amount of time that this process's waited-for children have been scheduled in user mode, [clock ticks]. + long unsigned int system_for_children = 0; ///< Amount of time that this process's waited-for children have been scheduled in kernel mode, [clock ticks]. + } time; + /** + * (Explanation for Linux 2.6) For processes running a real-time scheduling + * policy (policy below; see sched_setscheduler(2)), this is the negated + * scheduling priority, minus one; that is, a number in the range -2 to + * -100, corresponding to real-time priorities 1 to 99. For processes running + * under a non-real-time scheduling policy, this is the raw nice value + * (setpriority(2)) as represented in the kernel. The kernel stores nice + * values as numbers in the range 0 (high) to 39 (low), corresponding to + * the user-visible nice range of -20 to 19. + * + *Before Linux 2.6, this was a scaled value based on the scheduler + *weighting given to this process. + */ + long int priority = 0; + long int nice = 0; ///< The nice value (see setpriority(2)), a value in the range 19 (low priority) to -20 (high priority). + long int thread_count = 0; ///< Number of threads in this process (since Linux 2.6). + long int time_before_next_sig_alarm = 0; ///< The time in jiffies before the next SIGALRM is sent to the process due to an interval timer. Since kernel 2.6.17, this field is no longer maintained, and is hard coded as 0. + long int start_time = 0; ///< The time the process started after system boot. In kernels before Linux 2.6, this value was expressed in jiffies. Since Linux 2.6, the value is expressed in clock ticks (divide by sysconf(_SC_CLK_TCK)). + struct + { + long unsigned int virt = 0; ///< Virtual memory size in bytes. + long unsigned int resident_set = 0; ///< Resident Set Size: number of pages the process has in real memory. This is just the pages which count toward text, data, or stack space. This does not include pages which have not been demand-loaded in, or which are swapped out. + long unsigned int resident_set_limit = 0; ///< Current soft limit in bytes on the rss of the process; see the description of RLIMIT_RSS in getrlimit(2). + } size; + struct + { + long unsigned int start_code = 0; ///< The address above which program text can run. + long unsigned int end_code = 0; ///< The address below which program text can run. + long unsigned int start_stack = 0; ///< The address of the start (i.e., bottom) of the stack. + long unsigned int stack_pointer = 0; ///< The current value of ESP (stack pointer), as found in the kernel stack page for the process. + long unsigned int instruction_pointer = 0; ///< The current EIP (instruction pointer). + } addresses; + struct + { + long unsigned int pending = 0; ///< The bitmap of pending signals, displayed as a decimal number. Obsolete, because it does not provide information on real-time signals; use /proc/[pid]/status instead. + long unsigned int blocked = 0; ///< The bitmap of blocked signals, displayed as a decimal number. Obsolete, because it does not provide information on real-time signals; use /proc/[pid]/status instead. + long unsigned int ignored = 0; ///< The bitmap of ignored signals, displayed as a decimal number. Obsolete, because it does not provide information on real-time signals; use /proc/[pid]/status instead. + long unsigned int caught = 0; ///< The bitmap of caught signals, displayed as a decimal number. Obsolete, because it does not provide information on real-time signals; use /proc/[pid]/status instead. + } signals; + long unsigned int channel = 0; ///< This is the "channel" in which the process is waiting. It is the address of a system call, and can be looked up in a namelist if you need a textual name. (If you have an up-to-date /etc/psdatabase, then try ps -l to see the WCHAN field in action.) + long unsigned int swap_count = 0; ///< Number of pages swapped (not maintained). + long unsigned int swap_count_children = 0; ///< Cumulative nswap for child processes (not maintained). + int exit_signal = -1; ///< Signal to be sent to parent when we die. + int cpu_count = -1; ///< CPU number last executed on. + unsigned int realtime_priority = 0; ///< Real-time scheduling priority, a number in the range 1 to 99 for processes scheduled under a real-time policy, or 0, for non-real-time processes (see sched_setscheduler(2)). + unsigned int scheduling_policy = 0; ///< Scheduling policy (see sched_setscheduler(2)). Decode using the SCHED_* constants in linux/sched.h. + long long unsigned int aggregated_block_io_delays = 0; ///< Aggregated block I/O delays, measured in clock ticks (centiseconds). + long unsigned int guest_time = 0; ///< Guest time of the process (time spent running a virtual CPU for a guest operating system), measured in clock ticks. + long unsigned int guest_time_children = 0; ///< Guest time of the process's children, measured in clock ticks. +}; + +CORE_POSIX_DLL_PUBLIC const posix::Process& operator>>(const posix::Process& process, Stat& stat); +} +} +} +} +} +#endif // CORE_POSIX_LINUX_PROC_PROCESS_STAT_H_ diff --git a/external/process-cpp-minimal/include/core/posix/linux/proc/process/state.h b/external/process-cpp-minimal/include/core/posix/linux/proc/process/state.h new file mode 100644 index 0000000..df9d74b --- /dev/null +++ b/external/process-cpp-minimal/include/core/posix/linux/proc/process/state.h @@ -0,0 +1,51 @@ +/* + * Copyright © 2012-2013 Canonical Ltd. + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License version 3, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + * + * Authored by: Thomas Voß + */ +#ifndef CORE_POSIX_LINUX_PROC_PROCESS_STATE_H_ +#define CORE_POSIX_LINUX_PROC_PROCESS_STATE_H_ + +#include + +#include + +namespace core +{ +namespace posix +{ +namespace linux +{ +namespace proc +{ +namespace process +{ +enum class State +{ + undefined = -1, + running = 'R', + sleeping = 'S', + disk_sleep = 'D', + zombie = 'Z', + traced_or_stopped = 'T', + paging = 'W' +}; +} +} +} +} +} + +#endif // CORE_POSIX_LINUX_PROC_PROCESS_STATE_H_ diff --git a/external/process-cpp-minimal/include/core/posix/process.h b/external/process-cpp-minimal/include/core/posix/process.h new file mode 100644 index 0000000..48f7be2 --- /dev/null +++ b/external/process-cpp-minimal/include/core/posix/process.h @@ -0,0 +1,93 @@ +/* + * Copyright © 2013 Canonical Ltd. + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License version 3, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + * + * Authored by: Thomas Voß + */ + +#ifndef CORE_POSIX_PROCESS_H_ +#define CORE_POSIX_PROCESS_H_ + +#include +#include +#include +#include +#include + +#include +#include + +namespace core +{ +namespace posix +{ +enum class Signal; +class Self; +class WaitFlags; + +/** + * @brief The Process class models a process and possible operations on it. + * + * The process class is implicitly shared. + */ +class CORE_POSIX_DLL_PUBLIC Process : public Signalable +{ +public: + /** + * @brief Creates a process instance wrapping an existing process. + * @throw Throw std::system_error if pid is invalid, i.e., pid < 0. + * @param pid The process identifier of the existing process. + */ + explicit Process(pid_t pid); + + /** + * @brief Returns an invalid instance for testing purposes. + * @return An invalid instance. + */ + static Process invalid(); + + /** + * @brief Frees resources associated with the process. + */ + virtual ~Process() noexcept; + + /** + * @brief Query the pid of the process. + * @return The pid of the process. + */ + virtual pid_t pid() const; + + /** + * @brief Queries the id of the process group this process belongs to. + * @throw std::system_error in case of errors. + * @return The id of the process group this process belongs to. + */ + virtual ProcessGroup process_group_or_throw() const; + + /** + * @brief Queries the id of the process group this process belongs to. + * + * @return A tuple with the first element being the id of the process group + * this process belongs to and the second element a boolean flag indicating + * an error if true. + */ + virtual ProcessGroup process_group(std::error_code& se) const noexcept(true); + +private: + struct CORE_POSIX_DLL_LOCAL Private; + std::shared_ptr d; +}; +} +} +#endif // CORE_POSIX_PROCESS_H_ diff --git a/external/process-cpp-minimal/include/core/posix/process_group.h b/external/process-cpp-minimal/include/core/posix/process_group.h new file mode 100644 index 0000000..0c972b4 --- /dev/null +++ b/external/process-cpp-minimal/include/core/posix/process_group.h @@ -0,0 +1,65 @@ +/* + * Copyright © 2013 Canonical Ltd. + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License version 3, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + * + * Authored by: Thomas Voß + */ + +#ifndef CORE_POSIX_PROCESS_GROUP_H_ +#define CORE_POSIX_PROCESS_GROUP_H_ + +#include +#include + +#include + +namespace core +{ +namespace posix +{ +class Process; + +/** + * @brief The ProcessGroup class models a signalable group of process. + * + * Summary from http://en.wikipedia.org/wiki/Process_group: + * + * In POSIX-conformant operating systems, a process group denotes a collection + * of one or more processes. Process groups are used to control the distribution + * of signals. A signal directed to a process group is delivered individually to + * all of the processes that are members of the group. + */ +class CORE_POSIX_DLL_PUBLIC ProcessGroup : public Signalable +{ +public: + /** + * @brief Accesses the id of this process group. + * @return The id of this process group. + */ + virtual pid_t id() const; + + static ProcessGroup invalid(); + +protected: + friend class Process; + CORE_POSIX_DLL_LOCAL ProcessGroup(pid_t id); + +private: + struct CORE_POSIX_DLL_LOCAL Private; + std::shared_ptr d; +}; +} +} + +#endif // CORE_POSIX_PROCESS_GROUP_H_ diff --git a/external/process-cpp-minimal/include/core/posix/signal.h b/external/process-cpp-minimal/include/core/posix/signal.h new file mode 100644 index 0000000..d11724a --- /dev/null +++ b/external/process-cpp-minimal/include/core/posix/signal.h @@ -0,0 +1,117 @@ +/* + * Copyright © 2013 Canonical Ltd. + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License version 3, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + * + * Authored by: Thomas Voß + */ + +#ifndef CORE_POSIX_SIGNAL_H_ +#define CORE_POSIX_SIGNAL_H_ + +#include + +#include + +#include + +#include +#include + +namespace core +{ +namespace posix +{ +/** + * @brief The Signal enum collects the most common POSIX signals. + */ +enum class Signal +{ + unknown = 0, + sig_hup = SIGHUP, + sig_int = SIGINT, + sig_quit = SIGQUIT, + sig_ill = SIGILL, + sig_abrt = SIGABRT, + sig_fpe = SIGFPE, + sig_kill = SIGKILL, + sig_segv = SIGSEGV, + sig_pipe = SIGPIPE, + sig_alrm = SIGALRM, + sig_term = SIGTERM, + sig_usr1 = SIGUSR1, + sig_usr2 = SIGUSR2, + sig_chld = SIGCHLD, + sig_cont = SIGCONT, + sig_stop = SIGSTOP, + sig_tstp = SIGTSTP, + sig_ttin = SIGTTIN, + sig_ttou = SIGTTOU +}; + +/** + * @brief The SignalTrap class encapsulates functionality to trap and handle signals. + */ +class CORE_POSIX_DLL_PUBLIC SignalTrap +{ +public: + SignalTrap(const SignalTrap&) = delete; + virtual ~SignalTrap() = default; + + SignalTrap& operator=(const SignalTrap&) = delete; + bool operator==(const SignalTrap&) const = delete; + + /** + * @brief Returns true if the given signal is trapped by this instance. + */ + virtual bool has(Signal signal) = 0; + + /** + * @brief Starts observation of incoming signals, relaying them via + * signal_raised(). The call blocks until stop is called. + */ + virtual void run() = 0; + + /** + * @brief Stops execution of the signal trap. + */ + virtual void stop() = 0; + + /** + * @brief Emitted whenever a trapped signal is raised by the operating system. + */ + virtual core::Signal& signal_raised() = 0; + +protected: + SignalTrap() = default; +}; + +/** + * @brief Traps the specified signals for the entire process. + */ +CORE_POSIX_DLL_PUBLIC +std::shared_ptr trap_signals_for_process( + std::initializer_list blocked_signals); + +/** + * @brief Traps the specified signals for the current thread, and inherits + * the respective signal mask to all child-threads. + */ +CORE_POSIX_DLL_PUBLIC +std::shared_ptr trap_signals_for_all_subsequent_threads( + std::initializer_list blocked_signals); + +} +} + +#endif diff --git a/external/process-cpp-minimal/include/core/posix/signalable.h b/external/process-cpp-minimal/include/core/posix/signalable.h new file mode 100644 index 0000000..be28f61 --- /dev/null +++ b/external/process-cpp-minimal/include/core/posix/signalable.h @@ -0,0 +1,64 @@ +/* + * Copyright © 2013 Canonical Ltd. + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License version 3, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + * + * Authored by: Thomas Voß + */ + +#ifndef CORE_POSIX_SIGNALABLE_H_ +#define CORE_POSIX_SIGNALABLE_H_ + +#include +#include + +#include +#include + +namespace core +{ +namespace posix +{ +/** + * @brief The Signalable class abstracts the ability of an entity to be delivered a posix signal. + */ +class CORE_POSIX_DLL_PUBLIC Signalable +{ +public: + virtual ~Signalable() { } + + /** + * @brief Sends a signal to this signalable object. + * @throws std::system_error in case of problems. + * @param [in] signal The signal to be sent to the process. + */ + virtual void send_signal_or_throw(Signal signal); + + /** + * @brief Sends a signal to this signalable object. + * @param [in] signal The signal to be sent to the process. + * @param [out] e Set to contain an error if an issue arises. + */ + virtual void send_signal(Signal signal, std::error_code& e) noexcept(true); + +protected: + CORE_POSIX_DLL_LOCAL explicit Signalable(pid_t pid); + +private: + struct CORE_POSIX_DLL_LOCAL Private; + std::shared_ptr d; +}; +} +} + +#endif // CORE_POSIX_SIGNALABLE_H_ diff --git a/external/process-cpp-minimal/include/core/posix/standard_stream.h b/external/process-cpp-minimal/include/core/posix/standard_stream.h new file mode 100644 index 0000000..f50a9b7 --- /dev/null +++ b/external/process-cpp-minimal/include/core/posix/standard_stream.h @@ -0,0 +1,46 @@ +/* + * Copyright © 2013 Canonical Ltd. + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License version 3, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + * + * Authored by: Thomas Voß + */ + +#ifndef CORE_POSIX_STANDARD_STREAM_H_ +#define CORE_POSIX_STANDARD_STREAM_H_ + +#include + +#include + +namespace core +{ +namespace posix +{ +/** + * @brief The StandardStream enum wraps the POSIX standard streams. + */ +enum class StandardStream : std::uint8_t +{ + empty = 0, + stdin = 1 << 0, + stdout = 1 << 1, + stderr = 1 << 2 +}; + +CORE_POSIX_DLL_PUBLIC StandardStream operator|(StandardStream l, StandardStream r); +CORE_POSIX_DLL_PUBLIC StandardStream operator&(StandardStream l, StandardStream r); +} +} + +#endif // CORE_POSIX_STANDARD_STREAM_H_ diff --git a/external/process-cpp-minimal/include/core/posix/this_process.h b/external/process-cpp-minimal/include/core/posix/this_process.h new file mode 100644 index 0000000..584a94e --- /dev/null +++ b/external/process-cpp-minimal/include/core/posix/this_process.h @@ -0,0 +1,128 @@ +/* + * Copyright © 2013 Canonical Ltd. + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License version 3, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + * + * Authored by: Thomas Voß + */ + +#ifndef CORE_POSIX_THIS_PROCESS_H_ +#define CORE_POSIX_THIS_PROCESS_H_ + +#include + +#include +#include +#include +#include + +namespace core +{ +namespace posix +{ +class Process; +namespace this_process +{ +namespace env +{ +/** + * @brief for_each invokes a functor for every key-value pair in the environment. + * @param [in] functor Invoked for every key-value pair. + */ +CORE_POSIX_DLL_PUBLIC void for_each( + const std::function& functor) noexcept(true); + +/** + * @brief get queries the value of an environment variable. + * @throw std::runtime_error if there is no variable with the given key defined in the env. + * @param [in] key Name of the variable to query the value for. + * @return Contents of the variable. + */ +CORE_POSIX_DLL_PUBLIC std::string get_or_throw(const std::string& key); + +/** + * @brief get queries the value of an environment variable. + * @param [in] key Name of the variable to query the value for. + * @param [in] default_value Default value to return when key is not present in the environment. + * @return Contents of the variable or an empty string if the variable is not defined. + */ +CORE_POSIX_DLL_PUBLIC std::string get( + const std::string& key, + const std::string& default_value = std::string()) noexcept(true); + +/** + * @brief unset_or_throw removes the variable with name key from the environment. + * @throw std::system_error in case of errors. + * @param [in] key Name of the variable to unset. + */ +CORE_POSIX_DLL_PUBLIC void unset_or_throw(const std::string& key); + +/** + * @brief unset removes the variable with name key from the environment. + * @return false in case of errors, true otherwise. + * @param [in] key Name of the variable to unset. + * @param [out] se Receives error details if unset returns false. + */ +CORE_POSIX_DLL_PUBLIC bool unset(const std::string& key, + std::error_code& se) noexcept(true); + +/** + * @brief set_or_throw will adjust the contents of the variable identified by key to the provided value. + * @throw std::system_error in case of errors. + * @param [in] key Name of the variable to set the value for. + * @param [in] value New contents of the variable. + */ +CORE_POSIX_DLL_PUBLIC void set_or_throw(const std::string& key, + const std::string& value); +/** + * @brief set will adjust the contents of the variable identified by key to the provided value. + * @return false in case of errors, true otherwise. + * @param [in] key Name of the variable to set the value for. + * @param [in] value New contents of the variable. + * @param [out] se Receives the details in case of errors. + */ +CORE_POSIX_DLL_PUBLIC bool set(const std::string &key, + const std::string &value, + std::error_code& se) noexcept(true); +} + +/** + * @brief Returns a Process instance corresponding to this process. + */ +CORE_POSIX_DLL_PUBLIC Process instance() noexcept(true); + +/** + * @brief Query the parent of the process. + * @return The parent of the process. + */ +CORE_POSIX_DLL_PUBLIC Process parent() noexcept(true); + +/** + * @brief Access this process's stdin. + */ +CORE_POSIX_DLL_PUBLIC std::istream& cin() noexcept(true); + +/** + * @brief Access this process's stdout. + */ +CORE_POSIX_DLL_PUBLIC std::ostream& cout() noexcept(true); + +/** + * @brief Access this process's stderr. + */ +CORE_POSIX_DLL_PUBLIC std::ostream& cerr() noexcept(true); +} +} +} + +#endif // CORE_POSIX_THIS_PROCESS_H_ diff --git a/external/process-cpp-minimal/include/core/posix/visibility.h b/external/process-cpp-minimal/include/core/posix/visibility.h new file mode 100644 index 0000000..e99d849 --- /dev/null +++ b/external/process-cpp-minimal/include/core/posix/visibility.h @@ -0,0 +1,30 @@ +/* + * Copyright © 2013 Canonical Ltd. + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License version 3, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + * + * Authored by: Thomas Voß + */ + +#ifndef CORE_POSIX_VISIBILITY_H_ +#define CORE_POSIX_VISIBILITY_H_ + +#if __GNUC__ >= 4 +#define CORE_POSIX_DLL_PUBLIC __attribute__ ((visibility ("default"))) +#define CORE_POSIX_DLL_LOCAL __attribute__ ((visibility ("hidden"))) +#else +#define CORE_POSIX_DLL_PUBLIC +#define CORE_POSIX_DLL_LOCAL +#endif + +#endif // CORE_POSIX_VISIBILITY_H_ diff --git a/external/process-cpp-minimal/include/core/posix/wait.h b/external/process-cpp-minimal/include/core/posix/wait.h new file mode 100644 index 0000000..2b47cd6 --- /dev/null +++ b/external/process-cpp-minimal/include/core/posix/wait.h @@ -0,0 +1,104 @@ +/* + * Copyright © 2013 Canonical Ltd. + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License version 3, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + * + * Authored by: Thomas Voß + */ + +#ifndef CORE_POSIX_WAIT_H_ +#define CORE_POSIX_WAIT_H_ + +#include +#include +#include + +#include + +#include + +#include + +namespace core +{ +namespace posix +{ +namespace wait +{ + +/** + * @brief Flags enumerates different behavior when waiting for a child process to change state. + */ +enum class Flags : std::uint8_t +{ + continued = WCONTINUED, ///< Also wait for a child to continue after having been stopped. + untraced = WUNTRACED, ///< Also wait for state changes in untraced children. + no_hang = WNOHANG ///< Do not block if a child process hasn't changed state. +}; + +CORE_POSIX_DLL_PUBLIC Flags operator|(Flags l, Flags r); + +/** + * @brief The Result struct encapsulates the result of waiting for a process state change. + */ +struct CORE_POSIX_DLL_PUBLIC Result +{ + /** + * @brief The status of the process/wait operation. + */ + enum class Status + { + undefined, ///< Marks an undefined state. + no_state_change, ///< No state change occured. + exited, ///< The process exited normally. + signaled, ///< The process was signalled and terminated. + stopped, ///< The process was signalled and stopped. + continued ///< The process resumed operation. + } status = Status::undefined; + + /** + * @brief Union of result-specific details. + */ + union + { + /** + * Contains the exit status of the process if status == Status::exited. + */ + struct + { + exit::Status status; ///< Exit status of the process. + } if_exited; + + /** + * Contains the signal that caused the process to terminate if status == Status::signaled. + */ + struct + { + Signal signal; ///< Signal that caused the process to terminate. + bool core_dumped; ///< true if the process termination resulted in a core dump. + } if_signaled; + + /** + * Contains the signal that caused the process to terminate if status == Status::stopped. + */ + struct + { + Signal signal; ///< Signal that caused the process to terminate. + } if_stopped; + } detail; +}; +} +} +} + +#endif // CORE_POSIX_WAIT_H_ diff --git a/external/process-cpp-minimal/include/core/signal.h b/external/process-cpp-minimal/include/core/signal.h new file mode 100644 index 0000000..690d236 --- /dev/null +++ b/external/process-cpp-minimal/include/core/signal.h @@ -0,0 +1,297 @@ +/* + * Copyright © 2013 Canonical Ltd. + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License version 3, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + * + * Authored by: Thomas Voß + */ +#ifndef COM_UBUNTU_SIGNAL_H_ +#define COM_UBUNTU_SIGNAL_H_ + +#include + +#include +#include +#include +#include +#include + +namespace core +{ +/** + * @brief A signal class that observers can subscribe to. + * @tparam Arguments List of argument types passed on to observers when the signal is emitted. + */ +template +class Signal +{ +public: + /** + * @brief Slot is the function type that observers have to provide to connect to this signal. + */ + typedef std::function Slot; + +private: + struct SlotWrapper + { + void operator()(Arguments... args) + { + dispatcher(std::bind(slot, args...)); + } + + Slot slot; + Connection::Dispatcher dispatcher; + Connection connection; + }; + +public: + /** + * @brief Signal constructs a new instance. Never throws. + */ + inline Signal() noexcept(true) : d(new Private()) + { + } + + inline ~Signal() + { + std::lock_guard lg(d->guard); + for (auto slot : d->slot_list) + slot.connection.reset(); + } + + // Copy construction, assignment and equality comparison are disabled. + Signal(const Signal&) = delete; + Signal& operator=(const Signal&) = delete; + bool operator==(const Signal&) const = delete; + + /** + * @brief Connects the provided slot to this signal instance. + * + * Calling this method is thread-safe and synchronized with any + * other connect, signal emission or disconnect calls. + * + * @param slot The function to be called when the signal is emitted. + * @return A connection object corresponding to the signal-slot connection. + */ + inline Connection connect(const Slot& slot) const + { + // Helpers to initialize an invalid connection. + static const Connection::Disconnector empty_disconnector{}; + static const Connection::DispatcherInstaller empty_dispatcher_installer{}; + + // The default dispatcher immediately executes the function object + // provided as argument on whatever thread is currently running. + static const Connection::Dispatcher default_dispatcher + = [](const std::function& handler) { handler(); }; + + Connection conn{empty_disconnector, empty_dispatcher_installer}; + + std::lock_guard lg(d->guard); + + auto result = d->slot_list.insert( + d->slot_list.end(), + SlotWrapper{slot, default_dispatcher, conn}); + + // We implicitly share our internal state with the connection here + // by passing in our private bits contained in 'd' to the std::bind call. + // This admittedly uncommon approach allows us to cleanly manage connection + // and signal lifetimes without the need to mark everything as mutable. + conn.d->disconnector = std::bind( + &Private::disconnect_slot_for_iterator, + d, + result); + conn.d->dispatcher_installer = std::bind( + &Private::install_dispatcher_for_iterator, + d, + std::placeholders::_1, + result); + + return conn; + } + + /** + * @brief operator () emits the signal with the provided parameters. + * + * Please note that signal emissions might not be delivered immediately to + * registered slots, depending on whether the respective connection is dispatched + * via a queueing dispatcher. For that reason, the lifetime of the arguments has to + * exceed the scope of the call to this operator and its surrounding scope. + * + * @param args The arguments to be passed on to registered slots. + */ + inline void operator()(Arguments... args) + { + std::lock_guard lg(d->guard); + for(auto slot : d->slot_list) + { + slot(args...); + } + } + +private: + struct Private + { + typedef std::list SlotList; + + inline void disconnect_slot_for_iterator(typename SlotList::iterator it) + { + std::lock_guard lg(guard); + slot_list.erase(it); + } + + inline void install_dispatcher_for_iterator(const Connection::Dispatcher& dispatcher, + typename SlotList::iterator it) + { + std::lock_guard lg(guard); + it->dispatcher = dispatcher; + } + + std::mutex guard; + SlotList slot_list; + }; + std::shared_ptr d; +}; + +/** + * @brief A signal class that observers can subscribe to, + * template specialization for signals without arguments. + */ +template<> +class Signal +{ +public: + /** + * @brief Slot is the function type that observers have to provide to connect to this signal. + */ + typedef std::function Slot; + +private: + struct SlotWrapper + { + void operator()() + { + dispatcher(slot); + } + + Slot slot; + Connection::Dispatcher dispatcher; + Connection connection; + }; + +public: + /** + * @brief Signal constructs a new instance. Never throws. + */ + inline Signal() noexcept(true) : d(new Private()) + { + } + + inline ~Signal() + { + std::lock_guard lg(d->guard); + for (auto slot : d->slot_list) + slot.connection.reset(); + } + + // Copy construction, assignment and equality comparison are disabled. + Signal(const Signal&) = delete; + Signal& operator=(const Signal&) = delete; + bool operator==(const Signal&) const = delete; + + /** + * @brief Connects the provided slot to this signal instance. + * + * Calling this method is thread-safe and synchronized with any + * other connect, signal emission or disconnect calls. + * + * @param slot The function to be called when the signal is emitted. + * @return A connection object corresponding to the signal-slot connection. + */ + inline Connection connect(const Slot& slot) const + { + // Helpers to initialize an invalid connection. + static const Connection::Disconnector empty_disconnector{}; + static const Connection::DispatcherInstaller empty_dispatcher_installer{}; + + // The default dispatcher immediately executes the function object + // provided as argument on whatever thread is currently running. + static const Connection::Dispatcher default_dispatcher + = [](const std::function& handler) { handler(); }; + + Connection conn{empty_disconnector, empty_dispatcher_installer}; + + std::lock_guard lg(d->guard); + + auto result = d->slot_list.insert( + d->slot_list.end(), + SlotWrapper{slot, default_dispatcher, conn}); + + // We implicitly share our internal state with the connection here + // by passing in our private bits contained in 'd' to the std::bind call. + // This admittedly uncommon approach allows us to cleanly manage connection + // and signal lifetimes without the need to mark everything as mutable. + conn.d->disconnector = std::bind( + &Private::disconnect_slot_for_iterator, + d, + result); + conn.d->dispatcher_installer = std::bind( + &Private::install_dispatcher_for_iterator, + d, + std::placeholders::_1, + result); + + return conn; + } + + /** + * @brief operator () emits the signal. + * + * Please note that signal emissions might not be delivered immediately to + * registered slots, depending on whether the respective connection is dispatched + * via a queueing dispatcher. + */ + inline void operator()() + { + std::lock_guard lg(d->guard); + for(auto slot : d->slot_list) + { + slot(); + } + } + +private: + struct Private + { + typedef std::list SlotList; + + inline void disconnect_slot_for_iterator(typename SlotList::iterator it) + { + std::lock_guard lg(guard); + slot_list.erase(it); + } + + inline void install_dispatcher_for_iterator(const Connection::Dispatcher& dispatcher, + typename SlotList::iterator it) + { + std::lock_guard lg(guard); + it->dispatcher = dispatcher; + } + + std::mutex guard; + SlotList slot_list; + }; + std::shared_ptr d; +}; +} + +#endif // COM_UBUNTU_SIGNAL_H_ diff --git a/external/process-cpp-minimal/include/core/testing/cross_process_sync.h b/external/process-cpp-minimal/include/core/testing/cross_process_sync.h new file mode 100644 index 0000000..18d7132 --- /dev/null +++ b/external/process-cpp-minimal/include/core/testing/cross_process_sync.h @@ -0,0 +1,99 @@ +/* + * Copyright © 2013 Canonical Ltd. + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 3, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * Authored by: Thomas Voss + */ + +#ifndef CORE_TESTING_CROSS_PROCESS_SYNC_H_ +#define CORE_TESTING_CROSS_PROCESS_SYNC_H_ + +#include + +#include + +#include +#include + +namespace core +{ +namespace testing +{ +/** + * @brief A cross-process synchronization primitive that supports simple wait-condition-like scenarios. + */ +class CORE_POSIX_DLL_PUBLIC CrossProcessSync +{ + public: + struct Error + { + Error() = delete; + ~Error() = delete; + + /** + * @brief Thrown if any of the *_for functions times out. + */ + struct Timeout : public std::runtime_error + { + Timeout() : std::runtime_error("Timeout while waiting for event to happen.") + { + } + }; + }; + + /** + * @brief Constructs a new sync element. + */ + CrossProcessSync(); + + /** + * @brief Copy c'tor, duping the underlying fds. + * @param rhs The instance to copy. + */ + CrossProcessSync(const CrossProcessSync& rhs); + + /** + * @brief Closes the underlying fds. + */ + ~CrossProcessSync() noexcept; + + /** + * @brief operator =, dup's the underlying fds. + * @param rhs The instance to assign from. + * @return A mutable reference to this instance. + */ + CrossProcessSync& operator=(const CrossProcessSync& rhs); + + /** + * @brief Try to signal the other side that we are ready for at most duration milliseconds. + * @throw Error::Timeout in case of a timeout. + * @throw std::system_error for problems with the underlying pipe. + */ + void try_signal_ready_for(const std::chrono::milliseconds& duration); + + /** + * @brief Wait for the other sides to signal readiness for at most duration milliseconds. + * @return The number of ready signals that have been collected since creation. + * @throw Error::Timeout in case of a timeout. + * @throw std::system_error for problems with the underlying pipe. + */ + std::uint32_t wait_for_signal_ready_for(const std::chrono::milliseconds& duration); + + private: + int fds[2]; ///< The cross-process pipe. + std::uint32_t counter; ///< Counts the number of times the sync has been signalled. +}; +} +} +#endif // CORE_TESTING_CROSS_PROCESS_SYNC_H_ diff --git a/external/process-cpp-minimal/include/core/testing/fork_and_run.h b/external/process-cpp-minimal/include/core/testing/fork_and_run.h new file mode 100644 index 0000000..7bd5dec --- /dev/null +++ b/external/process-cpp-minimal/include/core/testing/fork_and_run.h @@ -0,0 +1,115 @@ +/* + * Copyright © 2012-2013 Canonical Ltd. + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License version 3, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + * + * Authored by: Thomas Voß + */ +#ifndef CORE_TESTING_FORK_AND_RUN_H_ +#define CORE_TESTING_FORK_AND_RUN_H_ + +#include +#include +#include + +#include + +namespace core +{ +namespace testing +{ +/** + * @brief The ForkAndRunResult enum models the different failure modes of fork_and_run. + */ +enum class ForkAndRunResult +{ + empty = 0, ///< Special value indicating no bit being set. + client_failed = 1 << 0, ///< The client failed. + service_failed = 1 << 1 ///< The service failed. +}; + +CORE_POSIX_DLL_PUBLIC ForkAndRunResult operator|(ForkAndRunResult lhs, ForkAndRunResult rhs); +CORE_POSIX_DLL_PUBLIC ForkAndRunResult operator&(ForkAndRunResult lhs, ForkAndRunResult rhs); + +/** + * @brief Forks two processes for both the service and the client. + * + * The function does the following: + * - Forks a process for the service and runs the respective closure. + * - Forks a process for the client and runs the respective closure. + * - After the client has finished, the service is signalled with sigterm. + * + * @throw std::system_error if an error occured during process interaction. + * @throw std::runtime_error for signalling all other error conditions. + * @param [in] service The service to be executed in a child process. + * @param [in] client The client to be executed in a child process. + * @return ForkAndRunResult indicating if either of service or client failed. + */ +CORE_POSIX_DLL_PUBLIC ForkAndRunResult fork_and_run( + const std::function& service, + const std::function& client); +} +} + +/** + * Test definition macro which runs a TEST in a forked process. + * Note that you can only use EXPECT_*, not + * ASSERT_*! + * + * Usage: + * TESTP(test_suite, test_name, { + * test code ... + * EXPECT_* ... + * }) + */ +#define TESTP(test_suite, test_name, CODE) \ + TEST(test_suite, test_name) { \ + auto test = [&]() { \ + CODE \ + return HasFailure() ? core::posix::exit::Status::failure \ + : core::posix::exit::Status::success; \ + }; \ + auto child = core::posix::fork( \ + test, \ + core::posix::StandardStream::empty); \ + auto result = child.wait_for(core::posix::wait::Flags::untraced); \ + EXPECT_EQ(core::posix::wait::Result::Status::exited, result.status); \ + EXPECT_EQ(core::posix::exit::Status::success, result.detail.if_exited.status); \ + } \ + +/** + * Test definition macro which runs a TEST_F in a forked process. + * Note that you can only use EXPECT_*, not ASSERT_*! + * + * Usage: + * TESTP_F(FixtureName, TestName, { + * ... test code ... + * EXPECT_* ... + * }) + */ +#define TESTP_F(test_fixture, test_name, CODE) \ + TEST_F(test_fixture, test_name) { \ + auto test = [&]() { \ + CODE \ + return HasFailure() ? core::posix::exit::Status::failure \ + : core::posix::exit::Status::success; \ + }; \ + auto child = core::posix::fork( \ + test, \ + core::posix::StandardStream::empty); \ + auto result = child.wait_for(core::posix::wait::Flags::untraced); \ + EXPECT_EQ(core::posix::wait::Result::Status::exited, result.status); \ + EXPECT_EQ(core::posix::exit::Status::success, result.detail.if_exited.status); \ + } \ + +#endif // CORE_TESTING_FORK_AND_RUN_H_ diff --git a/external/process-cpp-minimal/src/CMakeLists.txt b/external/process-cpp-minimal/src/CMakeLists.txt new file mode 100644 index 0000000..8daf938 --- /dev/null +++ b/external/process-cpp-minimal/src/CMakeLists.txt @@ -0,0 +1,47 @@ +# Copyright © 2013 Canonical Ltd. +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License version 3 as +# published by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +# +# Authored by: Thomas Voss +add_library( + process-cpp + + core/posix/backtrace.h + core/posix/backtrace.cpp + + core/posix/child_process.cpp + core/posix/exec.cpp + core/posix/fork.cpp + core/posix/process.cpp + core/posix/process_group.cpp + core/posix/signal.cpp + core/posix/signalable.cpp + core/posix/standard_stream.cpp + core/posix/wait.cpp + core/posix/this_process.cpp + + core/posix/linux/proc/process/oom_adj.cpp + core/posix/linux/proc/process/oom_score.cpp + core/posix/linux/proc/process/oom_score_adj.cpp + core/posix/linux/proc/process/stat.cpp + + core/testing/cross_process_sync.cpp + core/testing/fork_and_run.cpp +) + +target_link_libraries( + process-cpp + + ${Boost_LIBRARIES} + ${CMAKE_THREAD_LIBS_INIT} +) diff --git a/external/process-cpp-minimal/src/core/posix/CMakeLists.txt b/external/process-cpp-minimal/src/core/posix/CMakeLists.txt new file mode 100644 index 0000000..fbbaa81 --- /dev/null +++ b/external/process-cpp-minimal/src/core/posix/CMakeLists.txt @@ -0,0 +1,24 @@ +# Copyright © 2013 Canonical Ltd. +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License version 3 as +# published by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +# +# Authored by: Thomas Voss + +add_library( + posix-process + + process.cpp +) + +add_subdirectory(linux) + \ No newline at end of file diff --git a/external/process-cpp-minimal/src/core/posix/backtrace.cpp b/external/process-cpp-minimal/src/core/posix/backtrace.cpp new file mode 100644 index 0000000..7a02bbd --- /dev/null +++ b/external/process-cpp-minimal/src/core/posix/backtrace.cpp @@ -0,0 +1,153 @@ +/* + * Copyright © 2014 Canonical Ltd. + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License version 3, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + * + * Authored by: Thomas Voß + */ + +#include "backtrace.h" + +#include + +#include + +namespace bt = core::posix::backtrace; + +namespace impl +{ +std::tuple demangle(const std::string& symbol) +{ + int status = 1; + auto result = abi::__cxa_demangle(symbol.c_str(), + nullptr, + nullptr, + &status); + + if (!result || status != 0) + { + return std::make_tuple(std::string(), false); + } + + std::string s{result}; + ::free(result); + + return std::make_tuple(s, true); +} + +struct Frame : public bt::Frame +{ + struct Symbol : public bt::Frame::Symbol + { + Symbol(const char* symbol) : raw_(symbol) + { + auto first = raw_.find_first_of("("); + auto last = raw_.find_last_of(")"); + + if (first != std::string::npos && last != std::string::npos) + { + auto mangled_symbol = raw_.substr(first+1, + (last-1) - (first+1)); + + auto plus = mangled_symbol.find_first_of("+"); + if (plus != std::string::npos) + mangled_symbol.erase(plus); + + std::tie(demangled_, is_cxx_) = demangle(mangled_symbol); + if (!is_cxx_) + demangled_ = raw_; + } + } + + bool is_cxx() const + { + return is_cxx_; + } + + std::string demangled() const + { + return demangled_; + } + + std::string raw() const + { + return raw_; + } + + std::string raw_; + std::string demangled_; + bool is_cxx_ = false; + }; + + std::size_t depth_; + void* frame_pointer_; + Symbol symbol_; + + Frame(std::size_t depth, void* frame_pointer, const char* symbol) + : depth_(depth), + frame_pointer_(frame_pointer), + symbol_(symbol) + { + } + + std::size_t depth() const + { + return depth_; + } + + virtual void* frame_pointer() const + { + return frame_pointer_; + } + + const Symbol& symbol() const + { + return symbol_; + } +}; +} + +std::shared_ptr bt::Frame::Symbol::for_testing_from_raw_symbol(const char* symbol) +{ + return std::shared_ptr(new impl::Frame::Symbol(symbol)); +} + +void bt::visit_with_handler(const bt::FrameHandler& handler) +{ + static const unsigned int max_frames=64; + void *frames[max_frames]; + + auto frame_count = ::backtrace(frames, max_frames); + auto symbols = ::backtrace_symbols(frames, frame_count); + + struct Scope + { + Scope(char** symbols) : symbols(symbols) + { + } + + ~Scope() + { + ::free(symbols); + } + + char** symbols = nullptr; + } scope{symbols}; + + for (int i = 0; i < frame_count; i++) + { + impl::Frame frame(i, frames[i], symbols[i]); + if (!handler(frame)) + return; + } +} diff --git a/external/process-cpp-minimal/src/core/posix/backtrace.h b/external/process-cpp-minimal/src/core/posix/backtrace.h new file mode 100644 index 0000000..7a77ddd --- /dev/null +++ b/external/process-cpp-minimal/src/core/posix/backtrace.h @@ -0,0 +1,122 @@ +/* + * Copyright © 2014 Canonical Ltd. + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License version 3, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + * + * Authored by: Thomas Voß + */ + +#ifndef CORE_POSIX_BACKTRACE_H_ +#define CORE_POSIX_BACKTRACE_H_ + +#include + +#include +#include +#include + +namespace core +{ +namespace posix +{ +namespace backtrace +{ +/** + * @brief The Frame class models an individual frame of a backtrace. + */ +class Frame +{ +public: + /** + * @brief The Symbol class models the symbolic representation of a frame pointer. + */ + class Symbol + { + public: + + static std::shared_ptr for_testing_from_raw_symbol(const char* symbol); + + Symbol(const Symbol&) = delete; + virtual ~Symbol() = default; + + Symbol& operator=(const Symbol&) = delete; + + /** + * @brief is_cxx checks whether the symbol refers to a mangled C++ symbol. + * @return true iff the symbol refers to a mangled C++ symbol. + */ + virtual bool is_cxx() const = 0; + + /** + * @brief demangled returns the demangled C++ symbol name or raw. + */ + virtual std::string demangled() const = 0; + + /** + * @brief raw The raw symbolic representation of a frame pointer. + * @return + */ + virtual std::string raw() const = 0; + + protected: + Symbol() = default; + }; + + Frame(const Frame&) = delete; + virtual ~Frame() = default; + + Frame& operator=(const Frame&) = delete; + + /** + * @brief depth returns the depth of this frame in the overall backtrace. + */ + virtual std::size_t depth() const = 0; + + /** + * @brief frame_pointer returns the the raw frame pointer of this frame. + * @return + */ + virtual void* frame_pointer() const = 0; + + /** + * @brief symbol returns the symbolic representation of this frame. + */ + virtual const Symbol& symbol() const = 0; + +protected: + Frame() = default; +}; + +/** + * @brief FrameHandler is the functor invoked for every frame of a backtrace. + * + * A FrameHandler should return true if it wants to continue walking the stack + * or false otherwise. + */ +typedef std::function FrameHandler; + +/** + *@brief visit_with_handler iterates the backtrace of the calling program, + *invoking the handler for every frame. + * + * A FrameHandler should return true if it wants to continue walking the stack + * or false otherwise + * + * @param handler The handler invoked for every frame. + */ +void CORE_POSIX_DLL_PUBLIC visit_with_handler(const FrameHandler& handler); +} +} +} + +#endif // CORE_POSIX_BACKTRACE_H_ diff --git a/external/process-cpp-minimal/src/core/posix/child_process.cpp b/external/process-cpp-minimal/src/core/posix/child_process.cpp new file mode 100644 index 0000000..93d1cba --- /dev/null +++ b/external/process-cpp-minimal/src/core/posix/child_process.cpp @@ -0,0 +1,397 @@ +/* + * Copyright © 2013 Canonical Ltd. + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License version 3, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + * + * Authored by: Thomas Voß + */ + +#include + +#include +#include + +#include +#include +#include +#include + +#include +#include +#include + +#include +#include + +namespace io = boost::iostreams; + +namespace +{ + +struct DeathObserverImpl : public core::posix::ChildProcess::DeathObserver +{ + DeathObserverImpl(const std::shared_ptr& trap) + : on_sig_child_connection + { + trap->signal_raised().connect([this](core::posix::Signal signal) + { + switch (signal) + { + case core::posix::Signal::sig_chld: + on_sig_child(); + break; + default: + break; + } + }) + } + { + if (!trap->has(core::posix::Signal::sig_chld)) + throw std::logic_error( + "DeathObserver::DeathObserverImpl: Given SignalTrap" + " instance does not trap Signal::sig_chld."); + } + + bool add(const core::posix::ChildProcess& process) override + { + if (process.pid() == -1) + return false; + + std::lock_guard lg(guard); + + bool added = false; + auto new_process = std::make_pair(process.pid(), process); + std::tie(std::ignore, added) = children.insert(new_process); + + if (added) + { + // The process may have died between it's instantiation and it + // being added to the children map. Check that it's still alive. + int status{-1}; + if (::waitpid(process.pid(), &status, WNOHANG) != 0) // child no longer alive + { + // we missed the SIGCHLD signal so we must now manually + // inform our subscribers. + signals.child_died(new_process.second); + children.erase(new_process.first); + return false; + } + } + + return added; + } + + bool has(const core::posix::ChildProcess& process) const override + { + std::lock_guard lg(guard); + return children.count(process.pid()) > 0; + } + + const core::Signal& child_died() const override + { + return signals.child_died; + } + + void on_sig_child() override + { + pid_t pid{-1}; int status{-1}; + while (true) + { + pid = ::waitpid(0, &status, WNOHANG); + + if (pid == -1) + { + if (errno == ECHILD) + { + break; // No more children + } + continue; // Ignore stray SIGCHLD signals + } + else if (pid == 0) + { + break; // No more children + } + else + { + std::lock_guard lg(guard); + auto it = children.find(pid); + + if (it != children.end()) + { + if (WIFSIGNALED(status) || WIFEXITED(status)) + { + signals.child_died(it->second); + children.erase(it); + } + } + } + } + } + + mutable std::mutex guard; + std::unordered_map children; + core::ScopedConnection on_sig_child_connection; + struct + { + core::Signal child_died; + } signals; +}; +} + +std::unique_ptr +core::posix::ChildProcess::DeathObserver::create_once_with_signal_trap( + std::shared_ptr trap) +{ + static std::atomic has_been_created_once{false}; + + if (has_been_created_once.exchange(true)) + throw std::runtime_error + { + "DeathObserver::create_once_with_signal_trap: " + "Cannot create more than one instance." + }; + + try + { + std::unique_ptr result + { + new DeathObserverImpl{trap} + }; + + return result; + } catch(...) + { + // We make sure that a throwing c'tor does not impact our ability to + // retry creation of a DeathObserver instance. + has_been_created_once.store(false); + + std::rethrow_exception(std::current_exception()); + } + + assert(false && "We should never reach here."); + + // Silence the compiler. + return std::unique_ptr{}; +} + +namespace core +{ +namespace posix +{ +ChildProcess::Pipe ChildProcess::Pipe::invalid() +{ + static Pipe p; + static std::once_flag flag; + + std::call_once(flag, [&]() { p.close_read_fd(); p.close_write_fd(); }); + + return p; +} + +ChildProcess::Pipe::Pipe() +{ + int rc = ::pipe(fds); + + if (rc == -1) + throw std::system_error(errno, std::system_category()); +} + +ChildProcess::Pipe::Pipe(const ChildProcess::Pipe& rhs) : fds{-1, -1} +{ + if (rhs.fds[0] != -1) + fds[0] = ::dup(rhs.fds[0]); + + if (rhs.fds[1] != -1) + fds[1] = ::dup(rhs.fds[1]); +} + +ChildProcess::Pipe::~Pipe() +{ + if (fds[0] != -1) + ::close(fds[0]); + if (fds[1] != -1) + ::close(fds[1]); +} + +int ChildProcess::Pipe::read_fd() const +{ + return fds[0]; +} + +void ChildProcess::Pipe::close_read_fd() +{ + if (fds[0] != -1) + { + ::close(fds[0]); + fds[0] = -1; + } +} + +int ChildProcess::Pipe::write_fd() const +{ + return fds[1]; +} + +void ChildProcess::Pipe::close_write_fd() +{ + if (fds[1] != -1) + { + ::close(fds[1]); + fds[1] = -1; + } +} + +ChildProcess::Pipe& ChildProcess::Pipe::operator=(const ChildProcess::Pipe& rhs) +{ + if (fds[0] != -1) + ::close(fds[0]); + if (fds[1] != -1) + ::close(fds[1]); + + if (rhs.fds[0] != -1) + fds[0] = ::dup(rhs.fds[0]); + else + fds[0] = -1; + if (rhs.fds[1] != -1) + fds[1] = ::dup(rhs.fds[1]); + else + fds[1] = -1; + + return *this; +} + +struct ChildProcess::Private +{ + // stdin and stdout are always "relative" to the childprocess, i.e., we + // write to stdin of the child process and read from its stdout. + Private(pid_t pid, + const ChildProcess::Pipe& stderr, + const ChildProcess::Pipe& stdin, + const ChildProcess::Pipe& stdout) + : pipes{stderr, stdin, stdout}, + serr(pipes.stderr.read_fd(), io::never_close_handle), + sin(pipes.stdin.write_fd(), io::never_close_handle), + sout(pipes.stdout.read_fd(), io::never_close_handle), + cerr(&serr), + cin(&sin), + cout(&sout), + original_parent_pid(::getpid()), + original_child_pid(pid) + { + } + + ~Private() + { + // Check if we are in the original parent process. + if (original_parent_pid == getpid()) + { + // If so, check if we are considering a valid pid here. + // If so, we kill the original child. + if (original_child_pid != -1) + ::kill(original_child_pid, SIGKILL); + } + } + + struct + { + ChildProcess::Pipe stdin; + ChildProcess::Pipe stdout; + ChildProcess::Pipe stderr; + } pipes; + io::stream_buffer serr; + io::stream_buffer sin; + io::stream_buffer sout; + std::istream cerr; + std::ostream cin; + std::istream cout; + + // We need to store the original parent pid as we might have been forked + // and with our automatic cleanup in place, it might happen that the d'tor + // is called from the child process. + pid_t original_parent_pid; + pid_t original_child_pid; +}; + +ChildProcess ChildProcess::invalid() +{ + // We take the init process as child. + static const pid_t invalid_pid = 1; + return ChildProcess(invalid_pid, Pipe::invalid(), Pipe::invalid(), Pipe::invalid()); +} + +ChildProcess::ChildProcess(pid_t pid, + const ChildProcess::Pipe& stdin_pipe, + const ChildProcess::Pipe& stdout_pipe, + const ChildProcess::Pipe& stderr_pipe) + : Process(pid), + d(new Private{pid, stdin_pipe, stdout_pipe, stderr_pipe}) +{ +} + +ChildProcess::~ChildProcess() +{ +} + +wait::Result ChildProcess::wait_for(const wait::Flags& flags) +{ + int status = -1; + pid_t result_pid = ::waitpid(pid(), std::addressof(status), static_cast(flags)); + + if (result_pid == -1) + throw std::system_error(errno, std::system_category()); + + wait::Result result; + + if (result_pid == 0) + { + result.status = wait::Result::Status::no_state_change; + return result; + } + + if (WIFEXITED(status)) + { + result.status = wait::Result::Status::exited; + result.detail.if_exited.status = static_cast(WEXITSTATUS(status)); + } else if (WIFSIGNALED(status)) + { + result.status = wait::Result::Status::signaled; + result.detail.if_signaled.signal = static_cast(WTERMSIG(status)); + result.detail.if_signaled.core_dumped = WCOREDUMP(status); + } else if (WIFSTOPPED(status)) + { + result.status = wait::Result::Status::stopped; + result.detail.if_stopped.signal = static_cast(WSTOPSIG(status)); + } else if (WIFCONTINUED(status)) + { + result.status = wait::Result::Status::continued; + } + + return result; +} + +std::istream& ChildProcess::cerr() +{ + return d->cerr; +} + +std::ostream& ChildProcess::cin() +{ + return d->cin; +} + +std::istream& ChildProcess::cout() +{ + return d->cout; +} +} +} diff --git a/external/process-cpp-minimal/src/core/posix/exec.cpp b/external/process-cpp-minimal/src/core/posix/exec.cpp new file mode 100644 index 0000000..0b48479 --- /dev/null +++ b/external/process-cpp-minimal/src/core/posix/exec.cpp @@ -0,0 +1,75 @@ +/* + * Copyright © 2013 Canonical Ltd. + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License version 3, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + * + * Authored by: Thomas Voß + */ + +#include +#include +#include + +#include + +#include + +#include + +namespace core +{ +namespace posix +{ +ChildProcess exec(const std::string& fn, + const std::vector& argv, + const std::map& env, + const StandardStream& flags) +{ + std::function null_function = [](){}; + return exec(fn, argv, env, flags, null_function); +} + +ChildProcess exec(const std::string& fn, + const std::vector& argv, + const std::map& env, + const StandardStream& flags, + const std::function& child_setup) +{ + return posix::fork([fn, argv, env, child_setup]() + { + char** it; char** pargv; char** penv; + it = pargv = new char*[argv.size()+2]; + *it = ::strdup(fn.c_str()); + it++; + for (auto element : argv) + { + *it = ::strdup(element.c_str()); + it++; + } + *it = nullptr; + + it = penv = new char*[env.size()+1]; + for (auto pair : env) + { + *it = ::strdup((pair.first + "=" + pair.second).c_str()); + it++; + } + *it = nullptr; + + child_setup(); + return static_cast(execve(fn.c_str(), pargv, penv)); + }, flags); +} + +} +} diff --git a/external/process-cpp-minimal/src/core/posix/fork.cpp b/external/process-cpp-minimal/src/core/posix/fork.cpp new file mode 100644 index 0000000..1c9c1f3 --- /dev/null +++ b/external/process-cpp-minimal/src/core/posix/fork.cpp @@ -0,0 +1,180 @@ +/* + * Copyright © 2013 Canonical Ltd. + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License version 3, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + * + * Authored by: Thomas Voß + */ + +#include +#include + +#include "backtrace.h" + +#include +#include +#include + +#include + +namespace +{ +void redirect_stream_to_fd(int fd, int stream) +{ + auto rc = ::dup2(fd, stream); + if (rc == -1) + throw std::system_error(errno, std::system_category()); +} + +void print_backtrace(std::ostream& out, const std::string& line_prefix) +{ + core::posix::backtrace::visit_with_handler([&out, line_prefix](const core::posix::backtrace::Frame& frame) + { + out << line_prefix << std::dec << std::setw(2) << frame.depth() << "@" << std::hex << std::setw(14) << frame.frame_pointer() << ": " + << (frame.symbol().is_cxx() ? frame.symbol().demangled() : frame.symbol().raw()) << std::endl; + return true; + }); +} +} + +namespace core +{ +namespace posix +{ + +bool is_child(pid_t pid) { return pid == 0; } + +ChildProcess fork(const std::function& main, + const StandardStream& flags) +{ + ChildProcess::Pipe stdin_pipe{ChildProcess::Pipe::invalid()}; + ChildProcess::Pipe stdout_pipe{ChildProcess::Pipe::invalid()}; + ChildProcess::Pipe stderr_pipe{ChildProcess::Pipe::invalid()}; + + if ((flags & StandardStream::stdin) != StandardStream::empty) + stdin_pipe = ChildProcess::Pipe(); + if ((flags & StandardStream::stdout) != StandardStream::empty) + stdout_pipe = ChildProcess::Pipe(); + if ((flags & StandardStream::stderr) != StandardStream::empty) + stderr_pipe = ChildProcess::Pipe(); + + pid_t pid = ::fork(); + + if (pid == -1) + throw std::system_error(errno, std::system_category()); + + if (is_child(pid)) + { + posix::exit::Status result = posix::exit::Status::failure; + + try + { + stdin_pipe.close_write_fd(); + stdout_pipe.close_read_fd(); + stderr_pipe.close_read_fd(); + // We replace stdin and stdout of the child process first: + if ((flags & StandardStream::stdin) != StandardStream::empty) + redirect_stream_to_fd(stdin_pipe.read_fd(), STDIN_FILENO); + if ((flags & StandardStream::stdout) != StandardStream::empty) + redirect_stream_to_fd(stdout_pipe.write_fd(), STDOUT_FILENO); + if ((flags & StandardStream::stderr) != StandardStream::empty) + redirect_stream_to_fd(stderr_pipe.write_fd(), STDERR_FILENO); + + result = main(); + } catch(const std::exception& e) + { + std::cerr << "core::posix::fork(): An unhandled std::exception occured in the child process:" << std::endl + << " what(): " << e.what() << std::endl; + print_backtrace(std::cerr, " "); + } catch(...) + { + std::cerr << "core::posix::fork(): An unhandled exception occured in the child process." << std::endl; + print_backtrace(std::cerr, " "); + } + + // We have to ensure that we exit here. Otherwise, we run into + // all sorts of weird issues. + ::exit(static_cast(result)); + } + + // We are in the parent process, and create a process object + // corresponding to the newly forked process. + stdin_pipe.close_read_fd(); + stdout_pipe.close_write_fd(); + stderr_pipe.close_write_fd(); + + return ChildProcess(pid, + stdin_pipe, + stdout_pipe, + stderr_pipe); +} + +ChildProcess vfork(const std::function& main, + const StandardStream& flags) +{ + ChildProcess::Pipe stdin_pipe, stdout_pipe, stderr_pipe; + + pid_t pid = ::vfork(); + + if (pid == -1) + throw std::system_error(errno, std::system_category()); + + if (is_child(pid)) + { + posix::exit::Status result = posix::exit::Status::failure; + + try + { + // We replace stdin and stdout of the child process first: + stdin_pipe.close_write_fd(); + stdout_pipe.close_read_fd(); + stderr_pipe.close_read_fd(); + // We replace stdin and stdout of the child process first: + if ((flags & StandardStream::stdin) != StandardStream::empty) + redirect_stream_to_fd(stdin_pipe.read_fd(), STDIN_FILENO); + if ((flags & StandardStream::stdout) != StandardStream::empty) + redirect_stream_to_fd(stdout_pipe.write_fd(), STDOUT_FILENO); + if ((flags & StandardStream::stderr) != StandardStream::empty) + redirect_stream_to_fd(stderr_pipe.write_fd(), STDERR_FILENO); + + result = main(); + } catch(const std::exception& e) + { + std::cerr << "core::posix::fork(): An unhandled std::exception occured in the child process:" << std::endl + << " what(): " << e.what() << std::endl; + print_backtrace(std::cerr, " "); + } catch(...) + { + std::cerr << "core::posix::fork(): An unhandled exception occured in the child process." << std::endl; + print_backtrace(std::cerr, " "); + } + + // We have to ensure that we exit here. Otherwise, we run into + // all sorts of weird issues. + ::exit(static_cast(result)); + } + + // We are in the parent process, and create a process object + // corresponding to the newly forked process. + // Close the parent's pipe end + stdin_pipe.close_read_fd(); + stdout_pipe.close_write_fd(); + stderr_pipe.close_write_fd(); + + return ChildProcess(pid, + stdin_pipe, + stdout_pipe, + stderr_pipe); +} +} +} diff --git a/external/process-cpp-minimal/src/core/posix/linux/CMakeLists.txt b/external/process-cpp-minimal/src/core/posix/linux/CMakeLists.txt new file mode 100644 index 0000000..19f87b4 --- /dev/null +++ b/external/process-cpp-minimal/src/core/posix/linux/CMakeLists.txt @@ -0,0 +1,28 @@ +# Copyright © 2013 Canonical Ltd. +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License version 3 as +# published by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +# +# Authored by: Thomas Voss + +add_library( + linux-process + + process.cpp +) + +target_link_libraries( + linux-process + + posix-process +) + \ No newline at end of file diff --git a/external/process-cpp-minimal/src/core/posix/linux/proc/process/oom_adj.cpp b/external/process-cpp-minimal/src/core/posix/linux/proc/process/oom_adj.cpp new file mode 100644 index 0000000..3b1b67e --- /dev/null +++ b/external/process-cpp-minimal/src/core/posix/linux/proc/process/oom_adj.cpp @@ -0,0 +1,81 @@ +/* + * Copyright © 2013 Canonical Ltd. + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License version 3, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + * + * Authored by: Thomas Voß + */ + +#include + +#include + +#include +#include +#include + +#include + +namespace core +{ +namespace posix +{ +namespace linux +{ +namespace proc +{ +namespace process +{ + +int OomAdj::disable_value() +{ + return OOM_DISABLE; +} + +int OomAdj::min_value() +{ + return OOM_ADJUST_MIN; +} + +int OomAdj::max_value() +{ + return OOM_ADJUST_MAX; +} + +const posix::Process& operator>>(const posix::Process& process, OomAdj& adj) +{ + std::stringstream ss; ss << "/proc/" << process.pid() << "/oom_adj"; + std::ifstream in(ss.str()); + + in >> adj.value; + + return process; +} + +const posix::Process& operator<<(const posix::Process& process, const OomAdj& adj) +{ + if (!adj.is_valid()) + throw std::logic_error("Value for adjusting the oom score is invalid."); + + std::stringstream ss; ss << "/proc/" << process.pid() << "/oom_adj"; + std::ofstream out(ss.str()); + + out << adj.value; + + return process; +} +} +} +} +} +} diff --git a/external/process-cpp-minimal/src/core/posix/linux/proc/process/oom_score.cpp b/external/process-cpp-minimal/src/core/posix/linux/proc/process/oom_score.cpp new file mode 100644 index 0000000..d52420f --- /dev/null +++ b/external/process-cpp-minimal/src/core/posix/linux/proc/process/oom_score.cpp @@ -0,0 +1,49 @@ +/* + * Copyright © 2013 Canonical Ltd. + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License version 3, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + * + * Authored by: Thomas Voß + */ + +#include + +#include + +#include +#include + +namespace core +{ +namespace posix +{ +namespace linux +{ +namespace proc +{ +namespace process +{ +const posix::Process& operator>>(const posix::Process& process, OomScore& score) +{ + std::stringstream ss; ss << "/proc/" << process.pid() << "/oom_score"; + std::ifstream in(ss.str()); + + in >> score.value; + + return process; +} +} +} +} +} +} diff --git a/external/process-cpp-minimal/src/core/posix/linux/proc/process/oom_score_adj.cpp b/external/process-cpp-minimal/src/core/posix/linux/proc/process/oom_score_adj.cpp new file mode 100644 index 0000000..6a068d1 --- /dev/null +++ b/external/process-cpp-minimal/src/core/posix/linux/proc/process/oom_score_adj.cpp @@ -0,0 +1,76 @@ +/* + * Copyright © 2013 Canonical Ltd. + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License version 3, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + * + * Authored by: Thomas Voß + */ + +#include + +#include + +#include +#include +#include + +#include + +namespace core +{ +namespace posix +{ +namespace linux +{ +namespace proc +{ +namespace process +{ + +int OomScoreAdj::min_value() +{ + return OOM_SCORE_ADJ_MIN; +} + +int OomScoreAdj::max_value() +{ + return OOM_SCORE_ADJ_MAX; +} + +const posix::Process& operator>>(const posix::Process& process, OomScoreAdj& score_adj) +{ + std::stringstream ss; ss << "/proc/" << process.pid() << "/oom_score_adj"; + std::ifstream in(ss.str()); + + in >> score_adj.value; + + return process; +} + +const posix::Process& operator<<(const posix::Process& process, const OomScoreAdj& score_adj) +{ + if (!score_adj.is_valid()) + throw std::logic_error("Value for adjusting the oom score is invalid."); + + std::stringstream ss; ss << "/proc/" << process.pid() << "/oom_score_adj"; + std::ofstream out(ss.str()); + + out << score_adj.value; + + return process; +} +} +} +} +} +} diff --git a/external/process-cpp-minimal/src/core/posix/linux/proc/process/stat.cpp b/external/process-cpp-minimal/src/core/posix/linux/proc/process/stat.cpp new file mode 100644 index 0000000..6cb4484 --- /dev/null +++ b/external/process-cpp-minimal/src/core/posix/linux/proc/process/stat.cpp @@ -0,0 +1,106 @@ +/* + * Copyright © 2013 Canonical Ltd. + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License version 3, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + * + * Authored by: Thomas Voß + */ + +#include + +#include + +#include +#include +#include + +namespace core +{ +namespace posix +{ +namespace linux +{ +namespace proc +{ +namespace process +{ +std::istream& operator>>(std::istream& in, State& state) +{ + char c; in >> c; state = static_cast(c); + return in; +} + +std::istream& operator>>(std::istream& in, Stat& stat) +{ + in >> stat.pid + >> stat.executable + >> stat.state + >> stat.parent + >> stat.process_group + >> stat.session_id + >> stat.tty_nr + >> stat.controlling_process_group + >> stat.kernel_flags + >> stat.minor_faults_count + >> stat.minor_faults_count_by_children + >> stat.major_faults_count + >> stat.major_faults_count_by_children + >> stat.time.user + >> stat.time.system + >> stat.time.user_for_children + >> stat.time.system_for_children + >> stat.priority + >> stat.nice + >> stat.thread_count + >> stat.time_before_next_sig_alarm + >> stat.start_time + >> stat.size.virt + >> stat.size.resident_set + >> stat.size.resident_set_limit + >> stat.addresses.start_code + >> stat.addresses.end_code + >> stat.addresses.start_stack + >> stat.addresses.stack_pointer + >> stat.addresses.instruction_pointer + >> stat.signals.pending + >> stat.signals.blocked + >> stat.signals.ignored + >> stat.signals.caught + >> stat.channel + >> stat.swap_count + >> stat.swap_count_children + >> stat.exit_signal + >> stat.cpu_count + >> stat.realtime_priority + >> stat.scheduling_policy + >> stat.aggregated_block_io_delays + >> stat.guest_time + >> stat.guest_time_children; + + return in; +} + +const posix::Process& operator>>(const posix::Process& process, Stat& stat) +{ + std::stringstream ss; ss << "/proc/" << process.pid() << "/stat"; + std::ifstream in(ss.str()); + + in >> stat; + + return process; +} +} +} +} +} +} diff --git a/external/process-cpp-minimal/src/core/posix/linux/proc/process/state.cpp b/external/process-cpp-minimal/src/core/posix/linux/proc/process/state.cpp new file mode 100644 index 0000000..ddc5537 --- /dev/null +++ b/external/process-cpp-minimal/src/core/posix/linux/proc/process/state.cpp @@ -0,0 +1,40 @@ +/* + * Copyright © 2013 Canonical Ltd. + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License version 3, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + * + * Authored by: Thomas Voß + */ + +#include + +#include + +#include + +namespace core +{ +namespace posix +{ +namespace linux +{ +namespace proc +{ +namespace process +{ + +} +} +} +} +} diff --git a/external/process-cpp-minimal/src/core/posix/process.cpp b/external/process-cpp-minimal/src/core/posix/process.cpp new file mode 100644 index 0000000..049e7b1 --- /dev/null +++ b/external/process-cpp-minimal/src/core/posix/process.cpp @@ -0,0 +1,86 @@ +/* + * Copyright © 2013 Canonical Ltd. + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License version 3, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + * + * Authored by: Thomas Voß + */ + +#include + +#include + +#include +#include + +#include + +namespace core +{ +namespace posix +{ + +struct Process::Private +{ + pid_t pid; +}; + +Process Process::invalid() +{ + static const pid_t invalid_pid = 0; + Process p(invalid_pid); + p.d->pid = -1; + + return p; +} + +Process::Process(pid_t pid) + : Signalable(pid), + d(new Private{pid}) +{ + if (pid < 0) + throw std::runtime_error("Cannot construct instance for invalid pid."); +} + +Process::~Process() noexcept +{ +} + +pid_t Process::pid() const +{ + return d->pid; +} + +ProcessGroup Process::process_group_or_throw() const +{ + pid_t pgid = ::getpgid(pid()); + + if (pgid == -1) + throw std::system_error(errno, std::system_category()); + + return ProcessGroup(pgid); +} + +ProcessGroup Process::process_group(std::error_code& se) const noexcept(true) +{ + pid_t pgid = ::getpgid(pid()); + + if (pgid == -1) + { + se = std::error_code(errno, std::system_category()); + } + + return ProcessGroup(pgid); +} +} +} diff --git a/external/process-cpp-minimal/src/core/posix/process_group.cpp b/external/process-cpp-minimal/src/core/posix/process_group.cpp new file mode 100644 index 0000000..4098883 --- /dev/null +++ b/external/process-cpp-minimal/src/core/posix/process_group.cpp @@ -0,0 +1,47 @@ +/* + * Copyright © 2013 Canonical Ltd. + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License version 3, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + * + * Authored by: Thomas Voß + */ + +#include + +namespace core +{ +namespace posix +{ +struct ProcessGroup::Private +{ + pid_t id; +}; + +pid_t ProcessGroup::id() const +{ + return d->id; +} + +ProcessGroup::ProcessGroup(pid_t id) + : Signalable(-id), // We rely on ::kill to deliver signals, thus negate the id (see man 2 kill). + d(new Private{id}) +{ +} + +ProcessGroup ProcessGroup::invalid() +{ + static const pid_t invalid_pid = 1; + return ProcessGroup(invalid_pid); +} +} +} diff --git a/external/process-cpp-minimal/src/core/posix/signal.cpp b/external/process-cpp-minimal/src/core/posix/signal.cpp new file mode 100644 index 0000000..fd90047 --- /dev/null +++ b/external/process-cpp-minimal/src/core/posix/signal.cpp @@ -0,0 +1,221 @@ +/* + * Copyright © 2013 Canonical Ltd. + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License version 3, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + * + * Authored by: Thomas Voß + */ + +#include + +#include +#include +#include +#include + +#include + +#include + +namespace impl +{ +void set_thread_signal_mask(::sigset_t* new_mask, ::sigset_t* old_mask) +{ + ::pthread_sigmask(SIG_BLOCK, new_mask, old_mask); +} + +void set_process_signal_mask(::sigset_t* new_mask, ::sigset_t* old_mask) +{ + ::sigprocmask(SIG_BLOCK, new_mask, old_mask); +} + +class SignalTrap : public core::posix::SignalTrap +{ +public: + enum class Scope + { + process, + thread + }; + + enum class State + { + not_running, + running + }; + + SignalTrap(Scope scope, std::initializer_list blocked_signals) + : scope(scope), + state(State::not_running), + event_fd(::eventfd(0, EFD_CLOEXEC | EFD_NONBLOCK)) + { + if (event_fd == -1) + throw std::system_error(errno, std::system_category()); + + ::sigemptyset(&blocked_signals_mask); + + for(auto signal : blocked_signals) + ::sigaddset(&blocked_signals_mask, static_cast(signal)); + + switch (scope) + { + case Scope::process: + set_process_signal_mask(&blocked_signals_mask, &old_signals_mask); + break; + case Scope::thread: + set_thread_signal_mask(&blocked_signals_mask, &old_signals_mask); + break; + default: + break; + } + } + + ~SignalTrap() + { + switch (scope) + { + case Scope::process: + set_process_signal_mask(&old_signals_mask, nullptr); + break; + case Scope::thread: + set_thread_signal_mask(&old_signals_mask, nullptr); + break; + default: + break; + } + + ::close(event_fd); + } + + bool has(core::posix::Signal signal) override + { + return ::sigismember(&blocked_signals_mask, static_cast(signal)); + } + + void run() override + { + static constexpr int signal_fd_idx = 0; + static constexpr int event_fd_idx = 1; + + static constexpr int signal_info_buffer_size = 5; + + if (state.load() == State::running) + throw std::runtime_error("SignalTrap::run can only be run once."); + + state.store(State::running); + + // Make sure we clean up the signal fd whenever + // we leave the scope of run. + struct Scope + { + ~Scope() + { + if (signal_fd != -1) + ::close(signal_fd); + } + + int signal_fd; + } scope{::signalfd(-1, &blocked_signals_mask, SFD_CLOEXEC | SFD_NONBLOCK)}; + + if (scope.signal_fd == -1) + throw std::system_error(errno, std::system_category()); + + pollfd fds[2]; + signalfd_siginfo signal_info[signal_info_buffer_size]; + + for (;;) + { + fds[signal_fd_idx] = {scope.signal_fd, POLLIN, 0}; + fds[event_fd_idx] = {event_fd, POLLIN, 0}; + + auto rc = ::poll(fds, 2, -1); + + if (rc == -1) + { + if (errno == EINTR) + continue; + + break; + } + + if (rc == 0) + continue; + + if (fds[signal_fd_idx].revents & POLLIN) + { + auto result = ::read(scope.signal_fd, signal_info, sizeof(signal_info)); + + for (uint i = 0; i < result / sizeof(signalfd_siginfo); i++) + { + if (has(static_cast(signal_info[i].ssi_signo))) + { + on_signal_raised( + static_cast( + signal_info[i].ssi_signo)); + } + } + } + + if (fds[event_fd_idx].revents & POLLIN) + { + std::int64_t value{1}; + // Consciously void-ing the return value here. + // Not much we can do about an error. + auto result = ::read(event_fd, &value, sizeof(value)); + (void) result; + + break; + } + } + + state.store(State::not_running); + } + + void stop() override + { + static const std::int64_t value = {1}; + if (sizeof(value) != ::write(event_fd, &value, sizeof(value))) + throw std::system_error(errno, std::system_category()); + } + + core::Signal& signal_raised() override + { + return on_signal_raised; + } + +private: + Scope scope; + std::atomic state; + int event_fd; + core::Signal on_signal_raised; + ::sigset_t old_signals_mask; + ::sigset_t blocked_signals_mask; +}; +} + +std::shared_ptr core::posix::trap_signals_for_process( + std::initializer_list blocked_signals) +{ + return std::make_shared( + impl::SignalTrap::Scope::process, + blocked_signals); +} + +std::shared_ptr core::posix::trap_signals_for_all_subsequent_threads( + std::initializer_list blocked_signals) +{ + return std::make_shared( + impl::SignalTrap::Scope::thread, + blocked_signals); +} + diff --git a/external/process-cpp-minimal/src/core/posix/signalable.cpp b/external/process-cpp-minimal/src/core/posix/signalable.cpp new file mode 100644 index 0000000..5542fa8 --- /dev/null +++ b/external/process-cpp-minimal/src/core/posix/signalable.cpp @@ -0,0 +1,52 @@ +/* + * Copyright © 2013 Canonical Ltd. + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License version 3, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + * + * Authored by: Thomas Voß + */ + +#include + +namespace core +{ +namespace posix +{ +struct Signalable::Private +{ + pid_t pid; +}; + +Signalable::Signalable(pid_t pid) : d(new Private{pid}) +{ +} + +void Signalable::send_signal_or_throw(Signal signal) +{ + auto result = ::kill(d->pid, static_cast(signal)); + + if (result == -1) + throw std::system_error(errno, std::system_category()); +} + +void Signalable::send_signal(Signal signal, std::error_code& e) noexcept +{ + auto result = ::kill(d->pid, static_cast(signal)); + + if (result == -1) + { + e = std::error_code(errno, std::system_category()); + } +} +} +} diff --git a/external/process-cpp-minimal/src/core/posix/standard_stream.cpp b/external/process-cpp-minimal/src/core/posix/standard_stream.cpp new file mode 100644 index 0000000..66c796c --- /dev/null +++ b/external/process-cpp-minimal/src/core/posix/standard_stream.cpp @@ -0,0 +1,35 @@ +/* + * Copyright © 2013 Canonical Ltd. + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License version 3, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + * + * Authored by: Thomas Voß + */ + +#include + +namespace core +{ +namespace posix +{ +StandardStream operator|(StandardStream l, StandardStream r) +{ + return static_cast(static_cast(l) | static_cast(r)); +} + +StandardStream operator&(StandardStream l, StandardStream r) +{ + return static_cast(static_cast(l) & static_cast(r)); +} +} +} diff --git a/external/process-cpp-minimal/src/core/posix/this_process.cpp b/external/process-cpp-minimal/src/core/posix/this_process.cpp new file mode 100644 index 0000000..53f5241 --- /dev/null +++ b/external/process-cpp-minimal/src/core/posix/this_process.cpp @@ -0,0 +1,177 @@ +/* + * Copyright © 2013 Canonical Ltd. + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License version 3, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + * + * Authored by: Thomas Voß + */ + +#include +#include + +#include + +#include +#include +#include +#include + +#include +#include + +#if defined(_GNU_SOURCE) +#include +#else +extern char** environ; +#endif + +namespace core +{ +namespace posix +{ +namespace this_process +{ +namespace env +{ +namespace +{ +std::mutex& env_guard() +{ + static std::mutex m; + return m; +} +} + +void for_each(const std::function& functor) noexcept(true) +{ + std::lock_guard lg(env_guard()); + auto it = ::environ; + while (it != nullptr && *it != nullptr) + { + std::string line(*it); + functor(line.substr(0,line.find_first_of('=')), + line.substr(line.find_first_of('=')+1)); + ++it; + } +} + +std::string get_or_throw(const std::string& key) +{ + std::lock_guard lg(env_guard()); + + auto result = ::getenv(key.c_str()); + + if (result == nullptr) + { + std::stringstream ss; + ss << "Variable with name " << key << " is not defined in the environment"; + throw std::runtime_error(ss.str()); + } + + return std::string{result}; +} + +std::string get(const std::string& key, + const std::string& default_value) noexcept(true) +{ + std::lock_guard lg(env_guard()); + + auto result = ::getenv(key.c_str()); + return std::string{result ? result : default_value}; +} + +void unset_or_throw(const std::string& key) +{ + std::lock_guard lg(env_guard()); + + auto rc = ::unsetenv(key.c_str()); + + if (rc == -1) + throw std::system_error(errno, std::system_category()); +} + +bool unset(const std::string& key, + std::error_code& se) noexcept(true) +{ + std::lock_guard lg(env_guard()); + + auto rc = ::unsetenv(key.c_str()); + + if (rc == -1) + { + se = std::error_code(errno, std::system_category()); + return false; + } + + return true; +} + +void set_or_throw(const std::string& key, + const std::string& value) +{ + std::lock_guard lg(env_guard()); + + static const int overwrite = 0; + auto rc = ::setenv(key.c_str(), value.c_str(), overwrite); + + if (rc == -1) + throw std::system_error(errno, std::system_category()); +} + +bool set(const std::string &key, + const std::string &value, + std::error_code& se) noexcept(true) +{ + std::lock_guard lg(env_guard()); + + static const int overwrite = 0; + auto rc = ::setenv(key.c_str(), value.c_str(), overwrite); + + if (rc == -1) + { + se = std::error_code(errno, std::system_category()); + return false; + } + + return true; +} +} + +Process instance() noexcept(true) +{ + static const Process self{getpid()}; + return self; +} + +Process parent() noexcept(true) +{ + return Process(getppid()); +} + +std::istream& cin() noexcept(true) +{ + return std::cin; +} + +std::ostream& cout() noexcept(true) +{ + return std::cout; +} + +std::ostream& cerr() noexcept(true) +{ + return std::cerr; +} +} +} +} diff --git a/external/process-cpp-minimal/src/core/posix/wait.cpp b/external/process-cpp-minimal/src/core/posix/wait.cpp new file mode 100644 index 0000000..0d9eec4 --- /dev/null +++ b/external/process-cpp-minimal/src/core/posix/wait.cpp @@ -0,0 +1,33 @@ +/* + * Copyright © 2013 Canonical Ltd. + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License version 3, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + * + * Authored by: Thomas Voß + */ + +#include + +namespace core +{ +namespace posix +{ +namespace wait +{ +Flags operator|(Flags l, Flags r) +{ + return static_cast(static_cast(l) | static_cast(r)); +} +} +} +} diff --git a/external/process-cpp-minimal/src/core/testing/cross_process_sync.cpp b/external/process-cpp-minimal/src/core/testing/cross_process_sync.cpp new file mode 100644 index 0000000..cc03389 --- /dev/null +++ b/external/process-cpp-minimal/src/core/testing/cross_process_sync.cpp @@ -0,0 +1,99 @@ +/* + * Copyright © 2013 Canonical Ltd. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 3 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * Authored by: Thomas Voss + */ + +#include + +#include + +#include +#include + +namespace +{ +const int read_fd = 0; +const int write_fd = 1; +} + +core::testing::CrossProcessSync::CrossProcessSync() : counter(0) +{ + if (::pipe(fds) < 0) + throw std::system_error(errno, std::system_category()); +} + +core::testing::CrossProcessSync::CrossProcessSync(const CrossProcessSync& rhs) : counter(rhs.counter) +{ + fds[0] = ::dup(rhs.fds[0]); + fds[1] = ::dup(rhs.fds[1]); +} + +core::testing::CrossProcessSync::~CrossProcessSync() noexcept +{ + ::close(fds[0]); + ::close(fds[1]); +} + +core::testing::CrossProcessSync& core::testing::CrossProcessSync::operator=(const core::testing::CrossProcessSync& rhs) +{ + ::close(fds[0]); + ::close(fds[1]); + fds[0] = ::dup(rhs.fds[0]); + fds[1] = ::dup(rhs.fds[1]); + + counter = rhs.counter; + + return *this; +} + +void core::testing::CrossProcessSync::try_signal_ready_for(const std::chrono::milliseconds& duration) +{ + static const short empty_revents = 0; + pollfd poll_fd[1] = { { fds[write_fd], POLLOUT, empty_revents } }; + int rc = -1; + + if ((rc = ::poll(poll_fd, 1, duration.count())) < 0) + throw std::system_error(errno, std::system_category()); + else if (rc == 0) + throw Error::Timeout{}; + + static const std::uint32_t value = 1; + if (sizeof(value) != write(fds[write_fd], std::addressof(value), sizeof(value))) + throw std::system_error(errno, std::system_category()); +} + +std::uint32_t core::testing::CrossProcessSync::wait_for_signal_ready_for(const std::chrono::milliseconds& duration) +{ + static const short empty_revents = 0; + pollfd poll_fd[1] = { { fds[read_fd], POLLIN, empty_revents } }; + int rc = -1; + + if ((rc = ::poll(poll_fd, 1, duration.count())) < 0) + throw std::system_error(errno, std::system_category()); + else if (rc == 0) + throw Error::Timeout{}; + + std::uint32_t value = 0; + if (sizeof(value) != read(fds[read_fd], std::addressof(value), sizeof(value))) + throw std::system_error(errno, std::system_category()); + + if (value != 1) + throw std::system_error(errno, std::system_category()); + + counter += value; + + return counter; +} diff --git a/external/process-cpp-minimal/src/core/testing/fork_and_run.cpp b/external/process-cpp-minimal/src/core/testing/fork_and_run.cpp new file mode 100644 index 0000000..ba2efd6 --- /dev/null +++ b/external/process-cpp-minimal/src/core/testing/fork_and_run.cpp @@ -0,0 +1,78 @@ +/* + * Copyright © 2012-2013 Canonical Ltd. + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License version 3, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + * + * Authored by: Thomas Voß + */ + +#include + +#include +#include +#include + +core::testing::ForkAndRunResult core::testing::operator|( + core::testing::ForkAndRunResult lhs, + core::testing::ForkAndRunResult rhs) +{ + return static_cast( + static_cast (lhs) | static_cast(rhs)); +} + +core::testing::ForkAndRunResult core::testing::operator&( + core::testing::ForkAndRunResult lhs, + core::testing::ForkAndRunResult rhs) +{ + return static_cast( + static_cast (lhs) & static_cast(rhs)); +} + +core::testing::ForkAndRunResult core::testing::fork_and_run( + const std::function& service, + const std::function& client) +{ + core::testing::ForkAndRunResult result = core::testing::ForkAndRunResult::empty; + + auto service_process = core::posix::fork(service, core::posix::StandardStream::empty); + auto client_process = core::posix::fork(client, core::posix::StandardStream::empty); + + auto client_result = client_process.wait_for(core::posix::wait::Flags::untraced); + + switch (client_result.status) + { + case core::posix::wait::Result::Status::exited: + if (client_result.detail.if_exited.status == core::posix::exit::Status::failure) + result = result | core::testing::ForkAndRunResult::client_failed; + break; + default: + result = result | core::testing::ForkAndRunResult::client_failed; + break; + } + + service_process.send_signal_or_throw(core::posix::Signal::sig_term); + auto service_result = service_process.wait_for(core::posix::wait::Flags::untraced); + + switch (service_result.status) + { + case core::posix::wait::Result::Status::exited: + if (service_result.detail.if_exited.status == core::posix::exit::Status::failure) + result = result | core::testing::ForkAndRunResult::service_failed; + break; + default: + result = result | core::testing::ForkAndRunResult::service_failed; + break; + } + + return result; +} diff --git a/external/process-cpp-minimal/symbols.map b/external/process-cpp-minimal/symbols.map new file mode 100644 index 0000000..6d3c2a9 --- /dev/null +++ b/external/process-cpp-minimal/symbols.map @@ -0,0 +1,16 @@ +{ +global: + extern "C++" { + core::*; + typeinfo?for?core::*; + typeinfo?name?for?core::*; + VTT?for?core::*; + virtual?thunk?to?core::*; + vtable?for?core::*; + std::hash*; + }; +local: + extern "C++" { + *; + }; +}; \ No newline at end of file diff --git a/scripts/iptables-wrapper b/scripts/iptables-wrapper new file mode 100755 index 0000000..73e9421 --- /dev/null +++ b/scripts/iptables-wrapper @@ -0,0 +1,2 @@ +#!/system/bin/sh +echo "Dummy iptables wrapper" diff --git a/scripts/launch-abox.sh b/scripts/launch-abox.sh new file mode 100755 index 0000000..b37cdf5 --- /dev/null +++ b/scripts/launch-abox.sh @@ -0,0 +1,60 @@ +#!/bin/bash +set -e +set -x + +basepath=/home/phablet/android-box +rootfs=$basepath/rootfs +rootfs_overrides=$basepath/overrides +ramdisk=$basepath/ramdisk.img +systemdisk=$basepath/system.img +init_cmd="/init" + +if [ "$1" = "shell" ] ; then + init_cmd=/system/bin/sh +fi + +if [ -d $rootfs ] ; then + sudo umount --recursive $rootfs || true + rm -rf $rootfs +fi + +mkdir -p $rootfs +sudo mount -t tmpfs none $rootfs +(cd $rootfs ; cat $ramdisk | gzip -d | cpio -i) + +mkdir -p $rootfs/dev/pts +sudo mount -o ro,loop $systemdisk $rootfs/system + +if [ -d "$rootfs_overrides" ] ; then + for f in `ls $rootfs_overrides` ; do + if [ "$f" = "system" ] ; then + for f2 in `find $rootfs_overrides/system -type f` ; do + real_path=`echo $f2 | sed -e s:$rootfs_overrides::g` + sudo mount -o bind $f2 $rootfs/$real_path + done + else + cp $rootfs_overrides/$f $rootfs + fi + done +fi + +/home/phablet/bwrap \ + --ro-bind $rootfs / \ + --bind /home/phablet/android-box/iptables-wrapper /system/bin/iptables \ + --bind /home/phablet/android-box/iptables-wrapper /system/bin/ip6tables \ + --dev /dev \ + --proc /proc \ + --tmpfs /data \ + --unshare-user \ + --unshare-ipc \ + --unshare-pid \ + --unshare-net \ + --unshare-uts \ + --uid 0 \ + --gid 0 \ + --setenv PATH /system/bin:/system/sbin:/system/xbin \ + --chdir / \ + $init_cmd + +sudo umount --recursive $rootfs +rm -rf $rootfs diff --git a/scripts/setup-partial-armhf-chroot.sh b/scripts/setup-partial-armhf-chroot.sh new file mode 100755 index 0000000..b298cf6 --- /dev/null +++ b/scripts/setup-partial-armhf-chroot.sh @@ -0,0 +1,156 @@ +#!/bin/bash +# +# TODO: Rename this file without "armhf" when it's safe to do so. +# + +set -e + +name=${0} + +usage() { + echo "Usage: ${name} [options] mychroot-dir" + echo "options:" + echo " -a arch Select architecture, i.e. armhf, arm64, ppc... Default is armhf" + echo " -d dist Select distribution, i.e. vivid, wily. Default is vivid" + echo " -r rep Select an additional repository for bootstrap. Default is none" + echo + echo "please supply at least a directory to create partial chroot in. (eg, ./setup-partial-armhf-chroot.sh mychroot-dir)" +} + +# Default to vivid as we don't seem to have any working wily devices right now. +# Also Jenkins expects this script to default to vivid (TODO: update CI?) +arch=armhf +dist=vivid +sourceid=0 +repositories= +sources= + +while getopts a:d:r:h opt; do + case $opt in + a) + arch=$OPTARG + ;; + d) + dist=$OPTARG + ;; + r) + repositories="$repositories $OPTARG" + ((++sourceid)) + sources="$sources source$sourceid" + ;; + :) + echo "Option -$OPTARG requires an argument" + usage + exit 1 + ;; + h) + usage + exit 0 + ;; + \?) + echo "Invalid option: -$OPTARG" + usage + exit 1 + ;; + esac +done + +shift $((OPTIND-1)) + +if [ -z ${1} ]; then + usage + exit 1 +fi + +directory=${1} +echo "creating phablet-compatible $arch partial chroot for anbox compilation in directory ${directory}" + +if [ ! -d ${directory} ]; then + mkdir -p ${directory} +fi + +DEBCONTROL=$(pwd)/../debian/control + +pushd ${directory} > /dev/null + +# Empty dpkg status file, so that ALL dependencies are listed with dpkg-checkbuilddeps +echo "" > status + +# Manual error code checking is needed for dpkg-checkbuilddeps +set +e + +# Parse dependencies from debian/control +# dpkg-checkbuilddeps returns non-zero when dependencies are not met and the list is sent to stderr +builddeps=$(dpkg-checkbuilddeps -a ${arch} --admindir=. ${DEBCONTROL} 2>&1 ) +if [ $? -eq 0 ] ; then + exit 0 +fi +echo "${builddeps}" + +# now turn exit on error option +set -e + +# Sanitize dependencies list for submission to multistrap +# build-essential is not needed as we are cross-compiling +builddeps=$(echo ${builddeps} | sed -e 's/dpkg-checkbuilddeps://g' \ + -e 's/error://g' \ + -e 's/Unmet build dependencies://g' \ + -e 's/build-essential:native//g') +builddeps=$(echo ${builddeps} | sed 's/([^)]*)//g') +builddeps=$(echo ${builddeps} | sed -e 's/abi-compliance-checker//g') +builddeps=$(echo ${builddeps} | sed -e 's/multistrap//g') + +case ${arch} in + amd64 | i386 ) + source_url=http://archive.ubuntu.com/ubuntu + ;; + * ) + source_url=http://ports.ubuntu.com/ubuntu-ports + ;; +esac + +echo "[General] +arch=${arch} +directory=${directory} +unpack=false +noauth=true +bootstrap=Ubuntu ${sources} + +[Ubuntu] +packages=${builddeps} +source=${source_url} +suite=${dist} +components=main universe +" > mstrap.conf + +sourceid=0 +for x in ${repositories}; +do + ((++sourceid)) + echo "[source${sourceid}] +source=${x} +suite=${dist} +" >> mstrap.conf +done + +multistrap -f mstrap.conf + +rm -f var/cache/apt/archives/lock + +# Remove libc libraries that confuse the cross-compiler +rm -f var/cache/apt/archives/libc-dev*.deb +rm -f var/cache/apt/archives/libc6*.deb + +for deb in var/cache/apt/archives/* ; do + if [ ! -d ${deb} ] ; then + echo "unpacking: ${deb}" + dpkg -x ${deb} . + fi +done + +# Fix up symlinks which asssumed the usual root path +for broken_symlink in $(find . -name \*.so -type l -xtype l) ; do + ln -sf $(pwd)$(readlink ${broken_symlink}) ${broken_symlink} +done + +popd > /dev/null diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt new file mode 100644 index 0000000..e1b77d4 --- /dev/null +++ b/src/CMakeLists.txt @@ -0,0 +1,71 @@ +include_directories( + ${Boost_INCLUDE_DIRS} + ${GLIB_INCLUDE_DIRS} + ${GIO_INCLUDE_DIRS} + ${GIO-UNIX_INCLUDE_DIRS} + ${CMAKE_SOURCE_DIR}/src + ${CMAKE_SOURCE_DIR}/external/process-cpp-minimal/include + ${CMAKE_SOURCE_DIR}/external/android-emugl/host/include +) + +set(SOURCES + anbox/logger.cpp + anbox/utils.cpp + anbox/cli.cpp + anbox/runtime.cpp + anbox/version.cpp + anbox/daemon.cpp + anbox/config.cpp + anbox/pid_persister.cpp + anbox/container.cpp + anbox/container_connector.cpp + + anbox/common/fd.cpp + anbox/common/fd_sets.h + anbox/common/variable_length_array.h + + anbox/network/message_sender.h + anbox/network/message_receiver.h + anbox/network/message_processor.h + anbox/network/connector.h + anbox/network/connection_creator.h + anbox/network/qemu_pipe_connection_creator.cpp + anbox/network/published_socket_connector.cpp + anbox/network/connections.h + anbox/network/socket_connection.cpp + anbox/network/socket_messenger.cpp + anbox/network/delegate_message_processor.h + + anbox/graphics/opengles_message_processor.cpp + anbox/graphics/gl_renderer_server.cpp + + anbox/support/null_message_processor.cpp + anbox/support/boot_properties_message_processor.cpp + anbox/support/hwcontrol_message_processor.cpp + + anbox/cmds/version.cpp + anbox/cmds/run.cpp + anbox/cmds/shell.cpp + + anbox/do_not_copy_or_move.h + anbox/optional.h + anbox/defer_action.h) + +add_library(anbox-core ${SOURCES}) +target_link_libraries(anbox-core + ${Boost_LDFLAGS} + ${Boost_LIBRARIES} + pthread + process-cpp + bwrap + OpenglRender) + +add_executable(anbox main.cpp) +target_link_libraries(anbox + anbox-core) + +install( + TARGETS anbox + RUNTIME DESTINATION sbin + LIBRARY DESTINATION lib + ARCHIVE DESTINATION lib/static) diff --git a/src/anbox/cli.cpp b/src/anbox/cli.cpp new file mode 100644 index 0000000..bc7e6df --- /dev/null +++ b/src/anbox/cli.cpp @@ -0,0 +1,275 @@ +/* + * Copyright (C) 2016 Canonical, Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; version 3. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + * + * Authored by: Thomas Voß + * + */ + +#include +#include + +#include "anbox/cli.h" + +namespace cli = anbox::cli; +namespace po = boost::program_options; + +namespace { +namespace pattern { +static constexpr const char* help_for_command_with_subcommands = +"NAME:\n" +" %1% - %2%\n" +"\n" +"USAGE:\n" +" %3% [command options] [arguments...]"; + +static constexpr const char* commands = "COMMANDS:"; +static constexpr const char* command = " %1% %2%"; + +static constexpr const char* options = "OPTIONS:"; +static constexpr const char* option = " --%1% %2%"; +} + +void add_to_desc_for_flags(po::options_description& desc, const std::set& flags) +{ + for (auto flag : flags) + { + auto v = po::value()->notifier([flag](const std::string& s) + { + flag->notify(s); + }); + desc.add_options()(flag->name().as_string().c_str(), v, flag->description().as_string().c_str()); + } +} +} + +std::vector cli::args(int argc, char **argv) +{ + std::vector result; + for (int i = 1; i < argc; i++) result.push_back(argv[i]); + return result; +} + +const cli::Name& cli::Flag::name() const +{ + return name_; +} + +const cli::Description& cli::Flag::description() const +{ + return description_; +} + +cli::Flag::Flag(const Name& name, const Description& description) + : name_{name}, + description_{description} +{ +} + +cli::Command::FlagsWithInvalidValue::FlagsWithInvalidValue() : std::runtime_error{"Flags with invalid value"} +{ +} + +cli::Command::FlagsMissing::FlagsMissing() : std::runtime_error{"Flags are missing in command invocation"} +{ +} + +cli::Name cli::Command::name() const +{ + return name_; +} + +cli::Usage cli::Command::usage() const +{ + return usage_; +} + +cli::Description cli::Command::description() const +{ + return description_; +} + +cli::Command::Command(const cli::Name& name, const cli::Usage& usage, const cli::Description& description) + : name_(name), + usage_(usage), + description_(description) +{ +} + +cli::CommandWithSubcommands::CommandWithSubcommands(const Name& name, const Usage& usage, const Description& description) + : Command{name, usage, description} +{ + command(std::make_shared(*this)); +} + +cli::CommandWithSubcommands& cli::CommandWithSubcommands::command(const Command::Ptr& command) +{ + commands_[command->name().as_string()] = command; + return *this; +} + +cli::CommandWithSubcommands& cli::CommandWithSubcommands::flag(const Flag::Ptr& flag) +{ + flags_.insert(flag); + return *this; +} + +void cli::CommandWithSubcommands::help(std::ostream& out) +{ + out << boost::format(pattern::help_for_command_with_subcommands) + % name().as_string() % usage().as_string() + % name().as_string() << std::endl; + + if (flags_.size() > 0) + { + out << std::endl << pattern::options << std::endl; + for (const auto& flag : flags_) + out << boost::format(pattern::option) % flag->name() % flag->description() << std::endl; + } + + if (commands_.size() > 0) + { + out << std::endl << pattern::commands << std::endl; + for (const auto& cmd : commands_) + out << boost::format(pattern::command) % cmd.second->name() % cmd.second->description() << std::endl; + } +} + +int cli::CommandWithSubcommands::run(const cli::Command::Context& ctxt) +{ + po::positional_options_description pdesc; + pdesc.add("command", 1); + + po::options_description desc("Options"); + desc.add_options()("command", po::value()->required(), "the command to be executed"); + + add_to_desc_for_flags(desc, flags_); + + try + { + po::variables_map vm; + auto parsed = po::command_line_parser(ctxt.args) + .options(desc) + .positional(pdesc) + .style(po::command_line_style::unix_style) + .allow_unregistered() + .run(); + + po::store(parsed, vm); + po::notify(vm); + + return commands_[vm["command"].as()]->run(cli::Command::Context + { + ctxt.cin, + ctxt.cout, + po::collect_unrecognized(parsed.options, po::include_positional) + }); + } + catch (const po::error& e) + { + ctxt.cout << e.what() << std::endl; + help(ctxt.cout); + return EXIT_FAILURE; + } + + return EXIT_FAILURE; +} + +cli::CommandWithFlagsAndAction::CommandWithFlagsAndAction(const Name& name, const Usage& usage, const Description& description) + : Command{name, usage, description} +{ +} + +cli::CommandWithFlagsAndAction& cli::CommandWithFlagsAndAction::flag(const Flag::Ptr& flag) +{ + flags_.insert(flag); + return *this; +} + +cli::CommandWithFlagsAndAction& cli::CommandWithFlagsAndAction::action(const Action& action) +{ + action_ = action; + return *this; +} + +int cli::CommandWithFlagsAndAction::run(const Context& ctxt) +{ + po::options_description cd(name().as_string()); + + bool help_requested{false}; + cd.add_options()("help", po::bool_switch(&help_requested), "produces a help message"); + + add_to_desc_for_flags(cd, flags_); + + try + { + po::variables_map vm; + auto parsed = po::command_line_parser(ctxt.args).options(cd).style(po::command_line_style::unix_style).allow_unregistered().run(); + po::store(parsed, vm); + po::notify(vm); + + if (help_requested) + { + help(ctxt.cout); + return EXIT_SUCCESS; + } + + return action_(cli::Command::Context + { + ctxt.cin, + ctxt.cout, + po::collect_unrecognized(parsed.options, po::exclude_positional) + }); + } + catch (const po::error& e) + { + ctxt.cout << e.what() << std::endl; + help(ctxt.cout); + return EXIT_FAILURE; + } + + return EXIT_FAILURE; +} + +void cli::CommandWithFlagsAndAction::help(std::ostream& out) +{ + out << boost::format(pattern::help_for_command_with_subcommands) + % name().as_string() % description().as_string() + % name().as_string() << std::endl; + + if (flags_.size() > 0) + { + out << std::endl << boost::format(pattern::options) << std::endl; + for (const auto& flag : flags_) + out << boost::format(pattern::option) % flag->name() % flag->description() << std::endl; + } +} + +cli::cmd::Help::Help(Command& cmd) + : Command{cli::Name{"help"}, cli::Usage{"prints a short help message"}, cli::Description{"prints a short help message"}}, + command{cmd} +{ +} + +// From Command +int cli::cmd::Help::run(const Context &context) +{ + command.help(context.cout); + return EXIT_FAILURE; +} + +void cli::cmd::Help::help(std::ostream &out) +{ + command.help(out); +} diff --git a/src/anbox/cli.h b/src/anbox/cli.h new file mode 100644 index 0000000..c22ab28 --- /dev/null +++ b/src/anbox/cli.h @@ -0,0 +1,356 @@ +/* + * Copyright (C) 2016 Canonical, Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; version 3. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + * + * Authored by: Thomas Voß + * + */ +#ifndef BIOMETRY_UTIL_CLI_H_ +#define BIOMETRY_UTIL_CLI_H_ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "anbox/do_not_copy_or_move.h" +#include "anbox/optional.h" + +namespace anbox { +namespace cli { + +template +class SizeConstrainedString +{ +public: + SizeConstrainedString(const std::string& s) : s{s} + { + if(s.size() > max) + throw std::logic_error{"Max size exceeded " + std::to_string(max)}; + } + + const std::string& as_string() const + { + return s; + } + + operator std::string() const + { + return s; + } + +private: + std::string s; +}; + +template +bool operator<(const SizeConstrainedString& lhs, const SizeConstrainedString& rhs) +{ + return lhs.as_string() < rhs.as_string(); +} + +template +bool operator==(const SizeConstrainedString& lhs, const SizeConstrainedString& rhs) +{ + return lhs.as_string() == rhs.as_string(); +} + +template +std::ostream& operator<<(std::ostream& out, const SizeConstrainedString& scs) +{ + return out << std::setw(max) << std::left << scs.as_string(); +} + +// We are imposing size constraints to ensure a consistent CLI layout. +typedef SizeConstrainedString<20> Name; +typedef SizeConstrainedString<60> Usage; +typedef SizeConstrainedString<60> Description; + +/// @brief Flag models an input parameter to a command. +class Flag : public DoNotCopyOrMove +{ +public: + // Safe us some typing. + typedef std::shared_ptr Ptr; + + /// @brief notify announces a new value to the flag. + virtual void notify(const std::string& value) = 0; + /// @brief name returns the name of the Flag. + const Name& name() const; + /// @brief description returns a human-readable description of the flag. + const Description& description() const; + +protected: + /// @brief Flag creates a new instance, initializing name and description + /// from the given values. + Flag(const Name& name, const Description& description); + +private: + Name name_; + Description description_; +}; + +/// @brief TypedFlag implements Flag relying on operator<< and operator>> to read/write values to/from strings. +template +class TypedFlag : public Flag +{ +public: + typedef std::shared_ptr> Ptr; + + TypedFlag(const Name& name, const Description& description) : Flag{name, description} + { + } + + /// @brief value installs the given value in the flag. + TypedFlag& value(const T& value) + { + value_ = value; + return *this; + } + + /// @brief value returns the optional value associated with the flag. + const Optional& value() const + { + return value_; + } + + /// @brief notify tries to unwrap a value of type T from value. + void notify(const std::string& s) override + { + std::stringstream ss{s}; + T value; ss >> value; + value_ = value; + } + +private: + Optional value_; +}; + +/// @brief TypedReferenceFlag implements Flag, relying on operator<> to convert to/from string representations, +/// updating the given mutable reference to a value of type T. +template +class TypedReferenceFlag : public Flag +{ +public: + // Safe us some typing. + typedef std::shared_ptr> Ptr; + + /// @brief TypedReferenceFlag initializes a new instance with name, description and value. + TypedReferenceFlag(const Name& name, const Description& description, T& value) + : Flag{name, description}, + value_{value} + { + } + + /// @brief notify tries to unwrap a value of type T from value, + /// relying on operator>> to read from given string s. + void notify(const std::string& s) override + { + std::stringstream ss{s}; + ss >> value_.get(); + } + +private: + std::reference_wrapper value_; +}; + +/// @brief OptionalTypedReferenceFlag handles Optional references, making sure that +/// a value is always read on notify, even if the Optional wasn't initialized previously. +template +class OptionalTypedReferenceFlag : public Flag +{ +public: + typedef std::shared_ptr> Ptr; + + OptionalTypedReferenceFlag(const Name& name, const Description& description, Optional& value) + : Flag{name, description}, + value_{value} + { + } + + /// @brief notify tries to unwrap a value of type T from value. + void notify(const std::string& s) override + { + std::stringstream ss{s}; T value; ss >> value; + value_.get() = value; + } + +private: + std::reference_wrapper> value_; +}; + +/// @brief Command abstracts an individual command available from the daemon. +class Command : public DoNotCopyOrMove +{ +public: + // Safe us some typing + typedef std::shared_ptr Ptr; + + /// @brief FlagsMissing is thrown if at least one required flag is missing. + struct FlagsMissing : public std::runtime_error + { + /// @brief FlagsMissing initializes a new instance. + FlagsMissing(); + }; + + /// @brief FlagsWithWrongValue is thrown if a value passed on the command line is invalid. + struct FlagsWithInvalidValue : public std::runtime_error + { + /// @brief FlagsWithInvalidValue initializes a new instance. + FlagsWithInvalidValue(); + }; + + /// @brief Context bundles information passed to Command::run invocations. + struct Context + { + std::istream& cin; ///< The std::istream that should be used for reading. + std::ostream& cout; ///< The std::ostream that should be used for writing. + std::vector args; ///< The command line args. + }; + + /// @brief name returns the Name of the command. + virtual Name name() const; + + /// @brief usage returns a short usage string for the command. + virtual Usage usage() const; + + /// @brief description returns a longer string explaining the command. + virtual Description description() const; + + /// @brief run puts the command to execution. + virtual int run(const Context& context) = 0; + + /// @brief help prints information about a command to out. + virtual void help(std::ostream& out) = 0; + +protected: + /// @brief Command initializes a new instance with the given name, usage and description. + Command(const Name& name, const Usage& usage, const Description& description); + + /// @brief name adjusts the name of the command to n. + // virtual void name(const Name& n); + /// @brief usage adjusts the usage string of the comand to u. + // virtual void usage(const Usage& u); + /// @brief description adjusts the description string of the command to d. + // virtual void description(const Description& d); + +private: + Name name_; + Usage usage_; + Description description_; +}; + +/// @brief CommandWithSubcommands implements Command, selecting one of a set of actions. +class CommandWithSubcommands : public Command +{ +public: + typedef std::shared_ptr Ptr; + typedef std::function Action; + + /// @brief CommandWithSubcommands initializes a new instance with the given name, usage and description + CommandWithSubcommands(const Name& name, const Usage& usage, const Description& description); + + /// @brief command adds the given command to the set of known commands. + CommandWithSubcommands& command(const Command::Ptr& command); + + /// @brief flag adds the given flag to the set of known flags. + CommandWithSubcommands& flag(const Flag::Ptr& flag); + + // From Command + int run(const Context& context) override; + void help(std::ostream &out) override; + +private: + std::unordered_map commands_; + std::set flags_; +}; + +/// @brief CommandWithFlagsAction implements Command, executing an Action after handling +class CommandWithFlagsAndAction : public Command +{ +public: + typedef std::shared_ptr Ptr; + typedef std::function Action; + + /// @brief CommandWithFlagsAndAction initializes a new instance with the given name, usage and description + CommandWithFlagsAndAction(const Name& name, const Usage& usage, const Description& description); + + /// @brief flag adds the given flag to the set of known flags. + CommandWithFlagsAndAction& flag(const Flag::Ptr& flag); + + /// @brief action installs the given action. + CommandWithFlagsAndAction& action(const Action& action); + + // From Command + int run(const Context& context) override; + void help(std::ostream &out) override; + +private: + std::set flags_; + Action action_; +}; + +namespace cmd +{ +/// @brief HelpFor prints a help message for the given command on execution. +class Help : public Command +{ +public: + /// @brief HelpFor initializes a new instance with the given reference to a cmd. + explicit Help(Command& cmd); + + // From Command + int run(const Context &context) override; + void help(std::ostream &out) override; + +private: + /// @cond + Command& command; + /// @endcond +}; +} + +/// @brief args returns a vector of strings assembled from argc and argv. +std::vector args(int argc, char** argv); + +/// @brief make_flag returns a flag with the given name and description. +template +typename TypedFlag::Ptr make_flag(const Name& name, const Description& description) +{ + return std::make_shared>(name, description); +} + +/// @brief make_flag returns a flag with the given name and description, notifying updates to value. +template +typename TypedReferenceFlag::Ptr make_flag(const Name& name, const Description& desc, T& value) +{ + return std::make_shared>(name, desc, value); +} + +/// @brief make_flag returns a flag with the given name and description, updating the given optional value. +template +typename OptionalTypedReferenceFlag::Ptr make_flag(const Name& name, const Description& desc, Optional& value) +{ + return std::make_shared>(name, desc, value); +} + +} // namespace cli +} // namespace anbox + + +#endif diff --git a/src/anbox/cmds/run.cpp b/src/anbox/cmds/run.cpp new file mode 100644 index 0000000..f6f9a0f --- /dev/null +++ b/src/anbox/cmds/run.cpp @@ -0,0 +1,111 @@ +/* + * Copyright (C) 2016 Simon Fels + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 3, as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranties of + * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + * + */ + +#include + +#include "core/posix/signal.h" + +#include "anbox/logger.h" +#include "anbox/runtime.h" +#include "anbox/container.h" +#include "anbox/config.h" +#include "anbox/pid_persister.h" +#include "anbox/cmds/run.h" +#include "anbox/network/published_socket_connector.h" +#include "anbox/network/qemu_pipe_connection_creator.h" +#include "anbox/graphics/gl_renderer_server.h" + +#include + +namespace fs = boost::filesystem; + +namespace { +class NullConnectionCreator : public anbox::network::ConnectionCreator { +public: + void create_connection_for( + std::shared_ptr const& socket) override { + WARNING("Not implemented"); + socket->close(); + } +}; +} + +anbox::cmds::Run::Run() + : CommandWithFlagsAndAction{cli::Name{"run"}, cli::Usage{"run"}, cli::Description{"Run the the anbox system"}} +{ + flag(cli::make_flag(cli::Name{"rootfs"}, cli::Description{"Path to Android rootfs"}, rootfs_)); + // Just for the purpose to allow QtMir (or unity8) to find this on our /proc/*/cmdline + // for proper confinement etc. + flag(cli::make_flag(cli::Name{"desktop_file_hint"}, cli::Description{"Desktop file hint for QtMir/Unity8"}, desktop_file_hint_)); + action([this](const cli::Command::Context &ctx) { + if (rootfs_.empty() || !fs::is_directory(fs::path(rootfs_))) { + ctx.cout << "Not valid rootfs path provided" << std::endl; + return EXIT_FAILURE; + } + + auto trap = core::posix::trap_signals_for_process({core::posix::Signal::sig_term, + core::posix::Signal::sig_int}); + trap->signal_raised().connect([trap](const core::posix::Signal &signal) { + INFO("Signal %i received. Good night.", static_cast(signal)); + trap->stop(); + }); + + auto rt = Runtime::create(); + + auto renderer = std::make_shared(); + renderer->start(); + + // Socket which will be used by the qemud service inside the Android + // container for things like sensors, vibrtator etc. + auto qemud_connector = std::make_shared( + utils::string_format("%s/qemud", config::data_path()), + rt, + std::make_shared()); + + // The qemu pipe is used as a very fast communication channel between guest + // and host for things like the GLES emulation/translation, the RIL or ADB. + auto qemu_pipe_connector = std::make_shared( + utils::string_format("%s/qemu_pipe", config::data_path()), + rt, + std::make_shared(rt, renderer->socket_path())); + + auto spec = Container::Spec::Default(); + spec.rootfs_path = rootfs_; + spec.bind_paths.insert({qemud_connector->socket_file(), "/dev/qemud"}); + spec.bind_paths.insert({qemu_pipe_connector->socket_file(), "/dev/qemu_pipe"}); + spec.temporary_dirs.push_back("/data"); + // We isolate the container from accessing binder nodes of the host + // through the IPC namespace which gets support for binder with extra + // patches we require. + spec.dev_bind_paths.push_back("/dev/binder"); + // Required for shared memory allocations. TODO(morphis): Letting the guest + // access should be ok but needs more investigation. + spec.dev_bind_paths.push_back("/dev/ashmem"); + + auto container = Container::create(spec); + + rt->start(); + container->start(); + + trap->run(); + + container->stop(); + rt->stop(); + + return EXIT_SUCCESS; + }); +} diff --git a/src/anbox/cmds/run.h b/src/anbox/cmds/run.h new file mode 100644 index 0000000..541ee21 --- /dev/null +++ b/src/anbox/cmds/run.h @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2016 Simon Fels + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 3, as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranties of + * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + * + */ + +#ifndef ANBOX_CMDS_RUN_H_ +#define ANBOX_CMDS_RUN_H_ + +#include +#include +#include + +#include "anbox/cli.h" + +namespace anbox { +namespace cmds { +class Run : public cli::CommandWithFlagsAndAction { +public: + Run(); + +private: + std::string rootfs_; + std::string desktop_file_hint_; +}; +} // namespace cmds +} // namespace anbox + +#endif diff --git a/src/anbox/cmds/shell.cpp b/src/anbox/cmds/shell.cpp new file mode 100644 index 0000000..646a25c --- /dev/null +++ b/src/anbox/cmds/shell.cpp @@ -0,0 +1,36 @@ +/* + * Copyright (C) 2016 Simon Fels + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 3, as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranties of + * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + * + */ + +#include + +#include "core/posix/signal.h" + +#include "anbox/logger.h" +#include "anbox/config.h" +#include "anbox/container_connector.h" +#include "anbox/cmds/shell.h" + +namespace fs = boost::filesystem; + +anbox::cmds::Shell::Shell() + : CommandWithFlagsAndAction{cli::Name{"shell"}, cli::Usage{"shell"}, cli::Description{"Open a shell within the Anbox container"}} +{ + action([this](const cli::Command::Context &ctx) { + ContainerConnector connector; + return connector.run("/system/bin/sh"); + }); +} diff --git a/src/anbox/cmds/shell.h b/src/anbox/cmds/shell.h new file mode 100644 index 0000000..24e0b58 --- /dev/null +++ b/src/anbox/cmds/shell.h @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2016 Simon Fels + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 3, as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranties of + * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + * + */ + +#ifndef ANBOX_CMDS_RUN_QEMU_H_ +#define ANBOX_CMDS_RUN_QEMU_H_ + +#include +#include +#include + +#include "anbox/cli.h" + +namespace anbox { +namespace cmds { +class Shell : public cli::CommandWithFlagsAndAction { +public: + Shell(); + +private: + std::string rootfs_; +}; +} // namespace cmds +} // namespace anbox + +#endif diff --git a/src/anbox/cmds/version.cpp b/src/anbox/cmds/version.cpp new file mode 100644 index 0000000..538b42f --- /dev/null +++ b/src/anbox/cmds/version.cpp @@ -0,0 +1,33 @@ +/* + * Copyright (C) 2016 Canonical, Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; version 3. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + * + * Authored by: Thomas Voß + * + */ + +#include "anbox/cmds/version.h" +#include "anbox/version.h" + +anbox::cmds::Version::Version() + : CommandWithFlagsAndAction{cli::Name{"version"}, cli::Usage{"version"}, cli::Description{"print the version of the daemon"}} +{ + action([](const cli::Command::Context& ctxt) + { + std::uint32_t major, minor, patch; + anbox::version(major, minor, patch); + ctxt.cout << "anbox " << major << "." << minor << "." << patch << std::endl; + return 0; + }); +} diff --git a/src/anbox/cmds/version.h b/src/anbox/cmds/version.h new file mode 100644 index 0000000..37b38d4 --- /dev/null +++ b/src/anbox/cmds/version.h @@ -0,0 +1,38 @@ +/* + * Copyright (C) 2016 Canonical, Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; version 3. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + * + * Authored by: Thomas Voß + * + */ + +#ifndef ANBOX_CMDS_VERSION_H_ +#define ANBOX_CMDS_VERSION_H_ + +#include +#include +#include + +#include "anbox/cli.h" + +namespace anbox { +namespace cmds { +class Version : public cli::CommandWithFlagsAndAction { +public: + Version(); +}; +} // namespace cmds +} // namespace anbox + +#endif diff --git a/src/anbox/common/fd.cpp b/src/anbox/common/fd.cpp new file mode 100644 index 0000000..9e9a4ef --- /dev/null +++ b/src/anbox/common/fd.cpp @@ -0,0 +1,61 @@ +/* + * Copyright © 2014 Canonical Ltd. + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License version 3, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + * + * Authored by: Kevin DuBois + */ +#include "anbox/common/fd.h" + +#include +#include + +namespace anbox { +Fd::Fd() : + Fd{invalid} +{ +} + +Fd::Fd(IntOwnedFd fd) : + fd{std::make_shared(fd.int_owned_fd)} +{ +} + +Fd::Fd(int raw_fd) : + fd{new int{raw_fd}, + [](int* fd) + { + if (!fd) return; + if (*fd > Fd::invalid) ::close(*fd); + delete fd; + }} +{ +} + +Fd::Fd(Fd&& other) : + fd{std::move(other.fd)} +{ +} + +Fd& Fd::operator=(Fd other) +{ + std::swap(fd, other.fd); + return *this; +} + +Fd::operator int() const +{ + if (fd) return *fd; + return invalid; +} +} // namespace anbox diff --git a/src/anbox/common/fd.h b/src/anbox/common/fd.h new file mode 100644 index 0000000..37ca9c1 --- /dev/null +++ b/src/anbox/common/fd.h @@ -0,0 +1,50 @@ +/* + * Copyright © 2014 Canonical Ltd. + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License version 3, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + * + * Authored by: Kevin DuBois + */ + +#ifndef ANBOX_COMMON_FD_H_ +#define ANBOX_COMMON_FD_H_ + +#include + +namespace anbox { +struct IntOwnedFd +{ + int int_owned_fd; +}; +class Fd +{ +public: + //transfer ownership of the POD-int to the object. The int no longer needs close()ing, + //and has the lifetime of the Fd object. + explicit Fd(int fd); + explicit Fd(IntOwnedFd); + static int const invalid{-1}; + Fd(); //Initializes fd to the anbox::Fd::invalid; + Fd(Fd&&); + Fd(Fd const&) = default; + Fd& operator=(Fd); + + //bit of a convenient kludge. take care not to close or otherwise destroy the FD. + operator int() const; + +private: + std::shared_ptr fd; +}; +} // namespace anbox + +#endif diff --git a/src/anbox/common/fd_sets.h b/src/anbox/common/fd_sets.h new file mode 100644 index 0000000..fba4013 --- /dev/null +++ b/src/anbox/common/fd_sets.h @@ -0,0 +1,31 @@ +/* + * Copyright © 2013 Canonical Ltd. + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 3, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * Authored by: Robert Carr + */ + +#ifndef ANBOX_COMMON_FD_SETS_H_ +#define ANBOX_COMMON_FD_SETS_H_ + +#include +#include + +#include "anbox/common/fd.h" + +namespace anbox { +typedef std::vector> FdSets; +} // namespace anbox + +#endif diff --git a/src/anbox/common/variable_length_array.h b/src/anbox/common/variable_length_array.h new file mode 100644 index 0000000..b4f3526 --- /dev/null +++ b/src/anbox/common/variable_length_array.h @@ -0,0 +1,61 @@ +/* + * Copyright © 2014 Canonical Ltd. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + * + * Authored by: Alexandros Frantzis + */ + +#ifndef ANBOX_COMMON_VARIABLE_LENGTH_ARRAY_H_ +#define ANBOX_COMMON_VARIABLE_LENGTH_ARRAY_H_ + +#include +#include + +namespace anbox { +template +class VariableLengthArray +{ +public: + explicit VariableLengthArray(size_t size) : size_{size} + { + /* Don't call resize if the initial values of member variables are valid */ + if (size > BuiltInBufferSize) resize(size); + } + + void resize(size_t size) + { + if (size > BuiltInBufferSize) + effective_buffer = BufferUPtr{new unsigned char[size], heap_deleter}; + else + effective_buffer = BufferUPtr{builtin_buffer, null_deleter}; + + size_ = size; + } + + unsigned char* data() const { return effective_buffer.get(); } + size_t size() const { return size_; } + +private: + typedef std::unique_ptr BufferUPtr; + + static void null_deleter(unsigned char*) {} + static void heap_deleter(unsigned char* b) { delete[] b; } + + unsigned char builtin_buffer[BuiltInBufferSize]; + BufferUPtr effective_buffer{builtin_buffer, null_deleter}; + size_t size_; +}; +} // namespace anbox + +#endif diff --git a/src/anbox/config.cpp b/src/anbox/config.cpp new file mode 100644 index 0000000..b891dcd --- /dev/null +++ b/src/anbox/config.cpp @@ -0,0 +1,29 @@ +/* + * Copyright (C) 2016 Simon Fels + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 3, as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranties of + * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + * + */ + +#include + +#include "anbox/config.h" + +namespace anbox { +namespace config { +std::string data_path() { + // TODO(morphis): replace with proper compile time constant + return "/home/phablet/.local/share/anbox/"; +} +} // namespace config +} // namespace anbox diff --git a/src/anbox/config.h b/src/anbox/config.h new file mode 100644 index 0000000..694b5d2 --- /dev/null +++ b/src/anbox/config.h @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2016 Simon Fels + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 3, as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranties of + * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + * + */ + +#ifndef ANBOX_CONFIG_H_ +#define ANBOX_CONFIG_H_ + +#include + +namespace anbox { +namespace config { +std::string BubblewrapPath(); +std::string data_path(); +} // namespace config +} // namespace anbox + +#endif diff --git a/src/anbox/container.cpp b/src/anbox/container.cpp new file mode 100644 index 0000000..d7bb4fc --- /dev/null +++ b/src/anbox/container.cpp @@ -0,0 +1,123 @@ +/* + * Copyright (C) 2016 Simon Fels + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 3, as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranties of + * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + * + */ + +#include + +#include + +#include "core/posix/fork.h" +#include "core/posix/exec.h" + +#include "anbox/utils.h" +#include "anbox/logger.h" +#include "anbox/config.h" +#include "anbox/container.h" +#include "anbox/common/fd.h" + +extern "C" int bwrap_main(int argc, char **argv); + +namespace fs = boost::filesystem; +namespace anbox { + +Container::Spec Container::Spec::Default() { + Spec spec; + spec.init_command = "/init"; + spec.environment.insert({"PATH", "/system/bin:/system/sbin:/system/xbin"}); + return spec; +} +std::shared_ptr Container::create(const Container::Spec &spec) { + return std::shared_ptr(new Container{spec}); +} + +Container::Container(const Container::Spec &spec) : + spec_(spec), + child_(core::posix::ChildProcess::invalid()), + child_group_(core::posix::ProcessGroup::invalid()) { +} + +Container::~Container() { + stop(); +} + +void Container::start() { + std::vector arguments = { + "--ro-bind", spec_.rootfs_path, "/", + "--dev", "/dev", + "--proc", "/proc", + "--unshare-user", + "--unshare-ipc", + "--unshare-pid", + "--unshare-net", + "--unshare-uts", + // We will take UID 0 (root) inside the container + "--uid", "0", + // We will take GID 0 (root) inside the container + "--gid", "0", + "--chdir", "/", + "--pid-file", utils::string_format("%s/pid", config::data_path()), + }; + + for (const auto &dir : spec_.temporary_dirs) { + arguments.push_back("--tmpfs"); + arguments.push_back(dir); + } + + for (const auto &path : spec_.dev_bind_paths) { + arguments.push_back("--dev-bind"); + arguments.push_back(path); + arguments.push_back(path); + } + + for (const auto &path : spec_.bind_paths) { + arguments.push_back("--bind"); + arguments.push_back(path.first); + arguments.push_back(path.second); + } + + for (const auto &env : spec_.environment) { + arguments.push_back("--setenv"); + arguments.push_back(env.first); + arguments.push_back(env.second); + } + + arguments.push_back(spec_.init_command); + + child_ = core::posix::fork([&]() { + char **it, **pargv; + it = pargv = new char*[arguments.size() + 2]; + *it = strdup("bwrap"); + it++; + for (auto arg : arguments) { + *it = ::strdup(arg.c_str()); + it++; + } + *it = nullptr; + + if (bwrap_main(arguments.size() + 1, pargv)) + return core::posix::exit::Status::failure; + + return core::posix::exit::Status::success; + }, core::posix::StandardStream::empty); + + child_group_ = child_.process_group_or_throw(); +} + +void Container::stop() { + child_group_.send_signal_or_throw(core::posix::Signal::sig_kill); +} + +} // namespace anbox diff --git a/src/anbox/container.h b/src/anbox/container.h new file mode 100644 index 0000000..8e12134 --- /dev/null +++ b/src/anbox/container.h @@ -0,0 +1,65 @@ +/* + * Copyright (C) 2016 Simon Fels + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 3, as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranties of + * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + * + */ + +#ifndef ANBOX_CONTAINER_H_ +#define ANBOX_CONTAINER_H_ + +#include +#include +#include +#include + +#include "core/posix/child_process.h" + +#include "anbox/do_not_copy_or_move.h" +#include "anbox/runtime.h" + +#include + +namespace anbox { +class Container : public DoNotCopyOrMove, + public std::enable_shared_from_this { +public: + class Spec { + public: + static Spec Default(); + + std::string rootfs_path; + std::string init_command; + std::vector temporary_dirs; + std::map bind_paths; + std::vector dev_bind_paths; + std::map environment; + }; + + static std::shared_ptr create(const Spec &spec); + + virtual ~Container(); + + void start(); + void stop(); + +private: + Container(const Container::Spec &spec); + + Spec spec_; + core::posix::ChildProcess child_; + core::posix::ProcessGroup child_group_; +}; +} // namespace anbox + +#endif diff --git a/src/anbox/container_connector.cpp b/src/anbox/container_connector.cpp new file mode 100644 index 0000000..99f9a93 --- /dev/null +++ b/src/anbox/container_connector.cpp @@ -0,0 +1,148 @@ +/* + * Copyright (C) 2016 Simon Fels + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 3, as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranties of + * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + * + */ + +#include "anbox/container_connector.h" +#include "anbox/config.h" +#include "anbox/utils.h" +#include "anbox/logger.h" + +#include "core/posix/exec.h" + +#include + +#include + +#include +#include +#include + +namespace fs = boost::filesystem; + +namespace { +class NamespaceAttacher { +public: + enum class ns_type { + user, + pid, + uts, + mount, + ipc, + net, + }; + + static std::string ns_type_to_string(ns_type type) { + switch (type) { + case ns_type::user: + return "user"; + case ns_type::pid: + return "pid"; + case ns_type::uts: + return "uts"; + case ns_type::mount: + return "mnt"; + case ns_type::ipc: + return "ipc"; + case ns_type::net: + return "net"; + default: + break; + } + + BOOST_THROW_EXCEPTION(std::runtime_error("Unknown namespace type")); + } + + NamespaceAttacher(const std::vector &types, pid_t pid) : + pid_(pid) { + + attach(types); + } + + ~NamespaceAttacher() { + } + +private: + void attach(const std::vector &types) { + std::vector fds; + for (const auto &type : types) { + const auto path = anbox::utils::string_format("/proc/%lu/ns/%s", pid_, ns_type_to_string(type)); + if (!fs::exists(fs::path(path))) + BOOST_THROW_EXCEPTION(std::runtime_error("Failed to open namespace file")); + + const auto fd = ::open(path.c_str(), O_RDONLY); + if (fd < 0) + BOOST_THROW_EXCEPTION(std::runtime_error("Failed to open namespace file")); + + fds.push_back(fd); + } + + for (const auto &fd : fds) { + if (::setns(fd, 0) == -1) + BOOST_THROW_EXCEPTION(std::runtime_error("Could not attach to namespace")); + + ::close(fd); + } + } + + pid_t pid_; +}; +} + +namespace anbox { +ContainerConnector::ContainerConnector() { +} + +ContainerConnector::~ContainerConnector() { +} + +int ContainerConnector::run(const std::string &path) { + const auto pid = std::stol(utils::read_file_if_exists_or_throw( + utils::string_format("%s/pid", config::data_path()))); + + if (!fs::is_directory(fs::path(utils::string_format("/proc/%i", pid)))) + BOOST_THROW_EXCEPTION(std::runtime_error("Container isn't running")); + + namespaces_ = std::make_shared(std::vector{ + NamespaceAttacher::ns_type::user, + NamespaceAttacher::ns_type::mount, + NamespaceAttacher::ns_type::pid, + NamespaceAttacher::ns_type::uts, + NamespaceAttacher::ns_type::ipc, + NamespaceAttacher::ns_type::net, + }, pid); + + // A few things we want to preset in our env within the container shell + std::map env = { + { "ANDROID_ROOT", "/" } + }; + + auto child = core::posix::exec(path, {}, env, core::posix::StandardStream::empty, [this]() { + // We're now in the namespace bwrap spawns up for the container which has + // a /newroot directory pointing to the new root filesystem we get from + // Android and is what we need to change to in order to work within + // the Anroid system. + if (::chroot("/newroot") != 0) + BOOST_THROW_EXCEPTION(std::runtime_error("Failed to enter container root filesystem")); + + chdir("/"); + }); + + child.wait_for(core::posix::wait::Flags::untraced); + + return EXIT_SUCCESS; +} + +} // namespace diff --git a/src/anbox/container_connector.h b/src/anbox/container_connector.h new file mode 100644 index 0000000..9bed3ef --- /dev/null +++ b/src/anbox/container_connector.h @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2016 Simon Fels + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 3, as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranties of + * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + * + */ + +#ifndef ANBOX_CONTAINER_CONNECTOR_H_ +#define ANBOX_CONTAINER_CONNECTOR_H_ + +#include + +namespace { +class NamespaceAttacher; +} + +namespace anbox { +class ContainerConnector { +public: + ContainerConnector(); + ~ContainerConnector(); + + int run(const std::string &path); + +private: + std::shared_ptr namespaces_; +}; +} // namespace + +#endif diff --git a/src/anbox/daemon.cpp b/src/anbox/daemon.cpp new file mode 100644 index 0000000..6966cb1 --- /dev/null +++ b/src/anbox/daemon.cpp @@ -0,0 +1,55 @@ +/* + * Copyright (C) 2016 Simon Fels + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 3, as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranties of + * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + * + */ + +#include +#include + +#include "anbox/logger.h" +#include "anbox/daemon.h" +#include "anbox/config.h" + +#include "anbox/cmds/version.h" +#include "anbox/cmds/run.h" +#include "anbox/cmds/shell.h" + +#include + +namespace fs = boost::filesystem; + +namespace anbox { +Daemon::Daemon() : + cmd{cli::Name{"anbox"}, cli::Usage{"anbox"}, cli::Description{"The Android in a Box runtime"}} { + + cmd.command(std::make_shared()) + .command(std::make_shared()) + .command(std::make_shared()); +} + +int Daemon::Run(const std::vector &arguments) +try { + ensure_data_path(); + return cmd.run({std::cin, std::cout, arguments}); +} +catch(std::exception &err) { + ERROR("%s", err.what()); +} + +void Daemon::ensure_data_path() { + if (!fs::is_directory(fs::path(config::data_path()))) + fs::create_directories(fs::path(config::data_path())); +} +} // namespace anbox diff --git a/src/anbox/daemon.h b/src/anbox/daemon.h new file mode 100644 index 0000000..5f9a14f --- /dev/null +++ b/src/anbox/daemon.h @@ -0,0 +1,41 @@ +/* + * Copyright (C) 2016 Simon Fels + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 3, as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranties of + * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + * + */ + +#ifndef ANBOX_DAEMON_H_ +#define ANBOX_DAEMON_H_ + +#include +#include + +#include "anbox/do_not_copy_or_move.h" +#include "anbox/cli.h" + +namespace anbox { +class Daemon : public DoNotCopyOrMove { +public: + Daemon(); + + int Run(const std::vector &arguments); + +private: + void ensure_data_path(); + + cli::CommandWithSubcommands cmd; +}; +} // namespace anbox + +#endif diff --git a/src/anbox/defer_action.h b/src/anbox/defer_action.h new file mode 100644 index 0000000..905a8ec --- /dev/null +++ b/src/anbox/defer_action.h @@ -0,0 +1,44 @@ +/* + * Copyright (C) 2016 Simon Fels + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 3, as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranties of + * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + * + */ + +#ifndef ANBOX_DEFER_ACTION_H_ +#define ANBOX_DEFER_ACTION_H_ + +#include + +#include "anbox/do_not_copy_or_move.h" + +namespace anbox { + +class DeferAction : public DoNotCopyOrMove { +public: + DeferAction(const std::function action) : + action_(action) { + } + + ~DeferAction() { + if (action_) + action_(); + } + +private: + std::function action_; +}; + +} // namespace anbox + +#endif diff --git a/src/anbox/do_not_copy_or_move.h b/src/anbox/do_not_copy_or_move.h new file mode 100644 index 0000000..e7a9508 --- /dev/null +++ b/src/anbox/do_not_copy_or_move.h @@ -0,0 +1,38 @@ +/* + * Copyright (C) 2016 Simon Fels + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 3, as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranties of + * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + * + */ + +#ifndef ANBOX_DO_NOT_COPY_OR_MOVE_H_ +#define ANBOX_DO_NOT_COPY_OR_MOVE_H_ + +namespace anbox { + +class DoNotCopyOrMove +{ +public: + DoNotCopyOrMove(const DoNotCopyOrMove&) = delete; + DoNotCopyOrMove(DoNotCopyOrMove&&) = delete; + virtual ~DoNotCopyOrMove() = default; + DoNotCopyOrMove& operator=(const DoNotCopyOrMove&) = delete; + DoNotCopyOrMove& operator=(DoNotCopyOrMove&&) = delete; + +protected: + DoNotCopyOrMove() = default; +}; + +} + +#endif diff --git a/src/anbox/graphics/gl_renderer_server.cpp b/src/anbox/graphics/gl_renderer_server.cpp new file mode 100644 index 0000000..f37908b --- /dev/null +++ b/src/anbox/graphics/gl_renderer_server.cpp @@ -0,0 +1,79 @@ +/* + * Copyright (C) 2016 Simon Fels + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 3, as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranties of + * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + * + */ + +#include "anbox/logger.h" +#include "anbox/graphics/gl_renderer_server.h" + +#include "OpenglRender/render_api.h" + +#include +#include +#include + +namespace anbox { +namespace graphics { +GLRendererServer::GLRendererServer() { + // Force the host EGL/GLES libraries as translator implementation + ::setenv("ANDROID_EGL_LIB", "libEGL.so.1", 1); + ::setenv("ANDROID_GLESv1_LIB", "libGLESv1_CM.so.1", 1); + ::setenv("ANDROID_GLESv2_LIB", "libGLESv2.so.2", 1); + + if (!initLibrary()) + BOOST_THROW_EXCEPTION(std::runtime_error("Failed to initialize OpenGL renderer")); + + setStreamMode(RENDER_API_STREAM_MODE_UNIX); +} + +GLRendererServer::~GLRendererServer() { + destroyOpenGLSubwindow(); + stopOpenGLRenderer(); +} + +void logger_write(const char *format, ...) { + char message[2048]; + va_list args; + + va_start(args, format); + vsnprintf(message, sizeof(message) - 1, format, args); + va_end(args); + + DEBUG("%s", message); +} + +void GLRendererServer::start() { + emugl_logger_struct log_funcs; + log_funcs.coarse = logger_write; + log_funcs.fine = logger_write; + + char server_addr[256] = { 0 }; + if (!initOpenGLRenderer(1080, 1920, true, server_addr, sizeof(server_addr), log_funcs, logger_write)) + BOOST_THROW_EXCEPTION(std::runtime_error("Failed to setup OpenGL renderer")); + + socket_path_ = server_addr; + DEBUG("socket path %s", socket_path_); + + // Create the window we use for rendering the output we get from the + // Android container. This will internally construct a Mir surface + // and use the host EGL/GLES libraries for rendering. + showOpenGLSubwindow(0, 0, 0, 1080, 1920, 1080, 1920, 1.0f, 0); +} + +std::string GLRendererServer::socket_path() const { + return socket_path_; +} +} // namespace graphics +} // namespace anbox diff --git a/src/anbox/graphics/gl_renderer_server.h b/src/anbox/graphics/gl_renderer_server.h new file mode 100644 index 0000000..8eb1108 --- /dev/null +++ b/src/anbox/graphics/gl_renderer_server.h @@ -0,0 +1,42 @@ +/* + * Copyright (C) 2016 Simon Fels + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 3, as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranties of + * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + * + */ + +#ifndef ANBOX_GRAPHICS_GL_RENDERER_SERVER_H_ +#define ANBOX_GRAPHICS_GL_RENDERER_SERVER_H_ + +#include + +namespace anbox { +namespace graphics { + +class GLRendererServer { +public: + GLRendererServer(); + ~GLRendererServer(); + + void start(); + + std::string socket_path() const; + +private: + std::string socket_path_; +}; + +} // namespace graphics +} // namespace anbox + +#endif diff --git a/src/anbox/graphics/opengles_message_processor.cpp b/src/anbox/graphics/opengles_message_processor.cpp new file mode 100644 index 0000000..37d81bc --- /dev/null +++ b/src/anbox/graphics/opengles_message_processor.cpp @@ -0,0 +1,64 @@ +/* + * Copyright (C) 2016 Simon Fels + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 3, as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranties of + * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + * + */ + +#include "anbox/logger.h" +#include "anbox/graphics/opengles_message_processor.h" +#include "anbox/network/socket_messenger.h" +#include "anbox/network/connections.h" +#include "anbox/network/delegate_message_processor.h" + +namespace anbox { +namespace graphics { + +OpenGlesMessageProcessor::OpenGlesMessageProcessor(const std::string &renderer_socket_path, + const std::shared_ptr &rt, + const std::shared_ptr &messenger) : + client_messenger_(messenger) { + + connect_and_attach(renderer_socket_path, rt); +} + +OpenGlesMessageProcessor::~OpenGlesMessageProcessor() { +} + +void OpenGlesMessageProcessor::connect_and_attach(const std::string &socket_path, + const std::shared_ptr &rt) { + + auto socket = std::make_shared(rt->service()); + socket->connect(boost::asio::local::stream_protocol::endpoint(socket_path)); + + messenger_ = std::make_shared(socket); + renderer_ = std::make_shared( + messenger_, + 0, + std::make_shared>(), + std::make_shared([&](const std::vector &data) { + client_messenger_->send(reinterpret_cast(data.data()), data.size()); + return true; + })); + + renderer_->read_next_message(); +} + +bool OpenGlesMessageProcessor::process_data(const std::vector &data) { + messenger_->send(reinterpret_cast(data.data()), data.size()); + return true; +} + + +} // namespace graphics +} // namespace anbox diff --git a/src/anbox/graphics/opengles_message_processor.h b/src/anbox/graphics/opengles_message_processor.h new file mode 100644 index 0000000..05612cf --- /dev/null +++ b/src/anbox/graphics/opengles_message_processor.h @@ -0,0 +1,52 @@ +/* + * Copyright (C) 2016 Simon Fels + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 3, as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranties of + * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + * + */ + +#ifndef ANBOX_GRAPHICS_OPENGLES_MESSAGE_PROCESSOR_H_ +#define ANBOX_GRAPHICS_OPENGLES_MESSAGE_PROCESSOR_H_ + +#include + +#include + +#include "anbox/runtime.h" +#include "anbox/network/message_processor.h" +#include "anbox/network/socket_messenger.h" +#include "anbox/network/socket_connection.h" + +namespace anbox { +namespace graphics { +class OpenGlesMessageProcessor : public network::MessageProcessor { +public: + OpenGlesMessageProcessor(const std::string &renderer_socket_path, + const std::shared_ptr &rt, + const std::shared_ptr &messenger); + ~OpenGlesMessageProcessor(); + + bool process_data(const std::vector &data) override; + +private: + void connect_and_attach(const std::string &socket_path, + const std::shared_ptr &rt); + + std::shared_ptr client_messenger_; + std::shared_ptr messenger_; + std::shared_ptr renderer_; +}; +} // namespace graphics +} // namespace anbox + +#endif diff --git a/src/anbox/logger.cpp b/src/anbox/logger.cpp new file mode 100644 index 0000000..4b83bfe --- /dev/null +++ b/src/anbox/logger.cpp @@ -0,0 +1,154 @@ +/* + * Copyright (C) 2015 Canonical, Ltd. + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 3, as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranties of + * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + * + */ + +#include + +#define BOOST_LOG_DYN_LINK +#include +#include +#include +#include +#include +#include +#include + +#include "anbox/logger.h" + +namespace { +namespace attrs { +BOOST_LOG_ATTRIBUTE_KEYWORD(Severity, "anbox::Severity", anbox::Logger::Severity) +BOOST_LOG_ATTRIBUTE_KEYWORD(Location, "Location", anbox::Logger::Location) +BOOST_LOG_ATTRIBUTE_KEYWORD(Timestamp, "Timestamp", boost::posix_time::ptime) +} + +struct BoostLogLogger : public anbox::Logger { + BoostLogLogger() : + initialized_(false) { + } + + void Init(const anbox::Logger::Severity &severity = anbox::Logger::Severity::kWarning) override { + if (initialized_) + return; + + boost::log::formatter formatter = boost::log::expressions::stream + << "[" << attrs::Severity << " " + << boost::log::expressions::format_date_time< boost::posix_time::ptime >("Timestamp", "%Y-%m-%d %H:%M:%S") + << "] " + << boost::log::expressions::if_(boost::log::expressions::has_attr(attrs::Location)) + [ + boost::log::expressions::stream << "[" << attrs::Location << "] " + ] + << boost::log::expressions::smessage; + + boost::log::core::get()->remove_all_sinks(); + auto logger = boost::log::add_console_log(std::cout); + logger->set_formatter(formatter); + + // FIXME need to enable this once we found how we wrap this + // properly into our service architecture. For now left as + // it is. + boost::ignore_unused_variable_warning(severity); + // logger->set_filter(attrs::Severity < severity); + + initialized_ = true; + } + + void Log(Severity severity, const std::string& message, const boost::optional &loc) { + if (!initialized_) + Init(); + + if (auto rec = boost::log::trivial::logger::get().open_record()) { + boost::log::record_ostream out{rec}; + out << boost::log::add_value(attrs::Severity, severity) + << boost::log::add_value(attrs::Timestamp, boost::posix_time::microsec_clock::universal_time()) + << message; + + if (loc) { + // We have to pass in a temporary as boost::log (<= 1.55) expects a + // mutable reference to be passed to boost::log::add_value(...). + auto tmp = *loc; + out << boost::log::add_value(attrs::Location, tmp); + } + + boost::log::trivial::logger::get().push_record(std::move(rec)); + } + } + +private: + bool initialized_; +}; + +std::shared_ptr& MutableInstance() { + static std::shared_ptr instance{new BoostLogLogger()}; + return instance; +} + +void SetInstance(const std::shared_ptr& logger) { + MutableInstance() = logger; +} +} +namespace anbox { + +void Logger::Trace(const std::string& message, const boost::optional& location) { + Log(Severity::kTrace, message, location); +} + +void Logger::Debug(const std::string& message, const boost::optional& location) { + Log(Severity::kDebug, message, location); +} + +void Logger::Info(const std::string& message, const boost::optional& location) { + Log(Severity::kInfo, message, location); +} + +void Logger::Warning(const std::string& message, const boost::optional& location) { + Log(Severity::kWarning, message, location); +} + +void Logger::Error(const std::string& message, const boost::optional& location) { + Log(Severity::kError, message, location); +} + +void Logger::Fatal(const std::string& message, const boost::optional& location) { + Log(Severity::kFatal, message, location); +} + +std::ostream& operator<<(std::ostream& strm, anbox::Logger::Severity severity) { + switch (severity) { + case anbox::Logger::Severity::kTrace: return strm << "TT"; + case anbox::Logger::Severity::kDebug: return strm << "DD"; + case anbox::Logger::Severity::kInfo: return strm << "II"; + case anbox::Logger::Severity::kWarning: return strm << "WW"; + case anbox::Logger::Severity::kError: return strm << "EE"; + case anbox::Logger::Severity::kFatal: return strm << "FF"; + default: return strm << static_cast(severity); + } +} + +std::ostream& operator<<(std::ostream& out, const Logger::Location &location) { + return out << utils::string_format("%s:%d@%s", boost::filesystem::path(location.file).filename().string(), location.line, location.function); +} + +Logger& Log() { + return *MutableInstance(); +} + +void SetLogger(const std::shared_ptr& logger) { + SetInstance(logger); +} + +} // namespace anbox diff --git a/src/anbox/logger.h b/src/anbox/logger.h new file mode 100644 index 0000000..3604e8e --- /dev/null +++ b/src/anbox/logger.h @@ -0,0 +1,118 @@ +/* + * Copyright (C) 2015 Canonical, Ltd. + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 3, as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranties of + * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + * + */ + +#ifndef ANBOX_LOGGER_H_ +#define ANBOX_LOGGER_H_ + +#include + +#include +#include + +#include "anbox/do_not_copy_or_move.h" +#include "anbox/utils.h" + +namespace anbox { +// A Logger enables persisting of messages describing & explaining the +// state of the system. +class Logger : public DoNotCopyOrMove { +public: + // Severity enumerates all known severity levels + // applicable to log messages. + enum class Severity { + kTrace, + kDebug, + kInfo, + kWarning, + kError, + kFatal + }; + + // A Location describes the origin of a log message. + struct Location { + std::string file; // The name of the file that contains the log message. + std::string function; // The function that contains the log message. + std::uint32_t line; // The line in file that resulted in the log message. + }; + + virtual void Init(const Severity &severity = Severity::kWarning) = 0; + + virtual void Log(Severity severity, const std::string &message, const boost::optional& location) = 0; + + virtual void Trace(const std::string& message, const boost::optional& location = boost::optional{}); + virtual void Debug(const std::string& message, const boost::optional& location = boost::optional{}); + virtual void Info(const std::string& message, const boost::optional& location = boost::optional{}); + virtual void Warning(const std::string& message, const boost::optional& location = boost::optional{}); + virtual void Error(const std::string& message, const boost::optional& location = boost::optional{}); + virtual void Fatal(const std::string& message, const boost::optional& location = boost::optional{}); + + + template + void Tracef(const boost::optional& location, const std::string& pattern, T&&...args) { + Trace(utils::string_format(pattern, std::forward(args)...), location); + } + + template + void Debugf(const boost::optional& location, const std::string& pattern, T&&...args) { + Debug(utils::string_format(pattern, std::forward(args)...), location); + } + + template + void Infof(const boost::optional& location, const std::string& pattern, T&&...args) { + Info(utils::string_format(pattern, std::forward(args)...), location); + } + + template + void Warningf(const boost::optional& location, const std::string& pattern, T&&...args) { + Warning(utils::string_format(pattern, std::forward(args)...), location); + } + + template + void Errorf(const boost::optional& location, const std::string& pattern, T&&...args) { + Error(utils::string_format(pattern, std::forward(args)...), location); + } + + template + void Fatalf(const boost::optional& location, const std::string& pattern, T&&...args) { + Fatal(utils::string_format(pattern, std::forward(args)...), location); + } + +protected: + Logger() = default; +}; + +// operator<< inserts severity into out. +std::ostream& operator<<(std::ostream& out, Logger::Severity severity); + +// operator<< inserts location into out. +std::ostream& operator<<(std::ostream& out, const Logger::Location &location); + +// Log returns the mcs-wide configured logger instance. +// Save to call before/after main. +Logger& Log(); +// SetLog installs the given logger as mcs-wide default logger. +void SetLogger(const std::shared_ptr& logger); +} + +#define TRACE(...) anbox::Log().Tracef(anbox::Logger::Location{__FILE__, __FUNCTION__, __LINE__}, __VA_ARGS__) +#define DEBUG(...) anbox::Log().Debugf(anbox::Logger::Location{__FILE__, __FUNCTION__, __LINE__}, __VA_ARGS__) +#define INFO(...) anbox::Log().Infof(anbox::Logger::Location{__FILE__, __FUNCTION__, __LINE__}, __VA_ARGS__) +#define WARNING(...) anbox::Log().Warningf(anbox::Logger::Location{__FILE__, __FUNCTION__, __LINE__}, __VA_ARGS__) +#define ERROR(...) anbox::Log().Errorf(anbox::Logger::Location{__FILE__, __FUNCTION__, __LINE__}, __VA_ARGS__) +#define FATAL(...) anbox::Log().Fatalf(anbox::Logger::Location{__FILE__, __FUNCTION__, __LINE__}, __VA_ARGS__) + +#endif diff --git a/src/anbox/network/connection_context.cpp b/src/anbox/network/connection_context.cpp new file mode 100644 index 0000000..b9b74f8 --- /dev/null +++ b/src/anbox/network/connection_context.cpp @@ -0,0 +1,31 @@ +/* + * Copyright (C) 2016 Simon Fels + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 3, as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranties of + * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + * + */ + +#include "anbox/network/connection_context.h" + +namespace anbox { +namespace network { + +ConnectionContext::ConnectionContext( + std::function const connect_handler, + Connector const* connector) : + connect_handler(connect_handler), + connector(connector) { +} + +} // namespace anbox +} // namespace network diff --git a/src/anbox/network/connection_context.h b/src/anbox/network/connection_context.h new file mode 100644 index 0000000..40413bf --- /dev/null +++ b/src/anbox/network/connection_context.h @@ -0,0 +1,48 @@ +/* + * Copyright © 2014 Canonical Ltd. + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 3, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * Authored by: Alan Griffiths + */ + +#ifndef ANBOX_NETWORK_CONNECTION_CONTEXT_H_ +#define ANBOX_NETWORK_CONNECTION_CONTEXT_H_ + +#include +#include + +namespace anbox { +namespace network { +class Connector; + +class ConnectionContext +{ +public: + ConnectionContext(Connector const* connector) : + ConnectionContext([](){}, connector) {} + ConnectionContext( + std::function const connect_handler, + Connector const* connector); + + int fd_for_new_client(std::function const& connect_handler) const; + void handle_client_connect() const { connect_handler(); } + +private: + std::function const connect_handler; + Connector const* const connector; +}; +} // namespace anbox +} // namespace network + +#endif diff --git a/src/anbox/network/connection_creator.cpp b/src/anbox/network/connection_creator.cpp new file mode 100644 index 0000000..d177d5e --- /dev/null +++ b/src/anbox/network/connection_creator.cpp @@ -0,0 +1,16 @@ +/* + * Copyright (C) 2016 Simon Fels + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 3, as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranties of + * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + * + */ diff --git a/src/anbox/network/connection_creator.h b/src/anbox/network/connection_creator.h new file mode 100644 index 0000000..5125653 --- /dev/null +++ b/src/anbox/network/connection_creator.h @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2016 Simon Fels + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 3, as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranties of + * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + * + */ + +#ifndef ANBOX_NETWORK_CONNECTION_CREATOR_H_ +#define ANBOX_NETWORK_CONNECTION_CREATOR_H_ + +#include + +#include + +#include "anbox/do_not_copy_or_move.h" +namespace anbox { +namespace network { +class ConnectionCreator : public DoNotCopyOrMove { +public: + virtual void create_connection_for( + std::shared_ptr const& socket) = 0; +}; +} // namespace anbox +} // namespace network +#endif diff --git a/src/anbox/network/connections.h b/src/anbox/network/connections.h new file mode 100644 index 0000000..a98d658 --- /dev/null +++ b/src/anbox/network/connections.h @@ -0,0 +1,71 @@ +/* + * Copyright © 2012-2014 Canonical Ltd. + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 3, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * Authored by: Alan Griffiths + */ + +#ifndef ANBOX_NETWORK_CONNECTIONS_H_ +#define ANBOX_NETWORK_CONNECTIONS_H_ + +#include +#include +#include + +namespace anbox { +namespace network { +template +class Connections +{ +public: + Connections() {} + ~Connections() { clear(); } + + void add(std::shared_ptr const& connection) + { + std::unique_lock lock(mutex); + connections[connection->id()] = connection; + } + + void remove(int id) + { + std::unique_lock lock(mutex); + connections.erase(id); + } + + bool includes(int id) const + { + std::unique_lock lock(mutex); + return connections.find(id) != connections.end(); + } + + void clear() + { + std::unique_lock lock(mutex); + connections.clear(); + } + + +private: + Connections(Connections const&) = delete; + Connections& operator =(Connections const&) = delete; + + std::mutex mutex; + std::map> connections; +}; +} // namespace anbox +} // namespace network + + +#endif diff --git a/src/anbox/network/connector.h b/src/anbox/network/connector.h new file mode 100644 index 0000000..e624f49 --- /dev/null +++ b/src/anbox/network/connector.h @@ -0,0 +1,29 @@ +/* + * Copyright (C) 2016 Simon Fels + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 3, as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranties of + * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + * + */ + +#ifndef ANBOX_NETWORK_CONNECTOR_H +#define ANBOX_NETWORK_CONNECTOR_H + +namespace anbox { +namespace network { +class Connector { +public: +}; +} // namespace network +} // namespace anbox + +#endif diff --git a/src/anbox/network/delegate_message_processor.h b/src/anbox/network/delegate_message_processor.h new file mode 100644 index 0000000..43978b8 --- /dev/null +++ b/src/anbox/network/delegate_message_processor.h @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2016 Simon Fels + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 3, as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranties of + * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + * + */ + +#ifndef ANBOX_NETWORK_DELEGATE_MESSAGE_PROCESSOR_H_ +#define ANBOX_NETWORK_DELEGATE_MESSAGE_PROCESSOR_H_ + +#include "anbox/network/message_processor.h" + +#include + +namespace anbox { +namespace network { + +class DelegateMessageProcessor : public MessageProcessor { +public: + DelegateMessageProcessor(std::function&)> delegate) : + delegate_(delegate) { + } + + bool process_data(const std::vector &data) override { + if (!delegate_) + return false; + + return delegate_(data); + } + +private: + std::function&)> delegate_; +}; + +} // namespace network +} // namespace anbox + +#endif diff --git a/src/anbox/network/fd_socket_transmission.cpp b/src/anbox/network/fd_socket_transmission.cpp new file mode 100644 index 0000000..656ff76 --- /dev/null +++ b/src/anbox/network/fd_socket_transmission.cpp @@ -0,0 +1,192 @@ +/* + * Copyright © 2014 Canonical Ltd. + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License version 3, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + * + * Authored by: Kevin DuBois + */ + +#include "anbox/network/fd_socket_transmission.h" +#include "anbox/common/variable_length_array.h" + +#include +#include +#include +#include +#include +#include + +namespace anbox { +socket_error::socket_error(std::string const& message) : + std::system_error(errno, std::system_category(), message) +{ +} + +socket_disconnected_error::socket_disconnected_error(std::string const& message) : + std::system_error(errno, std::system_category(), message) +{ +} + +fd_reception_error::fd_reception_error(std::string const& message) : + std::runtime_error(message) +{ +} + +void send_fds( + Fd const& socket, + std::vector const& fds) +{ + if (fds.size() > 0) + { + // We send dummy data + struct iovec iov; + char dummy_iov_data = 'M'; + iov.iov_base = &dummy_iov_data; + iov.iov_len = 1; + + // Allocate space for control message + static auto const builtin_n_fds = 5; + static auto const builtin_cmsg_space = CMSG_SPACE(builtin_n_fds * sizeof(int)); + auto const fds_bytes = fds.size() * sizeof(int); + VariableLengthArray control{CMSG_SPACE(fds_bytes)}; + // Silence valgrind uninitialized memory complaint + memset(control.data(), 0, control.size()); + + // Message to send + struct msghdr header; + header.msg_name = NULL; + header.msg_namelen = 0; + header.msg_iov = &iov; + header.msg_iovlen = 1; + header.msg_controllen = control.size(); + header.msg_control = control.data(); + header.msg_flags = 0; + + // Control message contains file descriptors + struct cmsghdr *message = CMSG_FIRSTHDR(&header); + message->cmsg_len = CMSG_LEN(fds_bytes); + message->cmsg_level = SOL_SOCKET; + message->cmsg_type = SCM_RIGHTS; + + int* const data = reinterpret_cast(CMSG_DATA(message)); + int i = 0; + for (auto& fd : fds) + data[i++] = fd; + + auto const sent = sendmsg(socket, &header, MSG_NOSIGNAL); + if (sent < 0) + BOOST_THROW_EXCEPTION(std::runtime_error("Failed to send fds: " + std::string(strerror(errno)))); + } +} + +bool socket_error_is_transient(int error_code) +{ + return (error_code == EINTR); +} + +void receive_data(Fd const& socket, void* buffer, size_t bytes_requested, std::vector& fds) +{ + if (bytes_requested == 0) + BOOST_THROW_EXCEPTION(std::logic_error("Attempted to receive 0 bytes")); + + size_t bytes_read{0}; + unsigned fds_read{0}; + while (bytes_read < bytes_requested) + { + // Store the data in the buffer requested + struct iovec iov; + iov.iov_base = static_cast(buffer) + bytes_read; + iov.iov_len = bytes_requested - bytes_read; + + // Allocate space for control message + static auto const builtin_n_fds = 5; + static auto const builtin_cmsg_space = CMSG_SPACE(builtin_n_fds * sizeof(int)); + auto const fds_bytes = (fds.size() - fds_read) * sizeof(int); + VariableLengthArray control{CMSG_SPACE(fds_bytes)}; + + // Message to read + struct msghdr header; + header.msg_name = NULL; + header.msg_namelen = 0; + header.msg_iov = &iov; + header.msg_iovlen = 1; + header.msg_controllen = control.size(); + header.msg_control = control.data(); + header.msg_flags = 0; + + ssize_t const result = recvmsg(socket, &header, MSG_NOSIGNAL | MSG_WAITALL); + if (result == 0) + BOOST_THROW_EXCEPTION(socket_disconnected_error("Failed to read message from server: server has shutdown")); + if (result < 0) + { + if (socket_error_is_transient(errno)) + continue; + if (errno == EAGAIN) + continue; + if (errno == EPIPE) + BOOST_THROW_EXCEPTION( + boost::enable_error_info(socket_disconnected_error("Failed to read message from server")) + << boost::errinfo_errno(errno)); + + BOOST_THROW_EXCEPTION( + boost::enable_error_info(socket_error("Failed to read message from server")) + << boost::errinfo_errno(errno)); + } + + bytes_read += result; + + // If we get a proper control message, copy the received + // file descriptors back to the caller + struct cmsghdr const* const cmsg = CMSG_FIRSTHDR(&header); + if (cmsg) + { + if ((cmsg->cmsg_level == SOL_SOCKET) && (cmsg->cmsg_type == SCM_CREDENTIALS)) + BOOST_THROW_EXCEPTION(fd_reception_error("received SCM_CREDENTIALS when expecting fd")); + if (cmsg->cmsg_level != SOL_SOCKET || cmsg->cmsg_type != SCM_RIGHTS) + BOOST_THROW_EXCEPTION(fd_reception_error("Invalid control message for receiving file descriptors")); + + int const* const data = reinterpret_castCMSG_DATA(cmsg); + ptrdiff_t const header_size = reinterpret_cast(data) - reinterpret_cast(cmsg); + int const nfds = (cmsg->cmsg_len - header_size) / sizeof(int); + + // NOTE: This relies on the file descriptor cmsg being read + // (and written) atomically. + if (cmsg->cmsg_len > CMSG_LEN(fds_bytes) || (header.msg_flags & MSG_CTRUNC)) + { + for (int i = 0; i < nfds; i++) + ::close(data[i]); + BOOST_THROW_EXCEPTION(std::runtime_error("Received more fds than expected")); + } + + // We can't properly pass Fds through google::protobuf::Message, + // which is where these get shoved. + // + // When we have our own RPC generator plugin and aren't using deprecated + // Protobuf features this can go away. + for (int i = 0; i < nfds; i++) + fds[fds_read + i] = Fd{IntOwnedFd{data[i]}}; + + fds_read += nfds; + } + } + + if (fds_read < fds.size()) + { + for (auto fd : fds) + if (fd >= 0) + ::close(fd); + fds.clear(); + BOOST_THROW_EXCEPTION(std::runtime_error("Received fewer fds than expected")); + } +} +} // namespace anbox diff --git a/src/anbox/network/fd_socket_transmission.h b/src/anbox/network/fd_socket_transmission.h new file mode 100644 index 0000000..12454f3 --- /dev/null +++ b/src/anbox/network/fd_socket_transmission.h @@ -0,0 +1,50 @@ +/* + * Copyright © 2014 Canonical Ltd. + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License version 3, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + * + * Authored by: Kevin DuBois + */ + +#ifndef ANBOX_NETWORK_FD_SOCKET_TRANSMISSION_H_ +#define ANBOX_NETWORK_FD_SOCKET_TRANSMISSION_H_ + +#include "anbox/common/fd.h" + +#include +#include +#include + +namespace anbox +{ +struct socket_error : std::system_error +{ + socket_error(std::string const& message); +}; + +struct socket_disconnected_error : std::system_error +{ + socket_disconnected_error(std::string const& message); +}; + +struct fd_reception_error : std::runtime_error +{ + fd_reception_error(std::string const& message); +}; + +bool socket_error_is_transient(int error_code); +void send_fds(Fd const& socket, std::vector const& fd); +void receive_data(Fd const& socket, void* buffer, size_t bytes_requested, std::vector& fds); +} + +#endif diff --git a/src/anbox/network/message_processor.h b/src/anbox/network/message_processor.h new file mode 100644 index 0000000..5f0d5d1 --- /dev/null +++ b/src/anbox/network/message_processor.h @@ -0,0 +1,34 @@ +/* + * Copyright (C) 2016 Simon Fels + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 3, as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranties of + * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + * + */ + +#ifndef ANBOX_NETWORK_MESSAGE_PROCESSOR_H +#define ANBOX_NETWORK_MESSAGE_PROCESSOR_H + +#include +#include + +namespace anbox { +namespace network { +class MessageProcessor { +public: + virtual ~MessageProcessor() { } + virtual bool process_data(const std::vector &data) = 0; +}; +} // namespace network +} // namespace anbox + +#endif diff --git a/src/anbox/network/message_receiver.h b/src/anbox/network/message_receiver.h new file mode 100644 index 0000000..c7f7c3b --- /dev/null +++ b/src/anbox/network/message_receiver.h @@ -0,0 +1,48 @@ +/* + * Copyright © 2013-2014 Canonical Ltd. + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 3, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * Authored by: Kevin DuBois + */ + +#ifndef ANBOX_NETWORK_MESSAGE_RECEIVER_H_ +#define ANBOX_NETWORK_MESSAGE_RECEIVER_H_ + +#include +#include +#include + +#include "anbox/common/fd.h" + +namespace anbox { +namespace network { +class MessageReceiver +{ +public: + //receive message from the socket. 'handler' will be called when 'buffer' has been filled with exactly 'size' + typedef std::function AnboxReadHandler; + virtual void async_receive_msg(AnboxReadHandler const& handler, boost::asio::mutable_buffers_1 const& buffer) = 0; + virtual boost::system::error_code receive_msg(boost::asio::mutable_buffers_1 const& buffer) = 0; + virtual size_t available_bytes() = 0; + +protected: + MessageReceiver() = default; + virtual ~MessageReceiver() = default; + MessageReceiver(MessageReceiver const&) = delete; + MessageReceiver& operator=(MessageReceiver const&) = delete; +}; +} // namespace network +} // namespace anbox + +#endif diff --git a/src/anbox/network/message_sender.h b/src/anbox/network/message_sender.h new file mode 100644 index 0000000..750715d --- /dev/null +++ b/src/anbox/network/message_sender.h @@ -0,0 +1,40 @@ +/* + * Copyright © 2012 Canonical Ltd. + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 3, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * Authored by: Alan Griffiths + */ + +#ifndef ANBOX_NETWORK_MESSAGE_SENDER_H_ +#define ANBOX_NETWORK_MESSAGE_SENDER_H_ + +#include + +namespace anbox { +namespace network { +class MessageSender +{ +public: + virtual void send(char const* data, size_t length) = 0; + +protected: + MessageSender() = default; + virtual ~MessageSender() = default; + MessageSender(MessageSender const&) = delete; + MessageSender& operator=(MessageSender const&) = delete; +}; +} // namespace anbox +} // namespace network + +#endif diff --git a/src/anbox/network/published_socket_connector.cpp b/src/anbox/network/published_socket_connector.cpp new file mode 100644 index 0000000..aa0c4e7 --- /dev/null +++ b/src/anbox/network/published_socket_connector.cpp @@ -0,0 +1,124 @@ +/* + * Copyright (C) 2016 Simon Fels + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 3, as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranties of + * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + * + */ + +#include + +#include +#include +#include + +#include + +#include "anbox/network/published_socket_connector.h" +#include "anbox/network/connection_context.h" + +namespace { +bool socket_file_exists(std::string const& filename) +{ + struct stat statbuf; + bool exists = (0 == stat(filename.c_str(), &statbuf)); + /* Avoid removing non-socket files */ + bool is_socket_type = (statbuf.st_mode & S_IFMT) == S_IFSOCK; + return exists && is_socket_type; +} + +bool socket_exists(std::string const& socket_name) +{ + try + { + std::string socket_path{socket_name}; + + /* In case an abstract socket name exists with the same name*/ + socket_path.insert(std::begin(socket_path), ' '); + + /* If the name is contained in this table, it signifies + * a process is truly using that socket connection + */ + std::ifstream socket_names_file("/proc/net/unix"); + std::string line; + while (std::getline(socket_names_file, line)) + { + auto index = line.find(socket_path); + /* check for complete match */ + if (index != std::string::npos && + (index + socket_path.length()) == line.length()) + { + return true; + } + } + } + catch (...) + { + /* Assume the socket exists */ + return true; + } + return false; +} + +std::string remove_if_stale(std::string const& socket_name) +{ + if (socket_file_exists(socket_name) && !socket_exists(socket_name)) + { + if (std::remove(socket_name.c_str()) != 0) + { + BOOST_THROW_EXCEPTION( + boost::enable_error_info( + std::runtime_error("Failed removing stale socket file")) << boost::errinfo_errno(errno)); + } + } + return socket_name; +} +} + +namespace anbox { +namespace network { +PublishedSocketConnector::PublishedSocketConnector( + const std::string& socket_file, + const std::shared_ptr &rt, + const std::shared_ptr &connection_creator) : + socket_file_(remove_if_stale(socket_file)), + runtime_(rt), + connection_creator_(connection_creator), + acceptor_(rt->service(), socket_file_) { + + start_accept(); +} + +PublishedSocketConnector::~PublishedSocketConnector() { +} + +void PublishedSocketConnector::start_accept() { + auto socket = std::make_shared(runtime_->service()); + + acceptor_.async_accept( + *socket, + [this, socket](boost::system::error_code const& err) { + on_new_connection(socket, err); + }); +} + +void PublishedSocketConnector::on_new_connection( + std::shared_ptr const& socket, + boost::system::error_code const& err) { + + if (!err) + connection_creator_->create_connection_for(socket); + + start_accept(); +} +} // namespace network +} // namespace anbox diff --git a/src/anbox/network/published_socket_connector.h b/src/anbox/network/published_socket_connector.h new file mode 100644 index 0000000..b510fb8 --- /dev/null +++ b/src/anbox/network/published_socket_connector.h @@ -0,0 +1,56 @@ +/* + * Copyright (C) 2016 Simon Fels + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 3, as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranties of + * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + * + */ + +#ifndef ANBOX_PUBLISHEDSOCKETCONNECTOR_H +#define ANBOX_PUBLISHEDSOCKETCONNECTOR_H + +#include + +#include "anbox/do_not_copy_or_move.h" +#include "anbox/runtime.h" + +#include "anbox/network/connector.h" +#include "anbox/network/connection_creator.h" + +namespace anbox { +namespace network { +class PublishedSocketConnector : public DoNotCopyOrMove, + public Connector { +public: + explicit PublishedSocketConnector( + const std::string& socket_file, + const std::shared_ptr &rt, + const std::shared_ptr &connection_creator); + ~PublishedSocketConnector() noexcept; + + std::string socket_file() const { return socket_file_; } + +private: + void start_accept(); + void on_new_connection( + std::shared_ptr const& socket, + boost::system::error_code const& err); + + const std::string socket_file_; + std::shared_ptr runtime_; + std::shared_ptr connection_creator_; + boost::asio::local::stream_protocol::acceptor acceptor_; +}; +} // namespace network +} // namespace anbox + +#endif diff --git a/src/anbox/network/qemu_pipe_connection_creator.cpp b/src/anbox/network/qemu_pipe_connection_creator.cpp new file mode 100644 index 0000000..f4c5e49 --- /dev/null +++ b/src/anbox/network/qemu_pipe_connection_creator.cpp @@ -0,0 +1,103 @@ +/* + * Copyright (C) 2016 Simon Fels + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 3, as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranties of + * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + * + */ + +#include + +#include "anbox/logger.h" +#include "anbox/network/qemu_pipe_connection_creator.h" +#include "anbox/network/socket_messenger.h" +#include "anbox/graphics/opengles_message_processor.h" +#include "anbox/support/boot_properties_message_processor.h" +#include "anbox/support/null_message_processor.h" + +namespace ba = boost::asio; + +namespace anbox { +namespace network { +QemuPipeConnectionCreator::QemuPipeConnectionCreator(const std::shared_ptr &rt, + const std::string &renderer_socket_path) : + runtime_(rt), + next_connection_id_(0), + connections_(std::make_shared>()), + renderer_socket_path_(renderer_socket_path) { +} + +QemuPipeConnectionCreator::~QemuPipeConnectionCreator() { +} + +void QemuPipeConnectionCreator::create_connection_for( + std::shared_ptr const& socket) { + + auto const messenger = std::make_shared(socket); + const auto type = identify_client(messenger); + auto const processor = create_processor(type, messenger); + if (!processor) + BOOST_THROW_EXCEPTION(std::runtime_error("Unhandled client type")); + + auto const& connection = std::make_shared(messenger, next_id(), connections_, processor); + connections_->add(connection); + connection->read_next_message(); + + DEBUG("id %i", connection->id()); +} + +QemuPipeConnectionCreator::client_type QemuPipeConnectionCreator::identify_client( + std::shared_ptr const& messenger) { + + // The client will identify itself as first thing by writing a string + // in the format 'pipe:[:]\0' to the channel. + std::vector buffer; + for (;;) { + unsigned char byte[1] = { 0 }; + const auto err = messenger->receive_msg(ba::buffer(byte, 1)); + if (err) + break; + buffer.push_back(byte[0]); + if (byte[0] == 0x0) + break; + } + + std::string identifier_and_args = buffer.data(); + + DEBUG("identifier %s", identifier_and_args); + + if (utils::string_starts_with(identifier_and_args, "pipe:opengles")) + return client_type::opengles; + // Even if 'boot-properties' is an argument to the service 'qemud' here we + // take this as a own service instance as that is what it is. + else if (utils::string_starts_with(identifier_and_args, "pipe:qemud:boot-properties")) + return client_type::qemud_boot_properties; + + return client_type::invalid; +} + +std::shared_ptr QemuPipeConnectionCreator::create_processor(const client_type &type, const std::shared_ptr &messenger) { + if (type == client_type::opengles) + return std::make_shared(renderer_socket_path_, runtime_, messenger); + else if (type == client_type::qemud_boot_properties) + return std::make_shared(messenger); + + return std::make_shared(); +} + +int QemuPipeConnectionCreator::next_id() +{ + return next_connection_id_.fetch_add(1); +} + +} // namespace anbox +} // namespace network diff --git a/src/anbox/network/qemu_pipe_connection_creator.h b/src/anbox/network/qemu_pipe_connection_creator.h new file mode 100644 index 0000000..122ae28 --- /dev/null +++ b/src/anbox/network/qemu_pipe_connection_creator.h @@ -0,0 +1,67 @@ +/* + * Copyright (C) 2016 Simon Fels + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 3, as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranties of + * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + * + */ + +#ifndef ANBOX_NETWORK_QEMU_PIPE_CONNECTION_CREATOR_H_ +#define ANBOX_NETWORK_QEMU_PIPE_CONNECTION_CREATOR_H_ + +#include + +#include + +#include "anbox/do_not_copy_or_move.h" +#include "anbox/runtime.h" +#include "anbox/network/connections.h" +#include "anbox/network/socket_connection.h" +#include "anbox/network/connection_creator.h" +#include "anbox/network/socket_messenger.h" + +namespace anbox { +namespace network { + +class QemuPipeConnectionCreator : public ConnectionCreator { +public: + QemuPipeConnectionCreator( + const std::shared_ptr &rt, + const std::string &renderer_socket_path); + ~QemuPipeConnectionCreator() noexcept; + + void create_connection_for( + std::shared_ptr const& socket) override; + + enum class client_type { + invalid, + opengles, + qemud_boot_properties, + }; + +private: + int next_id(); + + client_type identify_client(std::shared_ptr const& messenger); + std::shared_ptr create_processor( + const client_type &type, const std::shared_ptr &messenger); + + std::shared_ptr runtime_; + std::atomic next_connection_id_; + std::shared_ptr> const connections_; + + std::string renderer_socket_path_; +}; +} // namespace anbox +} // namespace network + +#endif diff --git a/src/anbox/network/socket_connection.cpp b/src/anbox/network/socket_connection.cpp new file mode 100644 index 0000000..80c37d5 --- /dev/null +++ b/src/anbox/network/socket_connection.cpp @@ -0,0 +1,88 @@ +/* + * Copyright © 2012 Canonical Ltd. + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 3, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * Authored by: Alan Griffiths + */ + +#include "anbox/logger.h" + +#include "anbox/network/socket_connection.h" +#include "anbox/network/message_sender.h" +#include "anbox/network/message_receiver.h" + +#include +#include + +#include + +#include +#include + +namespace ba = boost::asio; +namespace bs = boost::system; + +namespace anbox { +namespace network { +SocketConnection::SocketConnection( + std::shared_ptr const& message_receiver, + int id_, + std::shared_ptr> const& connections, + std::shared_ptr const& processor) + : message_receiver_(message_receiver), + id_(id_), + connections_(connections), + processor_(processor) +{ +} + +SocketConnection::~SocketConnection() noexcept +{ +} + +void SocketConnection::read_next_message() +{ + auto callback = std::bind(&SocketConnection::on_read_size, + this, std::placeholders::_1, std::placeholders::_2); + message_receiver_->async_receive_msg(callback, ba::buffer(buffer_)); +} + +void SocketConnection::on_read_size(const boost::system::error_code& error, std::size_t bytes_read) +{ + if (error) + { + connections_->remove(id()); + BOOST_THROW_EXCEPTION(std::runtime_error(error.message())); + } + + std::vector data(bytes_read); + std::copy(buffer_.data(), buffer_.data() + bytes_read, data.data()); + + if (processor_->process_data(data)) + read_next_message(); + else + connections_->remove(id()); +} + +void SocketConnection::on_response_sent(bs::error_code const& error, std::size_t) +{ + if (error) + { + connections_->remove(id()); + BOOST_THROW_EXCEPTION(std::runtime_error(error.message())); + } +} +} // namespace anbox +} // namespace network + diff --git a/src/anbox/network/socket_connection.h b/src/anbox/network/socket_connection.h new file mode 100644 index 0000000..ad619b0 --- /dev/null +++ b/src/anbox/network/socket_connection.h @@ -0,0 +1,60 @@ +/* + * Copyright © 2012-2014 Canonical Ltd. + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 3, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * Authored by: Alan Griffiths + */ + +#ifndef ANBOX_NETWORK_SOCKET_CONNECTION_H_ +#define ANBOX_NETWORK_SOCKET_CONNECTION_H_ + +#include "anbox/network/connections.h" +#include "anbox/network/message_receiver.h" +#include "anbox/network/message_processor.h" + +#include + +#include + +namespace anbox { +namespace network { +class SocketConnection +{ +public: + SocketConnection( + std::shared_ptr const& message_receiver, + int id, + std::shared_ptr> const& connections, + std::shared_ptr const& processor); + + ~SocketConnection() noexcept; + + int id() const { return id_; } + + void read_next_message(); + +private: + void on_response_sent(boost::system::error_code const& error, std::size_t); + void on_read_size(const boost::system::error_code& ec, std::size_t bytes_read); + + std::shared_ptr const message_receiver_; + int const id_; + std::shared_ptr> const connections_; + std::shared_ptr const processor_; + std::array buffer_; +}; +} // namespace anbox +} // namespace network + +#endif diff --git a/src/anbox/network/socket_messenger.cpp b/src/anbox/network/socket_messenger.cpp new file mode 100644 index 0000000..e8885df --- /dev/null +++ b/src/anbox/network/socket_messenger.cpp @@ -0,0 +1,99 @@ +/* + * Copyright © 2013-2014 Canonical Ltd. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 3 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * Authored by: Kevin DuBois + */ + +#include "anbox/network/socket_messenger.h" +#include "anbox/common/variable_length_array.h" +#include "anbox/logger.h" + +#include + +#include +#include + +#include + +namespace bs = boost::system; +namespace ba = boost::asio; + +namespace { +/// Buffers need to be big enough to support messages +unsigned int const serialization_buffer_size = 2048; +} + +namespace anbox { +namespace network { +SocketMessenger::SocketMessenger(std::shared_ptr const& socket) + : socket(socket), + socket_fd{IntOwnedFd{socket->native_handle()}} +{ + socket->non_blocking(true); + boost::asio::socket_base::send_buffer_size option(64*1024); + socket->set_option(option); +} + +void SocketMessenger::send(char const* data, size_t length) +{ + VariableLengthArray whole_message{length}; + std::copy(data, data + length, whole_message.data()); + + try { + std::unique_lock lg(message_lock); + ba::write(*socket, + ba::buffer(whole_message.data(), whole_message.size()), + boost::asio::transfer_all()); + } + catch (std::exception &err) { + ERROR("Failed to write message: %s", err.what()); + } +} + +void SocketMessenger::async_receive_msg( + AnboxReadHandler const& handler, + ba::mutable_buffers_1 const& buffer) +{ + socket->async_read_some(buffer, handler); +} + +bs::error_code SocketMessenger::receive_msg( + ba::mutable_buffers_1 const& buffer) +{ + bs::error_code e; + size_t nread = 0; + + while (nread < ba::buffer_size(buffer)) + { + nread += boost::asio::read( + *socket, + ba::mutable_buffers_1{buffer + nread}, + e); + + if (e && e != ba::error::would_block) + break; + } + + return e; +} + +size_t SocketMessenger::available_bytes() +{ + boost::asio::socket_base::bytes_readable command{true}; + socket->io_control(command); + return command.get(); +} +} // namespace network +} // namespace anbox diff --git a/src/anbox/network/socket_messenger.h b/src/anbox/network/socket_messenger.h new file mode 100644 index 0000000..cb73a10 --- /dev/null +++ b/src/anbox/network/socket_messenger.h @@ -0,0 +1,51 @@ +/* + * Copyright © 2013-2014 Canonical Ltd. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 3 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * Authored by: Kevin DuBois + */ + +#ifndef ANBOX_NETWORK_SOCKET_MESSENGER_H_ +#define ANBOX_NETWORK_SOCKET_MESSENGER_H_ + +#include + +#include "anbox/common/fd_sets.h" +#include "anbox/network/message_sender.h" +#include "anbox/network/message_receiver.h" + +namespace anbox { +namespace network { +class SocketMessenger : public MessageSender, + public MessageReceiver { +public: + SocketMessenger(std::shared_ptr const& socket); + + void send(char const* data, size_t length); + + void async_receive_msg(AnboxReadHandler const& handle, boost::asio::mutable_buffers_1 const &buffer) override; + boost::system::error_code receive_msg(boost::asio::mutable_buffers_1 const& buffer) override; + size_t available_bytes() override; + + +private: + std::shared_ptr socket; + anbox::Fd socket_fd; + + std::mutex message_lock; +}; +} // namespace network +} // namespace anbox + +#endif diff --git a/src/anbox/network/stream_socket_transport.cpp b/src/anbox/network/stream_socket_transport.cpp new file mode 100644 index 0000000..aa52574 --- /dev/null +++ b/src/anbox/network/stream_socket_transport.cpp @@ -0,0 +1,74 @@ +/* + * Copyright (C) 2016 Simon Fels + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 3, as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranties of + * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + * + */ + +#include "anbox/common/variable_length_array.h" +#include "anbox/network/stream_socket_transport.h" +#include "anbox/network/fd_socket_transmission.h" + +#include + +#include +#include +#include +#include +#include + +#include +#include + +namespace anbox { +namespace network { + +StreamSocketTransport::StreamSocketTransport(const std::string &socket_path, + const std::shared_ptr &rt) : + socket_(std::make_shared(rt->service())), +{ + socket_.connect(boost::asio::local::stream_protocol::endpoint(socket_path)); + read_next_message(); +} + +void StreamSocketTransport::register_observer(std::shared_ptr const& observer) +{ + this->observer_ = observer; +} + +void StreamSocketTransport::unregister_observer(std::shared_ptr const& observer) +{ + if (this->observer_ != observer) + return; + + this->observer_.reset(); +} + +void StreamSocketTransport::send_message(std::vector const& buffer) +{ +} + +void StreamSocketTransport::read_next_message() { + auto callback = std::bind(&StreamSocketTransport::on_read_size, + this, + std::placeholders::_1, + std::placeholders::_2); + +} + +void StreamSocketTransport::on_read_size(const boost::system::error_code& ec, std::size_t bytes_read) { + +} + +} // namespace network +} // namespace anbox diff --git a/src/anbox/network/stream_socket_transport.h b/src/anbox/network/stream_socket_transport.h new file mode 100644 index 0000000..b2a9d4a --- /dev/null +++ b/src/anbox/network/stream_socket_transport.h @@ -0,0 +1,66 @@ +/* + * Copyright (C) 2016 Simon Fels + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 3, as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranties of + * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + * + */ + +#ifndef ANBOX_STREAM_SOCKET_TRANSPORT_H_ +#define ANBOX_STREAM_SOCKET_TRANSPORT_H_ + +#include "anbox/common/fd.h" +#include "anbox/runtime.h" +#include "anbox/network/socket_messenger.h" + +#include + +#include + +namespace anbox { +namespace network { +class StreamSocketTransport +{ +public: + class Observer { + public: + Observer() = default; + virtual ~Observer() = default; + + virtual void on_data_available() = 0; + virtual void on_disconnected() = 0; + + Observer(Observer const&) = delete; + Observer& operator=(Observer const&) = delete; + }; + + StreamSocketTransport(const std::string &socket_path, + const std::shared_ptr &rt); + + void register_observer(std::shared_ptr const& observer_); + void unregister_observer(std::shared_ptr const& observer_); + + void send_message(std::vector const& buffer); + void read_next_message(); + +private: + void on_read_size(const boost::system::error_code& ec, std::size_t bytes_read); + + std::shared_ptr observer_; + std::shared_ptr socket_; + SocketMessenger messenger_; +}; +} // namespace network +} // namespace anbox + + +#endif // STREAM_SOCKET_TRANSPORT_H_ diff --git a/src/anbox/optional.h b/src/anbox/optional.h new file mode 100644 index 0000000..c4a38c6 --- /dev/null +++ b/src/anbox/optional.h @@ -0,0 +1,32 @@ +/* + * Copyright (C) 2016 Canonical, Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; version 3. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + * + * Authored by: Thomas Voß + * + */ + +#ifndef ANBOX_OPTIONAL_H_ +#define ANBOX_OPTIONAL_H_ + +#include +#include + +namespace anbox +{ +template +using Optional = boost::optional; +} + +#endif diff --git a/src/anbox/pid_persister.cpp b/src/anbox/pid_persister.cpp new file mode 100644 index 0000000..c56ca27 --- /dev/null +++ b/src/anbox/pid_persister.cpp @@ -0,0 +1,45 @@ +/* + * Copyright (C) 2016 Simon Fels + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 3, as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranties of + * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + * + */ + +#include + +#include + +#include "anbox/config.h" +#include "anbox/utils.h" +#include "anbox/pid_persister.h" + +namespace fs = boost::filesystem; + +namespace anbox { + +PidPersister::PidPersister() : + path_(utils::string_format("%s/pid", config::data_path())) { + if (fs::exists(fs::path(path_))) + std::remove(path_.c_str()); + + std::ofstream file; + file.open(path_); + file << getpid(); + file.close(); +} + +PidPersister::~PidPersister() { + std::remove(path_.c_str()); +} + +} // namespace anbox diff --git a/src/anbox/pid_persister.h b/src/anbox/pid_persister.h new file mode 100644 index 0000000..fc7cfda --- /dev/null +++ b/src/anbox/pid_persister.h @@ -0,0 +1,34 @@ +/* + * Copyright (C) 2016 Simon Fels + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 3, as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranties of + * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + * + */ + +#ifndef ANBOX_PID_PERSISTER_H_ +#define ANBOX_PID_PERSISTER_H_ + +#include + +namespace anbox { +class PidPersister { +public: + PidPersister(); + ~PidPersister(); + +private: + std::string path_; +}; +} // namespace anbox + +#endif diff --git a/src/anbox/runtime.cpp b/src/anbox/runtime.cpp new file mode 100644 index 0000000..fac5976 --- /dev/null +++ b/src/anbox/runtime.cpp @@ -0,0 +1,100 @@ +/* + * Copyright (C) 2016 Simon Fels + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 3, as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranties of + * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + * + */ + +#include + +#include "anbox/logger.h" +#include "anbox/runtime.h" + +namespace +{ +// exception_safe_run runs service, catching all exceptions and +// restarting operation until an explicit shutdown has been requested. +// +// TODO(tvoss): Catching all exceptions is risky as they might signal unrecoverable +// errors. We should enable calling code to decide whether an exception should be considered +// fatal or not. +void exception_safe_run(boost::asio::io_service& service) { + while (true) { + try { + service.run(); + // a clean return from run only happens in case of + // stop() being called (we are keeping the service alive with + // a service::work instance). + break; + } + catch (const std::exception& e) { + std::cerr << "test" << e.what(); + } + catch (...) { + std::cerr << "Unknown exception caught while executing boost::asio::io_service"; + } + } +} +} +namespace anbox { + +std::shared_ptr Runtime::create(std::uint32_t pool_size) { + return std::shared_ptr(new Runtime(pool_size)); +} + +Runtime::Runtime(std::uint32_t pool_size) : + pool_size_{pool_size}, + service_{pool_size_}, + strand_{service_}, + keep_alive_{service_} { +} + +Runtime::~Runtime() { + try { + stop(); + } + catch(...) { + // Dropping all exceptions to satisfy the nothrow guarantee. + } +} + +void Runtime::start() { + for (unsigned int i = 0; i < pool_size_; i++) + workers_.push_back(std::thread{exception_safe_run, std::ref(service_)}); +} + +void Runtime::stop() { + service_.stop(); + + for (auto& worker : workers_) + if (worker.joinable()) + worker.join(); +} + +std::function)> Runtime::to_dispatcher_functional() +{ + // We have to make sure that we stay alive for as long as + // calling code requires the dispatcher to work. + auto sp = shared_from_this(); + return [sp](std::function task) + { + sp->strand_.post(task); + }; +} + +boost::asio::io_service& Runtime::service() { + return service_; +} + + +} // namespace anbox diff --git a/src/anbox/runtime.h b/src/anbox/runtime.h new file mode 100644 index 0000000..32dd684 --- /dev/null +++ b/src/anbox/runtime.h @@ -0,0 +1,77 @@ +/* + * Copyright (C) 2016 Simon Fels + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 3, as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranties of + * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + * + */ + +#ifndef ANBOX_RUNTIME_H_ +#define ANBOX_RUNTIME_H_ + +#include + +#include +#include +#include + +#include "anbox/do_not_copy_or_move.h" + +namespace anbox { + +// We bundle our "global" runtime dependencies here, specifically +// a dispatcher to decouple multiple in-process providers from one +// another , forcing execution to a well known set of threads. +class Runtime : public DoNotCopyOrMove, + public std::enable_shared_from_this { +public: + // Our default concurrency setup. + static constexpr const std::uint32_t worker_threads = 2; + + // create returns a Runtime instance with pool_size worker threads + // executing the underlying service. + static std::shared_ptr create(std::uint32_t pool_size = worker_threads); + + // Tears down the runtime, stopping all worker threads. + ~Runtime() noexcept(true); + + // start executes the underlying io_service on a thread pool with + // the size configured at creation time. + void start(); + + // stop cleanly shuts down a Runtime instance, + // joining all worker threads. + void stop(); + + // to_dispatcher_functional returns a function for integration + // with components that expect a dispatcher for operation. + std::function)> to_dispatcher_functional(); + + // service returns the underlying boost::asio::io_service that is executed + // by the Runtime. + boost::asio::io_service& service(); + +private: + // Runtime constructs a new instance, firing up pool_size + // worker threads. + Runtime(std::uint32_t pool_size); + + std::uint32_t pool_size_; + boost::asio::io_service service_; + boost::asio::io_service::strand strand_; + boost::asio::io_service::work keep_alive_; + std::vector workers_; +}; + +} // namespace anbox + +#endif diff --git a/src/anbox/support/boot_properties_message_processor.cpp b/src/anbox/support/boot_properties_message_processor.cpp new file mode 100644 index 0000000..e4120dd --- /dev/null +++ b/src/anbox/support/boot_properties_message_processor.cpp @@ -0,0 +1,113 @@ +/* + * Copyright (C) 2016 Simon Fels + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 3, as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranties of + * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + * + */ + +#include "anbox/logger.h" +#include "anbox/support/boot_properties_message_processor.h" +#include "anbox/network/socket_messenger.h" +#include "anbox/network/connections.h" +#include "anbox/network/delegate_message_processor.h" + +#include + +namespace ba = boost::asio; + +namespace { +static constexpr const long header_size{4}; +} + +namespace anbox { +namespace support { +BootPropertiesMessageProcessor::BootPropertiesMessageProcessor(const std::shared_ptr &messenger) : + messenger_(messenger) { +} + +BootPropertiesMessageProcessor::~BootPropertiesMessageProcessor() { +} + +bool BootPropertiesMessageProcessor::process_data(const std::vector &data) { + for (const auto &byte : data) + buffer_.push_back(byte); + + process_commands(); + + return true; +} + +void BootPropertiesMessageProcessor::process_commands() { + while (true) { + if (buffer_.size() < header_size) + break; + + char header[header_size] = { 0 }; + ::memcpy(header, buffer_.data(), header_size); + + unsigned int body_size = 0; + ::sscanf(header, "%04x", &body_size); + if (body_size != buffer_.size() - header_size) + break; + + std::string command; + // Make sure we only copy as much bytes as we have to and not more + command.insert(0, reinterpret_cast(buffer_.data()) + header_size, body_size); + + if (command == "list") + list_properties(); + else + DEBUG("Unknown command '%s'", command); + + const auto consumed = header_size + body_size; + buffer_.erase(buffer_.begin(), buffer_.begin() + consumed); + + const auto remaining = buffer_.size() - consumed; + if (remaining <= 0) + break; + } +} + +void BootPropertiesMessageProcessor::list_properties() { + std::vector properties = { + // Simple indicator to say we're anbox and if needed things in the container + // can adjust to this. Normally everything should detect we're qemu which + // should be enough for the simple cases. + "ro.anbox=1", + + "ro.hardware=goldfish", + + // Needed to let the gralloc HAL load the right implementation + "ro.kernel.qemu.gles=1", + + // Needed from different parts of the system in order to load + // the right implementation for qemu + "ro.kernel.qemu=1", + + // TODO(morphis): Using HDPI here for now but should be adjusted to the device + // we're running on. + "qemu.sf.lcd_density=240" + }; + + for (const auto &prop : properties) { + char header[header_size + 1]; + std::snprintf(header, header_size + 1, "%04x", prop.length()); + messenger_->send(header, header_size); + messenger_->send(prop.c_str(), prop.length()); + } + + // Send terminating NULL byte + messenger_->send(static_cast(""), 1); +} +} // namespace support +} // namespace anbox diff --git a/src/anbox/support/boot_properties_message_processor.h b/src/anbox/support/boot_properties_message_processor.h new file mode 100644 index 0000000..e5d2219 --- /dev/null +++ b/src/anbox/support/boot_properties_message_processor.h @@ -0,0 +1,49 @@ +/* + * Copyright (C) 2016 Simon Fels + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 3, as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranties of + * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + * + */ + +#ifndef ANBOX_SUPPORT_BOOT_PROPERTIES_MESSAGE_PROCESSOR_H_ +#define ANBOX_SUPPORT_BOOT_PROPERTIES_MESSAGE_PROCESSOR_H_ + +#include + +#include + +#include "anbox/runtime.h" +#include "anbox/network/message_processor.h" +#include "anbox/network/socket_messenger.h" +#include "anbox/network/socket_connection.h" + +namespace anbox { +namespace support { +class BootPropertiesMessageProcessor : public network::MessageProcessor { +public: + BootPropertiesMessageProcessor(const std::shared_ptr &messenger); + ~BootPropertiesMessageProcessor(); + + bool process_data(const std::vector &data) override; + +private: + void process_commands(); + void list_properties(); + + std::shared_ptr messenger_; + std::vector buffer_; +}; +} // namespace graphics +} // namespace anbox + +#endif diff --git a/src/anbox/support/hwcontrol_message_processor.cpp b/src/anbox/support/hwcontrol_message_processor.cpp new file mode 100644 index 0000000..8fcdd07 --- /dev/null +++ b/src/anbox/support/hwcontrol_message_processor.cpp @@ -0,0 +1,82 @@ +/* + * Copyright (C) 2016 Simon Fels + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 3, as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranties of + * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + * + */ + +#include "anbox/logger.h" +#include "anbox/support/hwcontrol_message_processor.h" +#include "anbox/network/socket_messenger.h" +#include "anbox/network/connections.h" + +#include + +namespace ba = boost::asio; + +namespace { +static constexpr const long header_size{4}; +} + +namespace anbox { +namespace support { +HwControlMessageProcessor::HwControlMessageProcessor(const std::shared_ptr &messenger) : + messenger_(messenger) { +} + +HwControlMessageProcessor::~HwControlMessageProcessor() { +} + +bool HwControlMessageProcessor::process_data(const std::vector &data) { + for (const auto &byte : data) + buffer_.push_back(byte); + + process_commands(); + + return true; +} + +void HwControlMessageProcessor::process_commands() { + while (true) { + if (buffer_.size() < header_size) + break; + + char header[header_size] = { 0 }; + ::memcpy(header, buffer_.data(), header_size); + + unsigned int body_size = 0; + ::sscanf(header, "%04x", &body_size); + if (body_size != buffer_.size() - header_size) + break; + + std::string command; + // Make sure we only copy as much bytes as we have to and not more + command.insert(0, reinterpret_cast(buffer_.data()) + header_size, body_size); + + if (command == "power:screen_state:wake") + DEBUG("Got screen wake command"); + else if (command == "power:screen_state:standby") + DEBUG("Got screen standby command"); + else + DEBUG("Unknown command '%s'", command); + + const auto consumed = header_size + body_size; + buffer_.erase(buffer_.begin(), buffer_.begin() + consumed); + + const auto remaining = buffer_.size() - consumed; + if (remaining <= 0) + break; + } +} +} // namespace support +} // namespace anbox diff --git a/src/anbox/support/hwcontrol_message_processor.h b/src/anbox/support/hwcontrol_message_processor.h new file mode 100644 index 0000000..45541be --- /dev/null +++ b/src/anbox/support/hwcontrol_message_processor.h @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2016 Simon Fels + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 3, as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranties of + * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + * + */ + +#ifndef ANBOX_SUPPORT_HWCONTROL_MESSAGE_PROCESSOR_H_ +#define ANBOX_SUPPORT_HWCONTROL_MESSAGE_PROCESSOR_H_ + +#include + +#include + +#include "anbox/runtime.h" +#include "anbox/network/message_processor.h" +#include "anbox/network/socket_messenger.h" +#include "anbox/network/socket_connection.h" + +namespace anbox { +namespace support { +class HwControlMessageProcessor : public network::MessageProcessor { +public: + HwControlMessageProcessor(const std::shared_ptr &messenger); + ~HwControlMessageProcessor(); + + bool process_data(const std::vector &data) override; + +private: + void process_commands(); + + std::shared_ptr messenger_; + std::vector buffer_; +}; +} // namespace graphics +} // namespace anbox + +#endif diff --git a/src/anbox/support/null_message_processor.cpp b/src/anbox/support/null_message_processor.cpp new file mode 100644 index 0000000..5efa21d --- /dev/null +++ b/src/anbox/support/null_message_processor.cpp @@ -0,0 +1,34 @@ +/* + * Copyright (C) 2016 Simon Fels + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 3, as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranties of + * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + * + */ + +#include "anbox/support/null_message_processor.h" + +#include + +namespace anbox { +namespace support { +NullMessageProcessor::NullMessageProcessor() { +} + +NullMessageProcessor::~NullMessageProcessor() { +} + +bool NullMessageProcessor::process_data(const std::vector&) { + return true; +} +} // namespace support +} // namespace anbox diff --git a/src/anbox/support/null_message_processor.h b/src/anbox/support/null_message_processor.h new file mode 100644 index 0000000..f46bb62 --- /dev/null +++ b/src/anbox/support/null_message_processor.h @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2016 Simon Fels + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 3, as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranties of + * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + * + */ + +#ifndef ANBOX_SUPPORT_NULL_MESSAGE_PROCESSOR_H_ +#define ANBOX_SUPPORT_NULL_MESSAGE_PROCESSOR_H_ + +#include + +#include + +#include "anbox/runtime.h" +#include "anbox/network/message_processor.h" + +namespace anbox { +namespace support { +class NullMessageProcessor : public network::MessageProcessor { +public: + NullMessageProcessor(); + ~NullMessageProcessor(); + + bool process_data(const std::vector &data) override; +}; +} // namespace graphics +} // namespace anbox + +#endif diff --git a/src/anbox/utils.cpp b/src/anbox/utils.cpp new file mode 100644 index 0000000..74f8882 --- /dev/null +++ b/src/anbox/utils.cpp @@ -0,0 +1,136 @@ +/* + * Copyright (C) 2016 Simon Fels + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 3, as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranties of + * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + * + */ + +#include +#include + +#include +#include +#include +#include + +#include + +#include "anbox/utils.h" + +namespace anbox { +namespace utils { + +std::vector collect_arguments(int argc, char **argv) { + std::vector result; + for (int i = 1; i < argc; i++) result.push_back(argv[i]); + return result; +} + +std::string read_file_if_exists_or_throw(const std::string &file_path) { + if (!boost::filesystem::is_regular(boost::filesystem::path(file_path))) + BOOST_THROW_EXCEPTION(std::runtime_error("File does not exist")); + + std::ifstream file; + file.open(file_path, std::ifstream::in); + std::string content; + file >> content; + file.close(); + return content; +} + +bool write_to_file(const std::string &file_path, const std::string &content) { + std::ofstream of; + of.open(file_path, std::ofstream::out); + of << content; + of.close(); + return true; +} + +int write_to_fd(int fd, const char *content, ssize_t len) { + while (len > 0) { + const auto res = write (fd, content, len); + if (res < 0 && errno == EINTR) + continue; + + if (res <= 0) { + if (res == 0) /* Unexpected short write, should not happen when writing to a file */ + errno = ENOSPC; + return -1; + } + + len -= res; + content += res; + } + return 0; +} + +int write_file_at(int dirfd, const char *path, const char *content) { + const auto fd = openat (dirfd, path, O_RDWR | O_CLOEXEC, 0); + if (fd == -1) + return -1; + + auto res = 0; + if (content) + res = write_to_fd (fd, content, strlen (content)); + + const auto errsv = errno; + close (fd); + errno = errsv; + + return res; +} + +bool string_starts_with(const std::string &text, const std::string &prefix) { + return text.compare(0, prefix.size(), prefix) == 0; +} + +std::string hex_dump(const uint8_t *data, uint32_t size) { + unsigned char buff[17]; + const uint8_t *pc = data; + std::stringstream buffer; + + if (size == 0) { + buffer << "NULL" << std::endl; + return buffer.str(); + } + + uint32_t i; + for (i = 0; i < size; i++) { + if ((i % 16) == 0) { + if (i != 0) + buffer << string_format(" %s", buff) << std::endl; + + buffer << string_format("%02x ", i); + } + + buffer << string_format(" %02x", static_cast(pc[i])); + + if ((pc[i] < 0x20) || (pc[i] > 0x7e)) + buff[i % 16] = '.'; + else + buff[i % 16] = pc[i]; + buff[(i % 16) + 1] = '\0'; + } + + while ((i % 16) != 0) { + buffer << " "; + i++; + } + + buffer << string_format(" %s", buff) << std::endl; + + return buffer.str(); +} + +} // namespace utils +} // namespace anbox diff --git a/src/anbox/utils.h b/src/anbox/utils.h new file mode 100644 index 0000000..d614fa3 --- /dev/null +++ b/src/anbox/utils.h @@ -0,0 +1,66 @@ +/* + * Copyright (C) 2016 Simon Fels + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 3, as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranties of + * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + * + */ + +#ifndef ANBOX_UTILS_H_ +#define ANBOX_UTILS_H_ + +#include + +#include +#include + +namespace anbox { +namespace utils { + +std::vector collect_arguments(int argc, char **argv); + +std::string read_file_if_exists_or_throw(const std::string &file_path); + +bool write_to_file(const std::string &file_path, const std::string &content = ""); +int write_to_fd(int fd, const char *content, ssize_t len); +int write_file_at(int dirfd, const char *path, const char *content); + +bool string_starts_with(const std::string &text, const std::string &prefix); + +std::string hex_dump(const uint8_t *data, uint32_t size); + +template +static std::string string_format(const std::string& fmt_str, Types&&... args); +} // namespace utils +} // namespace anbox + +namespace impl { +// Base case, just return the passed in boost::format instance. +inline boost::format& string_format(boost::format& f) +{ + return f; +} +// Sprintf recursively walks the parameter pack at compile time. +template +inline boost::format& string_format(boost::format& f, Head const& head, Tail&&... tail) { + return string_format(f % head, std::forward(tail)...); +} +} // namespace impl + +template +inline std::string anbox::utils::string_format(const std::string& format, Types&&... args) { + boost::format f(format); + return impl::string_format(f, std::forward(args)...).str(); +} + + +#endif diff --git a/src/anbox/version.cpp b/src/anbox/version.cpp new file mode 100644 index 0000000..8c0445f --- /dev/null +++ b/src/anbox/version.cpp @@ -0,0 +1,27 @@ +/* + * Copyright (C) 2016 Canonical, Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; version 3. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + * + * Authored by: Thomas Voß + * + */ + +#include "anbox/version.h" + +void anbox::version(std::uint32_t& major, std::uint32_t& minor, std::uint32_t& patch) +{ + major = anbox::build::version_major; + minor = anbox::build::version_minor; + patch = anbox::build::version_patch; +} diff --git a/src/anbox/version.h b/src/anbox/version.h new file mode 100644 index 0000000..7c1d11b --- /dev/null +++ b/src/anbox/version.h @@ -0,0 +1,42 @@ +/* + * Copyright (C) 2016 Canonical, Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; version 3. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + * + * Authored by: Thomas Voß + * + */ + +#ifndef ANBOX_VERSION_H_ +#define ANBOX_VERSION_H_ + +#include + +namespace anbox { +namespace build { +/// @brief version_major marks the major version of the library. The constant is meant to be used +/// by client code both at build and runtime, enabling version checks. +static constexpr const std::uint32_t version_major{1}; +/// @brief version_major marks the minor version of the library. The constant is meant to be used +/// by client code both at build and runtime, enabling version checks. +static constexpr const std::uint32_t version_minor{0}; +/// @brief version_patch marks the major version of the library. The constant is meant to be used +/// by client code both at build and runtime, enabling version checks. +static constexpr const std::uint32_t version_patch{1}; +} // namespace build + +/// @brief version queries the version of the library, placing the result in major, minor and patch. +void version(std::uint32_t& major, std::uint32_t& minor, std::uint32_t& patch); +} // namespace build + +#endif // ANBOX_VERSION_H_ diff --git a/src/main.cpp b/src/main.cpp new file mode 100644 index 0000000..78a5c79 --- /dev/null +++ b/src/main.cpp @@ -0,0 +1,24 @@ +/* + * Copyright (C) 2016 Simon Fels + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 3, as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranties of + * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + * + */ + +#include "anbox/daemon.h" +#include "anbox/utils.h" + +int main(int argc, char **argv) { + anbox::Daemon daemon; + return daemon.Run(anbox::utils::collect_arguments(argc, argv)); +} diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt new file mode 100644 index 0000000..e69de29